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 ElementName
1, 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.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With