【问题标题】:WPF Select all CheckBox in a DataGridWPF选择DataGrid中的所有复选框
【发布时间】:2018-08-03 23:28:11
【问题描述】:

我正在尝试选择 DataGrid 中的所有 CheckBox,但使用下面的代码没有得到任何结果

这是我在单击主 CheckBox 时调用的函数

private void CheckUnCheckAll(object sender, RoutedEventArgs e)
{
    CheckBox chkSelectAll = ((CheckBox)sender);
    if (chkSelectAll.IsChecked == true)
    {
        dgUsers.Items.OfType<CheckBox>().ToList().ForEach(x => x.IsChecked = true);
    }
    else
    {
        dgUsers.Items.OfType<CheckBox>().ToList().ForEach(x => x.IsChecked = false);
    }
}

dgUsers 是 DataGrid,但我意识到可以找到任何复选框。

这是我在数据网格中创建 CheckBox 时使用的 XAML

<DataGrid.Columns>
    <DataGridCheckBoxColumn x:Name="col0" HeaderStyle="{StaticResource ColumnHeaderGripperStyle}">
         <DataGridCheckBoxColumn.HeaderTemplate>
              <DataTemplate>
                   <CheckBox Click="CheckUnCheckAll" >
                   </CheckBox>
              </DataTemplate>
         </DataGridCheckBoxColumn.HeaderTemplate>
    </DataGridCheckBoxColumn>
<DataGrid.Columns>

这是我的 DataGrid 的图片

有没有办法以编程方式选择所有复选框?

编辑 我已经尝试关注this steps

你可以看到我的代码在那里是一样的,但对我不起作用

【问题讨论】:

  • 不,我尝试了下面的代码,但如果你看一看它没有工作,你会发现我在这里做的完全一样 dgUsers.Items.OfType().ToList ().ForEach(x => x.IsChecked = false);
  • @MarcosBrinnerpikatoons 您的代码实际上并不相同。我注意到的第一件事是您没有使用相同的事件。我不确定它是否会有所作为,但在链接中,该问题的 OP 正在使用 CheckedUnchecked 事件。您正在使用Click 事件。
  • 附带说明,您还需要指定当复选框处于 indeterminate 状态时应该发生什么(当IsChecked 属性的值设置为 null 时)并处理它,就像这样stackoverflow.com/a/31734331/2946329
  • dgUsers.Items.OfType().ToList() 将始终返回一个空列表,因为 dgUsers 找不到 CheckBox 类型的任何项目。类型应该在尖括号内是类型项目来源

标签: c# wpf xaml datagrid


【解决方案1】:

TLDR;这就是你想要的,代码如下:

执行此操作的正确位置是在您的 ViewModel 中。您的 CheckBox 可以具有三种状态,您可以使用所有这些状态:

  1. 已检查 - 每个项目都已检查
  2. 未选中 - 未选中任何项目
  3. 不确定 - 有些项目已检查,有些未检查

您需要在选中/取消选中某个项目时更新 CheckBox,并在 CheckBox 更改时更新所有项目 - 仅以一种方式实施此方法将使 CheckBox 处于无效状态,这可能会对用户体验产生负面影响。我的建议:一路走好,好好实施。为此,您需要了解导致更改的原因 - 条目的 CheckBox 或标题中的 CheckBox。

我会这样做:

首先,您需要为您的项目创建一个 ViewModel,我在这里使用了一个非常简化的模型,它只包含 IsChecked 属性。

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));
    }
}

您的主 ViewModel 将包含所有项目的集合。每当一个项目的IsChecked 属性发生变化时,您都必须检查是否所有 项目都被选中/取消选中并更新标题中的 CheckBox(或者更确切地说是其数据源的值)。

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));
    }
}

演示 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>

【讨论】:

  • 这正是我想要的,我有点复杂,但工作正常,我只需要等到我能够给你的赏金奖,因为网站让我等待 24 小时颁奖后
  • 一种使用 MVVM 的好方法。总是要走的路,而不是在后面的代码中这样做。
  • 这行得通,但我不同意使用 MVVM 是一种很好且干净的方式这一事实。此外,public event PropertyChangedEventHandler PropertyChanged 隐藏了继承的成员。您可能想要添加替代或新关键字。
  • 真的很好!! @Dorado,它没有隐藏继承的成员。你从哪里得到那个的?如果您实现 INotifyPropertyChanged 接口,它包括public event PropertyChangedEventHandler PropertyChanged。另一方面,如果您继承了类 Entry,并且您继承的基类也包含对 INotifyPropertyChanged 的​​引用,那么您将收到有关隐藏继承成员的警告。
  • 不错的解决方案,@Manfred。我很好奇你为什么包含 try{} 块;您是否希望在设置这些属性时引发异常?
【解决方案2】:

您在示例中所做的是遍历数据项而不是通过控件(我想您没有控件作为 ItemsSource)。
在您发布的链接中,YourClass 是 ViewModel 中的类,网格行的数据对象。

这应该可以在您的代码更改最少的情况下工作(但我更愿意在 ViewModel 中使用 CheckUncheckCommand + 将 IsChecked 绑定到 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;
    }
}

【讨论】:

    【解决方案3】:

    这是基于@Manfred 的solution 的修改。我使用Command 而不是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>
    

    视图模型:

    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));
        }
    }
    

    源代码可用here

    【讨论】:

    • 谢谢!对我来说,这应该是公认的答案。
    猜你喜欢
    • 2010-10-19
    • 2017-08-27
    • 1970-01-01
    • 2019-09-13
    • 2018-06-24
    • 1970-01-01
    • 2013-06-11
    • 1970-01-01
    • 2018-10-19
    相关资源
    最近更新 更多