Introduction


The CollectionView offers significant benefits to Developers. The aim of this article is to point these out and offer some tips using them. 

There are plenty of resources on the web listing the various functions of CollectionView.   The majority are introductory articles covering breadth rather than depth.
The reader unfamiliar with CollectionView should probably read some of those before this article - some are listed in the Further Reading section below.

By definition we’re talking collections and to the business developer that usually means DataGrids - which we will focus on.

Audience


The article is aimed at the intermediate to advanced WPF developer  

It is assumed the reader will download and study the sample application CollectionView Tips sample  since the reader will be able to see the full working code and experiment with it.   Since this is intended for an audience who will be confident reading code the code explanations will be kept brief.  It's already quite a long article without labouring explanations.

The sample is intended to illustrate specific techniques rather than industry best practices.  

Broad Functionality

It’s worth at least listing broad functionality before leaping into specifics.  As it’s name suggests the ICollectionView is a View on some sort of Collection.  It allows you to manipulate a collection which you are going to bind the UI to.  
You can:

  • Sort
  • Group
  • Filter
  • Traverse by setting Current record
  • Iterate all qualifying (filtered) items
  • Find the Index of an item
  • Raise event on Add/Remove of item

There are several classes which inherit from ICollectionView  but this article will gloss over the details and focus on CollectionView and ListCollectionView. If in doubt, choose ListCollectionView

Particular Advantages

There are several significant advantages of the CollectionView  which are particularly appealing to the MVVM developer.   From the ViewModel the developer may:

  • Set current and hence selected record in the View.
  • Detect and handle user insertion or deletion from the View.
  • Work with all the items as they will be presented - because it’s not virtualised.
  • Introduce another layer allowing cleaner decoupling of data
  • Filter very flexibly

It’s worth stressing here that you can keep the ViewModel and View totally decoupled whilst carrying out this manipulation. Great for the MVVM developer.   

Usage

Usage Introduction


MVVM developers often bind an ObservableCollection<T> to the ItemsSource of a DataGrid ( or ItemsControl ).  Instead of binding directly to the ObservableCollection, the sample instead binds to a CollectionView whose source is an ObservableCollection.

First obtain your CollectionView

Most code samples you see will have something like:

MyView = (CollectionView)CollectionViewSource.GetDefaultView(myObservableCollection);

As it says, that's obtaining the default view of the collection and it's often not really necessary to think any further than that.
  
This is fine until you decide you wish to use that one collection twice. If you set up two views in this way you will find as you manipulate one of them ( say by filtering ) then something surprising happens.  The two CollectionViews are actually referencing the same CollectionViewSource and you will find both are filtered. In order to avoid this you need to instantiate your own CollectionViewSource rather than use the default.
CollectionViewSource is one of the things which lies off the road most often travelled and therefore explanations are thin on the ground. You often don't really need to know about it at all as you're getting that default view and of course that's where most blogger and developers interest ends.  The key thing to bear in mind is you need different CollectionViewSources if you are going to manipulate one collection in two different ways.  
The CollectionViewSource is an object so you can explicitly declare one and instantiate it.
It has a Source property which is used to point to any collection.  Yes that's right - the collectionviewsource itself has a source.
You could therefore do:

CollectionViewSource cvs = new CollectionViewSource();
cvs.Source = People;
PeopleView = (CollectionView)cvs.View;

TwoCollectionViewsViewModel uses one line for each of it's two views:

PeopleView = (CollectionView)new CollectionViewSource { Source = People }.View;
LevelsPeopleView = (CollectionView)new CollectionViewSource { Source = People }.View;

A filter predicate is then applied which will only affect LevelsPeopleView:

LevelsPeopleView.Filter = FirstOfLevel_Filter;

This is how TwoCollectionViewsViewModel uses the one collection to show a list of levels in one datagrid ( the left one )  and the list of People in another.
Filtering will be explained in a later section.

The technique here means both DataGrids have the same underlying objects bound for each row.  When the user selects what appears to them to be a level in summary to the left that is literally the first Person object qualifying in the datagrid to the right.  That can be used to make that Person the current one in the main DataGrid.

However you obtain your CollectionView, this will be what the ItemsSource of the DataGrid binds to.


Sorting

You may sort your collection using the CollectionView by adding to the SortDescriptors collection.

