You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现DataGrid中仅允许单个复选框被选中?

问题分析

你之前的方案存在两个核心问题:

  1. 遍历整个集合修改Update属性,数据量大时效率极低;
  2. 重新赋值Items = new List<Item>(Items)完全多余——你的Item已经实现INotifyPropertyChanged,修改属性会自动通知UI更新,重复赋值反而会触发DataGrid全量刷新;
  3. “集合已修改”异常的根源是遍历集合时,修改项属性触发了绑定逻辑间接影响集合状态,或是ForEach遍历过程中项的PropertyChanged导致集合状态变化。

下面提供两种高效的实现方案:

方案一:维护选中项引用,仅修改必要项属性

核心思路是在ViewModel中记录当前选中的Item,当某Item的Update变为true时,仅取消之前选中项的状态,无需遍历整个集合。

步骤1:在ViewModel中添加选中项字段

private Item _currentSelectedItem;

步骤2:订阅Item的PropertyChanged事件

在ViewModel初始化时,给集合内每个Item订阅属性变化事件:

public YourViewModel()
{
    foreach (var item in Items)
    {
        item.PropertyChanged += OnItemPropertyChanged;
    }
}

步骤3:实现事件处理逻辑

private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName != nameof(Item.Update)) return;

    var targetItem = sender as Item;
    if (targetItem == null) return;

    if (targetItem.Update)
    {
        // 临时取消订阅避免递归触发事件
        if (_currentSelectedItem != null && _currentSelectedItem != targetItem)
        {
            _currentSelectedItem.PropertyChanged -= OnItemPropertyChanged;
            _currentSelectedItem.Update = false;
            _currentSelectedItem.PropertyChanged += OnItemPropertyChanged;
        }
        _currentSelectedItem = targetItem;
    }
    else
    {
        // 取消选中当前项时清空引用
        if (_currentSelectedItem == targetItem)
        {
            _currentSelectedItem = null;
        }
    }
}

优势

  • 时间复杂度O(1),仅修改前后两个项的属性,效率极高;
  • 不修改集合本身,彻底避免“集合已修改”异常;
  • 利用Item已实现的INotifyPropertyChanged自动更新UI,无需额外操作。

方案二:通过ViewModel选中项属性统一控制(更符合MVVM)

把选中逻辑集中到ViewModel层,新增SelectedItem属性关联复选框状态。

步骤1:在ViewModel中实现SelectedItem属性

private Item _selectedItem;
public Item SelectedItem
{
    get => _selectedItem;
    set
    {
        if (_selectedItem == value) return;

        // 取消之前选中项的状态
        if (_selectedItem != null)
        {
            _selectedItem.Update = false;
        }

        _selectedItem = value;

        // 设置当前选中项的状态
        if (_selectedItem != null)
        {
            _selectedItem.Update = true;
        }

        OnPropertyChanged(nameof(SelectedItem));
    }
}

步骤2:同步Item的Update属性到ViewModel

在Item的Update属性setter中通知ViewModel更新选中项(需Item持有ViewModel引用或通过事件传递):

private bool _update;
public bool Update
{
    get => _update;
    set
    {
        if (_update == value) return;
        _update = value;
        OnPropertyChanged(nameof(Update));

        if (_update)
        {
            _viewModel.SelectedItem = this;
        }
        else
        {
            if (_viewModel.SelectedItem == this)
            {
                _viewModel.SelectedItem = null;
            }
        }
    }
}

步骤3:保持复选框绑定不变

DataGrid中的复选框依然绑定到Item的Update属性:

<DataGridCheckBoxColumn Binding="{Binding Update, Mode=TwoWay}" Header="选择"/>

优势

  • 选中逻辑集中在ViewModel,符合MVVM职责分离原则;
  • 同样无需遍历集合,仅修改必要项属性,效率极高。

注意事项

  • 若后续需要动态增删集合中的Item,记得在添加时订阅PropertyChanged事件,删除时取消订阅,避免内存泄漏;
  • 绝对不要重新赋值集合(Items = new List<Item>(Items)),这会触发DataGrid全量刷新,严重影响性能。

内容的提问来源于stack exchange,提问作者Ros

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 23:01:37