CompositeCollection + CollectionContainer: Bind CollectionContainer.Collection to property of ViewModel that is used as DataTemplates DataType
Asked Answered
T

2

20

I do not get the correct Binding syntax to access the Cats and Dogs properties of MyViewModel within a DateTemplate that defines a CompositeCollection within its resources.

public class MyViewModel
{
    public ObservableCollection<Cat> Cats { get; private set; }
    public ObservableCollection<Dog> Dogs { get; private set; }
}
<DataTemplate DataType={x:Type local:MyViewModel}">
  <DataTemplate.Resources>
    <CompositeCollection x:Key="MyColl">
      <!-- How can I reference the Cats and Dogs properties of MyViewModel? -->
      <CollectionContainer Collection="{Binding Dogs, ????}">
      <CollectionContainer Collection="{Binding Cats, ????}">
    </CompositeCollection>
  </DataTemplate.Resources>
  <ListBox ItemsSource="{StaticResource MyColl}">
    <!-- ... -->
  </ListBox>
</DataTemplate>

What do I have to insert for ???? to bind the Dogs and Cats collections to the CollectionContainers?

Towelling answered 8/10, 2013 at 8:49 Comment(0)
T
60

Due to the issue with data binding on CollectionContainer as described http://social.msdn.microsoft.com/Forums/vstudio/en-US/b15cbd9d-95aa-47c6-8068-7ae9f7dca88a/collectioncontainer-does-not-support-relativesource?forum=wpf I now use the following approach:

<ListBox>
  <ListBox.Resources>
    <CollectionViewSource x:Key="DogCollection" Source="{Binding Dogs}"/>
    <CollectionViewSource x:Key="CatCollection" Source="{Binding Cats}"/>
  </ListBox.Resources>
  <ListBox.ItemsSource>
    <CompositeCollection>
      <CollectionContainer Collection="{Binding Source={StaticResource DogCollection}}"/>
      <CollectionContainer Collection="{Binding Source={StaticResource CatCollection}}"/>
    </CompositeCollection>
  </ListBox.ItemsSource>
  <!-- ... -->
</ListBox>

Edit: The CompositeCollection class does not derive from FrameworkElement and thus does not have a DataContext property to support data binding. It will only work if you use Binding providing a Source. Have a look here https://mcmap.net/q/393165/-how-do-you-bind-a-collectioncontainer-to-a-collection-in-a-view-model for more information.

Towelling answered 9/10, 2013 at 12:9 Comment(1)
This works great for me. But it would be more helpful if you could explain why this is required. I don't understand: 1) why can't I declare a CollectionContainer resource the same way and use it directly, instead of indirecting through the CollectionViewSource? and 2) why can't I declare a CompositeCollection as a resource and just bind to CollectionContainer objects within directly? What is so special about CollectionViewSource that it works here even when other types won't?Knap
A
4

Try giving your ListBox a name and refer to its DataContext in the bindings:

<ListBox x:Name="myList" ItemsSource="{DynamicResource MyColl}">
   <ListBox.Resources>
      <CompositeCollection x:Key="MyColl">
         <CollectionContainer Collection="{Binding DataContext.Dogs, Source={x:Reference myList}}"/>
         <CollectionContainer Collection="{Binding DataContext.Cats, Source={x:Reference myList}}"/>
      </CompositeCollection>
   </ListBox.Resources>
</ListBox>
Assistance answered 8/10, 2013 at 8:57 Comment(10)
This does not work. I get an exception that the name of the ListBox can not be resolved. I think this is due to the fact that the CompositeCollection is declared within DataTemplate.Resources and the ListBox is declared after.Towelling
just a suggestion.. why do you want collection in template resources.. can you move this in ListBox resources?Assistance
If I move the CompositeCollection to ListBox.Resources then how I can reference it as ListBox.ItemsSource?Towelling
instead of StaticResource use {DynamicResource MyColl}Assistance
Circular reference: This problem occurs if you use x:Reference within the element you want to reference. This post https://mcmap.net/q/393165/-how-do-you-bind-a-collectioncontainer-to-a-collection-in-a-view-model describes this problem too. It says that you have to move the CompositeCollection to the resources and use StaticResource - what I tried. But then I don't know how to bind the CollectionContainer to the ViewModel properties. Circular problem ;-)Towelling
it wont give the cyclical error in case you put collectin in your listbox resources and use dynamicresource to refer it... just tested this... working fine.. updated the answer with the tested codeAssistance
Can you give me a code sample for this? I tried it and it did not work.Towelling
This does not work, brings me an XamlParseException: Cannot call MarkupExtension.ProvideValue because of a cyclical dependency. Properties inside a MarkupExtension cannot reference objects that reference the result of the MarkupExtension. The affected MarkupExtensions are: System.Windows.Data.BindingTowelling
This works fine for me. However, the XAML editor complains with a NullReferenceException (i.e. "Object reference not set to an instance of an object"). It works fine at runtime, but the editor error is annoying; any way to declare this in a way that won't result in the editor error?Knap
@Towelling You can in fact also reference the collection using StaticResource, but because of the sequential processing of XAML, you have to specify the ItemsSource after the <ListBox.Resources> section for it to work. e.g.: <ListBox.ItemsSource><StaticResource ResourceKey="MyColl"/></ListBox.ItemsSource>.Conformity

© 2022 - 2024 — McMap. All rights reserved.