0

I am trying to make all my combo boxes re size themselves to the width of their longest item as soon as my view is loaded. I have stumbled upon the following question saying that this is impossible in XAML : How can I make a WPF combo box have the width of its widest element in XAML?.

So, now what I want to do is apply the code found in that initial question, and loop it for all the combo boxes that are in my view. Is there a way, from the code behind of my view, to get all the combo boxes contained in it so I can loop through them and adjust their width, instead of manually having to do it for each combo boxes?

EDIT:

The proposed duplicate answer does not seem to work with UserControl type views. Here is how my view code behind is declared:

public partial class QuickLookRequestView : UserControl, IView<QuickLookRequestViewModel>
{
    private QuickLookRequestViewModel _viewModel;

    public QuickLookRequestView()
    {
        InitializeComponent();

        // This cannot be done in XAML
        ResizeComboBoxToMaxItemWidth();
    }

    private void ResizeComboBoxToMaxItemWidth()
    {
        foreach (ComboBox comboBox in FindVisualChildren<ComboBox>(this))
        {
            double width = 0;
            foreach (ComboBoxItem item in comboBox.Items)
            {
                item.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                if (item.DesiredSize.Width > width)
                {
                    width = item.DesiredSize.Width;
                }   
            }
            comboBox.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
            comboBox.Width = comboBox.DesiredSize.Width + width;
        }
    }

    private IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
    {
        if (depObj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                if (child != null && child is T)
                {
                    yield return (T)child;
                }

                foreach (T childOfChild in FindVisualChildren<T>(child))
                {
                    yield return childOfChild;
                }
            }
        }
    }

    public QuickLookRequestViewModel ViewModel
    {
        get { return _viewModel; }
        set { _viewModel = value; }
    }
}

And while debugging, the application never goes into the foreach (ComboBox comboBox in FindVisualChildren<ComboBox>(this)) part of the code, so it does not find my combo boxes.

EDIT2:

It seems to be the following function in FindVisualChildren that doesn't find any visual child of my UserControl: for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)

EDIT3:

I no longer have the problem from my 1st and 2nd EDIT, though I do have a new one. One of my combo boxes displays items from an enum. This is done this way:

<ComboBox Name="ItemTypeComboBox" Margin="10,5,5,5" ItemsSource="{Binding ComboBoxItemTypes}" SelectedItem="{Binding SelectedComboBoxItemType}"/>

Where ComboBoxItemTypes is a get only property defined as follow:

public IEnumerable<EveItem.ItemTypes> ComboBoxItemTypes 
{
    get
    {
        return Enum.GetValues(typeof(EveItem.ItemTypes)).Cast<EveItem.ItemTypes>();
    }
}

And SelectedComboBoxItemType is of type ItemTypes, which is defined this way:

public enum ItemTypes
{
    Ore,
    Ice,
    Gas,
    Mineral,
    Pi
}

The problem I have is I get the following error message when FindVisualChildren() loops though that combo box:

Unable to cast object of type 'ItemTypes' to type 'System.Windows.Controls.ComboBoxItem'

How would I modify the FindVisualChildren() function to work with my combo box and enum type?

Community
  • 1
  • 1
Choub890
  • 1,163
  • 1
  • 13
  • 27
  • possible duplicate of [Find all controls in WPF Window by type](http://stackoverflow.com/questions/974598/find-all-controls-in-wpf-window-by-type) – sirdank Jun 12 '15 at 16:23
  • @sirdank this answer does not work for me, see edit – Choub890 Jun 12 '15 at 17:02
  • 2
    @Choub890 The [comment](http://stackoverflow.com/questions/974598/find-all-controls-in-wpf-window-by-type#comment1183266_978352) by Kyralessa indicates that you must execute FindVisualChildren() in the *Loaded* event of your control or you might get 0 children when calling it from constructor even after InitializeComponent(). – Absolom Jun 12 '15 at 18:07
  • @Absolom Thanks for this precision. The function now finds children, but one of my combo boxes displays some items of an enum, and I get a `Unable to cast object of type 'ItemTypes' to type 'System.Windows.Controls.ComboBoxItem'` error from that. I will update my question with this new problem. – Choub890 Jun 12 '15 at 18:39
  • @Choub890 have you succeeded by now? – Olaru Mircea Jun 12 '15 at 19:41
  • @olaruMircea No unfortunately, I am still stuck with my problem in EDIT3 – Choub890 Jun 12 '15 at 19:42
  • On what line of FindVisualChildren() is your exception thrown? Do you pass ComboBox as the generic parameter type for FindVisualChildren? – Absolom Jun 12 '15 at 20:27

2 Answers2

0
Form form = (your form, or control, or view, etc.)
IEnumerable<CheckBox> list = form.Controls.OfType<ComboBox>();

EDIT: Sorry, I see you're using WPF and not Windows Forms, I'm not sure of the equivalent for WPF.

  • This seems like an elegant method. My view is actually a `UserControl` though, and I can't seem to have access to the `Controls` property. Is there a way of adapting this answer for a `UserControl`? – Choub890 Jun 12 '15 at 16:43
  • Are you using Control.UserControl or Forms.UserControl? I can run `UserControl control = new UserControl()` and then `control.Controls` no problem, but I'm using Forms.UserControl – Stephen Arsenault Jun 12 '15 at 16:49
  • The `UserControl` is public, I am trying to access the property in its code behind (QuickLookRequestView.xaml.cs) like follows: `IEnumerable list = this.Controls.OfType();` and I don't have access to the `Controls` property that way. If it makes any difference, this is how the code behind is declared: `public partial class QuickLookRequestView : UserControl, IView` – Choub890 Jun 12 '15 at 16:51
  • [This answer](http://stackoverflow.com/questions/12747589/how-to-find-all-the-controls-in-wpf-on-a-usercontrol-component?lq=1) was accepted, but it appears to also be for Forms and not WPF. Might be of some help. Perhaps control.Children? – Stephen Arsenault Jun 12 '15 at 16:58
0

I've just tried this and it works as expected:

public class CustomComboBox : ComboBox
{
    private int _selected;

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        _selected = SelectedIndex;
        SelectedIndex = -1;
        Loaded += ComboBoxEx_Loaded;
    }

    void ComboBoxEx_Loaded(object sender, RoutedEventArgs e)
    {
        var popup = GetTemplateChild("PART_Popup") as Popup;
        var content = popup.Child as FrameworkElement;
        content.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        MinWidth = content.DesiredSize.Width;
        SelectedIndex = _selected;
    }
}

Codebehind:

public partial class MainWindow : Window
{
    public ObservableCollection<string> items { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        items = new ObservableCollection<string>();
        items.Add("test1");
        items.Add("test211111111111111");
        this.DataContext = this;
    }
}

And the XAML:

<Window x:Class="ComboItems.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ComboItems"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <local:CustomComboBox x:Name="myCB" ItemsSource="{Binding items}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>

EDIT 1

I've just tried it with enum too:

<Window x:Class="ComboItems.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ComboItems"
    xmlns:windows="clr-namespace:System.Windows;assembly=PresentationCore"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.Resources>
        <ObjectDataProvider x:Key="visibilityValues" 
                            ObjectType="{x:Type system:Enum}"
                            MethodName="GetValues">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="windows:Visibility" />
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Grid.Resources>
    <local:CustomComboBox x:Name="myCB"
                          FontSize="30" 
                          ItemsSource="{Binding Source={StaticResource visibilityValues}}" 
                          HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>

The font is also changed and here is the result:

enter image description here

Looks pretty nice to me.

Olaru Mircea
  • 2,570
  • 26
  • 49