今天要实现一个DataGrid的Select All功能。查了一下,多是用EventHandler完成的,看起来觉得有点乱,所以自己写了一个。

 

 

代码很简单

 

Xaml
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的话会感觉有点乱。

posted on 2010-12-31 14:34  Jason Li  阅读(2434)  评论(4编辑  收藏  举报