I created a couple of attached properties which handle this issue. I hope this comes in handy for someone!
First - a simple interface for your directionalised comparer. This extends IComparer but gives us one more property (SortDirection). Your implementation should use this to determine the correct ordering of elements (which would otherwise have been lost).
public interface ICustomSorter : IComparer
{
ListSortDirection SortDirection { get; set; }
}
Next is the attached behavior - this does two things: 1) tells the grid to use custom sort logic (AllowCustomSort=true) and b) gives us the ability to set this logic at a per-column level.
public class CustomSortBehaviour
{
public static readonly DependencyProperty CustomSorterProperty =
DependencyProperty.RegisterAttached("CustomSorter", typeof(ICustomSorter), typeof(CustomSortBehaviour));
public static ICustomSorter GetCustomSorter(DataGridColumn gridColumn)
{
return (ICustomSorter)gridColumn.GetValue(CustomSorterProperty);
}
public static void SetCustomSorter(DataGridColumn gridColumn, ICustomSorter value)
{
gridColumn.SetValue(CustomSorterProperty, value);
}
public static readonly DependencyProperty AllowCustomSortProperty =
DependencyProperty.RegisterAttached("AllowCustomSort", typeof(bool),
typeof(CustomSortBehaviour), new UIPropertyMetadata(false, OnAllowCustomSortChanged));
public static bool GetAllowCustomSort(DataGrid grid)
{
return (bool)grid.GetValue(AllowCustomSortProperty);
}
public static void SetAllowCustomSort(DataGrid grid, bool value)
{
grid.SetValue(AllowCustomSortProperty, value);
}
private static void OnAllowCustomSortChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var existing = d as DataGrid;
if (existing == null) return;
var oldAllow = (bool)e.OldValue;
var newAllow = (bool)e.NewValue;
if (!oldAllow && newAllow)
{
existing.Sorting += HandleCustomSorting;
}
else
{
existing.Sorting -= HandleCustomSorting;
}
}
private static void HandleCustomSorting(object sender, DataGridSortingEventArgs e)
{
var dataGrid = sender as DataGrid;
if (dataGrid == null || !GetAllowCustomSort(dataGrid)) return;
var listColView = dataGrid.ItemsSource as ListCollectionView;
if (listColView == null)
throw new Exception("The DataGrid's ItemsSource property must be of type, ListCollectionView");
// Sanity check
var sorter = GetCustomSorter(e.Column);
if (sorter == null) return;
// The guts.
e.Handled = true;
var direction = (e.Column.SortDirection != ListSortDirection.Ascending)
? ListSortDirection.Ascending
: ListSortDirection.Descending;
e.Column.SortDirection = sorter.SortDirection = direction;
listColView.CustomSort = sorter;
}
}
To use it, implement an ICustomComparer (with a parameterless constructor) and in your XAML:
<UserControl.Resources>
<converters:MyComparer x:Key="MyComparer"/>
<!-- add more if you need them -->
</UserControl.Resources>
<DataGrid behaviours:CustomSortBehaviour.AllowCustomSort="True" ItemsSource="{Binding MyListCollectionView}">
<DataGrid.Columns>
<DataGridTextColumn Header="Test" Binding="{Binding MyValue}" behaviours:CustomSortBehaviour.CustomSorter="{StaticResource MyComparer}" />
</DataGrid.Columns>
</DataGrid>
Best Answer
It took me the whole afternoon but I finally found a solution that is surprisingly simple, short and efficient:
To control the behaviors of the UI control in question (here a
DataGrid
) one might simply use aCollectionViewSource
. It acts as a kind of representative for the UI control inside your ViewModel without completely breaking the MVMM pattern.In the ViewModel declare both a
CollectionViewSource
and an ordinaryObservableCollection<T>
and wrap theCollectionViewSource
around theObservableCollection
:Then in the View part of the application you have nothing else to do as to bind the
ItemsSource
of theCollectionControl
to the View property of theCollectionViewSource
instead of directly to theObservableCollection
:From this point on you can use the
CollectionViewSource
object in your ViewModel to directly manipulate the UI control in the View.Sorting for example - as has been my primary problem - would look like this:
You see, very very simple and intuitive. Hope that this helps other people like it helped me.