using a CollectionViewSource with HierarchicalDataTemplate
Asked Answered
S

1

9

How would you go about using a CollectionViewSource (to supply sorting behavior) in conjunction with a HierarchicalDataTemplate's ItemsSource?

So given the code below, how could I apply sorting on the children at each level in the hierarchy?

<HierarchicalDataTemplate DataType="{x:Type l:CommandGroup}"
                          ItemsSource="{Binding Children}">
                    <HierarchicalDataTemplate.ItemContainerStyle>
                        <Style TargetType="{x:Type MenuItem}"
                               BasedOn="{StaticResource {x:Type MenuItem}}">
                            <Setter Property="Command"
                                    Value="{Binding Command}" />
                            <Setter Property="CommandParameter"
                                    Value="{Binding}" />
                        </Style>
                    </HierarchicalDataTemplate.ItemContainerStyle>
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>

I thought I might have been able to do the following but it breaks descendants from showing as well as producing the following binding error.

<HierarchicalDataTemplate DataType="{x:Type l:CommandGroup}">
                    <HierarchicalDataTemplate.ItemsSource>
                        <Binding>
                            <Binding.Source>
                                <CollectionViewSource Source="{Binding Children}" />
                            </Binding.Source>
                        </Binding>
                    </HierarchicalDataTemplate.ItemsSource>
                    <HierarchicalDataTemplate.ItemContainerStyle>
                        <Style TargetType="{x:Type MenuItem}"
                               BasedOn="{StaticResource {x:Type MenuItem}}">
                            <Setter Property="Command"
                                    Value="{Binding Command}" />
                            <Setter Property="CommandParameter"
                                    Value="{Binding}" />
                        </Style>
                    </HierarchicalDataTemplate.ItemContainerStyle>
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Children; DataItem=null; target element is 'CollectionViewSource' (HashCode=5114324); target property is 'Source' (type 'Object')

Edit : I ended up with the following - hope it helps someone else

 <Window.Resources>
    <l:SortedCollectionViewSource x:Key="SortedCollectionViewSource" Property="Name"/>
</Window.Resources>

<Window.ContextMenu>
    <ContextMenu>
        <MenuItem Header="Add new item..."
                  ItemsSource="{Binding AddNewItem.Children, Converter={StaticResource SortedCollectionViewSource}}">
            <MenuItem.ItemTemplate>
                <HierarchicalDataTemplate DataType="{x:Type l:CommandGroup}"
                                          ItemsSource="{Binding Children, Converter={StaticResource SortedCollectionViewSource}}">
                    <HierarchicalDataTemplate.ItemContainerStyle>
                        <Style TargetType="{x:Type MenuItem}"
                               BasedOn="{StaticResource {x:Type MenuItem}}">
                            <Setter Property="Command"
                                    Value="{Binding Command}" />
                            <Setter Property="CommandParameter"
                                    Value="{Binding}" />
                        </Style>
                    </HierarchicalDataTemplate.ItemContainerStyle>
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>
            </MenuItem.ItemTemplate>
        </MenuItem>
    </ContextMenu>
</Window.ContextMenu>

public class SortedCollectionViewSource : IValueConverter
{
    public string Property { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var cvs = new CollectionViewSource() { Source = value };
        cvs.SortDescriptions.Add(new SortDescription(Property, ListSortDirection.Ascending));

        return cvs.View;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
Seraglio answered 4/8, 2014 at 21:3 Comment(0)
O
0

as error says CollectionViewSource is not a FrameworkElement element so binding will not be able to resolve the property values.

as a solution you can have a read only property in view model for the same and bind it to the HierarchicalDataTemplate.ItemsSource

eg

    public CollectionViewSource MyCollectionView
    {
        get { return new CollectionViewSource() { Source = Children }; }
    }

and bind it as

    <HierarchicalDataTemplate DataType="{x:Type l:CommandGroup}"
                              ItemsSource="{Binding MyCollectionView}">

CollectionViewSource is xaml can normally bind to static/dynamic resources or static properties, other binding always have issue as CollectionViewSource does not pass DataContext

as a try you can try, but not sure if that works

<HierarchicalDataTemplate x:name="hTemplate" DataType="{x:Type l:CommandGroup}">
    <HierarchicalDataTemplate.ItemsSource>
        <CollectionViewSource Source="{Binding DataContext.Children,ElementName=hTemplate}" />

as above throws exception try this one, may this work if the CollectionViewSource is able to resolve the correct source collection

<HierarchicalDataTemplate x:name="hTemplate" DataType="{x:Type l:CommandGroup}">
    <HierarchicalDataTemplate.ItemsSource>
        <Binding>
            <Binding.Source>
                <CollectionViewSource Source="{Binding DataContext.Children,ElementName=hTemplate}" />
Osteopathy answered 5/8, 2014 at 3:19 Comment(5)
Thank you, but I was looking for a Xaml only solution. I also tried your last suggestion but it throws a similar error. {"Object of type 'System.Windows.Data.CollectionViewSource' cannot be converted to type 'System.Windows.Data.BindingBase'."}Seraglio
try the updated approach may this work for you. BTW what is the reason you need a CollectionViewSource? may we try to have a workaround.Osteopathy
This produces the same error. As mentioned in my initial post, the CollectionViewSource is to enable sortingSeraglio
ok, I was afraid of the same. are you willing to use attached behaviors? we can create an attached property as a workaround. however there will be some code involved but this may not change your existing classes. it will be as simple as adding a new class.Osteopathy
You've done a good job by using converter. by attached properties we can make this behavior attachable, practically there would be no difference except few lines of code. I'll write a sample for your comparison.Osteopathy

© 2022 - 2024 — McMap. All rights reserved.