我怎样才能让一个WPFcombobox有XAML中最宽的元素的宽度?

我知道如何在代码中做到这一点,但是这可以在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:

 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; } } } 

这不能在XAML中没有:

  • 创build一个隐藏的控制(Alan Hunford的答案)
  • 急剧改变ControlTemplate。 即使在这种情况下,也可能需要创buildItemsPresenter的隐藏版本。

原因是默认的ComboBox ControlTemplates(Aero,Luna等)都将ItemsPresenter嵌套在Popup中。 这意味着这些项目的布局被推迟到实际可见时为止。

一个简单的testing方法是修改默认的ControlTemplate,将最外层容器的MinWidth(Aero和Luna都是Grid)绑定到PART_Popup的ActualWidth。 当您单击放置button时,您将可以使ComboBox自动同步它的宽度,但不会在之前。

所以除非你可以在布局系统中强制执行Measure操作(你可以通过添加第二个控件来完成),但是我不认为这是可以做到的。

与往常一样,我打开一个简短,优雅的解决scheme – 但在这种情况下,一个代码隐藏或双控制/ ControlTemplate黑客是我见过的唯一的解决scheme。

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

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

附加行为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); } } 

它所做的就是调用ComboBox的一个名为SetWidthFromItems的扩展方法,它无形地展开和折叠,然后根据生成的ComboBoxItems计算宽度。 (IExpandCollapseProvider需要引用UIAutomationProvider.dll)

然后扩展方法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(); } } 

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

 comboBox.SetWidthFromItems(); 

在代码后面(例如在ComboBox.Loaded事件中)

是的,这个有点讨厌

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

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

根据上面的其他答案,这里是我的版本:

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

Horizo​​ntalAlignment =“Left”使用包含控件的全部宽度来停止控件。 高度=“0”隐藏物品控制。
保证金=“15,0”允许combobox项目附近的铬(不是铬不可知的,我害怕)。

我结束了一个“足够好”的解决scheme来解决这个问题,使combobox永远不会缩小到它所保持的最大尺寸以下,类似于旧的WinForms AutoSizeMode = GrowOnly。

我这样做的方式是使用自定义值转换器:

 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(); } } 

然后我在XAML中configurationcombobox如下所示:

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

请注意,对于每个combobox,您需要为每个combobox添加一个单独的GrowConverter实例,除非您需要一组尺寸放在一起,类似于Grid的SharedSizeScopefunction。

跟随马列克的回答:我非常喜欢这个实现,我写了一个实际的行为。 显然你需要Blend SDK,所以你可以引用System.Windows.Interactivity。

XAML:

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

码:

 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(); } } } 

你可以绑定你想要的任何容器的宽度。

 <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> 

为了得到你想要做的与你写的C#你想要做什么,我会看看impaling一个IValueConverter或IMultiValueConverter。

把一个包含相同内容的列表框放在Dropbox后面。 然后用这样的绑定强制正确的高度:

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

在我的情况下,一个简单得多的方法似乎可以做到这一点,我只是用了一个额外的stackPanel来包装combobox。

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

(在Visual Studio 2008中工作)

当我遇到每个UIElement具有的UpdateLayout()方法时,我一直在寻找答案。

现在很简单,谢天谢地!

只要调用ComboBox1.Updatelayout(); 在您设置或修改ItemSource

至于我,将ComboBox.Width扩展到整个列宽的解决scheme是将ColumnDefinition的宽度设置为“*”而不是“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> 

Alun Harford的做法是:

 <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> 

只需将宽度添加到combobox

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

这将保持宽度最宽的元素,但只有打开combobox一次。

 <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>