简体   繁体   中英

How to Select All CheckBox of a Column by DataGrid Header CheckBox in WPF DataGrid

I have a DataGrid with one CheckBoxColumn. In the header of that CheckBoxColumn I have added a CheckBox to Select all CheckBoxes of that Datagrid Row.

How can I achieve that?

My XAML Code for WPF dataGrid:

    <DataGrid AutoGenerateColumns="False" CanUserAddRows="False"  Grid.RowSpan="2" Height="130" HorizontalAlignment="Left" IsReadOnly="False" Margin="189,340,0,0" Name="dgCandidate" TabIndex="7" VerticalAlignment="Top" Width="466" Grid.Row="1" >
        <DataGrid.Columns>
            <DataGridTextColumn x:Name="colCandidateID" Binding="{Binding CandidateID}" Header="SlNo" MinWidth="20" IsReadOnly="True" />
            <DataGridTextColumn x:Name="colRegistraion" Binding="{Binding RegisterNo}" Header="Reg. No." IsReadOnly="True"  />
            <DataGridTextColumn x:Name="colCandidate" Binding="{Binding CandidateName}" Header="Name" MinWidth="250" IsReadOnly="True"  />

            <DataGridTemplateColumn>
                <DataGridTemplateColumn.Header>
                    <CheckBox Name="chkSelectAll" Checked="chkSelectAll_Checked" Unchecked="chkSelectAll_Unchecked"></CheckBox>
                </DataGridTemplateColumn.Header>
                <DataGridTemplateColumn.CellTemplate >
                    <DataTemplate >
                        <CheckBox x:Name="colchkSelect1" Checked="colchkSelect1_Checked" Unchecked="colchkSelect1_Unchecked" ></CheckBox>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

        </DataGrid.Columns>

    </DataGrid>

Convert your Candidate class into something like this:

public class Candidate : DependencyObject
{
    //CandidateID Dependency Property
    public int CandidateID
    {
        get { return (int)GetValue(CandidateIDProperty); }
        set { SetValue(CandidateIDProperty, value); }
    }
    public static readonly DependencyProperty CandidateIDProperty =
        DependencyProperty.Register("CandidateID", typeof(int), typeof(Candidate), new UIPropertyMetadata(0));
    //RegisterNo Dependency Property
    public int RegisterNo
    {
        get { return (int)GetValue(RegisterNoProperty); }
        set { SetValue(RegisterNoProperty, value); }
    }
    public static readonly DependencyProperty RegisterNoProperty =
        DependencyProperty.Register("RegisterNo", typeof(int), typeof(Candidate), new UIPropertyMetadata(0));
    //CandidateName Dependency Property
    public string CandidateName
    {
        get { return (string)GetValue(CandidateNameProperty); }
        set { SetValue(CandidateNameProperty, value); }
    }
    public static readonly DependencyProperty CandidateNameProperty =
        DependencyProperty.Register("CandidateName", typeof(string), typeof(Candidate), new UIPropertyMetadata(""));
    //BooleanFlag Dependency Property
    public bool BooleanFlag
    {
        get { return (bool)GetValue(BooleanFlagProperty); }
        set { SetValue(BooleanFlagProperty, value); }
    }
    public static readonly DependencyProperty BooleanFlagProperty =
        DependencyProperty.Register("BooleanFlag", typeof(bool), typeof(Candidate), new UIPropertyMetadata(false));
}

in MainWindow.xaml:

<DataGrid ItemsSource="{Binding CandidateList}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Id" Binding="{Binding CandidateID}"/>
        <DataGridTextColumn Header="RegNr" Binding="{Binding RegisterNo}"/>
        <DataGridTextColumn Header="Name" Binding="{Binding CandidateName}"/>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.Header>
                <CheckBox Checked="CheckBox_Checked" Unchecked="CheckBox_Checked"></CheckBox>
            </DataGridTemplateColumn.Header>
            <DataGridTemplateColumn.CellTemplate >
                <DataTemplate>
                    <CheckBox IsChecked="{Binding BooleanFlag}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

in MainWindow.xaml.cs:

    public MainWindow()
    {
        DataContext = this;
        CandidateList.Add(new Candidate()
        {
            CandidateID = 1,
            CandidateName = "Jack",
            RegisterNo = 123,
            BooleanFlag = true
        });
        CandidateList.Add(new Candidate()
        {
            CandidateID = 2,
            CandidateName = "Jim",
            RegisterNo = 234,
            BooleanFlag = false
        });
        InitializeComponent();
    }
    //List Observable Collection
    private ObservableCollection<Candidate> _candidateList = new ObservableCollection<Candidate>();
    public ObservableCollection<Candidate> CandidateList { get { return _candidateList; } }
    private void CheckBox_Checked(object sender, RoutedEventArgs e)
    {
        foreach (var item in CandidateList)
        {
            item.BooleanFlag = true;
        }
    }
    private void UnheckBox_Checked(object sender, RoutedEventArgs e)
    {
        foreach (var item in CandidateList)
        {
            item.BooleanFlag = false;
        }
    }

