tags:

views:

2294

answers:

6

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

Hi, Check out another post on the similar lines at http://stackoverflow.com/questions/826985/make-wpf-comboboxes-fill-a-whole-column-width

Please mark your question as "answered" if this answers your question.

Sudeep
Unless I've missed a trick (very possible!), that question seems unrelated (it's about setting the width of a combobox, but that's about all)
Alun Harford
Oops! I miss read the post.
Sudeep
+5  A: 

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.

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!).

Alun Harford
Will this approach size the combo wide enough so that the widest item is fully visible when it's the selected item? This is where I've seen problems.
jschroedl
Could you show your code.
Andrew Kalashnikov
A: 

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.

Aaron
+6  A: 

This can't be in XAML without either:

  • Creating a hidden control (Alan Hunford's answer)
  • Changing the ControlTemplate drastically. Even in this case, a hidden version of an ItemsPresenter may need to be created.

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.

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.

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.

micahtan
A: 

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

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

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.

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:

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

Cheetah