Search code examples
c#.netwpfmenubartext-alignment

How to set the Horizontal Alignment of the Input Gesture Texts in Menus in WPF?


I'm making an application with WPF and have a Menu. In that menu, most of the items have keyboard shortcuts, or input gestures. How do I set the horizontal alignment of the text that displays the input gesture?

What I want:

Target: Menu items with input gesture aligned to the right.

How it looks:

Actual: Menu items with input gesture aligned to the left.

I have already looked this problem up, but I seem to be the only person who cares about this.


Solution

  • In order to change the horizontal alignment of the input gesture text, you have to adapt the control template of MenuItem. You can either create it from scratch or create a copy of the default style using Blend or Visual Studio. I recommend the latter. If you want to now more about the required parts and visual states of the MenuItem control, you can refer to the documentation.

    In the following, I extracted the default style and only the control template and resources that are actually needed to change the input gesture text alignment. The essential change is in the menuGestureText TextBlock.

    <TextBlock x:Name="menuGestureText" Grid.Column="4" Margin="{TemplateBinding Padding}" Opacity="0.7" Text="{TemplateBinding InputGestureText}" VerticalAlignment="Center" HorizontalAlignment="Right"/>
    

    As you can see, I set the HorizontalAlignment to Right. Here is the essential XAML markup needed:

    <SolidColorBrush x:Key="Menu.Static.Background" Color="#FFF0F0F0"/>
    <SolidColorBrush x:Key="Menu.Static.Border" Color="#FF999999"/>
    <SolidColorBrush x:Key="Menu.Static.Foreground" Color="#FF212121"/>
    <SolidColorBrush x:Key="Menu.Disabled.Foreground" Color="#FF707070"/>
    <SolidColorBrush x:Key="MenuItem.Selected.Background" Color="#3D26A0DA"/>
    <SolidColorBrush x:Key="MenuItem.Selected.Border" Color="#FF26A0DA"/>
    <SolidColorBrush x:Key="MenuItem.Highlight.Background" Color="#3D26A0DA"/>
    <SolidColorBrush x:Key="MenuItem.Highlight.Border" Color="#FF26A0DA"/>
    <SolidColorBrush x:Key="MenuItem.Highlight.Disabled.Background" Color="#0A000000"/>
    <SolidColorBrush x:Key="MenuItem.Highlight.Disabled.Border" Color="#21000000"/>
    
    <Geometry x:Key="Checkmark">F1 M 10.0,1.2 L 4.7,9.1 L 4.5,9.1 L 0,5.2 L 1.3,3.5 L 4.3,6.1L 8.3,0 L 10.0,1.2 Z</Geometry>
    
    <ControlTemplate x:Key="{ComponentResourceKey ResourceId=SubmenuItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}" TargetType="{x:Type MenuItem}">
       <Border x:Name="templateRoot" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" Height="22" 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" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
             <Border x:Name="GlyphPanel" Background="{StaticResource MenuItem.Selected.Background}" BorderThickness="1" BorderBrush="{StaticResource MenuItem.Selected.Border}" ClipToBounds="False" HorizontalAlignment="Center" Height="22" Margin="-1,0,0,0" VerticalAlignment="Center" Visibility="Hidden" Width="22">
                <Path x:Name="Glyph" Data="{StaticResource Checkmark}" FlowDirection="LeftToRight" Fill="{StaticResource Menu.Static.Foreground}" Height="11" Width="10"/>
             </Border>
             <ContentPresenter x:Name="menuHeaderContainer" ContentSource="Header" Grid.Column="2" HorizontalAlignment="Left" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
             <TextBlock x:Name="menuGestureText" Grid.Column="4" Margin="{TemplateBinding Padding}" Opacity="0.7" Text="{TemplateBinding InputGestureText}" VerticalAlignment="Center" HorizontalAlignment="Right"/>
          </Grid>
       </Border>
       <ControlTemplate.Triggers>
          <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="{StaticResource MenuItem.Highlight.Background}"/>
             <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Border}"/>
          </Trigger>
          <Trigger Property="IsEnabled" Value="False">
             <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{StaticResource Menu.Disabled.Foreground}"/>
             <Setter Property="Fill" TargetName="Glyph" Value="{StaticResource Menu.Disabled.Foreground}"/>
          </Trigger>
          <MultiTrigger>
             <MultiTrigger.Conditions>
                <Condition Property="IsHighlighted" Value="True"/>
                <Condition Property="IsEnabled" Value="False"/>
             </MultiTrigger.Conditions>
             <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Disabled.Background}"/>
             <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Disabled.Border}"/>
          </MultiTrigger>
       </ControlTemplate.Triggers>
    </ControlTemplate>
    
    <Style x:Key="MenuItemStyle" TargetType="{x:Type MenuItem}">
       <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
       <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
       <Setter Property="Background" Value="Transparent"/>
       <Setter Property="BorderBrush" Value="Transparent"/>
       <Setter Property="BorderThickness" Value="1"/>
       <Setter Property="ScrollViewer.PanningMode" Value="Both"/>
       <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
       <Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=SubmenuItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
       <Style.Triggers>
          <Trigger Property="Role" Value="TopLevelHeader">
             <Setter Property="Background" Value="Transparent"/>
             <Setter Property="BorderBrush" Value="Transparent"/>
             <Setter Property="Foreground" Value="{StaticResource Menu.Static.Foreground}"/>
             <Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=TopLevelHeaderTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
             <Setter Property="Padding" Value="6,0"/>
          </Trigger>
          <Trigger Property="Role" Value="TopLevelItem">
             <Setter Property="Background" Value="{StaticResource Menu.Static.Background}"/>
             <Setter Property="BorderBrush" Value="{StaticResource Menu.Static.Border}"/>
             <Setter Property="Foreground" Value="{StaticResource Menu.Static.Foreground}"/>
             <Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=TopLevelItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
             <Setter Property="Padding" Value="6,0"/>
          </Trigger>
          <Trigger Property="Role" Value="SubmenuHeader">
             <Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=SubmenuHeaderTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
          </Trigger>
       </Style.Triggers>
    </Style>
    

    You can use the style by referencing it as ItemContainerStyle in Menu or assign the style to MenuItems.

    <Menu ItemContainerStyle="{StaticResource MenuItemStyle}">
       <MenuItem Header="File">
          <MenuItem Header="New file" InputGestureText="Ctrl+N"/>
          <MenuItem Header="New template" InputGestureText="Ctrl+Shift+N"/>
       </MenuItem>
    </Menu>
    

    Another option is to create an implicit style that is applied automatically to all MenuItems in scope.

    <Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MenuItemStyle}"/>
    

    This is the result, the input gesture text is aligned to the right.

    Menu with input gesture text aligned to the right.