背景

WPF+Prism,View和ViewModel,通过Binding来实现数据的更新和界面的刷新,我的需求是做一个表格,第一列为CheckBox,同时这一列的header也是CheckBox,勾选了header的CheckBox,可以实现所有行的CheckBox全选的功能
界面如下:
image

实现方法

xaml代码:

<DataGrid
    x:Name="dgRoundRobin"
    Grid.Row="2"
    Height="300"
    Margin="0,5,0,0"
    AutoGenerateColumns="False"
    BorderBrush="Black"
    BorderThickness="1"
    CanUserAddRows="{Binding DgRobinCanUserAddRows}"
    GridLinesVisibility="All"
    ItemsSource="{Binding DgRoundRobinItems}">
    <DataGrid.Columns>
        <DataGridCheckBoxColumn Binding="{Binding IsChecked}">
            <DataGridCheckBoxColumn.HeaderTemplate>
                <DataTemplate>
                    <CheckBox Command="{Binding DataContext.CbRoundRobinAllCheckCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
                </DataTemplate>
            </DataGridCheckBoxColumn.HeaderTemplate>
        </DataGridCheckBoxColumn>

        <DataGridTextColumn
            Width="150"
            Binding="{Binding MAC}"
            Header="MAC" />
        <DataGridTextColumn
            Width="100"
            Binding="{Binding SendCount}"
            Header="发送次数" />
        <DataGridTextColumn
            Width="100"
            Binding="{Binding RcvCount}"
            Header="接收次数" />
        <DataGridTextColumn
            Width="100"
            Binding="{Binding SuccessRate}"
            Header="成功率" />
        <DataGridTextColumn
            Width="100"
            Binding="{Binding Status}"
            Header="状态" />
    </DataGrid.Columns>
</DataGrid>

我感觉主要的难点就在xaml中的代码编写,这样的方法是创建了一个DataGridCheckBoxColumn,然后修改了它的HeaderTemplate,在其中进行了CheckBox的绑定,<CheckBox Command="{Binding DataContext.CbRoundRobinAllCheckCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
这一行是重点,我们用常规手段直接给header中的CheckBox绑定command是行不通的,因为WPF中没有DataGridCheckBoxColumn映射它(DataGridCheckBoxColumn不是控件,它没有父控件)
所以需要我们手动找到DataGrid元素,找到他身上的DataContext来绑定CbRoundRobinAllCheckCommand

ViewModel中的代码:
DataGrid的ItemSource绑定的DgRoundRobinItems,这个的定义如下:

private ObservableCollection<DgRoundRobinDeviceInfo> _dgRoundRobinItems = new ObservableCollection<DgRoundRobinDeviceInfo>();
public ObservableCollection<DgRoundRobinDeviceInfo> DgRoundRobinItems
{
    get { return _dgRoundRobinItems; }
    set { SetProperty(ref _dgRoundRobinItems, value); }
}

ObservableCollection实现了INotifyCollectionChangedINotifyPropertyChanged,所以这个集合中的Item有新增
或者删除的话,它会自动的调用UI进行更新,特别方便,以前我总是用List声明一个集合,但是当集合中的元素有新增或删除的时候,都得手动重新刷新下集合才行。
但是我还有个需求,就是想当集合中的Item的某个属性有变化的时候,也可以自动的刷新UI界面,不需要我自己来处理,后来发现还真的可以,只需要将Item的Class继承BindableBase(这是Prism提供的,Community.Mvvm.Toolkit应该是用的ObservableObject),这样子,就可以实现我的需求,代码如下:

public class DgRoundRobinDeviceInfo : BindableBase
{
    private bool _isChecked = false;
    public bool IsChecked
    {
        get { return _isChecked; }
        set { SetProperty(ref _isChecked, value); } 
    }

    public string MAC { get; set; }
    public int SendCount { get; set; }
    public int RcvCount { get; set; }
    public string SuccessRate { get; set; }
    public string Status { get; set; }
}

可以看到,我在DgRoundRobinDeviceInfo中的IsChecked属性上面实现了自动更新。