Proper way to use CollectionViewSource in ViewModel

David S picture David S · Jan 2, 2014 · Viewed 78.8k times · Source

I used Drag and Drop to bind Data Source object (a DB model) to DataGrid (basically following this example in Entity Framework Databinding with WPF.

Everything works fine with this implementation.

XAML

<Window.Resources>    
<CollectionViewSource x:Key="categoryViewSource"  
    d:DesignSource="{d:DesignInstance {x:Type local:Category}, CreateList=True}"/>
</Window.Resources>
<Grid DataContext="{StaticResource categoryViewSource}">
..

Code Behind

private void Window_Loaded(object sender, RoutedEventArgs e)
{
   System.Windows.Data.CollectionViewSource categoryViewSource =
      ((System.Windows.Data.CollectionViewSource)(this.FindResource("categoryViewSource")));

  _context.Categories.Load();
  categoryViewSource.Source = _context.Categories.Local;        
}

ViewModel

public MainWindow()
{
    InitializeComponent();
    this.DataContext = new MyViewModel();
}

However, when I try to use the same code from within ViewModel, it doesn‘t work (FindResource is not available), besides, I don’t think this is the right approach (i.e. to use x:Key in MVVM).

I would really appreciate any help to point me what is the right way to implement CollectionViewSource and DataBinding with DataGrid.

Answer

akjoshi picture akjoshi · Jan 3, 2014

You have two options to use CollectionViewSource properly with MVVM -

  1. Expose an ObservableCollection of items (Categories in your case) through your ViewModel and create CollectionViewSource 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=Wind‌​owsBase"

    see this - Filtering collections from XAML using CollectionViewSource

  2. Create and Expose an ICollectionView directly from your ViewModel

    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="." />