When you do this then the CollectionView uses reflection on your type to decide what those strings in each SortDescriptor actually translate into.  This may mean you want to consider writing your own custom IComparer because that can use a type known at compilation and hence will be quicker.  You’d spend a long time looking for a way to use that IComparer with just a CollectionView though.

If you wish to use this technique then use a ListCollectionView since it has a CustomSort property.

listCollectionView.CustomSort = new customIComparer();

Let's consider custom sorting first.  DynamicFilteringViewModel uses a custom sort:

            PeopleView.CustomSort = new PersonComparer();
…..
    public class PersonComparer : IComparer
    {
        public int Compare(object x, object y)
        {
            Person p1 = x as Person;
            Person p2 = y as Person;
            int result = p1.OrganizationLevel.CompareTo(p2.OrganizationLevel);
            if (result == 0)
            {
                result = p1.LastName.CompareTo(p2.LastName);
            }
            if (result == 0)
            {
                result = p1.FirstName.CompareTo(p2.FirstName);
            }
            return result;
        }
    }

Notice that multiple sort criteria are quite tricky to deal with. The code applies OrganizationLevel as the primary sort, then LastName and then FirstName.  It is arguable whether the added efficiency repays the effort and somewhat obscure code.  

The sort is in ascending order, if you wanted to sort in descending order then you would switch p1 and p2 around for the CompareTo:

int result = p2.OrganizationLevel.CompareTo(p1.OrganizationLevel);

SortDescriptions are much easier to understand and the sample has the relevant lines of code commented out, so you can easily try them.

PeopleView.SortDescriptions.Clear();
PeopleView.SortDescriptions.Add(new SortDescription("OrganizationLevel", ListSortDirection.Ascending));
PeopleView.SortDescriptions.Add(new SortDescription("LastName", ListSortDirection.Ascending));
PeopleView.SortDescriptions.Add(new SortDescription("FirstName", ListSortDirection.Ascending));

The developer is best advised to present a maximum of 300 or so records at a time to the user in any collection.  With this in mind the inefficiency of SortDescriptors can often be irrelevent and this is usually the best approach.

Of course you can still sort the underlying collection and not use the sorting facilites in CollectionView - this is most useful in scenarios where the users want the one sort order and the collection is not particularly dynamic. Maybe that list of personnel as it was yesterday is going to be good enough all day today.

You will see the TwoCollectionViews usercontrol  uses LINQ

People = new ObservableCollection<Person>(
             ppl.OrderBy(x=>x.OrganizationLevel)
             .ThenBy(x=>x.LastName).ThenBy(x=>x.FirstName).Select(x=>x).ToList());

This sort would need to be repeated if an item was added but is easy to write if you are used to working with LINQ.  Arguably still easier to write than the somewhat obscure IComparer.


Filtering


This is a very useful aspect of the CollectionView due to the fact you can use the ObservableCollection as a sort of repository and change just the filtering as the user chooses different criteria.

As MSDN says - the filter is a Predicate method which is used to decide whether a row is included in the view or not.  Return true and it’s included, false and it’s not.  MSDN shows very simple filters and the first of these is shown below ( to encourage the reader to keep reading).

Simple Filtering

From MSDN:

private void ShowOnlyBargainsFilter(object sender, FilterEventArgs e)
{
    AuctionItem product = e.Item as AuctionItem;
    if (product != null)
    {
        // Filter out products with price 25 or above
        if (product.CurrentPrice < 25)
        {
            e.Accepted = true;
        }
        else
        {
            e.Accepted = false;
        }
    }
}

Slightly more Complicated


The sample demonstrates a slightly more complicated filter and a very flexible one.  

TwoCollectionsView offers the user the ability to click on a level in the left datagrid and navigate to the first Person of that level in the right.

In order to pick out just the first person in each level (for the left DataGrid ) the filter relies on the fact the collection is iterated first to last.  It returns true if the level changes from the last .

private int LastLevel = -1;
private bool FirstOfLevel_Filter(object item)
{
    Person p = item as Person;
    if (p != null)
    {
        if (p.OrganizationLevel == LastLevel)
        {
           return false;
        }
        LastLevel = p.OrganizationLevel;
        return true;
    }
    return false;
}

The code is pretty self explanatory, setting LastLevel so the first will definitely qualify and using that variable to decide which is the first of each level.

Complicated Filtering


