Wpf 要应用于不同模型的Listbox数据模板

Wpf 要应用于不同模型的Listbox数据模板,wpf,xaml,.net-core,data-binding,datatemplate,Wpf,Xaml,.net Core,Data Binding,Datatemplate,我有两个表,它们也只包含一个键值。 我创建了一个列表框来显示和修改它 表1为t类型1,字段为Type1字符串 表2为t类型2,字段为Type2字符串 我已经写了这个DataTemplate: <DataTemplate x:Key="ListboxItems"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="

我有两个表,它们也只包含一个键值。 我创建了一个列表框来显示和修改它

  • 表1为
    t类型1
    ,字段为
    Type1
    字符串
  • 表2为
    t类型2
    ,字段为
    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>
然后我无法在
t类型2
列表框中使用它

这里是我使用它的地方:

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

我缺少什么?

多个数据模板 处理此问题的通常方法是为每种类型创建一个不同的数据模板,例如为
TType1
TType2

<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>
其他完全依赖于代码但更易于重用的选项有:

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

我不提供这些选项的示例,因为它们很复杂,并且严重依赖于您的需求。此外,我建议使用第一种方法创建多个数据模板,因为我认为从维护和灵活性的角度来看,这是最有利的。

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

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>
虚拟机中的源

 private ObservableCollection<BaseModel> source;

    public ObservableCollection<BaseModel> Source
    {
        get { return source; }
        set
        {
            source = value;
            this.RaisePropertyChanged(nameof(Source));
        }
    }
私有可观测采集源;
公共可观测收集源
{
获取{返回源;}
设置
{
来源=价值;
此.RaisePropertyChanged(名称(来源));
}
}

如果要对同一项目类的不同实例或其他更复杂的场景使用不同的数据模板,通常只使用DataTemplateSelector。对于不同的项目类,根据数据类型自动选择不同的数据模板要简单得多。因为您已经在示例中设置了DataTemplates的数据类型,所以ModelItemTemplateSelector类是完全冗余的。它所做的一切都已在WPF中实现,只需从DataTemplates中删除x:Key属性即可。@Clemens是的,你说得对,谢谢你的话。
<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>
<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>
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);
    }
}
 <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));
        }
    }