Search code examples
c#.netwpfmvvmcollectionviewsource

Proper way to use CollectionViewSource in ViewModel


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.


Solution

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