The DynamicFilteringView allows the user to pick from a selection of criteria and Filter on any mix of these on a click of the Filter button.

DynamicFilteringViewModel lives up to it’s name by illustrating an approach using a list of predicates.  Where any of them are false it returns false by using some LINQ to iterate through the list:

private List<Predicate<Person>> criteria = new List<Predicate<Person>>();
 
private bool dynamic_Filter(object item)
{
    Person p = item as Person;
    bool isIn = true;
    if (criteria.Count() == 0)
        return isIn;
    isIn = criteria.TrueForAll(x => x(p));
 
    return isIn;
}

The list is first cleared and then selected criteria are added:

private void ExecuteApplyFilter()
{
    DateTime _zeroDay = new DateTime(1, 1, 1);
    DateTime _now = DateTime.Now;
 
    criteria.Clear();
 
    if (yearsChosen > 0)
    {
        criteria.Add(new Predicate<Person>(x => yearsChosen < (
            (_zeroDay + (_now - x.BirthDate)).Year - 1)
                                                            ));
    }
 
    if (letterChosen != "Any")
    {
        criteria.Add(new Predicate<Person>(x => x.LastName.StartsWith(letterChosen)));
    }
    if (genderChosen != "Any")
    {
        criteria.Add(new Predicate<Person>(x => x.Gender.Equals(genderChosen.Substring(0, 1))));
    }
 
    PeopleView.Filter = dynamic_Filter;
    RaisePropertyChanged("PeopleView");
    // Bring the current person back into view in case it moved
    if (CurrentPerson != null)
    {
        Person current = CurrentPerson;
        PeopleView.MoveCurrentToFirst();
        PeopleView.MoveCurrentTo(current);
    }


The user sees three combo boxes which they can select any combination of options from.  Those picked are added to the list of predicates with  the specific value to compare.  If there are no criteria then everything qualifies, if not then all are checked and must be true for the record to be included.

All but the most complicated requirements can usually be broken down into simple steps and tackled with this approach.  A particular advantage is that each of the  individual criteria can be simple, discrete and easy to understand.  Should something go wrong then you can test each criteria separately.
There's probably a pattern with a name for this technique somewhere.

Performance and Repository


By decoupling the presentation from the underlying ObservableCollection of data, filtering offers a useful way of improving perceived performance. The window can load a small subset of data initially with a filter set to say the user’s team data or top 100 records. The user will then get to see this data quickly and the filter will be matched by an obvious UI indicator of some sort like a ComboBox default. 

An asynchronous task or service call can then be made for another set of data added to the ObservableCollection. The UI will not change because the filter is in place.

Or a subset can be shown initially and the user will only see a small change as the next 50 or so records are fetched.

This process can be repeated with a chain of calls and or it can be done as a lazy load as the user chooses a different filter criteria.

Since the contents of the ObservableCollection do not have thread affinity, each subset of data can be safely appended from within a non UI thread without any Dispatcher.Invoke complications. In this manner the ObservableCollection will eventually be filled and the user can switch filters and see the data immediately. This approach is simpler than using a separate repository which of course might occasionally be preferable.

Programmatic Selection of Record


The ability to set current record in the ViewModel and that then selects it in the View.
This is a great feature of the CollectionView which sounds absolutely brilliant at first, but there’s a complication.
 

The Good


When you set IsSynchronizedWithCurrentItem:

<DataGrid ……
          IsSynchronizedWithCurrentItem="True"
          EnableRowVirtualization="True"
          >

This means the selecteditem in the view will match the current item in your bound collectionview.    You can set current in several ways, the most useful being MoveCurrentTo(). 
You can see this working as you spin the application up. The Viewmodel uses CollectionView.MoveCurrentTo:

        private Person CurrentPerson
        {
            get { return PeopleView.CurrentItem as Person; }
            set
            {
                PeopleView.MoveCurrentTo(value);
                RaisePropertyChanged();
             
            }
        }
 
…….
            Person _person = ppl[121];
            CurrentPerson = _person;


This makes the 121st Person in the list the Current item in the collection and the DataGridRow becomes the selected item in the DataGrid.
The DataGrid is virtualized by default but the reader may notice the property is explicitly set to true just to underline this.  
What;s so great about that?
If the code instead used a DataGridRow to navigate then there would be a problem with selecting that item since it would be virtual. There would be no corresponding control to select.
The convenience of not needing to worry about Virtualized rows is one of the plusses of CollectionView.  As you can see, MoveCurrentTo copes with an item which is virtualized with no problem.

The not so Good

There is a “gotcha” with this MoveCurrentTo.  It suffers from the same issue which making a datagridrow selected does.  The problem is that the row in the view is current but it will not have focus and it may not be in view.  Just doing MoveCurrentTo will give you a very light grey background on a row which the user has to scroll down to.  Not good.  But of course the sample quite clearly manages to focus the row.  How's it do that?

Scrolling

The sample scrolls the selected row up into view by using a Behavior which is set on the DataGrid.

The behaviour is attached thus:

<DataGrid ……
          >
    <i:Interaction.Behaviors>
        <support:ScrollDataGridRowIntoView />
    </i:Interaction.Behaviors>

The critical part of the code is where it calls ScrollIntoView on the selected item.

datagrid.ScrollIntoView(datagrid.SelectedItem);

Full Behavior:

    class ScrollDataGridRowIntoView : Behavior<DataGrid>
    {
        protected override void OnAttached()
        {
             
            base.OnAttached();
            this.AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
        }
 
        void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (sender is DataGrid)
            {
                DataGrid datagrid = (sender as DataGrid);
                if (datagrid.SelectedItem != null)
                {
                    datagrid.Dispatcher.BeginInvoke( (Action)(() =>
                        {
                            datagrid.UpdateLayout();
                            if (datagrid.SelectedItem !=  null)
                            {
                                datagrid.ScrollIntoView(datagrid.SelectedItem);
                            }
                        }));
                }
            }
        }
        protected override void OnDetaching()
        {
            base.OnDetaching();
            this.AssociatedObject.SelectionChanged -=  AssociatedObject_SelectionChanged;
 
        }
    }
}

