Create a TextBoxSearch to filter from ListView WPF

17,006

Solution 1

Ok after creating a test Project i can't reproduce your exception

here is my working code:

MainWindow.xaml

<Window x:Class="gregory.bmclub.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <TextBox Text="{Binding TextSearch,UpdateSourceTrigger=PropertyChanged}"/>
        <ListView Height="380" HorizontalAlignment="Left" Name="lsNames" VerticalAlignment="Top" Width="170" 
             ScrollViewer.VerticalScrollBarVisibility="Visible" 
             ScrollViewer.HorizontalScrollBarVisibility="Visible" 
             SelectedItem="{Binding SelectedEmployee}" 
             ItemsSource="{Binding View}" Grid.RowSpan="2" Grid.Row="1">
            <!--ItemsSource changed to "View"-->
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="FirstName" DisplayMemberBinding="{Binding FirstName}" Width="80" />
                    <GridViewColumn Header="Surname" DisplayMemberBinding="{Binding Surname}" Width="80" />
                </GridView>
            </ListView.View>
        </ListView>
    </StackPanel>
</Window>

MainWindow.cs

using System.Windows;

namespace gregory.bmclub
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            DataContext = new EmployeeListViewModel();
        }
    }
}

EmployeeViewModel.cs

namespace gregory.bmclub
{
    public class EmployeeViewModel
    {
        string firstname;

        public string FirstName
        {
            get { return firstname; }
            set { firstname = value; }
        }
    }
}

EmployeeListViewModel.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Data;
using System.ComponentModel;

namespace gregory.bmclub
{
    class EmployeeListViewModel : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        #endregion

        public EmployeeListViewModel()//modified to public
        {
            EmployeeList = new ObservableCollection<EmployeeViewModel>(GetEmployees());
            this._view = new ListCollectionView(this.employeeList);
        }

        #region nonModifiedCode

        private ListCollectionView _employeeCol;
        public ICollectionView EmployeeCollection
        {
            get { return this._employeeCol; }
        }

        private ObservableCollection<EmployeeViewModel> employeeList;
        public ObservableCollection<EmployeeViewModel> EmployeeList
        {
            get { return employeeList; }
            set
            {
                employeeList = value;
                OnPropertyChanged("EmployeeList");
            }
        }

        private ListCollectionView _view;
        public ICollectionView View
        {
            get { return this._view; }
        }

        private string _TextSearch;
        public string TextSearch
        {
            get { return _TextSearch; }
            set
            {
                _TextSearch = value;
                OnPropertyChanged("TextSearch");

                if (String.IsNullOrEmpty(value))
                    View.Filter = null;
                else
                    View.Filter = new Predicate<object>(o => ((EmployeeViewModel)o).FirstName == value);
            }
        }

        #endregion

        //created for testing
        private List<EmployeeViewModel> GetEmployees()
        {
            var mylist = new List<EmployeeViewModel>();
            mylist.Add(new EmployeeViewModel() { FirstName = "nummer1" });
            mylist.Add(new EmployeeViewModel() { FirstName = "nummer2" });

            return mylist;
        }
    }
}

Solution 2

i had the following code working with me but i had to ditch the Textsearch method and a pplied a different one i added view lines of code hopefully that make your code to work.

private EmployeeListViewModel()
    : base("")
{
    EmployeeList = new ObservableCollection<EmployeeViewModel>(GetEmployees());
    this._view = new ListCollectionView(this.employeeList);
     myEmployeeList = new CollectionViewSource();
        myEmployeeList.Source = this.EscortList;
        myEmployeeList.Filter += ApplyFilter;
}
    internal CollectionViewSource employeeList { get; set; }

        internal CollectionViewSource myEmployeeList { get; set; }
        private ObservableCollection<EmployeeViewModel> employeeList;
    public ObservableCollection<EmployeeViewModel> EmployeeList
    {
        get { return employeeList; }
        set
        {
            employeeList = value;
            OnPropertyChanged("EmployeeList");
        }
    }

