Can you use a CollectionViewSource inside a DataTemplate?
Asked Answered
C

1

7

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

Cracy answered 20/10, 2010 at 15:26 Comment(2)
Would really like to get an answer here more appropriate to the original way of doing it, or even if the CollectionViewSource was inside the Resource part of the DataTemplate.Alta
Actually, I found an even easier way to do it that I really like... I just move the CollectionViewSource into a converter, and use that to set the return value. For now we're just 'newing' one up at binding-time since that's all we need to do, but technically we could return pre-hydrated ones that we have references to as needed. Damn spiffy if you ask me and works like a champ!Cracy
C
4

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

Cracy answered 21/10, 2010 at 8:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.