Search code examples
wpfvb.netsortingcollectionviewsource

CollectionViewSource with custom sort


I'm new to WPF and I'm having difficulty trying to sort a CollectionViewSource with a custom sort. Here's the situation:

I have a SearchView that is called with a parameter which becomes it's datacontext like so:

mainView.SetGlobalOverlay(New SearchView With {.DataContext = interventionViewModel})

In the SearchView.xaml, I then bind the CollectionViewSource to the collection :

<CollectionViewSource x:Key="UnitsCollection"
                          Filter="UnitsCollection_Filter"
                          Source="{Binding Path=Units}" />

Now, I already have an IComparer interface implemented in another shared class. This comparer is used on a ListCollectionView somewhere else in the sourcecode and works fine. Now, how can I re-use this comparer on a CollectionViewSource?


Solution

  • In order to use the custom sorter for the CollectionViewSource, you have to wait until the ItemsControl (e.g. a list box) is loaded; then you can get the ListCollectionView using the View property of the CollectionViewSource.

    As illustration, here is a small example that displays a list of integers in two different ways: the upper list box applies a custom sort order, whereas the lower list box is unsorted:

    screen shot

    MainWindow.xaml:

    <Window x:Class="WpfApplication27.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:clr="clr-namespace:System;assembly=mscorlib"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="300">
        <Window.Resources>
            <CollectionViewSource x:Key="MyCollectionViewSource1" Source="{Binding RawData}" />
            <CollectionViewSource x:Key="MyCollectionViewSource2" Source="{Binding RawData}" />
        </Window.Resources>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <ListBox Grid.Row="0" Margin="5" Background="LightSkyBlue"
                     ItemsSource="{Binding Source={StaticResource MyCollectionViewSource1}}"/>
            <ListBox Grid.Row="1" Margin="5" Background="LightYellow"
                     ItemsSource="{Binding Source={StaticResource MyCollectionViewSource2}}"/>
        </Grid>
    </Window>
    

    MainWindow.xaml.cs:

    using System.Collections;
    using System.Collections.ObjectModel;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WpfApplication27
    {
        public partial class MainWindow : Window
        {
            public ObservableCollection<int> RawData { get; private set; }
    
            public MainWindow()
            {
                RawData = new ObservableCollection<int> { 10, 222, 1, 333, 2, 777, 6 };
    
                InitializeComponent();
    
                DataContext = this;            
    
                this.Loaded += MainWindow_Loaded;
            }
    
            void MainWindow_Loaded(object sender, RoutedEventArgs e)
            {
                CollectionViewSource source = (CollectionViewSource)(this.Resources["MyCollectionViewSource1"]);
                ListCollectionView view = (ListCollectionView)source.View;
                view.CustomSort = new CustomSorter();
            }
        }
    
        // Sort by number of digits (descending), then by value (ascending)
        public class CustomSorter : IComparer
        {
            public int Compare(object x, object y)
            {
                int digitsX = x.ToString().Length;
                int digitsY = y.ToString().Length;
                if (digitsX < digitsY)
                {
                    return 1;
                }
                else if (digitsX > digitsY)
                {
                    return -1;
                }
                return (int) x - (int) y;
            }
        }
    }