【发布时间】:2015-01-28 14:43:28
【问题描述】:
我目前没有任何代码可以分享。只是一个设计问题。
我有一个定义标签的类和一个关联的条目类型,我想将其绑定到 ListBox。例如,如果类型是“邮政编码”,我需要 ListBox 将行创建为 TextBlock 和 TextBox。对于“是/否”,我需要它知道创建一个 TextBlock,旁边有一个 CheckBox。这些不同的行类型可能有 7 或 8 种。
解决这个问题的最佳方法是什么?
【问题讨论】:
我目前没有任何代码可以分享。只是一个设计问题。
我有一个定义标签的类和一个关联的条目类型,我想将其绑定到 ListBox。例如,如果类型是“邮政编码”,我需要 ListBox 将行创建为 TextBlock 和 TextBox。对于“是/否”,我需要它知道创建一个 TextBlock,旁边有一个 CheckBox。这些不同的行类型可能有 7 或 8 种。
解决这个问题的最佳方法是什么?
【问题讨论】:
查看ItemTemplateSelector 属性。此属性允许您提供自定义逻辑,以选择为集合中的每个项目使用哪个模板。
首先在资源字典中定义你的各种模板...
<Application>
<Application.Resources>
<DataTemplate x:Key="TextBoxTemplate">
<!-- template here -->
</DataTemplate>
<DataTemplate x:Key="CheckBoxTemplate">
<!-- template here -->
</DataTemplate>
</Application.Resources>
</Application>
然后,创建一个自定义的 DataTemplateSelector...
public class MyTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var myObj= item as MyObject;
if (myObj != null)
{
if (myObj.MyType is PostalCode)
{
return Application.Resources["TextBoxTemplate"] as DataTemplate;
}
if (myObj.MyType is YesNo)
{
return Application.Resources["CheckBoxTemplate"] as DataTemplate;
}
}
return null;
}
}
那么,只需使用ItemTemplateSelector 属性...
<Window>
<Window.Resources>
<local:MyTemplateSelector x:Key="tempSelector" />
</Window.Resources>
<ListBox ItemSource="{Binding items}" ItemTemplateSelector="{StaticResource tempSelector}" />
</Window>
【讨论】:
DataTemplate 的DataType 属性,那么您不需要使用DataTemplateSelector 类。
您可以使用DataTrigger 类。
DataTrigger 允许您在数据对象的属性值与指定值匹配时设置属性值。
或者,您可以使用DataTemplateSelector 类。
通常,当您有多个相同类型的对象的 DataTemplate 并且您希望提供自己的逻辑以根据每个数据对象的属性选择要应用的 DataTemplate 时,您会创建一个 DataTemplateSelector。
【讨论】:
解决此问题的最佳方法是拥有一个包含您想在ListBox 中看到的所有项目的集合属性,将该集合绑定到显示项目列表的控件,并使用不同的数据模板更改用于每种类型项目的视觉效果。
例如,您可能有一个邮政编码类型:
public class PostalCodeEntry
{
public string Value { get; set; } // Implement using standard INotifyPropertyChanged pattern
}
还有一个“布尔”类型:
public class BooleanEntry
{
public bool Value { get; set; } // Implement using standard INotifyPropertyChanged pattern
}
您说您想要为每个条目类型设置一个标签,因此基类是个好主意:
public abstract class EntryBase
{
public string Label { get; set; } // Implement using standard INotifyPropertyChanged pattern
}
那么BooleanEntry 和PostalCodeEntry 将派生自EntryBase。
这是 Models 排序的。您只需要这些的集合,以便您可以从 UI 绑定到它们。将适当的集合属性添加到您的 Window 或 ViewModel:
public ObservableCollection<EntryBase> Entries { get; private set; }
在您的 UI(View,在 XAML 中实现)中,使用知道如何绑定到项目列表并将它们可视化的控件实例。在 WPF 中,这将是 ItemsControl 或从它派生的控件,例如 ListBox 或 ListView:
<ItemsControl ItemsSource="{Binding Entries}" />
您可以看到我们如何将ItemsSource 属性绑定到名为Entries 的代码隐藏属性。 ItemsControl(及其后代)知道如何将这些项目转换为可视化表示。默认情况下,您的自定义对象(在我们的示例中为EntryBase)将被转换为字符串并显示为文本块。但是,通过使用data templates,您可以控制从对象到视觉的转换是如何发生的。
如果您像这样将几个数据模板添加到资源部分:
<Window ... xmlns:my="clr-namespace:---your namespace here---">
<Window.Resources>
<DataTemplate DataType="{x:Type my:PostalCodeEntry}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Label}" />
<TextBox Text="{Binding Value}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type my:BooleanEntry}">
<CheckBox Content="{Binding Label}" IsChecked="{Binding Value}" />
</DataTemplate>
</Window.Resources>
然后在此之后添加<ItemsControl ... 元素,然后您应该会看到PostalCodeEntry 类型的TextBlock/TextBox 组合和BooleanEntry 类型的CheckBox。
希望如果您能完成这项工作,它将让您了解如何扩展它以应对其他模型类型及其匹配的数据模板。
【讨论】: