primfaktor
primfaktor

Reputation: 2999

How can I get the CollectionView that is defined in XAML

I wanted to bind to an ObservableCollection in XAML and also apply the grouping there. In principle, this worked fine.

<UserControl.Resources>
    <CollectionViewSource x:Key="cvs" Source="{Binding Path=TestTemplates}">
        <CollectionViewSource.SortDescriptions>
            <scm:SortDescription PropertyName="Title"/>
        </CollectionViewSource.SortDescriptions>
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="TestCategory"/>
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>
</UserControl.Resources>

Then the data binding expression became ItemsSource="{Binding Source={StaticResource ResourceKey=cvs}}" instead of ItemsSource="{Binding Path=TestTemplates}".

At first, everything seemed cool, until I wanted to refresh the UI from the view model. The problem is, that CollectionViewSource.GetDefaultView(TestTemplates) returned a different view than the one from XAML where the grouping was applied. Thus, I could not set selection or do anything useful with it.

I could fix it by binding the list again directly to the view model's property and setting up the grouping in the code-behind. But I'm not that happy with this solution.

private void UserControlLoaded(object sender, RoutedEventArgs e)
{
    IEnumerable source = TemplateList.ItemsSource;
    var cvs = (CollectionView)CollectionViewSource.GetDefaultView(source);
    if (cvs != null)
    {
        cvs.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
        cvs.GroupDescriptions.Add(new PropertyGroupDescription("TestCategory"));
    }
}

I assume, the reason for that is already given by John Skeet here.

Nevertheless, I would expect that there should be a way to get the right view. Am I wrong?

Upvotes: 9

Views: 6240

Answers (3)

primfaktor
primfaktor

Reputation: 2999

Found a way, based on J. Lennon's answer. If I pass something that has access to the resources with my command, then I can look up the CollectionViewSource there.

In XAML (CollectionViewResource as above):

<Button Command="{Binding Command}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}">Do it!</Button>

And in the VM code:

private void Execute(object parm)
{
    var fe = (FrameworkElement)parm;
    var cvs = (CollectionViewSource)fe.FindResource("cvs");
    cvs.View.Refresh();
}

The Execute is the one that is given to the RelayCommand.

This would answer the question, but I don't like it very much. Opinions?

Upvotes: 2

J. Lennon
J. Lennon

Reputation: 3361

You could not just do that?

var _viewSource = this.FindResource("cvs") as CollectionViewSource;

If the data is connected, I assume that will have an updated view.

Upvotes: 6

Kent Boogaart
Kent Boogaart

Reputation: 178820

I tend to just expose the collection view from the VM rather than have the view define it:

public ICollection<Employee> Employees
{
    get { ... }
}

public ICollectionView EmployeesView
{
    get { ... }
}

That way your VM has full control over what is exposed to the view. It can, for example, change the sort order in response to some user action.

Upvotes: 6

Related Questions