This works because as the DataGrid is synchronized setting the Current item in the CollectionView selects the corresponding DataGridRow in the view. SelectionChanging fires on the DataGrid and the ScrollIntoView is invoked.
The code could be simpler if implemented as an eventhandler.  Making it a behaviour makes for easier re-use and centralises the code out from code behind in each Window/UserControl into a separate class.

Using this the user gets to see the row, even if it was out of view.
 
This is only part of the story though.
The row still hasn't got focus so it’s still grey and not as obviously selected as a user will expect.  Since the row doesn't have focus, the user cannot traverse rows in the grid just by clicking the up or down arrows.

Focus

Focussing on the row is handled by an Attached Behavior. This has to be an Attached Behavior because we need to apply it via a style.  You would get an error if you tried to connect a Behavior similar to the above one via a style.  By making this such a Behavior it becomes re-usable like the scroll behavior but as you will see this comes at the price of a fair bit of added code.

The business part of this is very simple:

DataGridRow row = e.OriginalSource as DataGridRow;
// If focus is already on a cell then don't focus back out of it
if (!(Keyboard.FocusedElement is DataGridCell) && row != null)
{
    row.Focusable = true;
    Keyboard.Focus(row);
}

The full Behavior looks quite complicated but is just there to attach that piece of code to the DataGridRow Selected event:

    public static bool GetIsDataGridRowFocussedWhenSelected(DataGridRow dataGridRow)
    {
        return (bool)dataGridRow.GetValue(IsDataGridRowFocussedWhenSelectedProperty);
    }
 
    public static void SetIsDataGridRowFocussedWhenSelected(
      DataGridRow dataGridRow, bool value)
    {
        dataGridRow.SetValue(IsDataGridRowFocussedWhenSelectedProperty, value);
    }
 
    public static readonly DependencyProperty IsDataGridRowFocussedWhenSelectedProperty =
        DependencyProperty.RegisterAttached(
        "IsDataGridRowFocussedWhenSelected",
        typeof(bool),
        typeof(DataGridRowBehavior),
        new UIPropertyMetadata(false, OnIsDataGridRowFocussedWhenSelectedChanged));
 
    static void OnIsDataGridRowFocussedWhenSelectedChanged(
      DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        DataGridRow item = depObj as DataGridRow;
        if (item == null)
            return;
 
        if (e.NewValue is bool == false)
            return;
 
        if ((bool)e.NewValue)
            item.Selected += OndataGridRowSelected;
        else
            item.Selected -= OndataGridRowSelected;
    }
    static void OndataGridRowSelected(object sender, RoutedEventArgs e)
    {
        DataGridRow row = e.OriginalSource as DataGridRow;
        // If focus is already on a cell then don't focus back out of it
        if (!(Keyboard.FocusedElement is DataGridCell) && row != null)
        {
            row.Focusable = true;
            Keyboard.Focus(row);
        }
    }
 
}

