Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make a WPF combo box have the width of its widest element in XAML?

Tags:

c#

combobox

wpf

I know how to do it in code, but can this be done in 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;         }     } } 
like image 310
Jeno Csupor Avatar asked Jun 23 '09 19:06

Jeno Csupor


1 Answers

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

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

The Attached Behavior 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)

Then extension method 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)

like image 71
Fredrik Hedblad Avatar answered Sep 24 '22 15:09

Fredrik Hedblad