今天要实现一个DataGrid的Select All功能。查了一下,多是用EventHandler完成的,看起来觉得有点乱,所以自己写了一个。
代码很简单
1 < UserControl x:Class ="SilverlightApp.SelectAllAndOrder.SelectAllPage"
2 xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:d ="http://schemas.microsoft.com/expression/blend/2008"
5 xmlns:mc ="http://schemas.openxmlformats.org/markup-compatibility/2006"
6 xmlns:Primitives ="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"
7 mc:Ignorable ="d"
8 d:DesignHeight ="500" d:DesignWidth ="400" xmlns:sdk ="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" >
9
10 < UserControl.Resources >
11 < ResourceDictionary >
12 < Style x:Key ="SelectAllTemplate" TargetType ="Primitives:DataGridColumnHeader" >
13 < Setter Property ="ContentTemplate" >
14 < Setter.Value >
15 < DataTemplate >
16 < CheckBox IsChecked =" {Binding DataContext.IsSelectAll,Mode=TwoWay,ElementName=LayoutRoot} " />
17 </ DataTemplate >
18 </ Setter.Value >
19 </ Setter >
20 </ Style >
21 < DataTemplate x:Key ="CheckBoxColumn" >
22 < CheckBox IsChecked =" {Binding IsSelected, Mode=TwoWay} " />
23 </ DataTemplate >
24 </ ResourceDictionary >
25 </ UserControl.Resources >
26
27 < Grid x:Name ="LayoutRoot" Margin ="15" Background ="White" >
28 < Grid.RowDefinitions >
29 < RowDefinition Height ="400" />
30 < RowDefinition Height ="Auto" />
31 </ Grid.RowDefinitions >
32
33 < sdk:DataGrid ItemsSource =" {Binding Users} " AutoGenerateColumns ="False" IsReadOnly ="True" RowHeight ="25" CanUserReorderColumns ="False" >
34 < sdk:DataGrid.Columns >
35 < sdk:DataGridTemplateColumn Width ="55" HeaderStyle =" {StaticResource SelectAllTemplate} "
36 CellTemplate =" {StaticResource CheckBoxColumn} " ></ sdk:DataGridTemplateColumn >
37 < sdk:DataGridTextColumn Width ="120" Header ="姓名" Binding =" {Binding Name} " />
38 </ sdk:DataGrid.Columns >
39 </ sdk:DataGrid >
40
41 < StackPanel Orientation ="Horizontal" Grid.Row ="1" >
42 < Button Content ="Add One Column" Click ="AddColumn" />
43 < Button Margin ="10,0" Content ="Delete One Column" Click ="DeleteColumn" />
44 </ StackPanel >
45 </ Grid >
46 </ UserControl >
47
本来打算在DataGridHeader的DataTemplate里直接绑定IsSelectAll了,但是Debug后发现没有DataContext。
后来又使用<sdk:DataGridTemplateColumn Header="{Binding XXX}" /> 这种方法,倒是有DataContext,但是是个Binding类型,不起作用。
最后找到了上面的方法。虽然经常用ElementName属性,但是从没这么用过。感觉很奇妙。
下面是后台代码:
1 public partial class SelectAllPage : UserControl
2 {
3 private SelectAllViewModel _dataContext;
4
5 public SelectAllPage()
6 {
7 InitializeComponent();
8
9 this .Loaded += (s, e) =>
10 {
11 this .DataContext = _dataContext = new SelectAllViewModel();
12 };
13 }
14
15 private void AddColumn( object sender, RoutedEventArgs e)
16 {
17 _dataContext.AddUser();
18 }
19
20 private void DeleteColumn( object sender, RoutedEventArgs e)
21 {
22 _dataContext.DeleteUser();
23 }
24
25 }
26
27 public class SelectAllViewModel : INotifyPropertyChanged
28 {
29 public event PropertyChangedEventHandler PropertyChanged;
30 public ObservableCollection < UserViewModel > Users { get ; set ; }
31
32 public bool IsSelectAll
33 {
34 get
35 {
36 int userCount = Users.Count;
37 int selectedUserCount = Users.Count(u => u.IsSelected);
38 if (userCount == 0 ) // 如果一列没有,则处于不被选中状态
39 return false ;
40 return userCount == selectedUserCount; // 当被选中的人数与总人数相等时处于选中状态
41 }
42 set
43 {
44 foreach (var mapping in Users)
45 {
46 mapping.IsSelected = value;
47 }
48 }
49 }
50
51 public SelectAllViewModel()
52 {
53 // 在生成对象的时候注册PropertyChanged事件,这样当某个User被选中时就可以通知SelectAll进行状态更新
54 Users = new ObservableCollection < UserViewModel > ();
55 Users.Add( new UserViewModel(UserPropertyChanged));
56 Users.Add( new UserViewModel(UserPropertyChanged));
57 Users.Add( new UserViewModel(UserPropertyChanged));
58 Users.Add( new UserViewModel(UserPropertyChanged));
59
60 // 注册CollectionChanged事件
61 Users.CollectionChanged += UsersCollectionChanged;
62 }
63
64 public void AddUser()
65 {
66 Users.Add( new UserViewModel(UserPropertyChanged));
67 }
68
69 public void DeleteUser()
70 {
71 if (Users.Count > 0 )
72 Users.RemoveAt( 0 );
73 }
74
75 private void UserPropertyChanged( object sender, PropertyChangedEventArgs e)
76 {
77 if (e.PropertyName == " IsSelected " )
78 { // 如果是由于某一行前面的CheckBox被选中而触发改事件,则通知SelectAll更新时间
79 if ( this .PropertyChanged != null )
80 PropertyChanged( this , new PropertyChangedEventArgs( " IsSelectAll " ));
81 }
82 }
83 private void UsersCollectionChanged( object sender, NotifyCollectionChangedEventArgs e)
84 {
85 // 如果表格里增加了列或减少了列,也应该通知SelectAll更新状态
86 // 比如先全部选中,然后点击Add Column,这时新增加的列应该不被选中,因此SelectAll也应该不被选中
87 if ( this .PropertyChanged != null )
88 PropertyChanged( this , new PropertyChangedEventArgs( " IsSelectAll " ));
89 }
90 }
91
92 public class UserViewModel : INotifyPropertyChanged
93 {
94 private static int i = 0 ;
95 public event PropertyChangedEventHandler PropertyChanged;
96
97 public UserViewModel(PropertyChangedEventHandler eventHandler)
98 {
99 this .PropertyChanged += eventHandler;
100 }
101
102 public string Name
103 {
104 get { return " Name " + i ++ ; }
105 }
106
107 private bool _isSelected;
108 public bool IsSelected
109 {
110 get { return _isSelected; }
111 set
112 {
113 _isSelected = value;
114 if ( this .PropertyChanged != null ) // 不是为了通知界面更新CheckBox状态,而是为了通知SelectAll更新状态
115 PropertyChanged( this , new PropertyChangedEventArgs( " IsSelected " ));
116 }
117 }
118 }
这样就可以了。感觉Select All一般属于一种附属类的功能,如果大量使用EventHandler的话会感觉有点乱。