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 最初以错误的大小显示第一个项目的主要内容,如果未能解决你的问题,请参考以下文章
WPF:为啥我的 ComboBox SelectedItem 不显示?