2

How do I change a custom menu item's background when I hover it? I changed the background property but it doesn't change, it stays the default blue color. I also tried to change the trigger style on hover, but again, it doesn't help, it gets ignored.

The idea is to hover the menu item and then a custom color should appear instead of the default blue color.

Here is the code:

    <Style TargetType="{x:Type Menu}" x:Key="TopbarMenu" BasedOn="{StaticResource BaseStyle}">
    <Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Foreground" Value="{StaticResource WhiteBrush}"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="HorizontalAlignment" Value="Center"/>
    <Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style TargetType="{x:Type MenuItem}" x:Key="DropdownMenuButton" BasedOn="{StaticResource BaseStyle}">
    <Setter Property="TextOptions.TextFormattingMode" Value="Display"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="Margin" Value="6 0 6 0"/>
</Style>

I think it is something connected to templates, but I'm not very competent with templates, and I could be fundamentally wrong and the problem could be something else.

  • It's not a direct duplicate, but you can find your answer by looking to this question: [How to set MouseOver event/trigger for border in XAML?](https://stackoverflow.com/questions/2388429/how-to-set-mouseover-event-trigger-for-border-in-xaml) – Jeroen van Langen Nov 20 '18 at 12:53
  • It's not a duplicate, these are two different problems. Here a simple background property change or a simple style trigger is not enough. – Christian Panov Nov 20 '18 at 12:57

1 Answers1

4

This is the way I'm doing it... works perfectly for me:

<ControlTemplate TargetType="{x:Type MenuItem}" x:Key="MenuItemTemplate">
    <Border x:Name="Border" Padding="10,5,10,5" BorderThickness="0" Margin="0">
        <ContentPresenter ContentSource="Header" x:Name="HeaderHost" RecognizesAccessKey="True" />
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsHighlighted" Value="true">
            <Setter Property="Background" TargetName="Border" Value="Blue"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

and use with:

<MenuItem Template="{StaticResource MenuItemTemplate}" Header="test" />

[EDIT]

As per request here is some stuff to support submenus correctly:

<ControlTemplate TargetType="MenuItem" x:Key="rootMenuItem">
    <Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <Grid VerticalAlignment="Center">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
            <Path x:Name="GlyphPanel" Data="F1M10,1.2L4.7,9.1 4.5,9.1 0,5.2 1.3,3.5 4.3,6.1 8.3,0 10,1.2z" Fill="{TemplateBinding Foreground}" FlowDirection="LeftToRight" Margin="3" Visibility="Collapsed" VerticalAlignment="Center"/>
            <ContentPresenter ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="1" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
            <Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom">
                <Border x:Name="SubMenuBorder" BorderBrush="#FF999999" BorderThickness="1" Background="#FFF0F0F0" Padding="2">
                    <ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
                        <Grid RenderOptions.ClearTypeHint="Enabled">
                            <Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
                            </Canvas>
                            <Rectangle Fill="#FFD7D7D7" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
                            <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
                        </Grid>
                    </ScrollViewer>
                </Border>
            </Popup>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsSuspendingPopupAnimation" Value="True">
            <Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
        </Trigger>
        <Trigger Property="Icon" Value="{x:Null}">
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsChecked" Value="True">
            <Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
            <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="IsHighlighted" Value="True">
            <Setter Property="Background" TargetName="templateRoot" Value="BLUE"/>
            <Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF26A0DA"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="#FF707070"/>
            <Setter Property="Fill" TargetName="GlyphPanel" Value="#FF707070"/>
        </Trigger>
        <Trigger Property="CanContentScroll" SourceName="SubMenuScrollViewer" Value="False">
            <Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
            <Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>



