0

I've seen lots of similar questions but I've not been able to find either a question/answer or tutorial which clearly lists out all of the components required to get this to work. I'm trying to follow MVVM but as this is entirely a UI concern I'm not against doing some code-behind.

What I am trying to achieve:

  • ListView.ItemsSource bound to an ObservableCollection<T>
  • Filter the displayed items in ListView based on a TextBox
  • Filter is updated as user types in TextBox

In my ViewModel I have something like this:

private ObservableCollection<Customer> _customers;
public ObservableCollection<Customer> Customers
{
    get { return _customers; }
    set
    {
        _customers= value;
        RaisePropertyChanged("Customers");
    }
}

private Customer _selected_Customer;
public Customer Selected_Customer
{
    get { return _selected_Customer; }
    set
    {
        _selected_Customer= value;
        RaisePropertyChanged("Selected_Customer");
    }
}

private string _filtered_Name;
public string Filtered_Name
{
    get { return _filtered_Name; }
    set
    {
        _filtered_Name = value;
        RaisePropertyChanged("Filtered_Name");
    }
}

And in my XAML it's like this:

<CollectionViewSource x:Key="cvs"
                      x:Name="Customer_Details_View"
                      Source="{Binding Path=Customers}"/>

<TextBox x:Name="Filtered_Name" Text="{Binding Filtered_Name, Mode=TwoWay}"/>

<ListView ItemsSource="{Binding ElementName=Customer_Details_View}"
          SelectedItem="{Binding Selected_Customer, Mode=TwoWay}">

I want to filter my ObservableCollection<Customer> with the following logic: Customer.Name.ToLower().Contains(Filtered_Name.ToLower())

How to I bind the TextBox.Text to the CollectionViewSource or utilize the CollectionViewSource.Filter event to apply the above filter?

WSC
  • 903
  • 10
  • 30

1 Answers1

0

You'll probably want to declare the collectionviewsource in your VM and bind directly to it. Once you've done that you can change the filter as answered in this question CollectionViewSource, how to filter data?

VM Example, may not work without massaging.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace CollectionViewSourceExample
{
    class MainWindowViewModel : INotifyPropertyChanged
    {
        private string _filter;

        public MainWindowViewModel()
        {
            SourceCollection = new ObservableCollection<string>();
            SourceCollection.Add("Fred Flintstone");
            SourceCollection.Add("Wilma Flintstone");
            SourceCollection.Add("Bambam Flintstone");
            SourceCollection.Add("Barny Rubble");
            SourceCollection.Add("Betty Rubble");

            ViewSource = (ListCollectionView)CollectionViewSource.GetDefaultView(SourceCollection);
            ViewSource.Filter = SourceFilter;
            ViewSource.IsLiveFiltering = true;
        }

        private bool SourceFilter(object obj)
        {
            string val = (string)obj;
            return string.IsNullOrWhiteSpace(Filter) || val.Contains(Filter);
        }

        public string Filter
        {
            get { return _filter; }
            set
            {
                _filter = value;
                PropertyChanged(this, new PropertyChangedEventArgs("Filter"));
                ViewSource.Refresh();
            }
        }

        public ObservableCollection<string> SourceCollection { get; }
        public ListCollectionView ViewSource { get; }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}
  • Can you expand on that please? If I'm declaring my `CollectionViewSource` in the ViewModel where does the filtering go? Surely that would end up with a tightly coupled View and ViewModel? I also don't quite understand what to do with that answer linked... As with all the others I've looked at it lacks a comprehensive explanation of how to get from a simple bound `ObservableCollection` to the desired result. – WSC Apr 02 '20 at 12:47
  • You've already got a bound property for the filter value. When that is changed in the VM the filter updates. It's the same as exposing a collection except it's a viewsource. The UI is no more tightly bound to the VM than it was before. – Phoenix Stoneham Apr 02 '20 at 12:49
  • Ok, so if I declare in my VM something like: `public CollectionViewSource Filtered_View { get { return _filtered_View; } set { _filtered_View = value; RaisePropertyChanged("Filtered_View"); } }` How should I use the Filtered_Name property in the VM? Sorry, I'm sure I'm just being a bit slow but I cannot get my head around what I need to do. – WSC Apr 02 '20 at 12:53