开发者

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; }
}
开发者_JAVA技巧

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.


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


You can also use the CollectionViewSource.GetDefaultView with ListCollectionView to do the sorting for you, tho you do have to set it for all the child collections as well. I use a linq query to flatten my list to find all the children and set the sorting.

foreach (var structure in Model.Structures.Flatten(t => t.SubItems))
{
    var view = CollectionViewSource
        .GetDefaultView(structure.SubItems) as ListCollectionView;

    if (view != null)
    {
        view.CustomSort = (x,y) => string.Compare( x, y );
    }
}

elsewhere

public static IEnumerable<T> Flatten<T>(
    this IEnumerable<T> list, Func<T, IEnumerable<T>> subitems)
{
    foreach (T child in list)
    {
        yield return child;

        foreach (T other in Flatten(subitems(child), subitems))
        {
            yield return other;
        }
    }
}


I developed a View Model library which does a number of things, including letting you display hierarchical data in sorted order. The library is described in this article. The piece doesn't include a sorting example, but you can easily modify the Demo 2's View Model to sort its hierarchical data. Just do the following:

  1. The first of three boolean arguments passed to SampleTreeNodePM's base constructor indicates if the View Model collections should use the same indices as the Domain Model collections or not. Change this value to false.
  2. The last argument to the DesiredPosition method override's return value is a boolean that indicates whether to sort the View Model collection or not. Change this value to true.
  3. Following from the previous step, you now need to modify the Domain Model object, SampleTreeNode, to implement IComparable<SampleTreeNode>. The CompareTo method need simply delegate the job to the Name string property:

    public int CompareTo( SampleTreeNode other )
    {
        return Name.CompareTo( other.Name );
    }
    

Regarding updating the position "on the fly" as your question says, just call my library's HierarchicalPresentationModel.UpdatePosition method after making the change.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