<Style TargetType="MenuItem">
    <Setter Property="Template">
        <Setter.Value>

            <ControlTemplate TargetType="{x:Type MenuItem}">
                <Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
                    <Grid Margin="-1">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition MinWidth="22" SharedSizeGroup="MenuItemIconColumnGroup" Width="Auto"/>
                            <ColumnDefinition Width="13"/>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="30"/>
                            <ColumnDefinition SharedSizeGroup="MenuItemIGTColumnGroup" Width="Auto"/>
                            <ColumnDefinition Width="20"/>
                        </Grid.ColumnDefinitions>
                        <ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
                        <Border x:Name="GlyphPanel" BorderBrush="#FF26A0DA" BorderThickness="1" Background="#3D26A0DA" Height="22" Margin="-1,0,0,0" Visibility="Hidden" VerticalAlignment="Center" Width="22">
                            <Path x:Name="Glyph" Data="F1M10,1.2L4.7,9.1 4.5,9.1 0,5.2 1.3,3.5 4.3,6.1 8.3,0 10,1.2z" Fill="#FF212121" FlowDirection="LeftToRight" Height="11" Width="9"/>
                        </Border>
                        <ContentPresenter ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="2" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" HorizontalAlignment="Left" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
                        <TextBlock Grid.Column="4" Margin="{TemplateBinding Padding}" Opacity="0.7" Text="{TemplateBinding InputGestureText}" VerticalAlignment="Center"/>
                        <Path x:Name="RightArrow" Grid.Column="5" Data="M0,0L4,3.5 0,7z" Fill="#FF212121" HorizontalAlignment="Left" Margin="10,0,0,0" VerticalAlignment="Center"/>
                        <Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" HorizontalOffset="-2" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Right" VerticalOffset="-3">
                            <Border x:Name="SubMenuBorder" BorderBrush="#FF999999" BorderThickness="1" Background="#FFF0F0F0" Padding="2">
                                <ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
                                    <Grid RenderOptions.ClearTypeHint="Enabled">
                                        <Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                            <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
                                        </Canvas>
                                        <Rectangle Fill="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
                                        <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
                                    </Grid>
                                </ScrollViewer>
                            </Border>
                        </Popup>
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsSuspendingPopupAnimation" Value="True">
                        <Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
                    </Trigger>
                    <Trigger Property="Icon" Value="{x:Null}">
                        <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
                    </Trigger>
                    <Trigger Property="IsChecked" Value="True">
                        <Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
                        <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
                    </Trigger>
                    <Trigger Property="IsHighlighted" Value="True">
                        <Setter Property="Background" TargetName="templateRoot" Value="BLUE"/>
                        <Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF26A0DA"/>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="False">
                        <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="#FF707070"/>
                        <Setter Property="Fill" TargetName="Glyph" Value="#FF707070"/>
                        <Setter Property="Fill" TargetName="RightArrow" Value="#FF707070"/>
                    </Trigger>
                    <Trigger Property="CanContentScroll" SourceName="SubMenuScrollViewer" Value="False">
                        <Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
                        <Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

and the usage test:

<Menu HorizontalAlignment="Center" VerticalAlignment="Center">
    <MenuItem Header="test" Template="{StaticResource rootMenuItem}">
        <MenuItem Header="test" >
            <MenuItem Header="test">
                <MenuItem Header="test">
                    <MenuItem Header="test">
                        <MenuItem Header="test">
                            <MenuItem Header="test" />
                            <MenuItem Header="test" />
                        </MenuItem>
                        <MenuItem Header="test" />
                    </MenuItem>
                    <MenuItem Header="test" />
                </MenuItem>
                <MenuItem Header="test" />
            </MenuItem>
            <MenuItem Header="test">
                <MenuItem Header="test">
                    <MenuItem Header="test" />
                    <MenuItem Header="test" />
                </MenuItem>
                <MenuItem Header="test">
                    <MenuItem Header="test">
                        <MenuItem Header="test" />
                        <MenuItem Header="test" />
                    </MenuItem>
                    <MenuItem Header="test" />
                </MenuItem>
            </MenuItem>
        </MenuItem>
    </MenuItem>
    <MenuItem Header="test" Template="{StaticResource rootMenuItem}">
        <MenuItem Header="test">
            <MenuItem Header="test" />
            <MenuItem Header="test" />
        </MenuItem>
    </MenuItem>
</Menu>

Beware, the root-item has another template than the child-items! in this demo it's done with static template binding for the root elements but in a real use case you can do this with a style selector like this:

<Style TargetType="{x:Type MenuItem}" x:Key="{x:Type MenuItem}">
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <Style.Triggers>
        <Trigger Property="Role" Value="SubmenuHeader">
            <Setter Property="Template" Value="{StaticResource {x:Static MenuItem.SubmenuHeaderTemplateKey}}"/>
        </Trigger>
        <Trigger Property="Role" Value="SubmenuItem">
            <Setter Property="Template" Value="{StaticResource {x:Static MenuItem.SubmenuItemTemplateKey}}"/>
        </Trigger>
    </Style.Triggers>
</Style>

[/EDIT]

[EDIT2] For the arrow to disappear, you need a StyleSelector as mentioned. this class has to be implemented assuming yout two styles are named "MenuItemWithChildren" and "MenuItemWithoutChildren"

class ItemStyleSelector : StyleSelector
{
    public override Style SelectStyle(object item, DependencyObject container)
    {
        if(item is MenuItem mItem)
        {
            if(mItem.Items.Count > 0)
            {
                return App.Current.Resources["MenuItemWithChildren"] as Style;
            }
            else
            {
                return App.Current.Resources["MenuItemWithoutChildren"] as Style;
            }
        }

        return base.SelectStyle(item, container);
    }
}

then you have to define two different styles & templates for each of the items, with and without children: Important: these have to be defined in app.xaml or some resource-dictionary which is linked there, else the ItemStyleSelector class wont find them. E.g. Window.Resources won't be found.

