Listbox DataTemplate 应用于不同的模型

问题描述

我有两个表,其中也只包含一个作为键的值。 我创建了一个列表框来显示修改它。

  • 表 1 为 TTypes1,字段为 Type1 字符串
  • 表 2 为 TTypes2,字段为 Type2 字符串

我写了这个DataTemplate

<DataTemplate x:Key="ListBoxItems">
    <Grid>
        <Grid.ColumnDeFinitions>
            <ColumnDeFinition Width="*" />
            <ColumnDeFinition Width="Auto" />
        </Grid.ColumnDeFinitions>

        <Label Grid.Column="0" Content="{Binding}" />
        <StackPanel Grid.Column="1" Orientation="Horizontal">
            <!--  edit to swap with save  -->
            <Button
                Content="&#xE74E;"
                ContentTemplate="{StaticResource IconPanelButton}"
                DockPanel.Dock="Right"
                Style="{StaticResource MahApps.Styles.Button.Flat}"
                ToolTipService.ToolTip="Save"
                Visibility="{Binding IsVisible}" />
            <!--  Cancel - visible only on edit  -->
            <Button
                Click="LockUnlock_Click"
                Content="{Binding Icon}"
                ContentTemplate="{StaticResource IconPanelButton}"
                DockPanel.Dock="Right"
                Style="{StaticResource MahApps.Styles.Button.Flat}"
                ToolTipService.ToolTip="{Binding ToolTip}" />
            <!--  Delete  -->
            <Button
                Click="LockUnlock_Click"
                Content="{Binding Icon}"
                ContentTemplate="{StaticResource IconPanelButton}"
                DockPanel.Dock="Right"
                Style="{StaticResource MahApps.Styles.Button.Flat}"
                ToolTipService.ToolTip="{Binding ToolTip}" />
            <!--  Add  -->
            <Button
                Click="LockUnlock_Click"
                Content="{Binding Icon}"
                ContentTemplate="{StaticResource IconPanelButton}"
                DockPanel.Dock="Right"
                Style="{StaticResource MahApps.Styles.Button.Flat}"
                ToolTipService.ToolTip="{Binding ToolTip}" />
        </StackPanel>
    </Grid>
</DataTemplate>

这是列表框,但我无法让它按我的意愿工作。

如果我这样离开:

<Label Grid.Column="0" Content="{Binding}" />

我没有看到文本,但看到的是 TTypes1TTypes2 类型。

但如果我写:

<Label Grid.Column="0" Content="{Binding Type1}" />

那么我不能在 TType2 列表框中使用它。

这里是我使用它的地方:

<ScrollViewer
    Margin="2"
    HorizontalScrollBarVisibility="Auto"
    VerticalScrollBarVisibility="Auto">
    <ListBox
        Margin="2"
        AlternationCount="2"
        BorderThickness="1"
        ItemsSource="{Binding TTypes1}"
        Selectedindex="0"
        SelectionMode="Single"
        ItemTemplate="{StaticResource ListBoxItems}"
        Style="{StaticResource MahApps.Styles.ListBox.Virtualized}">
    </ListBox>
</ScrollViewer>

第二个是:

<ScrollViewer
    Margin="2"
    HorizontalScrollBarVisibility="Auto"
    VerticalScrollBarVisibility="Auto">
    <ListBox
        Margin="2"
        AlternationCount="2"
        BorderThickness="1"
        ItemsSource="{Binding TTypes2}"
        Selectedindex="0"
        SelectionMode="Single"
        ItemTemplate="{StaticResource ListBoxItems}"
        Style="{StaticResource MahApps.Styles.ListBox.Virtualized}">
    </ListBox>
</ScrollViewer>

我错过了什么?

解决方法

多个数据模板

通常的处理方法是为每种类型创建一个不同的数据模板,例如TType1TType2

<DataTemplate x:Key="ListboxItemsTType1"
              DataType="{x:Type local:TType1}">
   <Grid>
      <Grid.ColumnDefinitions>
         <ColumnDefinition Width="*" />
         <ColumnDefinition Width="Auto" />
      </Grid.ColumnDefinitions>

      <Label Grid.Column="0"
             Content="{Binding Type1}" />
      <!-- ...other markup. -->
   </Grid>
</DataTemplate>
<DataTemplate x:Key="ListboxItemsTType2"
              DataType="{x:Type local:TType2}">
   <Grid>
      <Grid.ColumnDefinitions>
         <ColumnDefinition Width="*" />
         <ColumnDefinition Width="Auto" />
      </Grid.ColumnDefinitions>

      <Label Grid.Column="0"
             Content="{Binding Type2}" />
      <!-- ...other markup. -->
   </Grid>
</DataTemplate>

在您的ListBoxes 中引用特定模板。您还可以从数据模板中删除 x:Key,因此它们会自动应用于 ListBox 中的匹配类型。这也适用于列表中的混合项目。

<ScrollViewer Grid.Row="0"
              Margin="2"
              HorizontalScrollBarVisibility="Auto"
              VerticalScrollBarVisibility="Auto">
   <ListBox
      ItemTemplate="{StaticResource ListboxItems}"
      ...
   </ListBox>
</ScrollViewer>
<ScrollViewer Grid.Row="1"
              Margin="2"
              HorizontalScrollBarVisibility="Auto"
              VerticalScrollBarVisibility="Auto">
   <ListBox
      ...
      ItemTemplate="{StaticResource ListboxItems}"
   </ListBox>
</ScrollViewer>

其他方法

