为何使用Dynamic Data无法筛选WPF数据?我的代码问题出在哪?
问题描述
我希望筛选出包含字符1、2、3的数据,以下是我的WPF代码,但点击对应按钮后并未实现筛选效果,不需要的数据仍保留在ListBox中。另外我不喜欢ReactiveUI,不想将其与Dynamic Data结合使用,请问我的代码存在什么问题?
XAML代码
<Window x:Class="WpfApp1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp1" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto"></RowDefinition> <RowDefinition Height="*"></RowDefinition> </Grid.RowDefinitions> <Grid VerticalAlignment="Top"> <Grid.ColumnDefinitions> <ColumnDefinition Width="auto"></ColumnDefinition> <ColumnDefinition Width="auto"></ColumnDefinition> <ColumnDefinition Width="auto"></ColumnDefinition> </Grid.ColumnDefinitions> <Button Click="Button_Click">1</Button> <Button Grid.Column="1" Click="Button_Click_1">2</Button> <Button Grid.Column="2" Click="Button_Click_2">3</Button> </Grid> <StackPanel Grid.Row="1"> <ListBox x:Name="LB"> </ListBox> </StackPanel> </Grid> </Window>
后台代码
using DynamicData.Binding; using DynamicData; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApp1 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); for (int i = 0; i <= 100; i++) { SourceList.Add(i.ToString()); } SourceList.Connect().Bind(this.BindableList).Subscribe(); LB.ItemsSource = BindableList; } private readonly SourceList<string> SourceList = new SourceList<string>(); public IObservableCollection<string> BindableList { get; } = new ObservableCollectionExtended<string>(); private void Button_Click(object sender, RoutedEventArgs e) { SourceList.Connect().Filter(_ => _.Contains("1")).Bind(this.BindableList).Subscribe(); } private void Button_Click_1(object sender, RoutedEventArgs e) { SourceList.Connect().Filter(_ => _.Contains("2")).Bind(this.BindableList).Subscribe(); } private void Button_Click_2(object sender, RoutedEventArgs e) { SourceList.Connect().Filter(_ => _.Contains("3")).Bind(this.BindableList).Subscribe(); } } }
问题分析与修复
你的代码主要有两个问题:
- 多订阅冲突:初始化时已经建立了
SourceList到BindableList的直接绑定订阅,每次点击按钮又会创建一个带筛选的新订阅。两个订阅同时作用于BindableList,旧订阅会持续同步所有原始数据,直接覆盖筛选后的结果,导致筛选失效。 - 未管理订阅生命周期:多次点击会创建大量重复订阅,不仅功能异常,还会造成内存泄漏。
修复思路
不需要每次点击都新建数据流,而是维护一个可动态更新筛选条件的单一订阅。用Subject传递筛选条件,让数据流自动响应条件变化。
修正后的后台代码
using DynamicData.Binding; using DynamicData; using System; using System.Reactive.Linq; using System.Windows; namespace WpfApp1 { public partial class MainWindow : Window { private readonly SourceList<string> SourceList = new SourceList<string>(); public IObservableCollection<string> BindableList { get; } = new ObservableCollectionExtended<string>(); private IDisposable _dataSubscription; private readonly Subject<string> _filterTrigger = new Subject<string>(); public MainWindow() { InitializeComponent(); // 填充初始数据 for (int i = 0; i <= 100; i++) { SourceList.Add(i.ToString()); } // 构建动态筛选的数据流 _dataSubscription = _filterTrigger .StartWith(string.Empty) // 初始显示全部数据 .Select(filter => SourceList.Connect().Filter(item => item.Contains(filter))) .Switch() // 自动切换到最新的筛选流,取消旧订阅 .Bind(BindableList) .Subscribe(); LB.ItemsSource = BindableList; } private void Button_Click(object sender, RoutedEventArgs e) { _filterTrigger.OnNext("1"); } private void Button_Click_1(object sender, RoutedEventArgs e) { _filterTrigger.OnNext("2"); } private void Button_Click_2(object sender, RoutedEventArgs e) { _filterTrigger.OnNext("3"); } // 清理订阅,避免内存泄漏 protected override void OnClosed(EventArgs e) { base.OnClosed(e); _dataSubscription?.Dispose(); _filterTrigger?.Dispose(); } } }
核心改进点
_filterTrigger作为筛选条件的发布者,点击按钮时推送新的筛选字符Switch()操作符确保始终只有最新的筛选数据流生效,解决多订阅冲突- 窗口关闭时手动释放资源,避免内存泄漏
- 初始化时通过
StartWith设置初始状态,显示所有数据
内容的提问来源于stack exchange,提问作者Melon NG
相关产品推荐
相关产品推荐

