Reputation: 603
I'm having trouble doing something that seemed really easy but which is not actually.
I have a ListView in which i binded an ObservableCollection, and i want a ContextMenu to appear when i rightclick an element of that ListView.
In that ContextMenu, i want a MenuItem that says "Add to Playlist" and inside it, the list of all of my playlists.
So i did this, which looks right to me:
<ListView Grid.Row="0" Grid.Column="1" x:Name="ListBoxSelectedFolder" ItemsSource="{Binding Path=SelectedFolder.PlayableElements}">
<ListView.Resources>
<ContextMenu x:Key="ContextMenu">
<MenuItem Header="Add to" ItemsSource="{Binding Path=Playlists}">
<MenuItem Header="{Binding Name}"/>
</MenuItem>
<MenuItem Header="Remove from All" />
</ContextMenu>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}"/>
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Type" DisplayMemberBinding="{Binding Extension}" />
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
</GridView>
</ListView.View>
</ListView>
The thing is that the only thing i get in my submenu is the name of the item i clicked on: somehow, it's binding with the collection of the ListView, SelectedFolder.PlayableElements, because both SelectedFolder.PlayableElements and Playlists have a Name property.
So there is some sort of conflict between the two bindings, and i don't know how to solve it.
Thanks in advance for your response.
Upvotes: 2
Views: 1687
Reputation: 63387
The ContextMenu
is detached from the visual tree so Binding inside its scope with ElementName
or RelativeSource
is not possible in a normal way. You need some proxy technique to bridge the cutting. Here we have the Freezable
element which can inherit data context as well as allow binding to walkup the visual tree even in this situation easily. For convenience we should use the DiscreteObjectKeyFrame
because its Value can accept all kinds of object. If you care about the name, then you can define your own custom Freezable object.
<ListView Grid.Row="0" Grid.Column="1" x:Name="ListBoxSelectedFolder" ItemsSource="{Binding Path=SelectedFolder.PlayableElements}">
<ListView.Resources>
<DiscreteObjectKeyFrame x:Key="proxy" Value="{Binding Playlists, RelativeSource={RelativeSource AncestorType=Window}}"/>
<ContextMenu x:Key="ContextMenu">
<MenuItem Header="Add to" ItemsSource="{Binding Value, Source={StaticResource proxy}}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
<MenuItem Header="Remove from All" />
</ContextMenu>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}"/>
</Style>
</ListView.Resources>
<!-- ... -->
</ListView>
Edit - this is to help you understand how you can mix the info from both Playlists and SelectedFolder:
<ListView Grid.Row="0" Grid.Column="1" x:Name="ListBoxSelectedFolder" ItemsSource="{Binding Path=SelectedFolder.PlayableElements}">
<ListView.Resources>
<DiscreteObjectKeyFrame x:Key="proxy" Value="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>
<ContextMenu x:Key="ContextMenu" DataContext="{Binding Value, Source={StaticResource proxy}}">
<MenuItem Header="Add to" ItemsSource="{Binding Playlists}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
<MenuItem Header="Playable elements" ItemsSource="{Binding SelectedFolder.PlayableElements}"/>
<MenuItem Header="Remove from All" />
</ContextMenu>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}"/>
</Style>
</ListView.Resources>
<!-- ... -->
</ListView>
As you can see the added menu item (right above the Remove from all
) has its ItemsSource
set to SelectedFolder.PlayableElements
. The ContextMenu
now has its DataContext
set to the Window
instance via proxy
. So all the Bindings used inside its scope without explicit Source
and RelativeSource
and ElementName
set will get a resolved source of the DataContext (your window).
Upvotes: 1
Reputation: 805
With code you showed you just bound another menu item to the name property. I guess what you are looking for is an ItemTemplate property of MenuItem. This way you can define what data should be shown for each child of this menu item. Check the markup below:
<ListView Grid.Row="0" Grid.Column="1" x:Name="ListBoxSelectedFolder" ItemsSource="{Binding Path=SelectedFolder.PlayableElements}">
<ListView.Resources>
<ContextMenu x:Key="ContextMenu">
<MenuItem Header="Add to" ItemsSource="{Binding Path=Playlists}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Path=Name}" />
</DataTemplate>
</MenuItem.ItemTemplate>
<MenuItem Header="{Binding Name}"/>
</MenuItem>
<MenuItem Header="Remove from All" />
</ContextMenu>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}"/>
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Type" DisplayMemberBinding="{Binding Extension}" />
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
</GridView>
</ListView.View>
</ListView>
Upvotes: 0