WPF中的表格数据展示一般使用DataGrid控件。
DataGrid可用AutoGenerateColumns属性自动生成对应的列。其中Column的类型包含 DataGridTextColumn、DataGridCheckBoxColumn、DataGridComboBoxColumn、DataGridHyperlinkColumn、DataGridTemplateColumn等5种格式。
1. DataGridCheckBoxColumn 实现效果
复选框类型列,显示效果如下:
2. xaml中设置CheckBox的主要代码
<DataGridCheckBoxColumn Width="100"
Binding="{Binding IsSelected,NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}">
<DataGridCheckBoxColumn.HeaderTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding DataContext.IsAllSelected, RelativeSource={RelativeSource AncestorType=DataGrid}}"
Command="{Binding DataContext.SelectAllCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=IsChecked}"/>
</DataTemplate>
</DataGridCheckBoxColumn.HeaderTemplate>
<DataGridCheckBoxColumn.HeaderStyle>
<Style BasedOn="{StaticResource CenteredHeaderStyle}" TargetType="{x:Type DataGridColumnHeader}"/>
</DataGridCheckBoxColumn.HeaderStyle>
<DataGridCheckBoxColumn.ElementStyle>
<Style TargetType="CheckBox">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGridCheckBoxColumn.ElementStyle>
<DataGridCheckBoxColumn.EditingElementStyle>
<Style TargetType="CheckBox">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGridCheckBoxColumn.EditingElementStyle>
</DataGridCheckBoxColumn>
2.1 实现 CheckBoxColumn 的Header中显示复选框
自定义HeaderTemplate
<DataGridCheckBoxColumn.HeaderTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding DataContext.IsAllSelected, RelativeSource={RelativeSource AncestorType=DataGrid}}"
Command="{Binding DataContext.SelectAllCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=IsChecked}"/>
</DataTemplate>
</DataGridCheckBoxColumn.HeaderTemplate>
实现Header的CheckBox选择状态和CheckBoxColumn列单元格的选中状态联动。当选中Header的CheckBox或者取消选中Header的CheckBox时,CheckBoxColumn列单元格的选中状态相应地发生变化。同样,当选择CheckBoxColumn列单元格时,Header中的CheckBox也相应地会有三种选择状态切换。
- 实现方案
- 在
ViewModel中定义IsAllSelected属性用于标识所有数据行的选中状态; - 在
ViewModel中定义SelectAllCommand用于处理Header中CheckBox选中或者取消选中操作; - 在
ViewModel中监听IsSelected修改状态,从而及时更新Header中的CheckBox选中状态;
3. 完整样例代码
3.1 MainWindow.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestDataGrid"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2" x:Class="TestDataGrid.MainWindow"
mc:Ignorable="d"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<Style x:Key="ColumnHeaderGripperStyle" TargetType="{x:Type Thumb}">
<Setter Property="Width" Value="8"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Cursor" Value="SizeWE"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DataGridColumnHeaderStyle1" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<CheckBox x:Name="chkToggleSelection" VerticalAlignment="Center">
</CheckBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="CheckBoxHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<CheckBox x:Name="chkToggleSelection" VerticalAlignment="Center">
</CheckBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="CenteredHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
</Window.Resources>
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<DataGrid x:Name="mydg" ItemsSource="{Binding Products}" AutoGenerateColumns="False" Margin="10"
CanUserAddRows="False">
<!--<i:Interaction.Triggers>
<i:EventTrigger EventName="CellEditEnding">
<i:InvokeCommandAction Command="{Binding CellValueChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>-->
<DataGrid.Columns>
<DataGridCheckBoxColumn Width="100"
Binding="{Binding IsSelected,NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}">
<DataGridCheckBoxColumn.HeaderTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding DataContext.IsAllSelected, RelativeSource={RelativeSource AncestorType=DataGrid}}"
Command="{Binding DataContext.SelectAllCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=IsChecked}"/>
</DataTemplate>
</DataGridCheckBoxColumn.HeaderTemplate>
<DataGridCheckBoxColumn.HeaderStyle>
<Style BasedOn="{StaticResource CenteredHeaderStyle}" TargetType="{x:Type DataGridColumnHeader}"/>
</DataGridCheckBoxColumn.HeaderStyle>
<DataGridCheckBoxColumn.ElementStyle>
<Style TargetType="CheckBox">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGridCheckBoxColumn.ElementStyle>
<DataGridCheckBoxColumn.EditingElementStyle>
<Style TargetType="CheckBox">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGridCheckBoxColumn.EditingElementStyle>
</DataGridCheckBoxColumn>
<DataGridTextColumn Header="Product Name" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Price" Binding="{Binding Price, StringFormat=C}"/>
<!-- DataGridComboBoxColumn 绑定 -->
<DataGridComboBoxColumn Header="Category">
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding DataContext.Categories, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
<Setter Property="DisplayMemberPath" Value="Name"/>
<Setter Property="SelectedValuePath" Value="Id"/>
<Setter Property="SelectedValue" Value="{Binding CategoryId, UpdateSourceTrigger=PropertyChanged}"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding DataContext.Categories, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
<Setter Property="DisplayMemberPath" Value="Name"/>
<Setter Property="SelectedValuePath" Value="Id"/>
<Setter Property="SelectedValue" Value="{Binding CategoryId, UpdateSourceTrigger=PropertyChanged}"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
3.2 MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
private bool? isAllSelected;
public bool? IsAllSelected
{
get { return isAllSelected; }
set
{
if (value != isAllSelected)
{
isAllSelected = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsAllSelected)));
}
}
}
public ObservableCollection<Product> Products { get; set; }
public ObservableCollection<Category> Categories { get; set; }
public ObservableCollection<string> MySources { get; set; }
public ICommand SelectAllCommand { get; }
public ICommand CellValueChangedCommand { get; }
public MainViewModel()
{
// 初始化分类数据
Categories = new ObservableCollection<Category>
{
new Category { Id = 1, Name = "Electronics" },
new Category { Id = 2, Name = "Clothing" },
new Category { Id = 3, Name = "Home Appliances" }
};
// 初始化产品数据
Products = new ObservableCollection<Product>
{
new Product { Name = "Laptop", Price = 999.99m, CategoryId = 1 },
new Product { Name = "Shirt", Price = 29.99m, CategoryId = 2 },
new Product { Name = "Washing Machine", Price = 499.99m, CategoryId = 3 }
};
foreach (var item in Products)
{
item.PropertyChanged += Item_PropertyChanged;
}
MySources = new ObservableCollection<string>()
{
"Laptop" ,
"Shirt" ,
"Washing Machine"
};
SelectAllCommand = new RelayCommand(SelectAll);
CellValueChangedCommand = new RelayCommand(CellValueChanged);
IsAllSelected = false;
}
private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
UpdateHeaderCheckBoxState();
}
private void SelectAll(object obj)
{
if (obj is bool isSelected)
{
foreach (var item in Products)
{
item.IsSelected = isSelected;
}
}
}
private void CellValueChanged(object obj)
{
UpdateHeaderCheckBoxState();
}
public void UpdateHeaderCheckBoxState()
{
var allChecked = Products.All(item => item.IsSelected == true);
var allUnchecked = Products.All(item => item.IsSelected == false);
IsAllSelected = allChecked ? true : (allUnchecked ? false : (bool?)null);
}
public event PropertyChangedEventHandler PropertyChanged;
}
3.3 Models
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Product:INotifyPropertyChanged
{
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set {
if (value != IsSelected)
{
isSelected = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSelected)));
}
}
}
public string Name { get; set; }
public decimal Price { get; set; }
public int CategoryId { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
3.4 RelayCommand.cs
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
完整项目参考:github.com/RunWangA/WP…