none
Why do MenuItems with children not allow Commands or IsChecked options? RRS feed

  • Question

  • I like WPF a lot, especially after being introduced to the eventless MV VM format. I'm not a UI Designer by any means, but I am working on some UserControls for a couple interfaces, and I have only one major complaint so far: a MenuItem with children does not have a working "IsChecked" or "Command" binding.

    I understand that wanting this functionality is a little unusual, so here's some context. The user control is for generating a list of options, some of which have subcategories. I can turn the parent option on/off, and then have several options beneath that. My current workaround is to have an extra Child MenuItem that governs the parent option, as demonstrated below.

    <MenuItem Header="Parent Option">
        <MenuItem Header="Parent On" IsCheckable="True" IsChecked="{Binding hasParentOption}"/>
        <MenuItem Header="Child Option" IsCheckable="True" IsChecked="{Binding hasChildOption}"/>
    </MenuItem>
    I would much, much rather be able to just set the Parent Option MenuItem to "IsCheckable = true," but my brief research suggests the way a menu item is coded means it can either be checkable, or have children, and not both. The same is true for running a command (I tried to hack it by adding a check mark as the icon and flipping it's visibility) but the command bound to the menu item will not fire if it has a child element. 

    This is, apparently, intended behavior. Why? Is there a fundamental principle of UI/Menu design that I am violating in trying to do this? It seems trivial to allow both Commands and Checkability in addition to having children, so I can only imagine that there is a good reason for not allowing it that I am missing.
    Monday, May 13, 2019 4:24 PM

Answers

  • I'm not exactly looking for a workaround, since I can imagine several off the top of my head. I'm trying to understand why MSDN made the decision to restrict the functionality of the button.

    The idea of a menuitem that has children is that this is more a header rather than something you'd have a command.

    It could well be whoever was working on this noticed the click event was being used up by the popup process and they justified this as a "feature" rather than a bug.

    But.

    You can put anything you like in a menu item header.

    eg

    which looks like:

                        <MenuItem>
                            <MenuItem.Header>
                                <CheckBox IsChecked="{Binding AppSettings.ShowElevationMap, Mode=TwoWay}">
                                    _Elevation Map
                                </CheckBox>
                            </MenuItem.Header>
                            <MenuItem>
                                <MenuItem.Header>
                                    <StackPanel Width="160">
                                        <TextBlock Text="Opacity" />
                                        <Slider
                                            Value="{Binding AppSettings.ElevationOpacity, Mode=TwoWay}" />
                                    </StackPanel>
                                </MenuItem.Header>
                            </MenuItem>
                        </MenuItem>

    You can check and uncheck that checkbox.


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    • Marked as answer by Jacek Adams Friday, May 17, 2019 6:09 PM
    Friday, May 17, 2019 9:33 AM
    Moderator

All replies

  • Hi    Jacek Adams,

    >>The same is true for running a command (I tried to hack it by adding a check mark as the icon and flipping it's visibility) but the command bound to the menu item will not fire if it has a child element. 

    As far as I know, If the MenuItem has sub items, you will not get a Click event.  The MenuItem handles the MouseLeftButtonDown event (to open its submenu), so the Click event is never raised.

    So, I recommend that you to avoid using the Click event on the parent menu item.  You can use the PreviewMouseLeftButtonDown and PreviewMouseLeftButtonUp events with your own logic to determine if a Click action has occurred.  

    <Window.CommandBindings>
            <CommandBinding Command="New" CanExecute="NewCommand_CanExecute" Executed="NewCommand_Executed" />
        </Window.CommandBindings>
        <Grid>
            <Menu DockPanel.Dock="Top">
                <MenuItem Header="Parent Option" Margin="0,0,533,371"  PreviewMouseLeftButtonDown="MenuItem_PreviewMouseLeftButtonDown"  >
                    <MenuItem Header="Parent On" IsCheckable="True" Command="New" />
                    <MenuItem Header="Child Option" IsCheckable="True"  />
                </MenuItem>
            </Menu>
        </Grid>
     
    Best Regards

    Yong Lu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, May 14, 2019 6:47 AM
    Moderator
  • Hi Jacek,
    there are no problems to binding Command or IsChecked. See the following demo.

    XAML

    <Window x:Class="Window31"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="Window31" Height="450" Width="800">
      <Window.DataContext>
        <local:Window31VM/>
      </Window.DataContext>
      <StackPanel>
        <Menu>
          <MenuItem Header="Parent Option">
            <MenuItem Header="Parent On" 
                      IsCheckable="True" 
                      IsChecked="{Binding hasParentOption}"
                      Command="{Binding Cmd}"
                      CommandParameter="ParentOption"/>
            <MenuItem Header="Child Option" 
                      IsCheckable="True" 
                      IsChecked="{Binding hasChildOption}"
                      Command="{Binding Cmd}"
                      CommandParameter="ChildOption"/>
          </MenuItem>
        </Menu>
        <CheckBox Content="hasParentOption" IsChecked="{Binding hasParentOption}"/>
        <CheckBox Content="hasChildOption" IsChecked="{Binding hasChildOption}"/>
        <TextBlock Text="{Binding Info}"/>
      </StackPanel>
    </Window>

    and the ViewModel

    Imports System.ComponentModel
    Imports System.Runtime.CompilerServices
    
    Public Class Window31VM
      Implements INotifyPropertyChanged
    
      Private _hasParentOption As Boolean
      Public Property hasParentOption As Boolean
        Get
          Return Me._hasParentOption
        End Get
        Set(value As Boolean)
          Me._hasParentOption = value
          OnPropChanged()
        End Set
      End Property
    
      Private _hasChildOption As Boolean
      Public Property hasChildOption As Boolean
        Get
          Return Me._hasChildOption
        End Get
        Set(value As Boolean)
          Me._hasChildOption = value
          OnPropChanged()
        End Set
      End Property
    
      Public Property Info As String
    
      Public ReadOnly Property Cmd As ICommand
        Get
          Return New RelayCommand(AddressOf CmdExec)
        End Get
      End Property
    
      Private Sub CmdExec(obj As Object)
        Info = obj.ToString
        OnPropChanged(NameOf(Info))
      End Sub
    
      Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
      Private Sub OnPropChanged(<CallerMemberName> Optional propName As String = "")
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
      End Sub
    End Class
    


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Wednesday, May 15, 2019 6:07 AM
  • I didn't go so far as to say the click events are turned off because I wasn't able to confirm that, that is nice to know. My real question is why this is the case though. What principle am I violating when I want the menu with children to be clickable?
    Wednesday, May 15, 2019 7:08 PM
  • Maybe I wasn't clear enough, but my comment is that the parent option itself cannot have bindings. The "Parent on" menu item is really hacky, and in my case extremely non-intuitive to use.
    Wednesday, May 15, 2019 7:08 PM
  • I didn't go so far as to say the click events are turned off because I wasn't able to confirm that, that is nice to know. My real question is why this is the case though. What principle am I violating when I want the menu with children to be clickable?

    Hi    Jacek Adams

    You may can try to refer the Routed Events.

    Routed Events Overview
    https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/routed-events-overview

    Best Regards

    Yong Lu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, May 16, 2019 4:11 AM
    Moderator
  • I'm not exactly looking for a workaround, since I can imagine several off the top of my head. I'm trying to understand why MSDN made the decision to restrict the functionality of the button.

    Thursday, May 16, 2019 3:05 PM
  • I'm not exactly looking for a workaround, since I can imagine several off the top of my head. I'm trying to understand why MSDN made the decision to restrict the functionality of the button.

    Hi   Jacek Adams,

    This may be related to the WPF freamwork structure.

    Best Regards

    Yong Lu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, May 17, 2019 5:01 AM
    Moderator
  • I'm not exactly looking for a workaround, since I can imagine several off the top of my head. I'm trying to understand why MSDN made the decision to restrict the functionality of the button.

    The idea of a menuitem that has children is that this is more a header rather than something you'd have a command.

    It could well be whoever was working on this noticed the click event was being used up by the popup process and they justified this as a "feature" rather than a bug.

    But.

    You can put anything you like in a menu item header.

    eg

    which looks like:

                        <MenuItem>
                            <MenuItem.Header>
                                <CheckBox IsChecked="{Binding AppSettings.ShowElevationMap, Mode=TwoWay}">
                                    _Elevation Map
                                </CheckBox>
                            </MenuItem.Header>
                            <MenuItem>
                                <MenuItem.Header>
                                    <StackPanel Width="160">
                                        <TextBlock Text="Opacity" />
                                        <Slider
                                            Value="{Binding AppSettings.ElevationOpacity, Mode=TwoWay}" />
                                    </StackPanel>
                                </MenuItem.Header>
                            </MenuItem>
                        </MenuItem>

    You can check and uncheck that checkbox.


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    • Marked as answer by Jacek Adams Friday, May 17, 2019 6:09 PM
    Friday, May 17, 2019 9:33 AM
    Moderator
  • That "You can add anything to the header" piece is something that makes a lot more sense. I don't think the "display submenu" actually consumes the click event, I believe it's something like "MouseOver" unless you set a property that requires an explicit click. By default, hovering over anything with children opens the submenu.

    Regardless, using <MenuItem.Header> as an element is very, very useful. Thank you very much.

    Friday, May 17, 2019 6:12 PM