如何使WPF组合框具有XAML中最宽元素的宽度?

时间:2021-02-11 17:14:06

I know how to do it in code, but can this be done in XAML ?

我知道如何在代码中完成,但这能在XAML中完成吗?

Window1.xaml:

Window1.xaml:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <ComboBox Name="ComboBox1" HorizontalAlignment="Left" VerticalAlignment="Top">
            <ComboBoxItem>ComboBoxItem1</ComboBoxItem>
            <ComboBoxItem>ComboBoxItem2</ComboBoxItem>
        </ComboBox>
    </Grid>
</Window>

Window1.xaml.cs:

Window1.xaml.cs:

using System.Windows;
using System.Windows.Controls;

namespace WpfApplication1
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            double width = 0;
            foreach (ComboBoxItem item in ComboBox1.Items)
            {
                item.Measure(new Size(
                    double.PositiveInfinity, double.PositiveInfinity));
                if (item.DesiredSize.Width > width)
                    width = item.DesiredSize.Width;
            }
            ComboBox1.Measure(new Size(
                double.PositiveInfinity, double.PositiveInfinity));
            ComboBox1.Width = ComboBox1.DesiredSize.Width + width;
        }
    }
}

14 个解决方案

#1


28  

This can't be in XAML without either:

在XAML中,这两种情况都不可能出现:

  • Creating a hidden control (Alan Hunford's answer)
  • 创建一个隐藏控件(Alan Hunford的回答)
  • Changing the ControlTemplate drastically. Even in this case, a hidden version of an ItemsPresenter may need to be created.
  • 往ControlTemplate中加故事板动画大大改变。即使在这种情况下,也可能需要创建ItemsPresenter的隐藏版本。

The reason for this is that the default ComboBox ControlTemplates that I've come across (Aero, Luna, etc.) all nest the ItemsPresenter in a Popup. This means that the layout of these items is deferred until they are actually made visible.

原因是我遇到的默认ComboBox控件模板(Aero、Luna等)都将ItemsPresenter嵌在弹出窗口中。这意味着这些项目的布局被延迟,直到它们被实际显示。

An easy way to test this is to modify the default ControlTemplate to bind the MinWidth of the outermost container (it's a Grid for both Aero and Luna) to the ActualWidth of PART_Popup. You'll be able to have the ComboBox automatically synchronize it's width when you click the drop button, but not before.

一个简单的测试方法是修改默认的ControlTemplate,将最外层容器的最小宽度(它是Aero和Luna的网格)绑定到PART_Popup的实际宽度。您可以让ComboBox自动同步它的宽度,当您点击下拉按钮,但不是之前。

So unless you can force a Measure operation in the layout system (which you can do by adding a second control), I don't think it can be done.

因此,除非您能够在布局系统中强制执行度量操作(您可以通过添加第二个控件来实现),否则我认为这是不可能实现的。

As always, I'm open to an short, elegant solution -- but in this case a code-behind or dual-control/ControlTemplate hacks are the only solutions I have seen.

与往常一样,我对一个简短、优雅的解决方案持开放态度——但在这种情况下,我看到的唯一解决方案是代码隐藏或双重控制/控制模板修改。

#2


50  

You can't do it directly in Xaml but you can use this Attached Behavior. (The Width will be visible in the Designer)

你不能直接在Xaml中做,但是你可以使用这个附加的行为。(宽度将在设计器中可见)

<ComboBox behaviors:ComboBoxWidthFromItemsBehavior.ComboBoxWidthFromItems="True">
    <ComboBoxItem Content="Short"/>
    <ComboBoxItem Content="Medium Long"/>
    <ComboBoxItem Content="Min"/>
</ComboBox>

The Attached Behavior ComboBoxWidthFromItemsProperty

附加行为ComboBoxWidthFromItemsProperty

public static class ComboBoxWidthFromItemsBehavior
{
    public static readonly DependencyProperty ComboBoxWidthFromItemsProperty =
        DependencyProperty.RegisterAttached
        (
            "ComboBoxWidthFromItems",
            typeof(bool),
            typeof(ComboBoxWidthFromItemsBehavior),
            new UIPropertyMetadata(false, OnComboBoxWidthFromItemsPropertyChanged)
        );
    public static bool GetComboBoxWidthFromItems(DependencyObject obj)
    {
        return (bool)obj.GetValue(ComboBoxWidthFromItemsProperty);
    }
    public static void SetComboBoxWidthFromItems(DependencyObject obj, bool value)
    {
        obj.SetValue(ComboBoxWidthFromItemsProperty, value);
    }
    private static void OnComboBoxWidthFromItemsPropertyChanged(DependencyObject dpo,
                                                                DependencyPropertyChangedEventArgs e)
    {
        ComboBox comboBox = dpo as ComboBox;
        if (comboBox != null)
        {
            if ((bool)e.NewValue == true)
            {
                comboBox.Loaded += OnComboBoxLoaded;
            }
            else
            {
                comboBox.Loaded -= OnComboBoxLoaded;
            }
        }
    }
    private static void OnComboBoxLoaded(object sender, RoutedEventArgs e)
    {
        ComboBox comboBox = sender as ComboBox;
        Action action = () => { comboBox.SetWidthFromItems(); };
        comboBox.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
    }
}

What it does is that it calls an extension method for ComboBox called SetWidthFromItems which (invisibly) expands and collapses itself and then calculates the Width based on the generated ComboBoxItems. (IExpandCollapseProvider requires a reference to UIAutomationProvider.dll)

它所做的是为ComboBox调用名为SetWidthFromItems的扩展方法,该方法(无形)展开和折叠自身,然后根据生成的ComboBoxItems计算宽度。(iexpand折叠提供程序需要对UIAutomationProvider.dll的引用)

Then extension method SetWidthFromItems

然后扩展方法SetWidthFromItems

public static class ComboBoxExtensionMethods
{
    public static void SetWidthFromItems(this ComboBox comboBox)
    {
        double comboBoxWidth = 19;// comboBox.DesiredSize.Width;

        // Create the peer and provider to expand the comboBox in code behind. 
        ComboBoxAutomationPeer peer = new ComboBoxAutomationPeer(comboBox);
        IExpandCollapseProvider provider = (IExpandCollapseProvider)peer.GetPattern(PatternInterface.ExpandCollapse);
        EventHandler eventHandler = null;
        eventHandler = new EventHandler(delegate
        {
            if (comboBox.IsDropDownOpen &&
                comboBox.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
            {
                double width = 0;
                foreach (var item in comboBox.Items)
                {
                    ComboBoxItem comboBoxItem = comboBox.ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem;
                    comboBoxItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                    if (comboBoxItem.DesiredSize.Width > width)
                    {
                        width = comboBoxItem.DesiredSize.Width;
                    }
                }
                comboBox.Width = comboBoxWidth + width;
                // Remove the event handler. 
                comboBox.ItemContainerGenerator.StatusChanged -= eventHandler;
                comboBox.DropDownOpened -= eventHandler;
                provider.Collapse();
            }
        });
        comboBox.ItemContainerGenerator.StatusChanged += eventHandler;
        comboBox.DropDownOpened += eventHandler;
        // Expand the comboBox to generate all its ComboBoxItem's. 
        provider.Expand();
    }
}

This extension method also provides to ability to call

这个扩展方法还提供了调用的能力

comboBox.SetWidthFromItems();

in code behind (e.g in the ComboBox.Loaded event)

在代码后面(e。组合框的g。加载事件)

#3


10  

Yeah, this one is a bit nasty.

是的,这个有点恶心。

What I've done in the past is to add into the ControlTemplate a hidden listbox (with its itemscontainerpanel set to a grid) showing every item at the same time but with their visibility set to hidden.

我过去所做的是向ControlTemplate中添加一个隐藏的列表框(其itemscontainerpanel设置为一个网格),该列表框同时显示每个项目,但其可见性设置为hidden。

I'd be pleased to hear of any better ideas that don't rely on horrible code-behind or your view having to understand that it needs to use a different control to provide the width to support the visuals (yuck!).

我很高兴听到任何更好的想法,不依赖于可怕的代码隐藏或您的视图必须理解它需要使用不同的控件来提供宽度来支持视觉效果(呸!)

#4


6  

Based on the other answers above, here's my version:

基于以上其他答案,我的观点如下:

<Grid HorizontalAlignment="Left">
    <ItemsControl ItemsSource="{Binding EnumValues}" Height="0" Margin="15,0"/>
    <ComboBox ItemsSource="{Binding EnumValues}" />
</Grid>

HorizontalAlignment="Left" stops the controls using the full width of the containing control. Height="0" hides the items control.
Margin="15,0" allows for additional chrome around combo-box items (not chrome agnostic I'm afraid).

horizontalalign ="Left"使用包含控件的全宽度来停止控件。Height="0"隐藏项控件。Margin=“15,0”允许在combo-box项目周围添加额外的chrome(恐怕不是chrome不可知论者)。

#5


4  

I ended up with a "good enough" solution to this problem being to make the combo box never shrink below the largest size it held, similar to the old WinForms AutoSizeMode=GrowOnly.

最后,我找到了一个“足够好的”解决方案,使组合框永远不会缩小到最大大小以下,类似于旧的WinForms AutoSizeMode=GrowOnly。

The way I did this was with a custom value converter:

我使用自定义值转换器:

public class GrowConverter : IValueConverter
{
    public double Minimum
    {
        get;
        set;
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var dvalue = (double)value;
        if (dvalue > Minimum)
            Minimum = dvalue;
        else if (dvalue < Minimum)
            dvalue = Minimum;
        return dvalue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

Then I configure the combo box in XAML like so:

然后在XAML中配置组合框,如下所示:

 <Whatever>
        <Whatever.Resources>
            <my:GrowConverter x:Key="grow" />
        </Whatever.Resources>
        ...
        <ComboBox MinWidth="{Binding ActualWidth,RelativeSource={RelativeSource Self},Converter={StaticResource grow}}" />
    </Whatever>

Note that with this you need a separate instance of the GrowConverter for each combo box, unless of course you want a set of them to size together, similar to the Grid's SharedSizeScope feature.

需要注意的是,对于每个组合框,您需要一个单独的GrowConverter实例,除非您想要一组它们的大小,类似于网格的SharedSizeScope特性。

#6


2  

A follow up to Maleak's answer: I liked that implementation so much, I wrote an actual Behavior for it. Obviously you'll need the Blend SDK so you can reference System.Windows.Interactivity.

接下来是Maleak的回答:我非常喜欢这个实现,我为它编写了一个实际的行为。显然,您需要使用Blend SDK,以便您可以引用System.Windows.Interactivity。

XAML:

XAML:

    <ComboBox ItemsSource="{Binding ListOfStuff}">
        <i:Interaction.Behaviors>
            <local:ComboBoxWidthBehavior />
        </i:Interaction.Behaviors>
    </ComboBox>

Code:

代码:

using System;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Interactivity;

namespace MyLibrary
{
    public class ComboBoxWidthBehavior : Behavior<ComboBox>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.Loaded += OnLoaded;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.Loaded -= OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            var desiredWidth = AssociatedObject.DesiredSize.Width;

            // Create the peer and provider to expand the comboBox in code behind. 
            var peer = new ComboBoxAutomationPeer(AssociatedObject);
            var provider = peer.GetPattern(PatternInterface.ExpandCollapse) as IExpandCollapseProvider;
            if (provider == null)
                return;

            EventHandler[] handler = {null};    // array usage prevents access to modified closure
            handler[0] = new EventHandler(delegate
            {
                if (!AssociatedObject.IsDropDownOpen || AssociatedObject.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
                    return;

                double largestWidth = 0;
                foreach (var item in AssociatedObject.Items)
                {
                    var comboBoxItem = AssociatedObject.ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem;
                    if (comboBoxItem == null)
                        continue;

                    comboBoxItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                    if (comboBoxItem.DesiredSize.Width > largestWidth)
                        largestWidth = comboBoxItem.DesiredSize.Width;
                }

                AssociatedObject.Width = desiredWidth + largestWidth;

                // Remove the event handler.
                AssociatedObject.ItemContainerGenerator.StatusChanged -= handler[0];
                AssociatedObject.DropDownOpened -= handler[0];
                provider.Collapse();
            });

            AssociatedObject.ItemContainerGenerator.StatusChanged += handler[0];
            AssociatedObject.DropDownOpened += handler[0];

            // Expand the comboBox to generate all its ComboBoxItem's. 
            provider.Expand();
        }
    }
}

#7


0  

You can bind the Width any container you wish.

您可以绑定任何容器的宽度。

<Window x:Class="WpfApplication1.Window1"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Title="Window1" Height="300" Width="300" x:Name="Window1">
<Grid>
    <ComboBox 
       Name="ComboBox1"
       HorizontalAlignment="Left"
       VerticalAlignment="Top">
       <ComboBox.Width>
          <Binding ElementName="Window1" Path="ActualWidth"/>
       </ComboBox.Width>
          <ComboBoxItem>ComboBoxItem1</ComboBoxItem>
          <ComboBoxItem>ComboBoxItem2</ComboBoxItem>
    </ComboBox>
</Grid>

To get exactly what you are trying to do with the C# you wrote I would look at impmenting an IValueConverter or IMultiValueConverter.

为了准确地了解您正在尝试使用您所编写的c#,我将研究如何对IValueConverter或IMultiValueConverter进行修改。

#8


0  

Put an listbox containing the same content behind the dropbox. Then enforce correct height with some binding like this:

在dropbox后面放一个包含相同内容的列表框。然后用一些这样的约束加强正确的高度:

<Grid>
       <ListBox x:Name="listBox" Height="{Binding ElementName=dropBox, Path=DesiredSize.Height}" /> 
        <ComboBox x:Name="dropBox" />
</Grid>

#9


0  

In my case a much simpler way seemed to do the trick, I just used an extra stackPanel to wrap the combobox.

在我的例子中,一种简单得多的方法似乎可以达到目的,我只是使用了一个额外的堆栈面板来包装combobox。

<StackPanel Grid.Row="1" Orientation="Horizontal">
    <ComboBox ItemsSource="{Binding ExecutionTimesModeList}" Width="Auto"
        SelectedValuePath="Item" DisplayMemberPath="FriendlyName"
        SelectedValue="{Binding Model.SelectedExecutionTimesMode}" />    
</StackPanel>

(worked in visual studio 2008)

(在visual studio 2008工作)

#10


0  

I was looking for the answer myself, when I came across the UpdateLayout() method that every UIElement has.

我正在自己寻找答案,这时我遇到了每个UIElement都有的UpdateLayout()方法。

It's very simple now, thankfully!

现在很简单了,谢天谢地!

Just call ComboBox1.Updatelayout(); after you set or modify the ItemSource.

就叫ComboBox1.Updatelayout();设置或修改ItemSource之后。

#11


0  

As for me, the solution for expanding ComboBox.Width to the whole column width, was setting the ColumnDefinition width to "*" instead of "Auto":

对于我来说,扩展组合框的解决方案。宽度为整列宽度,将列定义宽度设置为“*”而不是“Auto”:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="140" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <Label Content="List of items"
      Grid.Column="0" Margin="3" />

    <ComboBox
        Grid.Column="1" 
        ItemsSource="{Binding Path=DestinationSubDivisions}"
        SelectedValue="{Binding Path=TransferRequest.DestinationSubDivision}"
        DisplayMemberPath="Name"
        Margin="3" />
</Grid>

#12


0  

Alun Harford's approach, in practice :

阿伦·哈福德的方法,在实践中:

<Grid>

  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="*"/>
  </Grid.ColumnDefinitions>

  <!-- hidden listbox that has all the items in one grid -->
  <ListBox ItemsSource="{Binding Items, ElementName=uiComboBox, Mode=OneWay}" Height="10" VerticalAlignment="Top" Visibility="Hidden">
    <ListBox.ItemsPanel><ItemsPanelTemplate><Grid/></ItemsPanelTemplate></ListBox.ItemsPanel>
  </ListBox>

  <ComboBox VerticalAlignment="Top" SelectedIndex="0" x:Name="uiComboBox">
    <ComboBoxItem>foo</ComboBoxItem>
    <ComboBoxItem>bar</ComboBoxItem>
    <ComboBoxItem>fiuafiouhoiruhslkfhalsjfhalhflasdkf</ComboBoxItem>
  </ComboBox>

</Grid>

#13


0  

Just add a width to the combo box

只需向组合框添加一个宽度。

<ComboBox Name="ComboBox1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100">

#14


0  

This keeps the width to the widest element but only after opening the combo box once.

这将保持最宽元素的宽度,但只在打开组合框一次之后。

<ComboBox ItemsSource="{Binding ComboBoxItems}" Grid.IsSharedSizeScope="True" HorizontalAlignment="Left">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition SharedSizeGroup="sharedSizeGroup"/>
                </Grid.ColumnDefinitions>
                <TextBlock Text="{Binding}"/>
            </Grid>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

#1


28  

This can't be in XAML without either:

在XAML中,这两种情况都不可能出现:

  • Creating a hidden control (Alan Hunford's answer)
  • 创建一个隐藏控件(Alan Hunford的回答)
  • Changing the ControlTemplate drastically. Even in this case, a hidden version of an ItemsPresenter may need to be created.
  • 往ControlTemplate中加故事板动画大大改变。即使在这种情况下,也可能需要创建ItemsPresenter的隐藏版本。

The reason for this is that the default ComboBox ControlTemplates that I've come across (Aero, Luna, etc.) all nest the ItemsPresenter in a Popup. This means that the layout of these items is deferred until they are actually made visible.

原因是我遇到的默认ComboBox控件模板(Aero、Luna等)都将ItemsPresenter嵌在弹出窗口中。这意味着这些项目的布局被延迟,直到它们被实际显示。

An easy way to test this is to modify the default ControlTemplate to bind the MinWidth of the outermost container (it's a Grid for both Aero and Luna) to the ActualWidth of PART_Popup. You'll be able to have the ComboBox automatically synchronize it's width when you click the drop button, but not before.

一个简单的测试方法是修改默认的ControlTemplate,将最外层容器的最小宽度(它是Aero和Luna的网格)绑定到PART_Popup的实际宽度。您可以让ComboBox自动同步它的宽度,当您点击下拉按钮,但不是之前。

So unless you can force a Measure operation in the layout system (which you can do by adding a second control), I don't think it can be done.

因此,除非您能够在布局系统中强制执行度量操作(您可以通过添加第二个控件来实现),否则我认为这是不可能实现的。

As always, I'm open to an short, elegant solution -- but in this case a code-behind or dual-control/ControlTemplate hacks are the only solutions I have seen.

与往常一样,我对一个简短、优雅的解决方案持开放态度——但在这种情况下,我看到的唯一解决方案是代码隐藏或双重控制/控制模板修改。

#2


50  

You can't do it directly in Xaml but you can use this Attached Behavior. (The Width will be visible in the Designer)

你不能直接在Xaml中做,但是你可以使用这个附加的行为。(宽度将在设计器中可见)

<ComboBox behaviors:ComboBoxWidthFromItemsBehavior.ComboBoxWidthFromItems="True">
    <ComboBoxItem Content="Short"/>
    <ComboBoxItem Content="Medium Long"/>
    <ComboBoxItem Content="Min"/>
</ComboBox>

The Attached Behavior ComboBoxWidthFromItemsProperty

附加行为ComboBoxWidthFromItemsProperty

public static class ComboBoxWidthFromItemsBehavior
{
    public static readonly DependencyProperty ComboBoxWidthFromItemsProperty =
        DependencyProperty.RegisterAttached
        (
            "ComboBoxWidthFromItems",
            typeof(bool),
            typeof(ComboBoxWidthFromItemsBehavior),
            new UIPropertyMetadata(false, OnComboBoxWidthFromItemsPropertyChanged)
        );
    public static bool GetComboBoxWidthFromItems(DependencyObject obj)
    {
        return (bool)obj.GetValue(ComboBoxWidthFromItemsProperty);
    }
    public static void SetComboBoxWidthFromItems(DependencyObject obj, bool value)
    {
        obj.SetValue(ComboBoxWidthFromItemsProperty, value);
    }
    private static void OnComboBoxWidthFromItemsPropertyChanged(DependencyObject dpo,
                                                                DependencyPropertyChangedEventArgs e)
    {
        ComboBox comboBox = dpo as ComboBox;
        if (comboBox != null)
        {
            if ((bool)e.NewValue == true)
            {
                comboBox.Loaded += OnComboBoxLoaded;
            }
            else
            {
                comboBox.Loaded -= OnComboBoxLoaded;
            }
        }
    }
    private static void OnComboBoxLoaded(object sender, RoutedEventArgs e)
    {
        ComboBox comboBox = sender as ComboBox;
        Action action = () => { comboBox.SetWidthFromItems(); };
        comboBox.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
    }
}

What it does is that it calls an extension method for ComboBox called SetWidthFromItems which (invisibly) expands and collapses itself and then calculates the Width based on the generated ComboBoxItems. (IExpandCollapseProvider requires a reference to UIAutomationProvider.dll)

它所做的是为ComboBox调用名为SetWidthFromItems的扩展方法,该方法(无形)展开和折叠自身,然后根据生成的ComboBoxItems计算宽度。(iexpand折叠提供程序需要对UIAutomationProvider.dll的引用)

Then extension method SetWidthFromItems

然后扩展方法SetWidthFromItems

public static class ComboBoxExtensionMethods
{
    public static void SetWidthFromItems(this ComboBox comboBox)
    {
        double comboBoxWidth = 19;// comboBox.DesiredSize.Width;

        // Create the peer and provider to expand the comboBox in code behind. 
        ComboBoxAutomationPeer peer = new ComboBoxAutomationPeer(comboBox);
        IExpandCollapseProvider provider = (IExpandCollapseProvider)peer.GetPattern(PatternInterface.ExpandCollapse);
        EventHandler eventHandler = null;
        eventHandler = new EventHandler(delegate
        {
            if (comboBox.IsDropDownOpen &&
                comboBox.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
            {
                double width = 0;
                foreach (var item in comboBox.Items)
                {
                    ComboBoxItem comboBoxItem = comboBox.ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem;
                    comboBoxItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                    if (comboBoxItem.DesiredSize.Width > width)
                    {
                        width = comboBoxItem.DesiredSize.Width;
                    }
                }
                comboBox.Width = comboBoxWidth + width;
                // Remove the event handler. 
                comboBox.ItemContainerGenerator.StatusChanged -= eventHandler;
                comboBox.DropDownOpened -= eventHandler;
                provider.Collapse();
            }
        });
        comboBox.ItemContainerGenerator.StatusChanged += eventHandler;
        comboBox.DropDownOpened += eventHandler;
        // Expand the comboBox to generate all its ComboBoxItem's. 
        provider.Expand();
    }
}

This extension method also provides to ability to call

这个扩展方法还提供了调用的能力

comboBox.SetWidthFromItems();

in code behind (e.g in the ComboBox.Loaded event)

在代码后面(e。组合框的g。加载事件)

#3


10  

Yeah, this one is a bit nasty.

是的,这个有点恶心。

What I've done in the past is to add into the ControlTemplate a hidden listbox (with its itemscontainerpanel set to a grid) showing every item at the same time but with their visibility set to hidden.

我过去所做的是向ControlTemplate中添加一个隐藏的列表框(其itemscontainerpanel设置为一个网格),该列表框同时显示每个项目,但其可见性设置为hidden。

I'd be pleased to hear of any better ideas that don't rely on horrible code-behind or your view having to understand that it needs to use a different control to provide the width to support the visuals (yuck!).

我很高兴听到任何更好的想法,不依赖于可怕的代码隐藏或您的视图必须理解它需要使用不同的控件来提供宽度来支持视觉效果(呸!)

#4


6  

Based on the other answers above, here's my version:

基于以上其他答案,我的观点如下:

<Grid HorizontalAlignment="Left">
    <ItemsControl ItemsSource="{Binding EnumValues}" Height="0" Margin="15,0"/>
    <ComboBox ItemsSource="{Binding EnumValues}" />
</Grid>

HorizontalAlignment="Left" stops the controls using the full width of the containing control. Height="0" hides the items control.
Margin="15,0" allows for additional chrome around combo-box items (not chrome agnostic I'm afraid).

horizontalalign ="Left"使用包含控件的全宽度来停止控件。Height="0"隐藏项控件。Margin=“15,0”允许在combo-box项目周围添加额外的chrome(恐怕不是chrome不可知论者)。

#5


4  

I ended up with a "good enough" solution to this problem being to make the combo box never shrink below the largest size it held, similar to the old WinForms AutoSizeMode=GrowOnly.

最后,我找到了一个“足够好的”解决方案,使组合框永远不会缩小到最大大小以下,类似于旧的WinForms AutoSizeMode=GrowOnly。

The way I did this was with a custom value converter:

我使用自定义值转换器:

public class GrowConverter : IValueConverter
{
    public double Minimum
    {
        get;
        set;
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var dvalue = (double)value;
        if (dvalue > Minimum)
            Minimum = dvalue;
        else if (dvalue < Minimum)
            dvalue = Minimum;
        return dvalue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

Then I configure the combo box in XAML like so:

然后在XAML中配置组合框,如下所示:

 <Whatever>
        <Whatever.Resources>
            <my:GrowConverter x:Key="grow" />
        </Whatever.Resources>
        ...
        <ComboBox MinWidth="{Binding ActualWidth,RelativeSource={RelativeSource Self},Converter={StaticResource grow}}" />
    </Whatever>

Note that with this you need a separate instance of the GrowConverter for each combo box, unless of course you want a set of them to size together, similar to the Grid's SharedSizeScope feature.

需要注意的是,对于每个组合框,您需要一个单独的GrowConverter实例,除非您想要一组它们的大小,类似于网格的SharedSizeScope特性。

#6


2  

A follow up to Maleak's answer: I liked that implementation so much, I wrote an actual Behavior for it. Obviously you'll need the Blend SDK so you can reference System.Windows.Interactivity.

接下来是Maleak的回答:我非常喜欢这个实现,我为它编写了一个实际的行为。显然,您需要使用Blend SDK,以便您可以引用System.Windows.Interactivity。

XAML:

XAML:

    <ComboBox ItemsSource="{Binding ListOfStuff}">
        <i:Interaction.Behaviors>
            <local:ComboBoxWidthBehavior />
        </i:Interaction.Behaviors>
    </ComboBox>

Code:

代码:

using System;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Interactivity;

namespace MyLibrary
{
    public class ComboBoxWidthBehavior : Behavior<ComboBox>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.Loaded += OnLoaded;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.Loaded -= OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            var desiredWidth = AssociatedObject.DesiredSize.Width;

            // Create the peer and provider to expand the comboBox in code behind. 
            var peer = new ComboBoxAutomationPeer(AssociatedObject);
            var provider = peer.GetPattern(PatternInterface.ExpandCollapse) as IExpandCollapseProvider;
            if (provider == null)
                return;

            EventHandler[] handler = {null};    // array usage prevents access to modified closure
            handler[0] = new EventHandler(delegate
            {
                if (!AssociatedObject.IsDropDownOpen || AssociatedObject.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
                    return;

                double largestWidth = 0;
                foreach (var item in AssociatedObject.Items)
                {
                    var comboBoxItem = AssociatedObject.ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem;
                    if (comboBoxItem == null)
                        continue;

                    comboBoxItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                    if (comboBoxItem.DesiredSize.Width > largestWidth)
                        largestWidth = comboBoxItem.DesiredSize.Width;
                }

                AssociatedObject.Width = desiredWidth + largestWidth;

                // Remove the event handler.
                AssociatedObject.ItemContainerGenerator.StatusChanged -= handler[0];
                AssociatedObject.DropDownOpened -= handler[0];
                provider.Collapse();
            });

            AssociatedObject.ItemContainerGenerator.StatusChanged += handler[0];
            AssociatedObject.DropDownOpened += handler[0];

            // Expand the comboBox to generate all its ComboBoxItem's. 
            provider.Expand();
        }
    }
}

#7


0  

You can bind the Width any container you wish.

您可以绑定任何容器的宽度。

<Window x:Class="WpfApplication1.Window1"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Title="Window1" Height="300" Width="300" x:Name="Window1">
<Grid>
    <ComboBox 
       Name="ComboBox1"
       HorizontalAlignment="Left"
       VerticalAlignment="Top">
       <ComboBox.Width>
          <Binding ElementName="Window1" Path="ActualWidth"/>
       </ComboBox.Width>
          <ComboBoxItem>ComboBoxItem1</ComboBoxItem>
          <ComboBoxItem>ComboBoxItem2</ComboBoxItem>
    </ComboBox>
</Grid>

To get exactly what you are trying to do with the C# you wrote I would look at impmenting an IValueConverter or IMultiValueConverter.

为了准确地了解您正在尝试使用您所编写的c#,我将研究如何对IValueConverter或IMultiValueConverter进行修改。

#8


0  

Put an listbox containing the same content behind the dropbox. Then enforce correct height with some binding like this:

在dropbox后面放一个包含相同内容的列表框。然后用一些这样的约束加强正确的高度:

<Grid>
       <ListBox x:Name="listBox" Height="{Binding ElementName=dropBox, Path=DesiredSize.Height}" /> 
        <ComboBox x:Name="dropBox" />
</Grid>

#9


0  

In my case a much simpler way seemed to do the trick, I just used an extra stackPanel to wrap the combobox.

在我的例子中,一种简单得多的方法似乎可以达到目的,我只是使用了一个额外的堆栈面板来包装combobox。

<StackPanel Grid.Row="1" Orientation="Horizontal">
    <ComboBox ItemsSource="{Binding ExecutionTimesModeList}" Width="Auto"
        SelectedValuePath="Item" DisplayMemberPath="FriendlyName"
        SelectedValue="{Binding Model.SelectedExecutionTimesMode}" />    
</StackPanel>

(worked in visual studio 2008)

(在visual studio 2008工作)

#10


0  

I was looking for the answer myself, when I came across the UpdateLayout() method that every UIElement has.

我正在自己寻找答案,这时我遇到了每个UIElement都有的UpdateLayout()方法。

It's very simple now, thankfully!

现在很简单了,谢天谢地!

Just call ComboBox1.Updatelayout(); after you set or modify the ItemSource.

就叫ComboBox1.Updatelayout();设置或修改ItemSource之后。

#11


0  

As for me, the solution for expanding ComboBox.Width to the whole column width, was setting the ColumnDefinition width to "*" instead of "Auto":

对于我来说,扩展组合框的解决方案。宽度为整列宽度,将列定义宽度设置为“*”而不是“Auto”:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="140" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <Label Content="List of items"
      Grid.Column="0" Margin="3" />

    <ComboBox
        Grid.Column="1" 
        ItemsSource="{Binding Path=DestinationSubDivisions}"
        SelectedValue="{Binding Path=TransferRequest.DestinationSubDivision}"
        DisplayMemberPath="Name"
        Margin="3" />
</Grid>

#12


0  

Alun Harford's approach, in practice :

阿伦·哈福德的方法,在实践中:

<Grid>

  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="*"/>
  </Grid.ColumnDefinitions>

  <!-- hidden listbox that has all the items in one grid -->
  <ListBox ItemsSource="{Binding Items, ElementName=uiComboBox, Mode=OneWay}" Height="10" VerticalAlignment="Top" Visibility="Hidden">
    <ListBox.ItemsPanel><ItemsPanelTemplate><Grid/></ItemsPanelTemplate></ListBox.ItemsPanel>
  </ListBox>

  <ComboBox VerticalAlignment="Top" SelectedIndex="0" x:Name="uiComboBox">
    <ComboBoxItem>foo</ComboBoxItem>
    <ComboBoxItem>bar</ComboBoxItem>
    <ComboBoxItem>fiuafiouhoiruhslkfhalsjfhalhflasdkf</ComboBoxItem>
  </ComboBox>

</Grid>

#13


0  

Just add a width to the combo box

只需向组合框添加一个宽度。

<ComboBox Name="ComboBox1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100">

#14


0  

This keeps the width to the widest element but only after opening the combo box once.

这将保持最宽元素的宽度,但只在打开组合框一次之后。

<ComboBox ItemsSource="{Binding ComboBoxItems}" Grid.IsSharedSizeScope="True" HorizontalAlignment="Left">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition SharedSizeGroup="sharedSizeGroup"/>
                </Grid.ColumnDefinitions>
                <TextBlock Text="{Binding}"/>
            </Grid>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>