wpfdynamic-data

Why I cannot Filter my data by Dynamic Data?


I want to filter out the data containing letter 1, 2, 3.

Here is my code.

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>

Code-behind:

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();
        }
    }
}

It seems it only sorts it but does not filter it. The data I don't need is still in the ListBox.

PS: I dislike ReactiveUI and I don't want to use Dynamic Data with this.

What's wrong with my code?


Solution

  • In the code you gave, all new copies of the SourceList pipelines bind to the same ObservableCollection, so the strings just pile on to it, and it seems that the last one bound gets to dump its items first.

    Dynamic Data library .Filter operator accepts an IObservable of filter predicates, so you can do this, when connecting:

     SourceList
         .Connect()
         .Filter(_filter)
         .Bind(this.BindableList)
         .Subscribe();
      ...
     private BehaviorSubject<Func<string, bool>> _filter = new(x => true);
    

    And, then just push another filter on your button events:

    ```
    private void Button_Click(object sender, RoutedEventArgs e)
     {
         _filter.OnNext(_ => _.Contains("1"));
     }
    ```