如果您真的想保留单个数据模板,则必须根据绑定为数据上下文的对象的项目类型来切换绑定。有多种方法可以实现这一点。

这是一个使用 converter that converts an object to its type 中的 related question 的示例,请复制它。 Label 的样式将使用数据触发器根据该类型应用正确的绑定。

<local:DataTypeConverter x:Key="DataTypeConverter" />

<DataTemplate x:Key="ListboxItems">
   <Grid>
      <Grid.ColumnDefinitions>
         <ColumnDefinition Width="*" />
         <ColumnDefinition Width="Auto" />
      </Grid.ColumnDefinitions>

      <Label Grid.Column="0">
         <Label.Style>
            <Style TargetType="{x:Type Label}"
                   BasedOn="{StaticResource {x:Type Label}}">
               <Setter Property="Content"
                       Value="{x:Null}" />
               <Style.Triggers>
                  <DataTrigger Binding="{Binding Converter={StaticResource DataTypeConverter}}"
                               Value="{x:Type local:TType1}">
                     <Setter Property="Content"
                             Value="{Binding Type1}" />
                  </DataTrigger>
                  <DataTrigger Binding="{Binding Converter={StaticResource DataTypeConverter}}"
                               Value="{x:Type local:TType2}">
                     <Setter Property="Content"
                             Value="{Binding Type2}" />
                  </DataTrigger>
               </Style.Triggers>
            </Style>
         </Label.Style>
      </Label>
      <StackPanel Grid.Column="1"
                  Orientation="Horizontal">
         <!--  edit to swap with save  -->
         <Button Content="&#xE74E;"
                 ContentTemplate="{StaticResource IconPanelButton}"
                 DockPanel.Dock="Right"
                 Style="{StaticResource MahApps.Styles.Button.Flat}"
                 ToolTipService.ToolTip="Save"
                 Visibility="{Binding IsVisible}" />
         <!--  Cancel - visible only on edit  -->
         <Button Click="LockUnlock_Click"
                 Content="{Binding Icon}"
                 ContentTemplate="{StaticResource IconPanelButton}"
                 DockPanel.Dock="Right"
                 Style="{StaticResource MahApps.Styles.Button.Flat}"
                 ToolTipService.ToolTip="{Binding ToolTip}" />
         <!--  Delete  -->
         <Button Click="LockUnlock_Click"
                 Content="{Binding Icon}"
                 ContentTemplate="{StaticResource IconPanelButton}"
                 DockPanel.Dock="Right"
                 Style="{StaticResource MahApps.Styles.Button.Flat}"
                 ToolTipService.ToolTip="{Binding ToolTip}" />
         <!--  Add  -->
         <Button Click="LockUnlock_Click"
                 Content="{Binding Icon}"
                 ContentTemplate="{StaticResource IconPanelButton}"
                 DockPanel.Dock="Right"
                 Style="{StaticResource MahApps.Styles.Button.Flat}"
                 ToolTipService.ToolTip="{Binding ToolTip}" />
      </StackPanel>
   </Grid>
</DataTemplate>

其他完全依赖代码但更容易重用的选项是:

  • 创建一个与触发器相同的特殊值转换器,返回在代码中创建的绑定,其属性路径基于类型
  • 创建自定义标记扩展,根据类型自动选择属性路径

我不提供有关这些选项的示例,因为它们很复杂并且在很大程度上取决于您的要求。此外,我推荐第一种方法来创建多个数据模板,因为我认为从维护和灵活性的角度来看,这是最有利的。

,

如果您喜欢两个数据模板,我认为最好使用 ItemsControl.ItemTemplateSelector。 首先,您需要一个类继承类“DataTemplateSelector”并覆盖其方法以选择要使用的数据模板。

public class ModelItemTemplateSelector: DataTemplateSelector
{
    public DataTemplate Model1Template { get; set; }
    public DataTemplate Model2Template { get; set; }
    public override DataTemplate SelectTemplate(object item,DependencyObject container)
    {
        if(item is Model1)
        {
            return Model1Template;
        }
        else if(item is Model2)
        {
            return Model2Template;
        }
        return base.SelectTemplate(item,container);
    }
}

然后xaml中的代码如下

 <ListBox ItemsSource="{Binding Source}">
        <ListBox.ItemTemplateSelector>
            <local:ModelItemTemplateSelector Model1Template="{StaticResource Model1Template}" Model2Template="{StaticResource Model2Template}" />
        </ListBox.ItemTemplateSelector>
    </ListBox>

以及其他代码:

两个数据模板

 <DataTemplate x:Key="Model1Template" DataType="{x:Type local:Model1}">
        <TextBlock Text="{Binding Age}" />
    </DataTemplate>
    <DataTemplate x:Key="Model2Template" DataType="{x:Type local:Model2}">
        <TextBlock Text="{Binding Name}" />
    </DataTemplate>

两种类型

public class BaseModel : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged(string propertyName)
    {
        this.PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));
    }
}
public class Model1 : BaseModel
{
    private int age;

    public int Age
    {
        get { return age; }
        set
        {
            age = value;
            this.RaisePropertyChanged(nameof(Age));
        }
    }

}

public class Model2 : BaseModel
{
    private string name;

    public string Name
    {
        get { return name; }
        set
        {
            name = value;
            this.RaisePropertyChanged(nameof(Name));
        }
    }

}

虚拟机中的源

 private ObservableCollection<BaseModel> source;

    public ObservableCollection<BaseModel> Source
    {
        get { return source; }
        set
        {
            source = value;
            this.RaisePropertyChanged(nameof(Source));
        }
    }