項目中用到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">
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<ContentPresenter
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
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"/>