Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you use a CollectionViewSource inside a DataTemplate?

Is it possible to explicitly use a CollectionViewSource inside a data template? Normally we'd put the CollectionViewSource in the resources alongside the template, but our model doesn't allow that because the 'source' of the collectionviewsource is a property of the DataContext at this level in the tree, meaning there needs to be an instance at this level. Putting it out in the root of the resources would mean there was only one instance. We also can't simply use grouping on the outer level as these items don't exist until you're this far down the hierarchy, and not all siblings even have this property. So it makes sense logically that we instantiate the CollectionViewSource within the DataTemplate (in this instance a HierarchicalDataTemplate, but that's irrelevant.)

Specifically, we're trying to allow a specific sorting at this particular node level. Our only other choice is to sort in the ViewModel itself but that becomes a pain since we're using ObservableCollections which don't themselves support sorting. Actually, every article we've seen on the topic all state you should be using a CollectionViewSource precisely for that reason, hence this question.

For example, this works…

<HierarchicalDataTemplate x:Key="CategoryTemplate"
    ItemTemplate="{StaticResource TreeViewSymbolTemplate}"
    ItemsSource="{Binding Symbols}">

    <TextBlock Text="{Binding Name}" FontWeight="Bold" />

</HierarchicalDataTemplate>

But this doesn’t…

<HierarchicalDataTemplate x:Key="CategoryTemplate"
    ItemTemplate="{StaticResource TreeViewSymbolTemplate}">

    <HierarchicalDataTemplate.ItemsSource>
        <Binding>
            <Binding.Source>
                <CollectionViewSource Source="{Binding Symbols}" />
            </Binding.Source>
        </Binding>
    </HierarchicalDataTemplate.ItemsSource>

    <TextBlock Text="{Binding Name}" FontWeight="Bold" />

</HierarchicalDataTemplate>

Seems to me like it would, but it doesn’t. Again, we can't put the CollectionViewSource out at the same level as the data template as there needs to be one instance per template since each has its own set of items (although they will all share sorting criteria.)

M

like image 907
Mark A. Donohoe Avatar asked Oct 20 '10 15:10

Mark A. Donohoe


1 Answers

Ok... so this isn't exactly what I wanted to do, but the outcome is exactly the same, and nobody even commented, let alone answered, hence my putting this as one.

The original reason for us needing to do this was to have node-specific sorting. While we never did get the CollectionViewSource to work, we did manage to apply sorting directly to the nodes. The trick is to do it either at node creation time, or like we're doing it, when the node expands (we're using binding so we don't manually create the nodes.)

Note that our actual code does track if we've already applied the sort to the node in question so it isn't performed on each 'Expanded' event, but that code is irrelevant to this question so I omitted it for brevity. Still, you should add something similar.

Anyway, here's how you can do per-node sorting...

private void tvSymbols_Expanded(object sender, RoutedEventArgs e) {

    TreeViewItem node = e.OriginalSource as TreeViewItem;
    if(node==null) return;
    node.Items.SortDescriptions.Clear();
    node.Items.SortDescriptions.Add(new SortDescription("SomeField",      ListSortDirection.Ascending));
    node.Items.SortDescriptions.Add(new SortDescription("SomeOtherField", ListSortDirection.Descending));

}

Of course if anyone still figures out why the original question's code didn't work, lemme know!

Mark

like image 149
Mark A. Donohoe Avatar answered Sep 28 '22 09:09

Mark A. Donohoe