private ListCollectionView _view;
// the collection below is the collection you will need to be your listview itemsource {Binding View}
public ICollectionView View
{
     //you need to return your CollectionViewSource here
     get { return myEmployeeList._view; }
}

// you need to use the following filtering methods as it did work for methods
 private void OnFilterChanged()
    {
        myEmployeeList.View.Refresh();
    }

    private string filter;

    public string Filter
    {
        get { return this.filter; }
        set
        {
            this.filter = value;
            OnFilterChanged();
        }
    }

    void ApplyFilter(object sender, FilterEventArgs e)
    {
        EmployeeViewModel svm = (EmployeeViewModel)e.Item;

        if (string.IsNullOrWhiteSpace(this.Filter) || this.Filter.Length == 0)
        {
            e.Accepted = true;
        }
        else
        {
        // you can change the property you want to search your model
            e.Accepted = svm.Surname.Contains(Filter);
        }
    }

here is my Xaml code to bind to Listview

<ListView Name="lsvEscort" HorizontalAlignment="Left" Height="297" ItemsSource="{Binding View}">

here is my text search filter binding path

<TextBox x:Name="txtSearch"  Grid.Column="1" Text="{Binding Path=Filter,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

hopefully this will resolve your issue

Share:
17,006

Related videos on Youtube

greg
Author by

greg

Updated on September 16, 2022

Comments

  • greg
    greg over 1 year

    I'm creating a application that allows a user to add some Employee details into EntityFramework model using WPF.

    So far, I have a ListView to represent a list of employee names, and when you select the name of the employee, it selects that specific data in another ListView. I have accomplished this using a Predicate and a ICollectionSource.

    But what I want to achieve now, is to have a so called search engine. So when a user types in an employees name in a TextBox it filters the names of the employee names, depending on what is typed into the search box.

    I have used This Link as a guide, but I am not too sure how to implement it within my own design; in the example they have used a Resource and used an Array.

    This is what I have tried instead, using a Predicate;

        private EmployeeListViewModel()
            : base("")
        {
            EmployeeList = new ObservableCollection<EmployeeViewModel>(GetEmployees());
            this._view = new ListCollectionView(this.employeeList);
        }
    
        private ListCollectionView _view;
        public ICollectionView View
        {
             get { return this._view; }
        }
    
        private string _TextSearch;
        public string TextSearch
        {
            get { return _TextSearch; }
            set
            {
                _TextSearch = value;
                OnPropertyChanged("TextSearch");
    
                if (String.IsNullOrEmpty(value))
                    View.Filter = null;
                else
                    View.Filter = new Predicate<object>(o => ((EmployeeViewModel)o).FirstName == value);
            }
        }
    

    in my view;

    <TextBox Height="23" Name="txtSearch" VerticalAlignment="Bottom" Margin="70,0,0,183" Width="100" Grid.Row="1"
              Text="{Binding TextSearch, UpdateSourceTrigger=PropertyChanged}"/>
    

    But what seems to happen is when I type something in, it throws this exception;

    Object reference not set to an instance of an object.

    So my question is, how can I implement this so it actually enables me to filter the list of names like in a searchbox?

    Any help would be grateful or guidance how to achieve this.

  • WiiMaxx
    WiiMaxx about 11 years
    so it looks like your problem is somewhere else
  • greg
    greg about 11 years
    Hi @WiiMaxx, It was to do with my binding. I was still binding my NameList to the collection, rather then to the View. It now seems to work. Thanks :).
  • Kappacake
    Kappacake over 2 years
    you define private ListCollectionView _employeeCol; and public ICollectionView EmployeeCollection but never use them
  • Kappacake
    Kappacake over 2 years
    Also, I don't really understand how you can convert a string from the text search into a view model like you do on this line: (EmployeeViewModel)o, which makes me very confused as to why this is marked as a solution