WPF列表相关控件的一些用法

结合一个小例子看下DataGrid、ListBox和ListView三个控件的简单用法。后续随着了解程度的深入不断完善本文。

例子效果

  • 从左向右依次是ListBox、ListView和DataGrid。
  • ListBox和DataGrid用来呈现学生列表信息,前者只显示学生名字,后者显示学生详细信息;中间的ListView显示选中学生的爱好信息。
  • ListBox和DataGrid的选中项可以同步。
  • 可以在DataGrid中删除选中行,或在选中行上方添加一行。

View

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
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="500">
<StackPanel Orientation="Vertical">
<DockPanel>
<ListBox DockPanel.Dock="Left" MinWidth="100" MaxWidth="150" Margin="5"
SelectedItem="{Binding SelectedStudent,Mode=TwoWay}"
ItemsSource="{Binding Students}"
DisplayMemberPath="Name"/>

<ListView DockPanel.Dock="Left" MinWidth="150" MaxWidth="200" Margin="5"
ItemsSource="{Binding SelectedStudent.Hobbys}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="姓名" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="频率" DisplayMemberBinding="{Binding Frequency}"/>
<GridViewColumn Header="地点" DisplayMemberBinding="{Binding Address}"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>

<DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding Students}"
SelectionMode="Single"
SelectionUnit="FullRow"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserResizeColumns="True"
CanUserSortColumns="False"
CanUserReorderColumns="False"
SelectedItem="{Binding SelectedStudent,Mode=TwoWay}"
MinHeight="200" Margin="5">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding Id}" IsReadOnly="True"/>
<DataGridTextColumn Header="姓名" Binding="{Binding Name}" />
<DataGridTextColumn Header="年龄" Binding="{Binding Age}" />
<DataGridTextColumn Header="座右铭" Binding="{Binding Motto}" />
</DataGrid.Columns>

<DataGrid.InputBindings>
<KeyBinding Key="V" Modifiers="Ctrl" Command="{Binding PasteCommand}"/>
</DataGrid.InputBindings>

<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="插入行"
Command="{Binding InsertLineCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu},Path=PlacementTarget}"/>
<MenuItem Header="删除行"
Command="{Binding DeleteLineCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu},Path=PlacementTarget}"/>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
</DockPanel>
</StackPanel>
</Window>

  • ListBox可以绑定一个对象数组,但可以指定显示对象的哪个属性(通过DisplayMemberPath设置)。
  • 为ListBox绑定SelectedItem属性绑定后台数据SelectedStudent,且为双向绑定,可以随时获取最新的选中项。
  • ListView也可以绑定一个对象数组,且可以为每一列GridViewColumn指定绑定的属性。
  • 将ListView的ItemsSource绑定到SelectedStudent.Hobbys,实现所选学生发生变化时,爱好列表同步更新。
  • 同理,也为DataGrid的SelectedItem属性绑定后台数据SelectedStudent,实现与ListView的选中同步。
  • ContextMenu和DataGrid不在同一个可视化树中,无法直接找到DataGrid,借助PlacementTarget获得上下文菜单指向的控件来获取DataGrid,并作为命令参数传递出去。
  • 通过KeyBinding可以方便地处理键盘事件。

ViewModel

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
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;

namespace WpfApp1 {
public partial class MainWindowViewModel : ObservableObject {
[ObservableProperty]
private ObservableCollection<Student> _students = new ObservableCollection<Student>();

[ObservableProperty]
private Student _selectedStudent = null;

[RelayCommand]
private void InsertLine(Object paramater) {
var newStudent = new Student() { Name = "XXX", Motto = "XXX" };
Students.Insert(Students.IndexOf(SelectedStudent), newStudent);
SelectedStudent = newStudent;
}

[RelayCommand]
private void DeleteLine(Object paramater) {
var res = Students.Remove(SelectedStudent);
SelectedStudent = null;
}

[RelayCommand]
private void Paste() {
MessageBox.Show("Paste");
}

public MainWindowViewModel() {
Students.Clear();
List<Hobby> hobbies = new List<Hobby>() {
new Hobby(){Name="跑步",Frequency=3,Address="江滩"},
new Hobby(){Name="散步",Frequency=5,Address="江滩"},
};
List<Hobby> hobbies2 = new List<Hobby>() {
new Hobby(){Name="瑜伽",Frequency=3,Address="江滩"},
new Hobby(){Name="健身",Frequency=5,Address="江滩"},
};

Students.Clear();
Students.Add(new Student(1, "小红", 32, "Hello World 1!") {
Hobbys = new ObservableCollection<Hobby>(hobbies),
});
Students.Add(new Student(2, "小宇", 32, "Hello World 2!") {
Hobbys = new ObservableCollection<Hobby>(hobbies),
});
Students.Add(new Student(3, "小明", 32, "Hello World 3!") {
Hobbys = new ObservableCollection<Hobby>(hobbies2),
});
Students.Add(new Student(4, "小亮", 32, "Hello World 4!") {
Hobbys = new ObservableCollection<Hobby>(hobbies2),
});
}
}

public partial class Student : ObservableObject {
public Student() { }

public Student(int id, string name, int age, string motto) {
Id = id;
Name = name;
Age = age;
Motto = motto;
}

[ObservableProperty]
private int _id;
[ObservableProperty]
private string _name;
[ObservableProperty]
private int _age;
[ObservableProperty]
private string _motto;
[ObservableProperty]
private ObservableCollection<Hobby> _hobbys = new ObservableCollection<Hobby>();
}

public partial class Hobby : ObservableObject {
[ObservableProperty]
private string _name;

[ObservableProperty]
private int _frequency;

[ObservableProperty]
private string _address;
}
}

以上ViewModel的定义没有特别之处,典型的MVVM方式。

(转载本站文章请注明作者和出处lihaohello.top,请勿用于任何商业用途)

评论