Source for CompositeCollection: why can't I bind against the data context of another control but have to use a CollectionViewSource?
Asked Answered
G

1

6

In another question I recently asked, I was told to use a CompositeCollection in order to access various sources for a ListBox.

The example used a XmlDataProvider to provide some dummy data. I, however, have a view model, which contains the data.

It took me some time to bind my ListBox against the view model's data. Eventually I figured it out, but now I'd like to understand why my previous approaches didn't work.

The key to success was a CollectionViewSource. My initial attempts were:

<CollectionContainer Collection="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Movies}"/>
<CollectionContainer Collection="{Binding ElementName=Window, Path=DataContext.Movies}"/>

My idea was to find the Window, which has the appropriate DataContext, and bind against the data. You can do that via FindAncestor or via ElementName, so I tried both. That seemed very logically to me, but apparently I was wrong. I didn't see nothing when I ran the application.
I also tried binding against another control which has the data context; e.g. the StackPanel.

So, why don't I get the data with FindAncestor and ElementName1, but have to provide a CollectionViewSource explicitly?


Here's the code that is working.

<StackPanel DockPanel.Dock="Top">
    <ListBox ItemTemplateSelector="{StaticResource CustomDataTemplateSelector}">
        <ListBox.Resources>
            <CollectionViewSource x:Key="ViewSource" Source="{Binding Movies}"/>
        </ListBox.Resources>
        <ListBox.ItemsSource>
            <CompositeCollection>
                <CollectionContainer Collection="{Binding Source={StaticResource ViewSource}}"/>
                <CollectionContainer Collection="{Binding Source={StaticResource MyButtonsData}}"/>
            </CompositeCollection>
        </ListBox.ItemsSource>
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel IsItemsHost="True"
                   Width="{Binding (FrameworkElement.ActualWidth),
                               RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
</StackPanel>

1 No, I didn't forget to name the window and there wasn't a typo either.

Gunther answered 21/8, 2014 at 19:3 Comment(2)
With your initial attempt you should have some binding errors in the debug console. They are of great help when debugging this type of issue.Hellenist
@Hellenist Actually, I don't. I only get the hint "cannot resolve property of type object" which is fixed once I add the data context via DesignInstance.Gunther
G
7

I've found a thread on microsoft.com discussing that issue.
Seems that this 'bug' is known for years, but has never been fixed.

The workaround I'm using (CollectionViewSource) is suggested there, too.

Furthermore, you can indeed not use ElementName. I don't know for what reason, but the workaround for ElementName is using x:Reference as suggested in another question's thread.

<CollectionContainer Collection="{Binding Source={x:Reference dummy}, Path=DataContext.Movies}"/>

Interestingly, the XAML compiler will show an object reference not set to an instance of an object error while editing.
It's possible to compile and run though, provided you're not using an ancestor type, because than you'll get an XmlParseException because of a circular dependency.

To avoid the circular dependency error, you can put the CompositeCollection in the resources and link to there via StaticResource. Then you can use an ancestor type, too.

<ListBox.Resources>
    <CompositeCollection x:Key="CompositeCollection">
        <CollectionContainer Collection="{Binding Source={x:Reference stackPanel}, Path=DataContext.Movies}"/>
    </CompositeCollection>
</ListBox.Resources>
<ListBox.ItemsSource>
    <CompositeCollection>
        <CollectionContainer Collection="{Binding Source={StaticResource CompositeCollection}}"/>
    </CompositeCollection>
</ListBox.ItemsSource>
Gunther answered 21/8, 2014 at 20:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.