在項目中,凡是涉及到表格的地方用的最多的控件,自然少不了DataGrid的身影,它明瞭的展示各種數據讓人十分喜歡。現在要實現一個功能,使DataGrid具有全選和項選中的功能,如果在傳統後台代碼中完成這個事情可以説十分簡單,但是換到MVVM模式下呢? 不得不面臨一個很囧的情況,為了完成UI端CheckBox被選中後能在ViewModel中獲取到選中的數據,不得不在在業務實體之外添加一個字段IsChecked 來與我們的數據交互,這樣不僅影響美觀還影響心情。

DevExpress中GridControl獲取imagecombox選項值_數據

  為了實現這一點,無疑需要設置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);
        }

  到此為止,我們要做的工作基本已經完工,全選狀態、行數據的選擇狀態更新我們做相應處理了。