WPF 根据绑定值创建不同的 ListBox 行模板

WPF Creating different ListBox row templates based on a bound value

我目前没有任何代码可以分享。只是一个设计问题。

我有一个 class,它定义了一个标签和一个关联的条目类型,我想将其绑定到 ListBox。例如,如果类型是 "Postal Code",我需要 ListBox 将行创建为 TextBlock 和 TextBox。对于 "Yes/no",我需要它知道创建旁边带有复选框的 TextBlock。可能会有 7 或 8 种不同的行类型。

解决这个问题的最佳方法是什么?

您可以使用 DataTrigger class.

A DataTrigger allows you to set property values when the property value of the data object matches a specified Value.

或者,您可以使用 DataTemplateSelector class。

Typically, you create a DataTemplateSelector when you have more than one DataTemplate for the same type of objects and you want to supply your own logic to choose a DataTemplate to apply based on the properties of each data object.

看看 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>

解决此问题的最佳方法是创建一个集合 属性,其中包含您希望在 ListBox 中看到的所有项目,将该集合绑定到显示列表的控件项目,并使用不同的数据模板来更改用于每种项目的视觉效果。

例如,您的邮政编码类型可能是:

public class PostalCodeEntry
{
    public string Value { get; set; }  // Implement using standard INotifyPropertyChanged pattern
}

还有一个 "Boolean" 类型:

public class BooleanEntry
{
    public bool Value { get; set; }  // Implement using standard INotifyPropertyChanged pattern
}

你说你想要每个条目类型的标签,所以基础 class 是个好主意:

public abstract class EntryBase
{
    public string Label { get; set; }  // Implement using standard INotifyPropertyChanged pattern
}

然后 BooleanEntryPostalCodeEntry 将派生自 EntryBase

这是 型号 排序的。您只需要这些的集合,以便您可以从 UI 绑定到它们。将适当的集合 属性 添加到您的 WindowViewModel:

public ObservableCollection<EntryBase> Entries { get; private set; }

在您的 UI(View,在 XAML 中实现)中,使用知道如何绑定到列表的控件实例项目并将其可视化。在 WPF 中,这将是一个 ItemsControl 或从它派生的控件,例如 ListBoxListView:

<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 ... 元素,然后您应该看到 TextBlock/TextBox 组合用于 PostalCodeEntry 类型和 CheckBox 用于 BooleanEntry 类型。

希望如果您能使它正常工作,它会让您了解如何扩展它以处理其他模型类型及其匹配的数据模板。