views:

776

answers:

2

I'm trying to display a Wpf Treeview with items sorted by a CollectionViewSource.

Currently, everything is working except sorting using this code in my resource dictionary:

<HierarchicalDataTemplate DataType="{x:Type books:Container}" ItemsSource="{Binding Path=Items}">
    <nav:ContainerControl />
</HierarchicalDataTemplate>

What would be the syntax for changing the HierarchicalDataTemplate to bind to a CollectionViewSource that in turn pulls from the Items property?

I've tried variations of the code posted on Bea Stollnitz's blog with no success. I can't figure out how to set the source of the CollectionViewSource.

+1  A: 

Well let me just say that I hate my proposed solution, but it does work. Perhaps a WPF guru will enlighten us both with a better alternative. Of course if you were using a ViewModel behind your view, you could simply wrap the Items property of the model with a CollectionView in the ViewModel and be done with it.

But here's another solution. Basically, your HierarchicalDataTemplate can stay as is except you would add a Converter to the Binding. I implemented the following converter and changed the XAML accordingly.

<HierarchicalDataTemplate DataType="{x:Type books:Container}"
    ItemsSource="{Binding Items, Converter={x:Static local:CollectionViewConverter.Instance}}">
    <nav:ContainerControl />
</HierarchicalDataTemplate>

CollectionViewConverter.cs

public class CollectionViewConverter : IValueConverter
{

    public CollectionViewConverter() {}

    static CollectionViewConverter(){
        Instance = new CollectionViewConverter();
    }

    public static CollectionViewConverter Instance {
        get;
        set;
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var view = new ListCollectionView((System.Collections.IList)value);
        view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
        return view;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // not really necessary could just throw notsupportedexception
        var view = (CollectionView)value;
        return view.SourceCollection;
    }
}
Josh Einstein
Thanks for your solution.
awx
This solution is causing one minor issue however. This warning is printed in the Visual Studio Output window repeatedly."System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=VerticalContentAlignment; DataItem=null; target element is 'TreeViewItem' (Name=''); target property is 'VerticalContentAlignment' (type 'VerticalAlignment')"A MS guy in the MSDN groups says it can safely be ignored however.
awx
That error sounds like it's coming from the default template for TreeViewItem, not the binding that your question deals with.
Josh Einstein
A: 

I did as you suggested and wrapped the Items collection with a ListCollectionView:

private SortDescription _ItemsLcvSortDesc;
    private SortDescription ItemsLcvSortDesc
    {
        get
        {
            if (_ItemsLcvSortDesc == null)
                _ItemsLcvSortDesc = new SortDescription("SortOrder", ListSortDirection.Ascending);
            return _ItemsLcvSortDesc;
        }
    }

    private ListCollectionView _ItemsLcv;
    public ListCollectionView ItemsLcv
    {
        get
        {
            if (_ItemsLcv == null)
                _ItemsLcv = CollectionViewSource.GetDefaultView(Items) as ListCollectionView;
            _ItemsLcv.SortDescriptions.Add(ItemsLcvSortDesc);
            return _ItemsLcv;
        }
    }

Did I miss anything?

awx