如何在XAML中使WPF组合框具有其最宽元素的宽度?

106

我知道如何在代码中完成它,但是否可以在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;
        }
    }
}

请查看类似主题的另一篇帖子,网址为https://dev59.com/g3RA5IYBdhLWcg3wyRF6。如果这个回答解决了你的问题,请将你的问题标记为“已解答”。 - Sudeep
我在代码中也尝试了这种方法,但发现在Vista和XP之间测量可能会有所不同。在Vista上,DesiredSize通常包括下拉箭头的大小,但在XP上,宽度通常不包括下拉箭头。现在,我的结果可能是因为我试图在父窗口可见之前进行测量。在测量之前添加UpdateLayout()可以帮助解决问题,但可能会导致应用程序出现其他副作用。如果您愿意分享,我很想看看您提出的解决方案。 - jschroedl
你是如何解决你的问题的? - Andrew Kalashnikov
13个回答

1
我希望它只在下拉菜单打开时调整到最大元素大小,否则适应所选值。以下是该代码:

部分基于Frederik的答案(实际上对我不起作用)

public static class ComboBoxAutoWidthBehavior {
    public static readonly DependencyProperty ComboBoxAutoWidthProperty =
            DependencyProperty.RegisterAttached(
                "ComboBoxAutoWidth",
                typeof(bool),
                typeof(ComboBoxAutoWidthBehavior),
                new UIPropertyMetadata(false, OnComboBoxAutoWidthPropertyChanged)
            );

    public static bool GetComboBoxAutoWidth(DependencyObject obj) {
        return (bool) obj.GetValue(ComboBoxAutoWidthProperty);
    }

    public static void SetComboBoxAutoWidth(DependencyObject obj, bool value) {
        obj.SetValue(ComboBoxAutoWidthProperty, value);
    }

    private static void OnComboBoxAutoWidthPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs e) {
        if(dpo is ComboBox comboBox) {
            if((bool) e.NewValue) {
                comboBox.Loaded += OnComboBoxLoaded;
                comboBox.DropDownOpened += OnComboBoxOpened;
                comboBox.DropDownClosed += OnComboBoxClosed;
            } else {
                comboBox.Loaded -= OnComboBoxLoaded;
                comboBox.DropDownOpened -= OnComboBoxOpened;
                comboBox.DropDownClosed -= OnComboBoxClosed;
            }
        }
    }

    private static void OnComboBoxLoaded(object sender, EventArgs eventArgs) {
        ComboBox comboBox = (ComboBox) sender;
        comboBox.SetMaxWidthFromItems();
    }

    private static void OnComboBoxOpened(object sender, EventArgs eventArgs) {
        ComboBox comboBox = (ComboBox) sender;
        comboBox.Width = comboBox.MaxWidth;
    }

    private static void OnComboBoxClosed(object sender, EventArgs eventArgs) => ((ComboBox) sender).Width = double.NaN;
}

public static class ComboBoxExtensionMethods {
    public static void SetMaxWidthFromItems(this ComboBox combo) {
        double idealWidth = combo.MinWidth;
        string longestItem = combo.Items.Cast<object>().Select(x => x.ToString()).Max(x => (x?.Length, x)).x;
        if(longestItem != null && longestItem.Length >= 0) {
            string tmpTxt = combo.Text;
            combo.Text = longestItem;
            Thickness tmpMarg = combo.Margin;
            combo.Margin = new Thickness(0);
            combo.UpdateLayout();

            combo.Width = double.NaN;
            combo.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));

            idealWidth = Math.Max(idealWidth, combo.DesiredSize.Width);

            combo.Text = tmpTxt;
            combo.Margin = tmpMarg;
        }

        combo.MaxWidth = idealWidth;
    }
}

你可以像这样启用它:

<ComboBox behaviours:ComboBoxAutoWidthBehavior.ComboBoxAutoWidth="True" />

你也可以直接设置Width而不是MaxWidth,如果你想让它的行为像其他答案一样,那么可以删除DropDownOpened和Closed部分。

0

这将保持宽度为最宽的元素,但仅在打开组合框后才生效。

<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

我自己也在寻找答案时,发现每个UIElement都有一个UpdateLayout()方法。

现在非常简单,谢天谢地!

只需在设置或修改ItemSource后调用ComboBox1.Updatelayout();即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接