Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 936
Em1 Avatar asked Aug 21 '14 19:08

Em1


1 Answers

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>
like image 133
Em1 Avatar answered Sep 19 '22 22:09

Em1