Devz
Devz

Reputation: 603

Binding collection to ContextMenu submenu

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

Answers (2)

King King
King King

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

Ivan Zub
Ivan Zub

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

Related Questions