gulbaek gulbaek - 1 month ago 16
C# Question

Expand Wpf Treeview to support Sorting

Hi I created this small example, and I would like to expand it to support Sorting.

public class Country
{
public string Name { get; set; }
public int SortOrder { get; set; }
}


My xaml:

<TreeView Name="CountryTreeView" ItemsSource="{Binding}">
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>


And the code-behind:

readonly ObservableCollection<Country> Countries;

public MainWindow()
{
InitializeComponent();

Countries = new ObservableCollection<Country>
{
new Country{Name = "Denmark", SortOrder = 0},
new Country{Name = "Norway", SortOrder = 1},
new Country{Name = "Sweden", SortOrder = 2},
new Country{Name = "Iceland", SortOrder = 3},
new Country{Name = "Greenland", SortOrder = 4},
};

CountryTreeView.DataContext = Countries;
}


I would like to make it possible for the
Treeview
to Sort the
Countries
depending on the
SortOrder
value.

It needs to be able to do this on the fly.
So if I for example change
SortOrder
= 10 for Name = "Denmark" the
TreeView
would automatically reflect this.

Answer

I don't think there is a default sort for TreeViews. You can either sort the items prior to entering them into the collection, or overwrite the ObservableCollection to include a Sort method.

I overwrite it in one of my projects:

public class SortableObservableCollection<T> : ObservableCollection<T>
{
    // Constructors
    public SortableObservableCollection() : base(){}
    public SortableObservableCollection(List<T> l) : base(l){}
    public SortableObservableCollection(IEnumerable<T> l) :base (l) {}

    #region Sorting

    /// <summary>
    /// Sorts the items of the collection in ascending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    public void Sort<TKey>(Func<T, TKey> keySelector)
    {
        InternalSort(Items.OrderBy(keySelector));
    }

    /// <summary>
    /// Sorts the items of the collection in descending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    public void SortDescending<TKey>(Func<T, TKey> keySelector)
    {
        InternalSort(Items.OrderByDescending(keySelector));
    }

    /// <summary>
    /// Sorts the items of the collection in ascending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    /// <param name="comparer">An <see cref="IComparer{T}"/> to compare keys.</param>
    public void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
    {
        InternalSort(Items.OrderBy(keySelector, comparer));
    }

    /// <summary>
    /// Moves the items of the collection so that their orders are the same as those of the items provided.
    /// </summary>
    /// <param name="sortedItems">An <see cref="IEnumerable{T}"/> to provide item orders.</param>
    private void InternalSort(IEnumerable<T> sortedItems)
    {
        var sortedItemsList = sortedItems.ToList();

        foreach (var item in sortedItemsList)
        {
            Move(IndexOf(item), sortedItemsList.IndexOf(item));
        }
    }

    #endregion // Sorting
}

You would then sort it by calling something like

Countries.Sort(country => country.SortOrder);

I like overwriting it because it let me add additional functionality to it as well such as IndexOf or AddRange/RemoveRange