WPF MVVM下如何绑定DataGrid指定列符合条件的行数到TextBlock
WPF DataGrid 过滤后指定列条件计数实现方案
你之前写的Cnt属性不生效有三个核心问题:
- 统计数据源错误:你遍历的是原始集合
_penBills,没有经过PendingBills的Filter过滤,统计的不是DataGrid当前实际显示的行 - 没有更新触发机制:你加了
if (_cnt == 0)的判断,意味着只会在第一次访问属性时计算一次,后续账单增删、字段值修改、过滤条件变化时,既不会重新计算,也不会通知UI刷新值 - 统计逻辑和过滤规则互斥:你给
PendingBills设的过滤条件是PaidOn == "",但统计规则是PaidOn非空,就算计数逻辑正常执行,结果也永远是0,完全不符合预期。另外PaidOn本身是字符串类型的话,调用ToString()纯属多余,还可能在PaidOn为null时抛出空引用异常。
方案1:ViewModel 内实现(推荐,符合MVVM规范)
不需要使用值转换器,直接在ViewModel中做实时统计,可维护性更高,也不需要额外处理UI刷新问题,实现步骤如下:
- 原始数据源改用
ObservableCollection<Bills>,保证集合增删操作可被监听 - 监听
ICollectionView的CollectionChanged事件,在过滤、排序、行增删时触发计数刷新 - 如果
Bills类实现了INotifyPropertyChanged,额外监听单条账单的属性变更事件,行内字段修改时同步刷新计数 - 计数变化时通过
PropertyChanged事件通知UI更新
对应ViewModel代码示例:
public class BillsViewModel : INotifyPropertyChanged { private readonly ObservableCollection<Bills> _penBills; public ICollectionView PendingBills { get; private set; } private int _specificConditionCount; /// <summary> /// 指定列符合条件的账单计数 /// </summary> public int SpecificConditionCount { get => _specificConditionCount; private set { _specificConditionCount = value; OnPropertyChanged(nameof(SpecificConditionCount)); } } public BillsViewModel() { // 初始化可观察集合 _penBills = new ObservableCollection<Bills>(m.PBills); PendingBills = new ListCollectionView(_penBills) { Filter = o => ((Bills)o).PaidOn == "" }; // 监听视图变更:过滤、排序、增删行时自动刷新计数 PendingBills.CollectionChanged += (s, e) => RefreshConditionCount(); // 监听单条数据的属性变更:字段值修改时自动刷新计数 foreach (Bills bill in _penBills) { bill.PropertyChanged += (s, e) => { // 监听你需要判断的字段,比如BillNo、PaidOn if (e.PropertyName == nameof(Bills.BillNo) || e.PropertyName == nameof(Bills.PaidOn)) { RefreshConditionCount(); } }; } // 初始化计算一次 RefreshConditionCount(); } /// <summary> /// 重新计算符合条件的行数 /// </summary> private void RefreshConditionCount() { // 此处替换为你实际需要的判断规则 // 示例为统计BillNo非空的行数,其他判断示例: // 值为指定文本:x.BillNo == "目标文本" // 值为null:x.BillNo == null SpecificConditionCount = PendingBills.Cast<Bills>() .Count(x => !string.IsNullOrEmpty(x.BillNo)); } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); } }
前端XAML直接绑定对应属性即可:
<TextBlock Text="{Binding SpecificConditionCount, StringFormat='符合条件账单数: {0}'}"/>
方案2:使用IValueConverter实现(适合规则复用场景)
如果统计规则需要在多个页面复用,不想在每个ViewModel里重复写计数逻辑,可以实现值转换器:
首先编写转换器类:
public class SpecificBillCountConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is not ICollectionView view) return 0; // 此处替换为你实际需要的判断规则,也可以通过parameter传入条件参数做灵活适配 return view.Cast<Bills>().Count(x => !string.IsNullOrEmpty(x.BillNo)); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
在XAML中注册转换器后使用:
<!-- 窗口/用户控件资源节点注册转换器 --> <Window.Resources> <local:SpecificBillCountConverter x:Key="SpecificBillCountConv"/> </Window.Resources> <!-- 绑定传入ICollectionView,通过转换器输出计数 --> <TextBlock Text="{Binding PendingBills, Converter={StaticResource SpecificBillCountConv}, StringFormat='符合条件账单数: {0}'}"/>
注意:该方案有个缺陷:
ICollectionView触发CollectionChanged时,绑定源的引用本身没有变化,默认不会触发转换器重新执行,需要额外处理绑定刷新逻辑,稳定性不如ViewModel内实现的方案。
内容的提问来源于stack exchange,提问作者Tamal Banerjee
相关产品推荐
相关产品推荐

