Silverlight中在MVVM模式下对DatagridRow选择控件封装

  在项目中,凡是涉及到表格的地方用的最多的控件,自然少不了DataGrid的身影,它明了的展示各种数据让人十分喜欢。现在要实现一个功能,使DataGrid具有全选和项选中的功能,如果在传统后台代码中完成这个事情可以说十分简单,但是换到MVVM模式下呢? 不得不面临一个很囧的情况,为了完成UI端CheckBox被选中后能在ViewModel中获取到选中的数据,不得不在在业务实体之外添加一个字段IsChecked 来与我们的数据交互,这样不仅影响美观还影响心情。

  为了实现这一点,无疑需要设置DataGridTemplateColumn的CellTemplate,同时让每一个checkBox拥有这条数据的上下文引用,已方便在当我们选中或反选时能确定数据项,最好的设置时机当然是DataGrid的LoadingRow事件,可以捕获到我们所需要的一切。

  当然,为了实现MVVM,我们需要添加一下附加属性

 1 public static ObservableCollection<object> GetSelectedObjects(DependencyObject obj)
 2         {
 3             return (ObservableCollection<object>)obj.GetValue(SelectedObjectsProperty);
 4         }
 5 
 6         public static void SetSelectedObjects(DependencyObject obj, ObservableCollection<object> value)
 7         {
 8             obj.SetValue(SelectedObjectsProperty, value);
 9         }
10       
11         // 用于通知到ViewModel已经被选中的列
12         public static readonly DependencyProperty SelectedObjectsProperty =
13             DependencyProperty.RegisterAttached("SelectedObjects", typeof(ObservableCollection<object>), typeof(DataGrid), new PropertyMetadata(new ObservableCollection<object>()));
14 
15         public static bool GetMonitor(DependencyObject obj)
16         {
17             return (bool)obj.GetValue(MonitorProperty);
18         }
19 
20         public static void SetMonitor(DependencyObject obj, bool value)
21         {
22             obj.SetValue(MonitorProperty, value);
23         }
24 
25         // 监视器,用于在DataGrid初始化的时候能有时机注册LoadingRow事件
26         public static readonly DependencyProperty MonitorProperty =
27             DependencyProperty.RegisterAttached("Monitor", typeof(bool), typeof(DataGrid), new PropertyMetadata(false, OnMonitorStateChanged));
28 
29         private static void OnMonitorStateChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
30         {
31             var attachedDataGrid = dp as DataGrid;
32             _attachedDataGrid = attachedDataGrid;
33             _attachedDataGrid.LoadingRow += (s, ee) =>
34             {
35                 object dataContext = ee.Row.DataContext;
36                 var elementControl = _attachedDataGrid.Columns[0] as DataGridSelectableColumn;
37                 if (elementControl == null)
38                     throw new InvalidCastException("请将DataGridSelectableColumn放在DataGrid的第一列");
39                 FrameworkElement element = elementControl.GetCellContent(ee.Row);
40 
41                 var elementContext = new SelectableColumnObject() {RowDataContext = new WeakReference( dataContext) };
           //当CheckBox的IsChecked属性值变化之后发生
42 elementContext.OnChildItemStateChanged += elementContext_OnChildItemStateChanged; 43 element.DataContext = elementContext; 44 45 }; 46 } 47 48 static void elementContext_OnChildItemStateChanged(WeakReference rowDataContext, bool obj) 49 { 50 _currentColumn.UpdateChildItemSelectedState(rowDataContext, obj); 51 }

  我将这一列的数据上下文保存到了一个名为RowDataContext的字段中,这样就满足了当选择状态更新时我能知道是那条数据,那么剩下的工作就是列选择和全选状态更新的事件了。Silverlight中DataGrid没有HeaderTemplate,也没有其他事件能够帮助我知道Header的加载,这时候需要借助于xaml,如下

 1     <sdk:DataGridTemplateColumn.HeaderStyle>
 2         <Style TargetType="sdk:DataGridColumnHeader">
 3             <Setter Property="Padding" Value="4" />
 4             <Setter Property="Template">
 5                 <Setter.Value>
 6                     <ControlTemplate TargetType="sdk:DataGridColumnHeader">
 7                         <CheckBox VerticalAlignment="Center"  HorizontalAlignment="Center" VerticalContentAlignment="Center" Content="全选" Loaded="OnHeaderCheckBoxLoaded" />
 8                     </ControlTemplate>
 9                 </Setter.Value>
10             </Setter>
11         </Style>
12     </sdk:DataGridTemplateColumn.HeaderStyle>

  有了这里我们就可以直接在后台代码中定义事件进行处理

 private void OnHeaderCheckBoxLoaded(object sender, RoutedEventArgs e)
        {
            var checkBox = sender as CheckBox;
            if (checkBox == null)
                return;

            checkBox.Loaded -= this.OnHeaderCheckBoxLoaded;
            checkBox.Checked += (s2, e2) => this.UpdateAllItemSelectedState(true);
            checkBox.Unchecked += (s2, e2) => this.UpdateAllItemSelectedState(false);
        }

  到此为止,我们要做的工作基本已经完工,全选状态、行数据的选择状态更新我们做相应处理了。

源码下载:Silverlight下MVVM全选控件_无需额外添加字段.zip

原文地址:https://www.cnblogs.com/zeoy/p/3732268.html