如何实现DataGrid中仅允许单个复选框被选中?
问题分析
你之前的方案存在两个核心问题:
- 遍历整个集合修改
Update属性,数据量大时效率极低; - 重新赋值
Items = new List<Item>(Items)完全多余——你的Item已经实现INotifyPropertyChanged,修改属性会自动通知UI更新,重复赋值反而会触发DataGrid全量刷新; - “集合已修改”异常的根源是遍历集合时,修改项属性触发了绑定逻辑间接影响集合状态,或是
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
相关产品推荐
相关产品推荐

