Why
- 最近闲着无聊打算写一个小工具,其中用到了
TreeView
,发现了一个在某些时候一个很坑的东西–DataType
。
How
- 新建一个
Prism
项目。
- 以下是使用的主要树的绑定数据类
public partial class UserInfo : ObservableObject
{
[ObservableProperty]
private int _id;
[ObservableProperty]
private string? _userName;
[ObservableProperty]
private string? _password;
[ObservableProperty]
private string? _email;
[ObservableProperty]
private string? _phone;
[ObservableProperty]
private string? _address;
[ObservableProperty]
private string? _city;
[ObservableProperty]
private ObservableCollection<ClassBase> _classInfos = [];
}
public partial class ClassBase : ObservableObject
{
[ObservableProperty]
private string? _className;
[ObservableProperty]
private string? _classTeacher;
}
public partial class ClassOne : ClassBase
{
[ObservableProperty]
private string? _classOneTeacher;
}
public partial class ClassTwo : ClassBase
{
[ObservableProperty]
private string? _classTwoTeacher;
}
- 以下是对应的
MainViewModel
,使用模拟数据,最简单的100行数据。
public partial class MainViewModel : ObservableObject
{
private readonly IRegionManager iRegionManager;
private readonly IEventAggregator iEventAggregator;
public MainViewModel()
{
LoadUsers();
}
public MainViewModel(IRegionManager iRegionManager, IEventAggregator iEventAggregator) : this()
{
this.iRegionManager = iRegionManager;
this.iEventAggregator = iEventAggregator;
}
[ObservableProperty]
private ObservableCollection<UserInfo> _userInfos = [];
private void LoadUsers()
{
for (int i = 0; i < 100; i++)
{
UserInfo userInfo = new()
{
Id = i,
UserName = "User" + i,
Address = "Address" + i,
};
if (i % 2 == 0)
{
userInfo.ClassInfos.Add(new ClassOne()
{
ClassOneTeacher = "Teacher1"
});
}
else
{
userInfo.ClassInfos.Add(new ClassTwo()
{
ClassTwoTeacher = "Teacher2"
});
}
this.UserInfos.Add(userInfo);
}
}
}
<TreeView
Height="300"
ItemsSource="{Binding UserInfos, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ScrollViewer.VerticalScrollBarVisibility="Visible">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type common:UserInfo}" ItemsSource="{Binding ClassInfos}">
<TextBlock Text="{Binding UserName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type common:ClassOne}">
<TextBlock Text="{Binding ClassOneTeacher}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type common:ClassTwo}">
<TextBlock Text="{Binding ClassTwoTeacher}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
<TreeView
Height="300"
ItemsSource="{Binding UserInfos, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ScrollViewer.VerticalScrollBarVisibility="Visible">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="common:UserInfo" ItemsSource="{Binding ClassInfos}">
<TextBlock Text="{Binding UserName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="common:ClassOne">
<TextBlock Text="{Binding ClassOneTeacher}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="common:ClassTwo">
<TextBlock Text="{Binding ClassTwoTeacher}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
- 以上你可以发现,最主要的差别其实就是
DataType
的内容,一个是使用x:Type
,一个是使用直接使用字符串,你可以发现哪怕是第二种方法,智能提示也是没问题的,写的很丝滑,在设计时显示的也没啥差别,,一模一样的:

- 当你信心满满的运行的时候,你就会发现不是那么个事儿了:

- 你看,这写的是不是让人很生气,明明提示也有,设计时显示也是完美的,一运行就G了,这多让人挫败。其实这就涉及到强制指定
DataType
当前是一个类型而不是一个字符串,这样WPF
才能反射获取类型,如果只是字符串,会找不到然后直接调用ToString
方法。
Tips
- 上述情况其实也可能会出现在
TargetType
,所以在需要指定类型的时候最好是就直接指定类型,哪怕智能提示显示没问题,设计时显示也没问题,但是很可能运行时显示就不是那么回事了。