0

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.

oneManArmin
  • 606
  • 6
  • 24

1 Answers1

1

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.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • Thank you! Can you explain how this actually works? I must admit, I knew IEnumerables as necessary elements for a foreach loop to work only. I found articles on IEnumerable itself, but I still would be interested in how it works in this particular situation. – oneManArmin Jun 26 '17 at 20:55
  • You can bind or set the ItemsSource property of an ItemsControl to any object that implements the IEnumerable interface. The ItemsControl enumerates the source collection and applies the ItemTemplate to each individual item. – mm8 Jun 26 '17 at 21:00
  • I see. Thank you again. I didn't think along these lines previously, but I learned something new today about how C# actually works. – oneManArmin Jun 26 '17 at 21:08
  • There is one last thing, however, that I do not understand: I also wired a ComboBox, but in that case, I didn't need an IEnumerable. I just used SeasonSelector.ItemsSource = vid.Seasons, (where vid is a Video and SS is the ComboBox) in code behind and it worked perfectly. Are these 2 things generating the UI elements using a different method then? – oneManArmin Jun 26 '17 at 21:21
  • You can either bind or set the ItemsSource property to an IEnumerable. vid.Seasons returns an IEnumerable. – mm8 Jun 27 '17 at 09:27