wpf中为DataGrid添加checkbox支持多选全选

时间:2022-02-13 19:40:49
项目中用到DataGrid, 需要在第一列添加checkbox, 可以多选、全选。
其中涉及的概念DataTemplate, DataGridCellStyle, DataGridCellControlTemplate,Binding, OnPropertyChanged等。

有下面是实现思路:

1.继承INotifyPropertyChanged接口,实现OnPropertyChanged方法:
 public abstract class ViewModelBase : INotifyPropertyChanged

    {

      public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises this object's PropertyChanged event.
        /// </summary>
        /// <param name="propertyName">The property that has a new value</param>
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (null != handler)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

//..................

}

2. 实现viewModel, 添加IsSelected属性, 存储当前多选状态
        private bool _isSelected = true;

        public bool IsSelected
        {
            get { return _isSelected; }

            set
            {
                _isSelected = value;
                OnPropertyChanged("IsSelected");
            }
        }

3.在VM/Model准备好后, 我们接下来开始对DataGrid进行style自定义

4.准备checkbox的DataTemplate:

<DataTemplate x:Key="CheckboxDataTemplate1">
          <Grid>
          <CheckBox x:Name="_chkSelected"
                        Height="16"
                        HorizontalAlignment="Center"
                        VerticalAlignment="Center"
                        Background="{x:Null}"
                        VerticalContentAlignment="Center"
                        HorizontalContentAlignment="Center"
                        Click="_chkSelected_OnClick"
                        IsThreeState="False"
                        IsChecked="{Binding IsSelected, Mode=OneWay, FallbackValue=True}"
                        />
          </Grid>
        </DataTemplate>

此示例中checkbox只有简单的2种状态,
1)IsChecked属性绑定VM的IsSelected属性;
2)Mode为OneWay是因为我们需求是用户可以多选行然后点击某行头选中多行。此功能在Click事件中遍历当前所有选中的行,然后更改其VM的IsSelected属性。因此不需要TwoWay模式;
3)在Binding中添加了FallbackValue, 此属性指示当binding失败时给出的默认值。此例中因为DataTemplate也应用在列头, 而列头的DataContext和DataGridRow不同;
4)在Click事件处理函数中,判断当前点击的列头还是行头, 更改对应DataContext的IsSelected属性。 如:
      private void _chkSelected_OnClick(object sender, RoutedEventArgs e)
        {
            CheckBox chkSelected = e.OriginalSource as CheckBox;
            if (null == chkSelected)
            {
                return;
            }

            var studyModel = chkSelected.DataContext as StudyModel;

            bool isChecked = chkSelected.IsChecked.HasValue ? chkSelected.IsChecked.Value : true;
            FrameworkElement templateParent = chkSelected.TemplatedParent is FrameworkElement
                                                  ? (chkSelected.TemplatedParent as FrameworkElement).TemplatedParent as FrameworkElement
                                                  : null;

            if (templateParent is DataGridColumnHeader)
            {
                MainViewModel mvm = this.DataContext as MainViewModel;
                if (null != mvm)
                {
                    foreach (var sm in mvm.StudyList)
                    {
                        sm.IsSelected = isChecked;
                    }
                }
            }
            else if (templateParent is DataGridCell)
            {
                if (null != studyModel && null != this._grdStudyList.SelectedItems && this._grdStudyList.SelectedItems.Contains(studyModel))
                {
                    foreach (var otherSelected in this._grdStudyList.SelectedItems.OfType<StudyModel>())
                    {
                        otherSelected.IsSelected = isChecked;
                    }
                }
            }
        }

     其中MainViewModel为主VM, 其包含一个ObservableCollection<StudyModel>  StudyList, 而StudyModel包含IsSelected属性, 二者都实现OnpropertyChanged方法; _grdStudyList为xaml中的DataGrid

5.应用DataTemplate到DataGridColumnHeader和DataGridCell, 如:

      <Style x:Key="DataGridCheckboxColumnHeaderStyle1" TargetType="{x:Type DataGridColumnHeader}">
            <Setter Property="ContentTemplate" Value="{DynamicResource CheckboxDataTemplate1}"/>
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="VerticalAlignment" Value="Stretch"/>
      </Style>

      <Style x:Key="DataGridCheckboxCellStyle1" TargetType="{x:Type DataGridCell}">      
        <Setter Property="Padding" Value="20,0"/>                
        <Setter Property="ContentTemplate" Value="{DynamicResource CheckboxDataTemplate1}"/>     
        <Setter Property="Background" Value="#FFC1C1C1"/>        
        <Setter Property="BorderBrush" Value="{x:Null}"/>              
        <Setter Property="BorderThickness" Value="0"/>               
        <Setter Property="Template" Value="{DynamicResource DataGridCheckboxCellControlTemplate1}"/>      
        <Style.Triggers>                         
               <Trigger Property="IsSelected" Value="True">                              
                     <Setter Property="Background" Value="#FFC1C1C1"/>                       
                     <Setter Property="BorderBrush" Value="{x:Null}"/>                  
               </Trigger>           
        </Style.Triggers>           
       </Style>                    

       <ControlTemplate x:Key="DataGridCheckboxCellControlTemplate1" TargetType="{x:Type DataGridCell}">        
          <Border
                  BorderBrush="{TemplateBinding BorderBrush}"
                  BorderThickness="{TemplateBinding BorderThickness}"
                  Background="{TemplateBinding Background}"
                  SnapsToDevicePixels="True">                    
                 <ContentPresenter 
                      ContentTemplate="{TemplateBinding ContentTemplate}"
                      Content="{TemplateBinding Content}"
                      ContentStringFormat="{TemplateBinding ContentStringFormat}"
                      SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>                
           </Border>
       </ControlTemplate>

6.应用CellStyle和HeaderStyle到DataGrid:

      < DataGrid
                x:Name="_grdStudyList" 
                ItemsSource="{Binding StudyList}"
                AutoGenerateColumns="False"
                FrozenColumnCount="1" Background="#FF999797">            
          <DataGrid.Columns>                   
             <DataGridCheckBoxColumn 
                        x:Name="_dtcSelected"
                        Header="" 
                        HeaderStyle="{StaticResource DataGridCheckboxColumnHeaderStyle1}" 
                        CellStyle="{StaticResource DataGridCheckboxCellStyle1}"
                        MinWidth="60" CanUserReorder="False" MaxWidth="60"/>