<Style TargetType="MenuItem" x:Key="MenuItemWithChildren">
    <Setter Property="ItemContainerStyleSelector" Value="{StaticResource ItemStyleSelector}" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type MenuItem}">
                [Your Template goes here]
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<Style TargetType="MenuItem" x:Key="MenuItemWithoutChildren">
    <Setter Property="ItemContainerStyleSelector" Value="{StaticResource ItemStyleSelector}" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type MenuItem}">
                [Your Template goes here]
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

also your Styleselctor itself and a default style for the menuitem

<local:ItemStyleSelector x:Key="ItemStyleSelector" />
<Style TargetType="MenuItem">
    <Setter Property="ItemContainerStyleSelector" Value="{StaticResource ItemStyleSelector}" />
</Style>

and declare the selection in the menu:

<Menu ItemContainerStyleSelector="{StaticResource ItemStyleSelector}">
    <MenuItem Header="test" Template="{StaticResource rootMenuItem}">
        <MenuItem Header="test">
            <MenuItem Header="test" />
            <MenuItem Header="test" />
        </MenuItem>
    </MenuItem>
</Menu>

[/EDIT2]

FastJack
  • 866
  • 1
  • 10
  • 11
  • Thank you very much, it works indeed. Is it fast tho? Is performance taken into account? – Christian Panov Nov 20 '18 at 13:14
  • Never did any big performance tests but as far as i know, ControlTemplates and Triggers are the way to go in a WPF environment... I use this in multiple Tray-Icon based applications for our 6 developer team and we never had any performance problems regarding to the menus (which are multi-level cascading in some cases)... – FastJack Nov 20 '18 at 13:15
  • Okay great! By the way, can't i just use Style="{StaticResource DropdownMenuButton}" It seems to work for me, is it the same? From what i guess yours binds the template, and mine binds the whole style – Christian Panov Nov 20 '18 at 13:28
  • Any idea why the subitems from that menu don't appear? When i click on an item from the menu, there isn't even a dropdown menu, why is that? – Christian Panov Nov 20 '18 at 14:12
  • because it is not defined in the controltemplate... wait a second, i will update the answer – FastJack Nov 20 '18 at 14:14
  • in general, to edit these templates, you can right click on the desired MenuItem in the visual designer and select "Edit Template" > "Edit a Copy". This function will create exactly the xaml i posted in the edit above. Plus I added the style around it to apply automatically to all MenuItems – FastJack Nov 20 '18 at 14:45
  • Thank you very much, it works perfectly. I spent some time reworking it and customizing it, I'm only wondering how would I make the arrow disappear if there are no children menu items in a parent menu item? Is there a trigger which checks if a menu item has any child menu items in it? – Christian Panov Nov 20 '18 at 18:19
  • see [EDIT2] in my post ;) – FastJack Nov 21 '18 at 09:28
  • Hmm it seems to give me some type of an exception, why so? Here is my code for the whole menu item style: https://pastebin.com/PJjRXHK4 (Sorry, it's too long to paste it here). – Christian Panov Nov 21 '18 at 14:21
  • hmm if i try to exec it over here it crashes on {StaticResource UnavailableTextBrush} ... which is... unavailable? ;) - well... no... seems some other non-existent resource... i think it would help is you would paste the exception itself ;) – FastJack Nov 21 '18 at 14:30
  • It's just a brush I created. Well here is the whole project with all the tweaks from your second edit: github.com/KrisIStup/GraspIDE If you could try to work out something and see why it's crashing, it would be fantastic In the styles folder you can find the buttons file – Christian Panov Nov 21 '18 at 15:16
  • 1
    just created a pull request that fixes the error. was pretty simple ;) – FastJack Nov 22 '18 at 06:40
  • Thank you very much mate! Really grateful for your help, I'm just a back-end type programmer and front-end is new to me, but I'm getting it, I just need people like you to explain the things, I would buy you a drink but it's just a forum. Cheers! – Christian Panov Nov 22 '18 at 12:57
  • I only have one more question which is connected with the MVVM architecture. If I have many icons in the software, and I want to have a file which contains only public readonly strings that contain the paths to the icons, what would it be, I mean where would this file go? – Christian Panov Nov 22 '18 at 13:05
  • hmm.. i would add the icons as resources... but thats more or less personal preference. if you want to link them in a file i would recommend a ResourceDictionary in the Themes folder. And in my projects, the icon files themselfes reside in an "Assets" folder... but as said.. personal preference ;) – FastJack Nov 22 '18 at 14:16
  • Well yes thats how ive done it, ive put them in the resources folder. What im asking is what would be such a file - public readonly strings that contain the icons paths, called? Is it some kind of a data model? – Christian Panov Nov 22 '18 at 14:24
  • Why do I have a delay in calling triggers? I have a trigger for when a menu item is expanded its right arrow should become blue, and there is a slight delay, why is that? – Christian Panov Nov 22 '18 at 17:19
  • i think you should ask that in a separate question... your original question seems answered to me? – FastJack Nov 26 '18 at 07:42