silentman.it
silentman.it

Reputation: 361

WPF: Filtering a dataGrid on the fly

In my WPF Window I have a DataGrid control, with its ItemsSource bound to an ObservableCollection of items (let's say a simple object with a couple properties):

XAML: (Removed some xmlns stuff for brevity)

<Window>
    <Window.Resources>
        <CollectionViewSource x:Key="MyViewSource"
                              Source="{Binding MyItemList}"
                              Filter="MyItemList_Filter"/>
    </Window.Resources>

    <Window.DataContext>
        <!-- Some Ioc stuff -->
    </Window.DataContext>

    <StackPanel>
        <TextBox Text="{Binding TextFilter}" />
        <DataGrid Grid.Row="1" x:Name="dataGrid"
            ItemsSource="{Binding Source={StaticResource MyViewSource}}"
            SelectionUnit="FullRow"
            SelectionMode="Extended"
            CanUserAddRows="False"
            CanUserDeleteRows="False"
            HeadersVisibility="Column" />
    </StackPanel>
</Window>

ViewModel (cs):

public class ViewModel : ViewModelBase // From Galasoft MVVM Light toolkit
{

    #region TextFilter Property
    public const string TextFilterPropertyName = "TextFilter";

    private string _TextFilter;

    public string TextFilter
    {
        get
        {
            return _TextFilter;
        }

        set
        {
            if (_TextFilter == value)
            {
                return;
            }

            _TextFilter = value;

            RaisePropertyChanged(TextFilterPropertyName);
        }
    }
    #endregion // TextFilter Property

    #region MyItemList Property
    public const string MyItemListPropertyName = "MyItemList";

    private ObservableCollection<Item> _MyItemList;

    public ObservableCollection<Item> MyItemList
    {
        get
        {
            return _MyItemList;
        }

        set
        {
            if (_MyItemList == value)
            {
                return;
            }

            _MyItemList = value;

            RaisePropertyChanged(MyItemListPropertyName);
        }
    }
    #endregion // MyItemList Property


}

Filter method, from Window's code behind:

private void MyItemList_Filter(object sender, FilterEventArgs e)
{
    var vm = (ViewModel)this.DataContext;
    var item = (Item)e.Item;
    // ...Simplified...
    e.Accepted = item.PropertyToCheck.Contains(vm.TextFilter);
}

Filtering is applied only when filling MyItemList: how can I make the MyItemList_Filter be called (and DataGrid items be shown/hidden accordingly) on "live" TextFilter change?

Any help would be appreciated

Upvotes: 1

Views: 8557

Answers (2)

RamWill
RamWill

Reputation: 318

This can now be achieved using the NuGet package DataGridExtensions.

Filtering a WPF datagrid using DataGridExtensions

Upvotes: 1

mm8
mm8

Reputation: 169150

You could (should) move the filtering logic to the view model, e.g.:

public class ViewModel : ViewModelBase
{
    public const string TextFilterPropertyName = "TextFilter";

    private string _TextFilter;
    public string TextFilter
    {
        get
        {
            return _TextFilter;
        }
        set
        {
            if (_TextFilter == value)
                return;
            _TextFilter = value;
            RaisePropertyChanged(TextFilterPropertyName);
            Filter();
        }
    }

    public const string MyItemListPropertyName = "MyItemList";

    private ObservableCollection<Item> _MyItemList;
    public ObservableCollection<Item> MyItemList
    {
        get
        {
            return _MyItemList;
        }
        set
        {
            if (_MyItemList == value)
                return;

            _MyItemList = value;
            RaisePropertyChanged(MyItemListPropertyName);
        }
    }

    private ObservableCollection<Item> _filtered;
    public ObservableCollection<Item> FilteredList
    {
        get
        {
            return _filtered;
        }
        set
        {
            if (_filtered == value)
                return;

            _filtered = value;
            RaisePropertyChanged("FilteredList");
        }
    }

    private void Filter()
    {
        _filtered.Clear();
        foreach(var item in _MyItemList)
        {
            if (item.PropertyToCheck.Contains(TextFilter))
                _filtered.Add(item);
        }
    }
} 

That's where it belongs. Then you don't need to the CollectionViewSource:

<DataGrid Grid.Row="1" x:Name="dataGrid" ItemsSource="{Binding FilteredList}" ... />

Upvotes: 4

Related Questions