Strictly speaking the model should not know about the view and so the solution proposed by blindmeis, where the model change is updating every row in the datagrid, breaks the MVVM/Presentation Design pattern. Remember that in MVVM the dependency flow is View -> ViewModel -> Model so if you are referencing controls in your view model (or control codebehind) then you have effectively broken the pattern and you will probably run into issues further down the track.

I have added CheckBox to Select all CheckBox in Datagrid Row

if you mean select all checkbox in datagrid column , then i would say: simply update your itemssource collection with checked/unchecked.

public bool SelectAll
{
  get{return this._selectAll;}
  set
  {
     this._selectAll = value;
     this.MyItemsSourceCollection.ForEach(x=>x.MyRowCheckProperty=value);
     this.OnPropertyChanged("SelectAll");
  }
}

xaml

        <DataGridTemplateColumn>
            <DataGridTemplateColumn.Header>
                <CheckBox isChecked="{Binding SelectAll}"></CheckBox>
            </DataGridTemplateColumn.Header>
            <DataGridTemplateColumn.CellTemplate >
                <DataTemplate >
                    <CheckBox IsChecked="{Binding MyRowCheckProperty}"></CheckBox>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

i dunno if the xaml bindings are right, but i hope you can see my intention

It turns out that this is quite a lot harder to get right than one would hope.

The first problem is that you can't just bind the view model to the column header because it doesn't have the view model as its data context, so you need a binding proxy to correctly route the binding to the view model.

public class BindingProxy : Freezable
{
    public static readonly DependencyProperty DataProperty = DependencyProperty.Register(
        "Data", 
        typeof(object), 
        typeof(BindingProxy),
        new UIPropertyMetadata(null));

    public object Data
    {
        get { return this.GetValue(DataProperty); }
        set { this.SetValue(DataProperty, value); }
    }

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }
}

Now create a binding proxy in your data grid's resources:

<DataGrid.Resources>
    <aon:BindingProxy
        x:Key="DataContextProxy"
        Data="{Binding}" />
</DataGrid.Resources>

Then the column needs to be defined as:

<DataGridTemplateColumn>
    <DataGridTemplateColumn.HeaderTemplate>
        <DataTemplate>
            <CheckBox
                Command="{Binding
                    Data.SelectAllCommand,
                    Source={StaticResource DataContextProxy}}"
                IsChecked="{Binding
                    Data.AreAllSelected,
                    Mode=OneWay,
                    Source={StaticResource DataContextProxy},
                    UpdateSourceTrigger=PropertyChanged}"
                IsThreeState="True" />
        </DataTemplate>
    </DataGridTemplateColumn.HeaderTemplate>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <CheckBox
                IsChecked="{Binding
                    Path=IsSelected,
                    UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Note that there needs to be a binding to both the check box's IsChecked dependency property and its Command property and the IsChecked binding is OneWay . The IsChecked binding gets the check box to display the current state of the items and the Command binding performs the bulk selection. You need both.

Now in the view model:

public bool? AreAllSelected
{
    get
    {
        return this.Items.All(candidate => candidate.IsSelected)
        ? true
        : this.Items.All(candidate => !candidate.IsSelected)
            ? (bool?)false
            : null;
    }

    set
    {
        if (value != null)
        {
            foreach (var item in this.Items)
            {
                item.IsSelected = value.Value;
            }
        }

        this.RaisePropertyChanged();
    }
}

And the SelectAllCommand property is an implementation of ICommand where the Execute method is:

public void Execute(object parameter)
{
    var allSelected = this.AreAllSelected;

    switch (allSelected)
    {
        case true:
            this.AreAllSelected = false;
            break;
        case false:
        case null:
            this.AreAllSelected = true;
            break;
    }
}

Finally your row item view models (ie the things in Items ) need to raise PropertyChanged on the main view model each time the value of IsSelected changes. How you do that is pretty much up to you.

Xaml文件

<DataGrid Grid.Row="1" AutoGenerateColumns="False" ColumnWidth="auto" ItemsSource="{Binding DataPoints}">
                    <DataGrid.Columns>
                        <DataGridCheckBoxColumn Binding="{Binding IsEnable, UpdateSourceTrigger=PropertyChanged}">
                            <DataGridCheckBoxColumn.Header>
                                <StackPanel Orientation="Horizontal">
                                    <TextBlock Margin="2">启用</TextBlock>
                                    <CheckBox IsChecked="{Binding Data.SelectAll,Source={StaticResource EquipmentPointConfigBindingProxy}}" />
                                </StackPanel>

                            </DataGridCheckBoxColumn.Header>
                        </DataGridCheckBoxColumn>
     </DataGrid.Columns>
     </DataGrid>

.cs文件

  public class BindingProxy : Freezable
    {
        #region Overrides of Freezable

        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }

        #endregion

        public object Data
        {
            get { return (object)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
    }

.cs 业务

public ObservableCollection<EquipmentDataPointViewModel> DataPoints { get; set; }


        private bool selectAll;

        public bool SelectAll
        {
            get { return selectAll; }
            set
            {
                selectAll = value;
                foreach (var item in DataPoints)
                {
                    item.IsEnable = value;
                }
                this.RaisePropertyChanged("SelectAll");
            }
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM