Jak Mogę sprawić, by Skrzynka combo WPF miała szerokość najszerszego elementu w XAML?

Wiem jak to zrobić w kodzie, ale czy można to zrobić w 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;
        }
    }
}
Author: Dave Clemmer, 2009-06-23

14 answers

To nie może być w XAML bez żadnego:

    [[3]}Tworzenie ukrytej kontroli (odpowiedź Alana Hunforda)
  • zmiana ControlTemplate drastycznie. Nawet w tym przypadku może być konieczne utworzenie ukrytej wersji ItemsPresenter.

Powodem tego jest to, że domyślne ComboBox ControlTemplates, które spotkałem (Aero, Luna, itp.) wszystkie zagnieżdżają ItemsPresenter w wyskakującym okienku. Oznacza to, że układ tych elementów jest odroczony, dopóki nie zostaną faktycznie wykonane widoczne.

Łatwym sposobem na przetestowanie tego jest zmodyfikowanie domyślnego ControlTemplate, aby powiązać minimalną szerokość zewnętrznego kontenera (jest to siatka zarówno dla Aero, jak i Luna) z rzeczywistą szerokością PART_Popup. ComboBox automatycznie zsynchronizuje szerokość po kliknięciu przycisku upuść, ale nie wcześniej.

Więc jeśli nie można wymusić operacji miary w układzie (co można można {[14] } zrobić przez dodanie drugiej kontroli), nie sądzę, że może to być załatwione.

Jak zawsze, jestem otwarty na krótkie, eleganckie rozwiązanie -- ale w tym przypadku hacki code-behind lub dual-control/ControlTemplate są jedynymi rozwiązaniami, które widziałem.

 29
Author: micahtan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-24 16:02:24

Nie możesz tego zrobić bezpośrednio w Xaml, ale możesz użyć tego dołączonego zachowania. (Szerokość będzie widoczna w projektorze)

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

Dołączone Zachowanie 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);
    }
}

To, co robi, to to, że wywołuje metodę rozszerzenia dla ComboBox o nazwie SetWidthFromItems, która (niewidocznie) rozszerza się i zwija się, a następnie oblicza szerokość na podstawie wygenerowanych ComboBoxItems. (IExpandCollapseProvider wymaga odniesienia do UIAutomationProvider.dll)

Następnie metoda rozszerzenia 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();
    }
}

Ta metoda rozszerzenia zapewnia również możliwość wywołania

comboBox.SetWidthFromItems();

W kodzie za (np. w Comboboxie.Loaded event)

 51
Author: Fredrik Hedblad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-12 02:19:31

Ta jest trochę paskudna.

Co zrobiłem w przeszłości to dodać do ControlTemplate Ukryty listbox (z itemscontainerpanel ustawiony na siatkę) pokazując każdy element w tym samym czasie, ale z ich widoczność ustawiona na ukryty.

Z przyjemnością usłyszę o lepszych pomysłach, które nie polegają na okropnym kodzie-behind lub twoim widoku, który musi zrozumieć, że musi użyć innej kontroli, aby zapewnić szerokość obsługi wizualizacji (Fuj!).

 10
Author: Alun Harford,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-23 21:47:29

Na podstawie innych odpowiedzi powyżej, oto moja wersja:

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

Horizontalalalignment= "Left"zatrzymuje elementy sterujące przy użyciu pełnej szerokości kontrolki zawierającej. Height = " 0 " ukrywa kontrolę elementów.
Margin= "15,0" pozwala na dodatkowe chrome wokół elementów combo-box (obawiam się, że nie chrome agnostic).

 6
Author: Gaspode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-09 16:50:32

Skończyło się na "wystarczająco dobrym" rozwiązaniem tego problemu, aby pole kombi nigdy nie zmniejszyło się poniżej największego rozmiaru, jaki posiadał, podobnie jak stary WinForms AutoSizeMode = GrowOnly.

Sposób, w jaki to zrobiłem, był za pomocą niestandardowego konwertera wartości:

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

Następnie konfiguruję combo box w XAML TAK:

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

Zauważ, że w tym przypadku potrzebujesz osobnej instancji Growconvertera dla każdego pola combo, chyba że chcesz mieć ich zestaw do rozmiaru razem, podobny do funkcja SharedSizeScope w sieci.

 4
Author: Cheetah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-07 23:27:07

Kontynuacja odpowiedzi Maleaka: tak bardzo spodobała mi się Ta implementacja, że napisałem dla niej prawdziwe zachowanie. Oczywiście będziesz potrzebował zestawu SDK Blend, aby móc korzystać z systemu odniesienia.Okna.Interaktywność.

XAML:

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

Kod:

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();
        }
    }
}
 2
Author: Mike Post,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-23 03:54:52

Możesz powiązać szerokość dowolnego kontenera.

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

Aby uzyskać dokładnie to, co próbujesz zrobić z C# napisałeś chciałbym spojrzeć na impmenting IValueConverter lub IMultiValueConverter.

 0
Author: Aaron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-24 14:49:18

Umieść skrzynkę list zawierającą tę samą zawartość za dropbox. Następnie wyegzekwuj poprawną wysokość za pomocą jakiegoś wiązania w ten sposób:

<Grid>
       <ListBox x:Name="listBox" Height="{Binding ElementName=dropBox, Path=DesiredSize.Height}" /> 
        <ComboBox x:Name="dropBox" />
</Grid>
 0
Author: Matze,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-09 08:08:57

W moim przypadku o wiele prostszy sposób zdawał się zrobić trick, Właśnie użyłem dodatkowego panela do owinięcia comboboxu.

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

(pracował w visual studio 2008)

 0
Author: Entrodus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-13 13:07:34

Sama szukałam odpowiedzi, kiedy natknęłam się na metodę UpdateLayout(), którą ma każda UIElement.

To teraz bardzo proste, na szczęście!

Po ustawieniu lub zmodyfikowaniu ItemSource wystarczy wywołać ComboBox1.Updatelayout();.

 0
Author: Sinker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-13 04:26:41

Jak dla mnie, rozwiązanie do rozbudowy ComboBox.Szerokość do szerokości całej kolumny, ustawiono Szerokość zdefiniowania kolumny na " * "zamiast " 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>
 0
Author: Fragment,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-13 10:08:40

Podejście Aluna Harforda w praktyce:

<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>
 0
Author: Jan Van Overbeke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-26 09:39:50

Wystarczy dodać szerokość do pola combo

<ComboBox Name="ComboBox1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100">
 0
Author: Samidjo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-02 21:05:14

To utrzymuje szerokość do najszerszego elementu, ale tylko po jednokrotnym otwarciu skrzynki combo.

<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>
 0
Author: Wouter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-26 22:53:45