Search code examples
wpfxamlcustom-contextmenu

WPF: ContextMenu MenuItem from DataContext/ItemsSource?


I'm building a simple App to store television shows. I have a Video class for the shows with some fields and properties including one reference to an object of type VideoSeason, representing the seasons of TV shows. The corresponding UI element of a Video object is a Button, with a ContextMenu with some actions.

I would like to create a MenuItem inside the ContextMenu, which contains all the seasons added to a TV show represented as submenuitems. I know that to do this, I have to mark the ObservableCollection Seasons as the ItemsSource of the MenuItem Seasons and indicate that any submenuitem inside the MenuItem is bound to the Property SeasonNumber inside VideoSeason.

My problem is that I dont't really know, how to go about binding these submenuitems in XAML, not if this is actually possible. I've already tried some options (e.g., WPF ContextMenu itemtemplate, menuitem inside menuitem, or Binding WPF ContextMenu MenuItem to UserControl Property vs ViewModel Property), but I want only my MenuItem to be bound, not the whole CntextMenu.

Here is the relevant part of the Video class:

    public string Name { get; set; }       
    public int NextEpisode { get; set; }
    public ObservableCollection<VideoSeason> Seasons { get; set; }

And here is the relevant part of the XAML code:

       <ScrollViewer>
        <StackPanel Name="filmHolder" 
          Grid.Row="1" Grid.Column="0" >
            <ItemsControl Name="VideoUIElment">
                <ItemsControl.ItemTemplate>
                    <DataTemplate x:Uid="videoTemplate">
                        <Border CornerRadius="10" Padding="10, 10" Background="Silver">
                          <Button Name="filmLabel" Content="{Binding Name}"  FontSize="30" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center"
                              Click="FilmLabel_Click" BorderThickness="0">
                                <Button.ContextMenu>
                                    <ContextMenu Name="LocalMenu">
                                        <MenuItem Header="Rename"/>
                                        <MenuItem Header="Delete"/>
                                        <MenuItem Header="Add New Season" Name="NewSeason" Click="NewSeason_Click"/>
                                        <MenuItem Header="Seasons" ItemsSource="{Binding Seasons}">
                                            <!--<MenuItem.ItemTemplate This is one of the things I tried in vain>
                                                    <DataTemplate>
                                                    <MenuItem Header="{Binding SeasonNumber}"/>
                                                    </DataTemplate>
                                            </MenuItem.ItemTemplate>-->
                                        </MenuItem>
                                    </ContextMenu>
                                </Button.ContextMenu>
                            </Button>
                        </Border>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </ScrollViewer>

As it can be seen, the problematic part is nested in the DataTemplate belonging to the UI of the Video, which might be the cause of the problem, but I'm not sure.


Solution

  • If you bind the ItemsSource property of the ItemsControl to an IEnumerable<Video>, this should work:

    <ItemsControl Name="VideoUIElment" ItemsSource="{Binding Videos}">
        <ItemsControl.ItemTemplate>
            <DataTemplate x:Uid="videoTemplate">
                <Border CornerRadius="10" Padding="10, 10" Background="Silver">
                    <Button Name="filmLabel" Content="{Binding Name}"  FontSize="30" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center"
                                  Click="FilmLabel_Click" BorderThickness="0">
                        <Button.ContextMenu>
                            <ContextMenu Name="LocalMenu">
                                <MenuItem Header="Rename"/>
                                <MenuItem Header="Delete"/>
                                <MenuItem Header="Add New Season" Name="NewSeason" Click="NewSeason_Click"/>
                                <MenuItem Header="Seasons" ItemsSource="{Binding Seasons}">
                                    <MenuItem.ItemTemplate>
                                        <DataTemplate>
                                            <TextBlock Text="{Binding SeasonNumber}"/>
                                        </DataTemplate>
                                    </MenuItem.ItemTemplate>
                                </MenuItem>
                            </ContextMenu>
                        </Button.ContextMenu>
                    </Button>
                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    

    Make sure that SeasonNumber is a public property of the VideoSeason class.