WPF Select all CheckBox in a DataGrid

前端 未结 3 1902
别那么骄傲
别那么骄傲 2020-12-01 13:48

I\'m trying to select all CheckBox in a DataGrid but I didn\'t get any result using this code bellow

This is the function that I\'m calling when the main CheckBox is

相关标签:
3条回答
  • 2020-12-01 13:52

    What you do in your example is iterating through data item not through the controls(I suppose you have no controls as ItemsSource).
    In the link you have posted YourClass is the class from ViewModel, data object for grid's row.

    This one should work with minimal code changes on your side(but I would prefer to handle it in the ViewModel with something like CheckUncheckCommand + binding of IsChecked to the CommandParameter):

    <DataGridCheckBoxColumn x:Name="col0" HeaderStyle="{StaticResource ColumnHeaderGripperStyle}" DisplayIndex="0">
    
    private void CheckUnCheckAll(object sender, RoutedEventArgs e)
    {
        var chkSelectAll = sender as CheckBox;
        var firstCol = dgUsers.Columns.OfType<DataGridCheckBoxColumn>().FirstOrDefault(c => c.DisplayIndex == 0);
        if (chkSelectAll == null || firstCol == null || dgUsers?.Items == null)
        {
            return;
        }
        foreach (var item in dgUsers.Items)
        {
            var chBx = firstCol.GetCellContent(item) as CheckBox;
            if (chBx == null)
            {
                continue;
            }
            chBx.IsChecked = chkSelectAll.IsChecked;
        }
    }
    
    0 讨论(0)
  • 2020-12-01 14:03

    This is a modification based on @Manfred's solution. I use Command instead of event.

    XAML:

    <DataGrid ItemsSource="{Binding Students}" AutoGenerateColumns="True" CanUserAddRows="False">
        <DataGrid.Columns>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.HeaderTemplate>
                    <DataTemplate>
                        <CheckBox IsChecked="{Binding DataContext.IsAllSelected, RelativeSource={RelativeSource AncestorType=DataGrid}}" Command="{Binding DataContext.CheckAllStudentsCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
                    </DataTemplate>
                </DataGridTemplateColumn.HeaderTemplate>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <CheckBox IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}" Command="{Binding DataContext.CheckStudentCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
    

    ViewModel:

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        private List<Student> students;
    
        public List<Student> Students
        {
            get { return students; }
            set { students = value; OnPropertyChanged(); }
        }
    
        private bool? isAllSelected;
    
        public bool? IsAllSelected
        {
            get { return isAllSelected; }
            set { isAllSelected = value; OnPropertyChanged(); }
        }
    
        public RelayCommand CheckStudentCommand { get; private set; }
        public RelayCommand CheckAllStudentsCommand { get; private set; }
    
        public MainWindowViewModel()
        {
            Students = new List<Student>() { new Student { Name = "Walter" }, new Student { Name = "Jenny" }, new Student { Name = "Joe" } };
            CheckStudentCommand = new RelayCommand(OnCheckStudent);
            CheckAllStudentsCommand = new RelayCommand(OnCheckAllStudents);
            IsAllSelected = false;
        }
    
        private void OnCheckAllStudents()
        {
            if (IsAllSelected == true)
                Students.ForEach(x => x.IsChecked = true);
            else
                Students.ForEach(x => x.IsChecked = false);
        }
    
        private void OnCheckStudent()
        {
            if (Students.All(x => x.IsChecked))
                IsAllSelected = true;
            else if (Students.All(x => !x.IsChecked))
                IsAllSelected = false;
            else
                IsAllSelected = null;
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    Source code is available here

    0 讨论(0)
  • 2020-12-01 14:07

    TLDR; This is what you want, code below:

    The proper place to do this would be in your ViewModel. Your CheckBox can have three states, all of which you want to make use of:

    1. Checked - Every item is checked
    2. Unchecked - No item is checked
    3. Indeterminate - Some items are checked, some are not

    You will want to update the CheckBox whenever an item is checked/unchecked and update all items whenever the CheckBox was changed - implementing this only one way will leave the CheckBox in an invalid state which might have a negative impact on user experience. My suggestion: go all the way and implement it properly. To do this you need to be aware of which caused the change - the CheckBox of an entry or the CheckBox in the header.

    Here is how I would do it:

    First you need a ViewModel for your items, I've used a very simplified one here that only contains the IsChecked property.

    public class Entry : INotifyPropertyChanged
    {
        private bool _isChecked;
    
        public bool IsChecked
        {
            get => _isChecked;
            set
            {
                if (value == _isChecked) return;
                _isChecked = value;
                OnPropertyChanged();
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    Your main ViewModel will have a collection of all items. Whenever an item's IsChecked property changes, you'll have to check if all items are checked/unchecked and update the CheckBox in the header (or rather the value of its datasource).

    public class ViewModel : INotifyPropertyChanged
    {
        public List<Entry> Entries
        {
            get => _entries;
            set
            {
                if (Equals(value, _entries)) return;
                _entries = value;
                OnPropertyChanged();
            }
        }
    
        public ViewModel()
        {
            // Just some demo data
            Entries = new List<Entry>
            {
                new Entry(),
                new Entry(),
                new Entry(),
                new Entry()
            };
    
            // Make sure to listen to changes. 
            // If you add/remove items, don't forgat to add/remove the event handlers too
            foreach (Entry entry in Entries)
            {
                entry.PropertyChanged += EntryOnPropertyChanged;
            }
        }
    
        private void EntryOnPropertyChanged(object sender, PropertyChangedEventArgs args)
        {
            // Only re-check if the IsChecked property changed
            if(args.PropertyName == nameof(Entry.IsChecked))
                RecheckAllSelected();
        }
    
        private void AllSelectedChanged()
        {
            // Has this change been caused by some other change?
            // return so we don't mess things up
            if (_allSelectedChanging) return;
    
            try
            {
                _allSelectedChanging = true;
    
                // this can of course be simplified
                if (AllSelected == true)
                {
                    foreach (Entry kommune in Entries)
                        kommune.IsChecked = true;
                }
                else if (AllSelected == false)
                {
                    foreach (Entry kommune in Entries)
                        kommune.IsChecked = false;
                }
            }
            finally
            {
                _allSelectedChanging = false;
            }
        }
    
        private void RecheckAllSelected()
        {
            // Has this change been caused by some other change?
            // return so we don't mess things up
            if (_allSelectedChanging) return;
    
            try
            {
                _allSelectedChanging = true;
    
                if (Entries.All(e => e.IsChecked))
                    AllSelected = true;
                else if (Entries.All(e => !e.IsChecked))
                    AllSelected = false;
                else
                    AllSelected = null;
            }
            finally
            {
                _allSelectedChanging = false;
            }
        }
    
        public bool? AllSelected
        {
            get => _allSelected;
            set
            {
                if (value == _allSelected) return;
                _allSelected = value;
    
                // Set all other CheckBoxes
                AllSelectedChanged();
                OnPropertyChanged();
            }
        }
    
        private bool _allSelectedChanging;
        private List<Entry> _entries;
        private bool? _allSelected;
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    Demo XAML:

    <DataGrid ItemsSource="{Binding Entries}" AutoGenerateColumns="False" IsReadOnly="False" CanUserAddRows="False">
        <DataGrid.Columns>
            <DataGridCheckBoxColumn Binding="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}">
                <DataGridCheckBoxColumn.HeaderTemplate>
                    <DataTemplate>
                        <CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MainWindow}, Path=ViewModel.AllSelected}">Select All</CheckBox>
                    </DataTemplate>
                </DataGridCheckBoxColumn.HeaderTemplate>
            </DataGridCheckBoxColumn>
        </DataGrid.Columns>
    </DataGrid>
    
    0 讨论(0)
提交回复
热议问题