WPF ComboBox 最初以错误的大小显示第一个项目

Posted

技术标签:

【中文标题】WPF ComboBox 最初以错误的大小显示第一个项目【英文标题】:WPF ComboBox initially displays first item at wrong size 【发布时间】:2011-09-29 15:01:51 【问题描述】:

我在 WPF 窗口上有一个 ComboBox,这让我有些心痛,因为当第一次显示时,第一个选择不正确地呈现。 ComboBox 不只显示文本;它显示从 UserControl 继承的类型的对象。这是 ComboBox 本身的 XAML:

<ComboBox Grid.Column="0" 
          Height="69" 
          HorizontalAlignment="Stretch"
          HorizontalContentAlignment="Center"
          ItemsSource="Binding Path=ViewChoices, 
                        RelativeSource=RelativeSource AncestorType=x:Type UserControl,  
                        Mode=OneWay" 
          Margin="10" 
          Name="ViewPicker" 
          SelectionChanged="ViewPicker_SelectionChanged" 
          VerticalAlignment="Stretch" 
          VerticalContentAlignment="Center" />

如您所见,ComboBox 的 ItemsSource 绑定到拥有它的 UserControl 的一个名为 ViewChoices 的属性。 ViewChoices 对象是一个 ObservableCollection。

ComboBox 的内容在后面代码中的代码中设置,因为最终代码中的确切内容将从 XML 文件中读取;这些值现在是硬编码的。本质上,为读取的每个 XML 行创建一个 CameraViewChoice 对象,并将其添加到 ViewChoices 集合中。这一切都发生在 UserControl 的默认构造函数中,在调用 InitializeComponent() 之后。在 UserControl 的 Loaded 事件处理程序中,我有将 ComboBox 的 SelectedIndex 属性设置为 0 的代码。

CameraViewChoice 对象源自 UserControl。这是此控件的 Xaml:

<UserControl x:Class="CarSystem.CustomControls.CameraViewChoice"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:cs="clr-namespace:CarSystem.CustomControls"  
             mc:Ignorable="d" 
             d:DesignHeight="50" d:DesignWidth="50">

    <Border BorderBrush="Black" BorderThickness="1">
        <Grid>
            <Image Opacity="0.35" Source="..." Stretch="Uniform" />
            <Label Content="Binding Path=Text" 
                   FontSize="18" 
                   FontWeight="Bold" 
                   Foreground="White" 
                   HorizontalAlignment="Center" 
                   VerticalAlignment="Center" />
        </Grid>
    </Border>

</UserControl>

这是 CameraViewChoice 对象的代码隐藏:

public partial class CameraViewChoice : UserControl 

    public static readonly DependencyProperty AttachedCameraProperty =  DependencyProperty.Register( "AttachedCamera", typeof( Camera ), typeof( CameraViewChoice ), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.AffectsRender ) );

    public static readonly DependencyProperty TextProperty = DependencyProperty.Register( "Text", typeof( string ), typeof( CameraViewChoice ), new FrameworkPropertyMetadata( string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsParentMeasure | FrameworkPropertyMetadataOptions.AffectsRender ) );

    public Camera AttachedCamera 
        get  return (Camera) GetValue( AttachedCameraProperty ); 
        set  SetValue( AttachedCameraProperty, value ); 
    

    public string Text 
        get  return (string) GetValue( TextProperty ); 
        set  SetValue( TextProperty, value ); 
    

    public CameraViewChoice() 
        InitializeComponent();
    

一切正常,但有一个问题。当程序开始运行,第一次显示ComboBox时,选中的项显示全错。标签为空白,CameraViewChoice 控件显示过大,以至于底部被切断。我看到的是一个空白的 CameraViewChoice 对象,没有缩放到 ComboBox。

如果我在列表中选择一个项目,列表中的所有选项都会正确显示并且大小合适并且看起来很好,包括所选的选项。问题仅在于窗口首次显示时。

有人对正在发生的事情有任何想法吗?

托尼

编辑:

我在 Google 和 MSDN 杂志上做了一些研究,发现了 Josh Smith 的一篇关于数据模板的文章。从那里,我对我的 ComboBox 的 XAML 进行了以下更改:

<ComboBox Grid.Column="0" 
          Height="69" 
          HorizontalAlignment="Stretch"
          HorizontalContentAlignment="Center"
          ItemsSource="Binding Path=ViewChoices, 
                        RelativeSource=RelativeSource AncestorType=x:Type UserControl,  
                        Mode=OneWay" 
          Margin="10" 
          Name="ViewPicker" 
          SelectionChanged="ViewPicker_SelectionChanged" 
          VerticalAlignment="Stretch" 
          VerticalContentAlignment="Center">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <cs:CameraViewChoice Margin="10" />
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

这样更好,因为 ComboBox 中的项目不会改变大小,但初始显示仍然太大。也就是说,它在底部被切断。此外,所选项目的大小始终太大。因此,当您在列表中选择一项时,它会在组合框中显示部分剪裁。

我希望选择显示在 ComboBox 中,并且所有选项都适合它的一侧。对 CombobBox 的 ItemTemplate 的更改有何建议?

【问题讨论】:

你能拍几张它之前和之后的样子吗? "在 UserControl 的 Loaded 事件处理程序中,我有代码将 ComboBox 的 SelectedIndex 属性设置为 0。"--- 删除它看看会发生什么。 您能否发布 ComboBox 的其余 xaml 代码。由于您使用“RelativeSource AncestorType”,因此 UserControl CameraViewChoice 应该包含组合框,但我们看不到代码。 @seekle:我包含了 ComboBox 控件的所有 Xaml。我不想在该部分中包含 Xaml 的其余部分,因为它有很多而且不相关。 @Bolu:我已经从 Loaded 事件处理程序中删除了代码。当 ComboBox 显示时,没有选择任何内容。所以当然,当我从下拉列表中选择一些东西时,一切都被找到了。但问题是初始状态由索引 0 中的选择表示;在此 ComboBox 中选择任何内容是没有意义的。 【参考方案1】:

在 Loaded 事件中,如果您至少有 2 个项目,则将 SelectedIndex 设置为 1。此后,无论您有多少项目,在 ComboBox 上调用 InvalidateMeasure 和 UpdateLayout,然后将 SelectedIndex 设置为 0。

【讨论】:

我对这种方法寄予厚望,因为列表中总会有至少 2 个条目。但这没有任何区别。当 ComboBox 首次显示时,所选项目未正确缩放。 在UpdateLayout之前插入一个myCombo.IsDropDownOpen = true,然后在SelectedIndex设置为0之后插入myCombo.IsDropDownOpen = false怎么样?【参考方案2】:

这就是我认为正在发生的事情。

您正在使用标准 ComboBox 并向其动态添加 UIElement。首次显示 ComboBox 时,没有任何项目,因此它使用默认模板。在您开始向其添加 UIElements 后,渲染器将对其进行测量和排列。本质上,它只是在了解创建和插入 UIElement 之后应该是什么样子(但它仍然需要知道在那之前应该是什么样子)。

我的建议是从这种开发模式转向更通用的方法。无需动态创建 UIElement,只需创建 CameraChoices 的 ObservableCollection(或任何合适的名称)。通常这将包含在 ViewModel 中。

然后,与其创建一个 UserControl 并将其插入到 ComboBox 的 ItemsSource 中,不如为 ComboBox 创建一个 ItemsTemplate(您可以在其中使用 UserControl)。或者,您可以使用与 ObservableCollection 中的对象相同类型的 DataTemplate。

这将为显示项目列表提供更强大的机制,并为您提供一种获取原始数据的方法,而不必在收到 SelectionChanged 事件时处理 UIElement。

【讨论】:

我对 WPF 还是很陌生,我不确定我是否完全理解你在说什么。我正在创建的 UIElements 现在保存到 ObservableCollection 中。该集合绑定到 ComboBox 的 ItemsSource 属性。我认为您的意思是我应该为 ComboBox 的容器向 XAML 添加一个 ViewModel 并将 ObservableCollection 放入其中?然后我应该为 ComboBox 添加一个 ItemsTemplate 到 Xaml 并将我的 CameraViewChoice 放入其中? 关于这个 ComboBox 的用途的一句话。它嵌入在一个控件中,该控件显示由连接到计算机的设备生成的数据。数据包括 GPS 信息、数字图像和其他信息。控件可以一次显示来自一个或多个设备的信息。 ComboBox 允许用户选择控件应该使用哪种模式来显示数据。我必须即时生成选择,因为直到运行时我才知道有多少设备连接到计算机。 我的意思是,你应该有一个 ViewModel,它有一个返回 CameraChoices 集合(数据对象,而不是 UIElements)的属性。您可以将此集合绑定到 ComboBox 的 ItemsSource 属性并使用(多种方式之一)DataTemplate 来告诉 ComboBox 如何绘制内容。

以上是关于WPF ComboBox 最初以错误的大小显示第一个项目的主要内容,如果未能解决你的问题,请参考以下文章

C# WPF - 组合框

WPF中,ComboBox值显示问题

如何在 WPF ComboBox 中正确显示过长的文本

WPF:为啥我的 ComboBox SelectedItem 不显示?

MaterialDesign ComboBox 箭头调整大小和更改颜色 WPF

WPF:将 DataGrid 放入 ComboBox