WPF之TreeView🔥
CZerocheng 2/1/2024 WPF
# 效果演示

# 什么是TreeView
TreeView 是 Windows Presentation Foundation (WPF) 提供的一个控件,用于以树形结构展示层级化的数据。它允许用户以展开和折叠的方式浏览数据层级,常用于文件浏览器、组织结构图、菜单导航等场景。
# 优点
# 1、层级数据展示
直观的层级结构:TreeView 能够清晰地展示多层次的数据,使用户能够直观地理解数据之间的父子关系。 无限嵌套:支持无限层级的嵌套,适用于各种复杂的数据结构,如文件系统、组织架构等。
# 2、性能优化
虚拟化:支持 UI 虚拟化(UI Virtualization),仅渲染可见部分的节点,提升大量数据时的渲染性能。 延迟加载:可以实现按需加载子节点,减少初始加载时间,优化资源使用。
# 完整代码

# 先在Nuget上面安装上面两个类库
public class ClassModel: ObservableObject
{
private string grade;
public string Grade
{
get { return grade; }
set { SetProperty(ref grade, value); }
}
private string name;
public string Name
{
get { return name; }
set { SetProperty(ref name, value); }
}
private ObservableCollection<StudentModel> students;
public ObservableCollection<StudentModel> Students
{
get { return students; }
set { SetProperty(ref students, value); }
}
public ClassModel()
{
students = new ObservableCollection<StudentModel>();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class GradeModel:ObservableObject
{
private string name;
public string Name
{
get { return name; }
set { SetProperty(ref name, value); }
}
private ObservableCollection<ClassModel> classes ;
public ObservableCollection<ClassModel> Classes
{
get { return classes; }
set { SetProperty(ref classes,value); }
}
public GradeModel()
{
classes = new ObservableCollection<ClassModel>();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class StudentModel : ObservableObject
{
private string grade;
public string Grade
{
get { return grade; }
set { SetProperty(ref grade, value); }
}
private string className;
public string ClassName
{
get { return className; }
set { SetProperty(ref className, value); }
}
private string name;
public string Name
{
get { return name; }
set { SetProperty(ref name, value); }
}
private string subject;
public string Subject
{
get { return subject; }
set { SetProperty(ref subject, value); }
}
private double score;
public double Score
{
get { return score; }
set { SetProperty(ref score, value); }
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class TreeNode:ObservableObject
{
private string name;
public string Name
{
get { return name; }
set { SetProperty(ref name,value); }
}
private ObservableCollection<TreeNode> children;
public ObservableCollection<TreeNode> Children
{
get { return children; }
set { SetProperty(ref children,value); }
}
public TreeNode()
{
children = new ObservableCollection<TreeNode>();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<UserControl x:Class="TreeViewDataDemo.View.StudentGradeTabelView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TreeViewDataDemo.View"
xmlns:viewmodel="clr-namespace:TreeViewDataDemo.ViewModel"
xmlns:model="clr-namespace:TreeViewDataDemo.Model"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="8*"/>
</Grid.ColumnDefinitions>
<TreeView ItemsSource="{Binding Grades}"
SelectedItemChanged="TreeView_SelectedItemChanged"
Grid.Column="0"
Margin="10">
<TreeView.Resources>
<!-- 学生节点模板 -->
<DataTemplate x:Key="StudentTemplate">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
<!-- 班级节点模板 -->
<HierarchicalDataTemplate DataType="{x:Type model:ClassModel}" ItemsSource="{Binding Students}">
<TextBlock Text="{Binding Name}" />
<HierarchicalDataTemplate.ItemTemplate>
<StaticResource ResourceKey="StudentTemplate" />
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
<!-- 班级模板资源键 -->
<HierarchicalDataTemplate x:Key="ClassTemplate" DataType="{x:Type model:ClassModel}" ItemsSource="{Binding Students}">
<TextBlock Text="{Binding Name}" />
<HierarchicalDataTemplate.ItemTemplate>
<StaticResource ResourceKey="StudentTemplate" />
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
<!-- 年级节点模板 -->
<HierarchicalDataTemplate DataType="{x:Type model:GradeModel}" ItemsSource="{Binding Classes}">
<TextBlock Text="{Binding Name}" />
<HierarchicalDataTemplate.ItemTemplate>
<StaticResource ResourceKey="ClassTemplate" />
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
<DataGrid ItemsSource="{Binding Students}"
AutoGenerateColumns="False"
Grid.Column="1"
Margin="10"
IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="姓名" Binding="{Binding Name}" Width="*" />
<!-- 动态列,根据数据类型显示不同的信息 -->
<DataGridTextColumn Header="科目" Binding="{Binding Subject}" Width="*" />
<DataGridTextColumn Header="成绩" Binding="{Binding Score}" Width="*" />
<!-- 如果需要显示学生姓名或班级名称,可以添加更多列 -->
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
public partial class StudentGradeTabelView : UserControl
{
private StudentGradeTabelViewModel viewModel = new StudentGradeTabelViewModel();
public StudentGradeTabelView()
{
InitializeComponent();
this.DataContext = viewModel;
}
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
viewModel.GetModels(e);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class StudentGradeTabelViewModel:ObservableObject
{
public ObservableCollection<GradeModel> Grades { get; set; } = new ObservableCollection<GradeModel>();
public ObservableCollection<StudentModel> Students { get; set; } = new ObservableCollection<StudentModel>();
private ClassModel selectedClass;
public ClassModel SelectedClass
{
get => selectedClass;
set => SetProperty(ref selectedClass,value);
}
public StudentGradeTabelViewModel()
{
Grades = new ObservableCollection<GradeModel>();
Students = new ObservableCollection<StudentModel>();
// 自动生成全校成绩数据
GenerateSchoolGrades();
}
private string GetChineseNumber(int number)
{
string[] chineseNumbers = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十" };
if (number <= 10)
return chineseNumbers[number];
else
return number.ToString(); // 超过10的数字直接使用阿拉伯数字
}
public void GenerateSchoolGrades(int numberOfGrades = 6, int classesPerGrade = 3, int studentsPerClass = 30)
{
var random = new Random();
var subjects = new string[] { "语文"};
//var subjects = new string[] { "语文", "数学", "英语", "科学", "历史", "地理" };
Grades.Clear();
for (int gradeIndex = 1; gradeIndex <= numberOfGrades; gradeIndex++)
{
var grade = new GradeModel
{
Name = $"{gradeIndex}年级"
};
for (int classIndex = 1; classIndex <= classesPerGrade; classIndex++)
{
var className = $"{GetChineseNumber(classIndex)}班";
var classObj = new ClassModel
{
Name = className,
Grade = grade.Name
};
for (int studentIndex = 1; studentIndex <= studentsPerClass; studentIndex++)
{
var studentName = $"学生{GetChineseNumber(studentIndex)}";
foreach (var subject in subjects)
{
var score = Math.Round(random.NextDouble() * 40 + 60, 1); // 60 ~ 100 分
var studentGrade = new StudentModel
{
Grade = grade.Name,
ClassName = className,
Name = studentName,
Subject = subject,
Score = score
};
classObj.Students.Add(studentGrade);
}
}
grade.Classes.Add(classObj);
}
Grades.Add(grade);
}
}
public void GetModels(RoutedPropertyChangedEventArgs<object> e)
{
Students.Clear();
if (e.NewValue is ClassModel classmodel)
{
var grade = Grades.Where(it => it.Name == classmodel.Grade).First();
var clas = grade.Classes.Where(it=>it.Name == classmodel.Name).First();
foreach (var item in clas.Students)
{
Students.Add(item);
}
}
if (e.NewValue is StudentModel student)
Students.Add(student);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<Window x:Class="TreeViewDataDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TreeViewDataDemo"
xmlns:view="clr-namespace:TreeViewDataDemo.View"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<view:StudentGradeTabelView/>
</Grid>
</Window>
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
<Application x:Class="TreeViewDataDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewDataDemo"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16