如何过滤ObservableCollection中已用盘符绑定DataGridView并保留变更通知
解决方案
1. 现有代码BUG修正
首先纠正NetworkDrive类的属性通知错误:Name、IsLetterAvailable属性的SetAndNotify方法第三个参数错误传入了nameof(Letter),会导致这两个属性变更时无法发送正确的通知,UI无法响应变更,修改为对应属性名即可:
public class NetworkDrive : BaseNotify { private char letter; public char Letter { get => letter; set => SetAndNotify(ref letter, value, nameof(Letter)); } private string name; public string Name { get => name; // 修正属性名参数 set => SetAndNotify(ref name, value, nameof(Name)); } private bool isLetterAvailable; public bool IsLetterAvailable { get => isLetterAvailable; // 修正属性名参数 set => SetAndNotify(ref isLetterAvailable, value, nameof(IsLetterAvailable)); } }
2. 实现动态过滤的已用盘符集合
使用WPF原生ICollectionView作为绑定源即可解决过滤后丢失变更通知的问题,它是集合的视图层,支持过滤、排序、分组能力,不会修改原集合,且可以手动触发刷新同步项属性变更:
public class EditDriveViewModel : Screen { public ObservableCollection<NetworkDrive> NetworkDrives { get; } = new(); // 对外暴露的过滤后视图,直接绑定到DataGridView的ItemsSource public ICollectionView UsedNetworkDrivesView { get; } public EditDriveViewModel() { // 初始化视图,绑定原集合为数据源 UsedNetworkDrivesView = CollectionViewSource.GetDefaultView(NetworkDrives); // 设置过滤规则:仅显示IsLetterAvailable为false的已用盘符 UsedNetworkDrivesView.Filter = item => { return item is NetworkDrive drive && !drive.IsLetterAvailable; }; // 为所有预加载的盘符订阅属性变更事件,IsLetterAvailable变更时刷新视图 foreach (var drive in NetworkDrives) { SubscribeDrivePropertyChanged(drive); } // 若运行时会动态增减NetworkDrives集合项,监听集合变更同步订阅/取消订阅事件,避免内存泄漏 NetworkDrives.CollectionChanged += (s, e) => { if (e.NewItems != null) { foreach (NetworkDrive newDrive in e.NewItems) { SubscribeDrivePropertyChanged(newDrive); } } if (e.OldItems != null) { foreach (NetworkDrive oldDrive in e.OldItems) { UnsubscribeDrivePropertyChanged(oldDrive); } } }; } private void SubscribeDrivePropertyChanged(NetworkDrive drive) { drive.PropertyChanged += (s, e) => { if (e.PropertyName == nameof(NetworkDrive.IsLetterAvailable)) { UsedNetworkDrivesView.Refresh(); } }; } private void UnsubscribeDrivePropertyChanged(NetworkDrive drive) { drive.PropertyChanged -= (s, e) => { if (e.PropertyName == nameof(NetworkDrive.IsLetterAvailable)) { UsedNetworkDrivesView.Refresh(); } }; } }
3. ComboBox已用盘符标红+禁止选择
WPF场景
直接给ComboBox的项容器添加样式触发器即可,示例XAML如下:
<ComboBox ItemsSource="{Binding NetworkDrives}" DisplayMemberPath="Letter"> <ComboBox.ItemContainerStyle> <Style TargetType="ComboBoxItem"> <Style.Triggers> <DataTrigger Binding="{Binding IsLetterAvailable}" Value="False"> <!-- 禁止选择 --> <Setter Property="IsEnabled" Value="False"/> <!-- 标红 --> <Setter Property="Foreground" Value="Red"/> </DataTrigger> </Style.Triggers> </Style> </ComboBox.ItemContainerStyle> </ComboBox>
WinForms场景
处理DataGridView的两个事件即可:
- 监听
CellFormatting事件:判断当前单元格绑定的盘符IsLetterAvailable为false时,设置单元格Style.ForeColor为红色 - 监听
EditingControlShowing事件:拿到下拉编辑控件后,遍历所有项,将IsLetterAvailable为false的项设置为不可选
内容的提问来源于stack exchange,提问作者Guapo
相关产品推荐
相关产品推荐