That is attached via a style:

<DataGrid.RowStyle>
    <Style TargetType="{x:Type DataGridRow}">
        <Setter Property="support:DataGridRowBehavior.IsDataGridRowFocussedWhenSelected" Value="true"/>
    </Style>
</DataGrid.RowStyle>

A full explanation of Attached Behaviors is outside the scope of this article but luckily Josh Smith already wrote a good article.

As the name suggests they allow you to add behaviour via a WPF Attached Property.  Simple attached behaviors can be added pretty much by cutting and pasting Josh’s code from that article and modifying name, control type, event and of course your code to do stuff.

These two behaviors are kept separate largely for simplicity.  There is, however,  also the occasional requirement to bring into view but not focus or select without bringing into view. 


The Keyboard.Focus solution avoids having to walk the visual tree and find a cell to focus but introduces an edge case.  For this to work, keyboard focus cannot already be on a datagrid cell.  In most circumstances the user will have clicked a button, selected something on a combo or something which will guarantee this is not the case.

TwoCollectionViews illustrates a possible complication by presenting the level choice in a DataGrid.  The view still works because the DataGrid is ReadOnly and the one column set so it cannot take focus:

<DataGrid x:Name="dgLevels"  AutoGenerateColumns="False"
..
          IsReadOnly="True"
          >
    <DataGrid.Resources>
        <Style x:Key="NoFocusStyle" TargetType="{x:Type DataGridCell}">
            <Setter Property="Focusable" Value="False"/>
        </Style>
    </DataGrid.Resources>
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding OrganizationLevel}"  Header="Lvl" CellStyle="{StaticResource NoFocusStyle}" />
    </DataGrid.Columns>
</DataGrid>

In a real world business application you could instead just use a ListView to pick from and avoid the issue. 
Other edge cases could be imagined but are rare enough that this approach is the first thing to try.  If you absolutely need to have focus in one datagridcell when you traverse then there is a compromise you could take or a slightly different approach.  

Compromise

If you remove the check on whether the FocusedElement is a datagridcell then it will always fire but the user will find they need two clicks on the up or down arrow to traverse rows.   This compromise will be unacceptable for some groups of users whilst others won't notice - depending on how often they use keyboard arrows.

No Compromise

Should that be a problem then the simple solution with two datagrids is to use code behind and the DataGridRow Selected event for both DataGrids.  You can then set which has focus and only ignore when focus is in the current datagrid.

You can tie yourself in knots trying to cope with all imaginary possible edge cases and the best way to handle these is as and when you see a real problem.

What if you don't want the first Cell?


Keyboard focus on the row turns it all blue (by default) and puts focus on the first cell which will take it. In the event that you don’t wish to set focus to the first sell then that can be handled by setting IsTabStop=false on the cell of the column in question.  You can see this used in TwoCollectionViews which will put the focus on the second column.

Like the DataGridRow there is no actual cell there to refer to directly in markup so this is done by a style:

            <DataGrid.Resources>
                <Style x:Key="NoTabStopStyle" TargetType="{x:Type DataGridCell}">
                    <Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
                </Style>
            </DataGrid.Resources>
Using this in a Datagrid column:
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding OrganizationLevel}"  Header="Lvl" CellStyle="{StaticResource NoTabStopStyle}" />
                <DataGridTextColumn Binding="{Binding JobTitle}"  Header="Title" />

Row Number

Introduction

On some rare occasions users will request an index on rows in a DataGrid.  A particular tip on this is that users often change their minds when they realise this is not a reliable “key” identifying a record and that record is row 4 will change as they sort a DataGrid on a different column.  It is easy enough to implement.  Just bear in mind you may be taking it back out later and be careful to explain how it will work. 

Still want one?

The requirement can be fulfilled using 3 different techniques.

Semi Fixed Row Numbers

One possible requirement is for users who have an expected default sort order which they use almost all the time. This can be tackled using a filter.  A filter will be applied by iterating all records in a view. You can increment a counter and update a property in the row viewmodel which is bound in the view:

