WPF 组合框中所选项目的模板与复合集合中的项目不同
Different template for selected item in WPF combobox than items in composite collection
我有一个复合集合,其中包括:
- 包含内容 'Select a vendor'
的组合框项目
- 包含 Vendor 对象集合的集合容器,绑定到组合框
cmbVendor
从组合框中选择供应商后,将调用 ToString()
方法。
但是,我想显示所选供应商对象的 属性 Name
的值。
设置组合框 属性 DisplayMemberPath='Name'
有效,但 'Select a vendor' 在加载时不再显示,这是不需要的。
备注:
- 有时从代码隐藏中选择供应商对象
- 由于其他原因,我无法覆盖供应商对象的
ToString()
方法
有什么建议吗?
XAML
<UserControl.Resources>
<converters:VendorConverter x:Key='VendorConverter' />
<CollectionViewSource x:Key='VendorsCollection'
Source='{Binding Vendors}'>
</CollectionViewSource>
</UserControl.Resources>
<Grid>
<ComboBox Name='cmbVendor'
SelectedItem='{Binding Vendor, Converter={StaticResource VendorConverter}, Mode=TwoWay}'
IsSynchronizedWithCurrentItem='True'
IsEditable='True'
Width='{DynamicResource VendorCmbWidth}'>
<!--Make sure "Select a vendor" is selected-->
<i:Interaction.Behaviors>
<behaviour:SelectFirstItemBehavior />
</i:Interaction.Behaviors>
<ComboBox.Resources>
<DataTemplate DataType='{x:Type objects:Vendor}'>
<StackPanel Orientation='Horizontal'>
<TextBlock Text='{Binding Name}' />
</StackPanel>
</DataTemplate>
<DataTemplate DataType='{x:Type system:String}'>
<StackPanel Orientation='Horizontal'>
<TextBlock Text='{Binding }' />
</StackPanel>
</DataTemplate>
</ComboBox.Resources>
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem Content='Select a vendor' />
<CollectionContainer Collection='{Binding Source={StaticResource VendorsCollection}}' />
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>
</Grid>
</UserControl>
VendorConverter
internal class VendorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var vendor = value as Vendor;
if (vendor != null)
{
return vendor;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var comboboxItem = value as ComboBoxItem;
if (comboboxItem != null)
{
return null;
}
var vendor = value as Vendor;
if (vendor != null)
{
return vendor;
}
return null;
}
}
行为
internal class SelectFirstItemBehavior : Behavior<ComboBox>
{
protected override void OnAttached()
{
AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
}
protected override void OnDetaching()
{
AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
base.OnDetaching();
}
private void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var combobox = sender as ComboBox;
if (combobox != null && combobox.SelectedIndex == -1)
{
combobox.SelectedIndex = 0;
}
}
}
方法一
您应该能够将 TextSearch.TextPath="Name"
用于您的普通物品并 TextSearch.Text="Select a vendor"
直接分配给您的特殊物品:
<ComboBox
IsEditable="True"
TextSearch.TextPath="Name">
...
和
<CompositeCollection>
<ComboBoxItem Content='Select a vendor' TextSearch.Text="Select a vendor" />
<CollectionContainer Collection='{Binding Source={StaticResource VendorsCollection}}' />
</CompositeCollection>
方法二
仅在未选择任何内容时显示视觉提示文本:
<ComboBox
ItemsSource="{Binding Source={StaticResource VendorsCollection}}"
IsEditable="True">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Resources>
<VisualBrush x:Key="hintText" x:Shared="False" AlignmentX="Left" Stretch="None">
<VisualBrush.Visual>
<Grid Background="White">
<TextBlock Margin="4 3" Text="Select a vendor"/>
</Grid>
</VisualBrush.Visual>
</VisualBrush>
</Style.Resources>
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Background" Value="{StaticResource hintText}"/>
</Trigger>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Background" Value="{StaticResource hintText}"/>
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
通过这种方式,您可以使您的项目集合中没有多余的条目,并为您的实际项目使用 TextSearch.TextPath
和类似属性。
我有一个复合集合,其中包括:
- 包含内容 'Select a vendor' 的组合框项目
- 包含 Vendor 对象集合的集合容器,绑定到组合框
cmbVendor
从组合框中选择供应商后,将调用 ToString()
方法。
但是,我想显示所选供应商对象的 属性 Name
的值。
设置组合框 属性 DisplayMemberPath='Name'
有效,但 'Select a vendor' 在加载时不再显示,这是不需要的。
备注:
- 有时从代码隐藏中选择供应商对象
- 由于其他原因,我无法覆盖供应商对象的
ToString()
方法
有什么建议吗?
XAML
<UserControl.Resources>
<converters:VendorConverter x:Key='VendorConverter' />
<CollectionViewSource x:Key='VendorsCollection'
Source='{Binding Vendors}'>
</CollectionViewSource>
</UserControl.Resources>
<Grid>
<ComboBox Name='cmbVendor'
SelectedItem='{Binding Vendor, Converter={StaticResource VendorConverter}, Mode=TwoWay}'
IsSynchronizedWithCurrentItem='True'
IsEditable='True'
Width='{DynamicResource VendorCmbWidth}'>
<!--Make sure "Select a vendor" is selected-->
<i:Interaction.Behaviors>
<behaviour:SelectFirstItemBehavior />
</i:Interaction.Behaviors>
<ComboBox.Resources>
<DataTemplate DataType='{x:Type objects:Vendor}'>
<StackPanel Orientation='Horizontal'>
<TextBlock Text='{Binding Name}' />
</StackPanel>
</DataTemplate>
<DataTemplate DataType='{x:Type system:String}'>
<StackPanel Orientation='Horizontal'>
<TextBlock Text='{Binding }' />
</StackPanel>
</DataTemplate>
</ComboBox.Resources>
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem Content='Select a vendor' />
<CollectionContainer Collection='{Binding Source={StaticResource VendorsCollection}}' />
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>
</Grid>
</UserControl>
VendorConverter
internal class VendorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var vendor = value as Vendor;
if (vendor != null)
{
return vendor;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var comboboxItem = value as ComboBoxItem;
if (comboboxItem != null)
{
return null;
}
var vendor = value as Vendor;
if (vendor != null)
{
return vendor;
}
return null;
}
}
行为
internal class SelectFirstItemBehavior : Behavior<ComboBox>
{
protected override void OnAttached()
{
AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
}
protected override void OnDetaching()
{
AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
base.OnDetaching();
}
private void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var combobox = sender as ComboBox;
if (combobox != null && combobox.SelectedIndex == -1)
{
combobox.SelectedIndex = 0;
}
}
}
方法一
您应该能够将 TextSearch.TextPath="Name"
用于您的普通物品并 TextSearch.Text="Select a vendor"
直接分配给您的特殊物品:
<ComboBox
IsEditable="True"
TextSearch.TextPath="Name">
...
和
<CompositeCollection>
<ComboBoxItem Content='Select a vendor' TextSearch.Text="Select a vendor" />
<CollectionContainer Collection='{Binding Source={StaticResource VendorsCollection}}' />
</CompositeCollection>
方法二
仅在未选择任何内容时显示视觉提示文本:
<ComboBox
ItemsSource="{Binding Source={StaticResource VendorsCollection}}"
IsEditable="True">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Resources>
<VisualBrush x:Key="hintText" x:Shared="False" AlignmentX="Left" Stretch="None">
<VisualBrush.Visual>
<Grid Background="White">
<TextBlock Margin="4 3" Text="Select a vendor"/>
</Grid>
</VisualBrush.Visual>
</VisualBrush>
</Style.Resources>
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Background" Value="{StaticResource hintText}"/>
</Trigger>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Background" Value="{StaticResource hintText}"/>
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
通过这种方式,您可以使您的项目集合中没有多余的条目,并为您的实际项目使用 TextSearch.TextPath
和类似属性。