c# - wpf将不同的数据模板绑定到contentcontrol中不同类型的对象
问题描述
我是 WPF 和 MVVM 的新手。我想要做的是将两个不同的 DataTemplates 绑定到一个 ContentControl 中的两种不同类型的对象。每种对象对应一个DataTemplate。
这两种对象分别称为单元和组件。它们包含不同的属性。例如,一个 Unit 有 3 个属性:Id、Name和Manufacture。一个组件有 3 个属性Id、Type和Materials。示例代码如下:
public class Unit : INotifyPropertyChanged
{
private int _id;
private string _name;
private string _manufacture;
public int Id
{
get {return this._id}
set
{
this._id = value;
OnPropertyChanged("Id")
}
{
public string Name
{
get {return this._name}
set
{
this._id = value;
OnPropertyChanged("Name")
}
{
public string Manufacture
{
get {return this._manufacture}
set
{
this._id = value;
OnPropertyChanged("Manufacture")
}
{
public event PropertyChangedEventHandler PropertyChanged;
...
}
Component 类具有类似的结构。
在 MainWindow 中,左侧有一个 ListBox 列出对象的名称(我以后会将其更改为 TreeView),右侧有一个 ContentControl。我希望当我选择一个对象的名称时,该对象的详细信息将显示在右侧。MainWindow的代码如下:
<Windows.Resources>
<CollectionViewSource
Source="{Binding Source={x:Static Application.Current}, Path=UnitItems}"
x:Key="UnitDataView">
</CollectionViewSource>
<CollectionViewSource
Source="{Binding Source={x:Static Application.Current}, Path=ComponentItems}"
x:Key="ComponentDataView">
</CollectionViewSource>
<CompositeCollection x:Key="AllDataView
<CollectionContainer Collection="{Binding Source={StaticResource UnitDataView}}" />
<CollectionContainer Collection="{Binding Source={StaticResource ComponentDataView}}" />
</CompositeCollection>
<local: PartDataTemplateSelector x:Key="MyDataTemplateSelector"
UnitTemplate="{StaticResource unitTemplate}"
ComponentTemplate="{StaticResource componentTemplate}" />
</Windows.Resources>
<Grid>
<Grid.ColumnDefinition>
<ColumnDefinition>
<ColumnDefinition>
</Grid.ColumnDefinition>
<ListBox x:Name="ComponentListView" Grid.Column="0"
ItemsSource="{Binding Source={StaticResource AllDataView}}" />
<TabControl Grid.Column="1"
<TabItem Header="Basic Info">
<ContentControl x:Name="BasicInfoContent"
ContentTemplateSelector="{StaticResource MyDataTemplateSelector}"
Content="{Binding Source={StaticResource AllDataView}}">
</ContentControl>
</TabItem>
</TabControl>
</Grid>
UnitItems
和是 App.xaml.cs 中定义的ComponentItems
两个ObservableCollection<T>
对象。我在 App.xaml 中定义了一些 DataTemplates。示例代码如下:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..."
</ResourceDictionary.MergedDictionaries>
<DataTemplate DataType="{x:Type src:Unit}">
<!-- This template is to show the name of a unit object in the ListBox -->
</DataTemplate>
<DataTemplate DataType="{x:Type src:Component}">
<!-- This template is to show the name of a component object in the ListBox -->
</DataTemplate>
<DataTemplate x:Key="unitTemplate" DataType="{x:Type src:Unit}">
<!-- This template is to show the details of a unit object in the ContentControl -->
</DataTemplate>
<DataTemplate x:Key="componentTemplate" DataType="{x:Type src:Component}">
<!-- This template is to show the details of a component object in the ContentControl -->
</DataTemplate>
</Application.Resources>
我的自定义 DataTemplateSelector 如下:
class MyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate UnitTemplate { get; set; }
public DataTemplate ComponentTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
swith (item)
{
case Unit _:
return UnitTemplate;
case Component _:
return ComponentTemplate;
}
return null;
}
}
我已经阅读了这篇文章ContentTemplateSelector并尝试了 ContentTemplateSelector,但是由于我在 ContentControl 中使用 CompositeCollection 和 CollectionContainer 来绑定这两种对象,所以我的 DataTemplateSelector 类中的 item 对象接收的是 CompositeCollection 类型,不是 Unit 类型也不是 Component 类型,因此没有返回正确的模板。我也尝试了本文DataType Property中提到的方法,即为每个DataTemplate 设置一个DataType 属性,并将Path 设置为“/”。也许我误解了它,但它也不起作用,我认为它与 ContentTemplateSelector 有同样的问题。所以有人可以帮我解决这个问题吗?
这是我第一次在 Stack Overflow 上提问。我知道我的一些描述和代码对于这个问题来说是微不足道的,但我只是不想错过任何可能与我的问题有关的细节。我为此道歉。另外,如果我的编码风格和数据结构有任何问题,请随时指出。对此,我真的非常感激。感谢您的阅读和帮助!
解决方案
您不需要 DataTemplateSelector。只需确保可以自动选择详细的 DataTemplates,而不是为其分配密钥。
您的对象似乎也不需要两个集合。您不妨从一个公共基类派生 Unit 和 Component,并拥有一个基类引用集合。
最后应该有一个视图模型,除了对象集合之外,它还具有当前选定对象的属性。
以这个简化的示例视图模型为例:
public class Base
{
public int Id { get; set; }
}
public class Unit : Base
{
public string UnitData { get; set; }
}
public class Component : Base
{
public string ComponentData { get; set; }
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Base> Objects { get; }
= new ObservableCollection<Base>();
private Base selectedObject;
public Base SelectedObject
{
get { return selectedObject; }
set
{
selectedObject = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(SelectedObject)));
}
}
}
它的一个实例应该分配给窗口的 DataContext:
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
vm.Objects.Add(new Unit { Id = 1, UnitData = "Unit Data" });
vm.Objects.Add(new Component { Id = 2, ComponentData = "Component Data" });
DataContext = vm;
}
最后,XAML 将是这样的:
<ListBox ItemsSource="{Binding Objects}"
SelectedItem="{Binding SelectedObject}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:Unit}">
<TextBlock>
<Run Text="Unit, Id:"/>
<Run Text="{Binding Id}"/>
</TextBlock>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Component}">
<TextBlock>
<Run Text="Component, Id:"/>
<Run Text="{Binding Id}"/>
</TextBlock>
</DataTemplate>
</ListBox.Resources>
</ListBox>
<ContentControl Grid.Column="1" Content="{Binding SelectedObject}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:Unit}">
<StackPanel>
<TextBlock Text="{Binding Id}"/>
<TextBlock Text="{Binding UnitData}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Component}">
<StackPanel>
<TextBlock Text="{Binding Id}"/>
<TextBlock Text="{Binding ComponentData}"/>
</StackPanel>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
推荐阅读
- vue.js - Vue js更改外部组件的道具
- javascript - 关于 PDF 可填写表单中的 javascript setFocus 的问题 - setFocus 会影响标签顺序吗?
- mongodb - ReactJS - 如何从 MongoDB Date.now 更改日期格式
- c# - c# Image.FromFile - 批量操作?
- java - Java Staircase 打印方向错误(hackerrank)
- c# - ADO.NET 数据库应用程序运行时异常
- algorithm - 方案功能
- chisel - 将信号分配给非父模块
- swift - 如何在 UITraitEnvironmentLayoutDirection 之类的东西上使用 switch 语句?
- android - 如何将 JobService 建模为粘性服务?