private int LastLevel = -1;
private int RowNo = 0;
private bool FirstOfLevel_Filter(object item)
{
    Person p = item as Person;
    if (p != null)
    {
        if (p.OrganizationLevel == LastLevel)
        {
           return false;
        }
        LastLevel = p.OrganizationLevel;
        RowNo++;
        p.RowNo = RowNo;
        return true;
    }
    return false;
}

Here, an integer property RowNo is added to the Person object, incremented as each qualifies and would be bound in the view.  Especially if you will be filtering anyway, this will add practically no overhead even if your underlying collection is big.

As the user clicks on column headers of the DataGrid to sort or adds a new item, these will not be re-evaluated unless you call CollectionView.Refresh.  Note that calling Refresh is quite a heavy process.

Dynamic Row Numbers

The same technique is used in both methods within the RowNo Usercontrol.  The Row Number is obtained by using the method CollectionView.IndexOf(item) and adding 1. The complications lie in where that is called.  One approach is to use a converter.  A static is used as a bridging class to pass a reference to the CollectionView efficiently.

public static class CVHolder
{
    public static CollectionView CV;
}
public class RowFromObject : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        int rn = 0;
        if (CVHolder.CV == null || value == null)
            return rn;
 
        rn = CVHolder.CV.IndexOf(value);
        return rn+1;
    }
 
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return 1;
    }
}

Used in the View:

<DataGrid.Columns>
     <DataGridTextColumn Binding="{Binding ., Converter={StaticResource rowfromobject}, StringFormat=#;;#}" Header="Via Converter"/>

This rather complicates what would otherwise be a simple method but keeps the row viewmodel clean.

The second approach is demonstrated in the PersonVM viewmodel which is passed a reference to the CollectionView:

class PersonVM : NotifyUIBase
{
    public Person ThePerson { get; set; }
    public CollectionView CV { get; set; }
    public int RowNo
    {
        get
        {
            int row = -1;
            row = CV.IndexOf(this);
            return row + 1;
        }
    }
     
}

In the RowNoViewModel we can see the CollectionView set in the row viewmodels and the bridging static:

    People = new ObservableCollection<PersonVM>(
                 ppl.OrderBy(x => x.ThePerson.OrganizationLevel)
                 .ThenBy(x => x.ThePerson.LastName).ThenBy(x => x.ThePerson.FirstName).Select(x => x).ToList());
 
    PeopleView = (CollectionView)new CollectionViewSource { Source = People }.View;
    ppl.ForEach(p =>p.CV = PeopleView);
    CVHolder.CV = PeopleView;
    RaisePropertyChanged("PeopleView");
 
    People.CollectionChanged += People_CollectionChanged;
}
 
void People_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    RefreshView();
}

Note also how the CollectionChanged event is handled.   Insertion or deletion will fire this event and the CollectionView refresh will re-calculate the row numbers.
Raising the property changed event then tells the UI to read the values again.  Explicitly handling inserts and deletes is usually best, but this approach is occasionally useful.

private object RefreshView()
{
    PeopleView.Refresh();
    RaisePropertyChanged("PeopleView");
    return null;
}

The user can also sort the DataGrid by clicking column headers - the DataGrid sorting event will be fired and this is handled in code behind.  In order to keep the View and ViewModel de-coupled the View uses MVVM Light messaging to tell the ViewModel to refresh the CollectionView:


private void dg_Sorting(object sender, DataGridSortingEventArgs e)
{
    var msg = new RefreshView();
    Messenger.Default.Send<RefreshView>(msg);
}


Registering for the message in the view model:

public RowNoViewModel()
{
    if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
    {
        GetData();
    }
    InsertPerson = new RelayCommand<string>(ExecuteInsertPerson);
    Messenger.Default.Register<RefreshView>(this, (action) => RefreshView());
}


Further Reading


MSDN CollectionView
MSDN ListCollectionView
MSDN CollectionViewSource
Working with CollectionView in WPF
PRISM MVVM ( scroll down )
WPF Tutorial


Closing


Many WPF developers only turn to the CollectionView for grouping.  CollectionView offers some great functionality beyond this. The techniques described can solve fairly common requirements which are otherwise tricky without compromising MVVM principles.  The sample keeps View and ViewModel totally decoupled and uses XAML friendly behaviors to avoid code behind.