11

I have a wpf-mvvm application.

I have an observable collection in my viewmodel

public ObservableCollection<BatchImportResultMessageDto> ImportMessageList { get; set; } 

"BatchImportResultMessageDto" contains two properties..

result type..and message. Result type can be success or failure.

I need to display success in one list box ..and failure in another listbox.

I can do this..by having 2 observable collections in viewmodel to hold success/failure.

public ObservableCollection<BatchImportResultMessageDto> ImportFailureMessageList { get; set; } // To hold the failure messages.
public ObservableCollection<BatchImportResultMessageDto> ImportSuccessMessageList { get; set; } // To hold the sucess messages.

But is there any other better way so that i can filter it (without new two collections) ?

Askolein
  • 3,250
  • 3
  • 28
  • 40
Relativity
  • 6,690
  • 22
  • 78
  • 128
  • Yes - with markup extensions, see http://stackoverflow.com/questions/6461826/in-wpf-can-you-filter-a-collectionviewsource-without-code-behind – Slugart May 07 '15 at 16:33

2 Answers2

15

You can use a CollectionViewSource and make it a property of your view model, and bind to that instead of your ImportMessageList collection directly from the XAML. Set your ImportMessageList collection as the Source of the CollectionViewSource, and then configure a predicate to do your filtering on the CollectionViewSource.

Something like:

private ICollectionView messageListView;
public ICollectionView MessageListView
{
    get { return this.messageListView; }
    private set
    {
      if (value == this.messageListView)
      {
        return;
      }

      this.messageListView = value;
      this.NotifyOfPropertyChange(() => this.MessageListView);
    }
}

...


this.MessageListView = CollectionViewSource.GetDefaultView(this.ImportMessageList);
this.MessageListView.Filter = new Predicate<object>(this.FilterMessageList);

...

public bool FilterMessageList(object item)
{
  // inspect item as message here, and return true 
  // for that object instance to include it, false otherwise
  return true;
}
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
devdigital
  • 34,151
  • 9
  • 98
  • 120
12

You can do this by creating two CollectionViewSource objects and setting a filter on each.

How to create a CVS in xaml from a VM binding (Source):

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Window.Resources>
        <CollectionViewSource Source="{Binding}" x:Key="customerView">
           <CollectionViewSource.GroupDescriptions>
               <PropertyGroupDescription PropertyName="Country" />
           </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </Window.Resources>
    <ListBox ItemSource="{Binding Source={StaticResource customerView}}" />
</Window>

How to filter a CVS in a code behind (You can use reflection to look at the properties of your model if you don't want to make a reference to it. Source):

<CollectionViewSource x:Key="MyCVS"
                              Source="{StaticResource CulturesProvider}"
                              Filter="MyCVS_Filter" />

with (code behind)

void MyCVS_Filter(object sender, FilterEventArgs e)
{
    CultureInfo item = e.Item as CultureInfo;
    if (item.IetfLanguageTag.StartsWith("en-"))
    {
        e.Accepted = true;
    }
    else
    {
        e.Accepted = false;
    }
}
Dean Kuga
  • 11,878
  • 8
  • 54
  • 108
Ragepotato
  • 1,630
  • 11
  • 13
  • 10
    It doesn't always violate the mvvm pattern to put such code in the code-behind. If it's purely view related (filtering only) then it's fine to use code-behind. It may not be as nice as a binding or xaml property, but CollectionViewSource doesn't support that for filters. – Ben Zuill-Smith Dec 28 '15 at 23:23
  • @user2153378 it's okay because WPF and XAML also violate the MVVM pattern. – Sorensen Oct 25 '18 at 12:50
  • The code behind can also be moved into the viewmodel. – Plagon Sep 08 '20 at 13:56