Proper way to use CollectionViewSource in ViewModel
You have two options to use CollectionViewSource
properly with MVVM -
Expose an
ObservableCollection
of items (Categories
in your case) through yourViewModel
and createCollectionViewSource
in XAML like this -<CollectionViewSource Source="{Binding Path=Categories}"> <CollectionViewSource.SortDescriptions> <scm:SortDescription PropertyName="CategoryName" /> </CollectionViewSource.SortDescriptions> </CollectionViewSource>
scm:
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
see this -
Filtering
collections from XAML using CollectionViewSourceCreate and Expose an
ICollectionView
directly from yourViewModel
see this - How to Navigate, Group, Sort and Filter Data in WPF
Following example shows how to create a collection view and
bind it to a ListBox
View XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
x:Class="CustomerView">
<ListBox ItemsSource={Binding Customers} />
</Window>
View Codebehind:
public class CustomerView : Window
{
public CustomerView()
{
DataContext = new CustomerViewModel();
}
}
ViewModel:
public class CustomerViewModel
{
private readonly ICollectionView customerView;
public ICollectionView Customers
{
get { return customerView; }
}
public CustomerViewModel()
{
IList<Customer> customers = GetCustomers();
customerView = CollectionViewSource.GetDefaultView( customers );
}
}
Update:
Q. If there is no property to sort on? e.g. if there is an ObservableCollection
of string or int?
A. In that case you can Simply use . as the property name:
<scm:SortDescription PropertyName="." />
I found that it is handy to have a CollectionViewSource
in my ViewModel and bind the ListBox
(in my case) to the CollectionViewSource.View
while setting the CollectionViewSource.Source
to be the list I want to use.
Like so:
ViewModel:
public DesignTimeVM() //I'm using this as a Design Time VM
{
Items = new List<Foo>();
Items.Add(new Foo() { FooProp= "1", FooPrep= 20.0 });
Items.Add(new Foo() { FooProp= "2", FooPrep= 30.0 });
FooViewSource = new CollectionViewSource();
FooViewSource.Source = Items;
SelectedFoo = Items.First();
//More code as needed
}
XAML:
<ListBox ItemsSource="{Binding FooViewSource.View}" SelectedItem="{Binding SelectedFoo}"/>
This means I can do neat stuff in the VM as needed (from https://blogs.msdn.microsoft.com/matt/2008/08/28/collectionview-deferrefresh-my-new-best-friend/ ):
using (FooViewSource.DeferRefresh())
{
//Remove an old Item
//add New Item
//sort list anew, etc.
}
I suppose this is possible when using the ICollectionView
object also, but the demo code in the blog link seems to be some codebehind stuff, refering the listbox directly, which I'm trying to avoid.
BTW before you ask, here's how you use a Design Time VM: WPF Design Time View Model