How would you like your WPF application to have a Context Menu like this:

Context Menu Example

Well, It is not so hard. Here is how you do it:

This is the Context Menu that was defined for this example:

1: <TextBox Text="Right click Me" Margin="25">
2:    <TextBox.ContextMenu>
3:        <ContextMenu Style="{StaticResource ContextMenuStyle}">
4:           <MenuItem x:Name="MenuItem1" Header="Cut" Command="Cut"
5:                     Style="{StaticResource ContextMenuItem}"
6:               Tag="pack://application:,,,/ContextMenu;Component/Cut.png">
7:           </MenuItem>
8:           <MenuItem x:Name="MenuItem2" Header="Copy" Command="Copy"
9:                     Style="{StaticResource ContextMenuItem}"
10:              Tag="pack://application:,,,/ContextMenu;Component/Copy.png">
11:          </MenuItem>
12:          <MenuItem x:Name="MenuItem3" Header="Paste" Command="Paste"
13:                    Style="{StaticResource ContextMenuItem}"
14:             Tag="pack://application:,,,/ContextMenu;Component/Paste.png">
15:          </MenuItem>
16:           <MenuItem x:Name="MenuItem4" Header="Delete" Command="Delete"
17:                   Style="{StaticResource ContextMenuItem}"
18:             Tag="pack://application:,,,/ContextMenu;Component/Delete.png">
19:           </MenuItem>
20:     </ContextMenu>
21:   </TextBox.ContextMenu>
22: </TextBox>

As you can see both the Context Menu and The MenuItem have styles, and this is where all the magic happens :).

The context menu style is very simple:

1: <Style TargetType="{x:Type ContextMenu}" x:Key="ContextMenuStyle">
2:      <Setter Property="BorderBrush"
3:              Value="{StaticResource ContextMenuBorderColor}">
4:      </Setter>
5:      <Setter Property="BorderThickness"
6:              Value="1">
7:       </Setter>
8:      <Setter Property="Background"
9:             Value="{StaticResource ContextMenuBackgroundColor}">
10:      </Setter>
11:      <Setter Property="Padding"
12:              Value="-1">
13:       </Setter>
14:       <Setter Property="FontSize"
15:               Value="13">
16:       </Setter>
17: </Style>

The real work is with the MenuItem Style. It is basically a ControlTemplate for the MenuItem where I use an Image and a TextBlock to design the Item.

  • To create the disable Item effect I used a Trigger that is Triggered by the IsEnabled property of the MenuItem (I initially did not believe it will work , but it did!)
  • The Disabled look is achieved by a Border with a Gray Background and some opacity, so it looked grayed out.
  • Another trick is passing the Image URI in the Tag property and then binding to it from the Image.Source property using a Converter.

Ok so here is the code. You can download the sample project to check it all out.

1: <Style TargetType="{x:Type MenuItem}" x:Key="ContextMenuItem">
2:    <Setter Property="MenuItem.Template">
3:       <Setter.Value>
4:          <ControlTemplate>
5:             <Border HorizontalAlignment="Stretch" x:Name="Root" >
6:                <Grid>
7:                   <Grid.ColumnDefinitions>
8:                      <ColumnDefinition Width="26"></ColumnDefinition>
9:                      <ColumnDefinition Width="130"></ColumnDefinition>
10:                  </Grid.ColumnDefinitions>
11:                  <Border HorizontalAlignment="Stretch"
12:                          VerticalAlignment="Stretch"
13:                          x:Name="ImageWrapper"
14:                          Background="{StaticResource MenuItemImageColor}"
15:                          Grid.Column="0">
16:                     <Image Width="24" Height="24" Margin="1"
17:                        Source="{TemplateBinding Property=MenuItem.Tag,
18:                       Converter={x:Static data:URI2ImageConverter.Instance}}">
19:                    </Image>
20:                </Border>
21:               <Border Grid.Column="1" x:Name="NameWrapper"
22:                        HorizontalAlignment="Stretch" Padding="1,0,2,0">
23:                  <TextBlock Text="{TemplateBinding MenuItem.Header}"
24:                             x:Name="Name"
25:                             Foreground="{StaticResource MenuItemTextColor}"
26:                             VerticalAlignment="Center"
27:                             HorizontalAlignment="Stretch"
28:                             Margin="2,0,0,0" ></TextBlock>
29:               </Border>
30:               <Border x:Name="DisabledOverlay"
31:                       HorizontalAlignment="Stretch"
32:                       VerticalAlignment="Stretch"
33:                       Grid.Column="0" Grid.ColumnSpan="2"
34:                       Background="{StaticResource ContextMenuBorderColor}"
35:                       Opacity="0" >
36:               </Border>
37:           </Grid>
38:        </Border>
39:      <ControlTemplate.Triggers>
40:         <Trigger Property="IsMouseOver" Value="true" SourceName="Root">
41:            <Trigger.Setters>
42:               <Setter Property="Background"
43:                       TargetName="NameWrapper"
44:                       Value="{StaticResource MenuItemTextColor}" >
45:               </Setter>
46:               <Setter Property="Background"
47:                       TargetName="Name"
48:                        Value="{StaticResource MenuItemTextColor}" >
49:               </Setter>
50:               <Setter Property="Foreground"
51:                       TargetName="Name"
52:                       Value="{StaticResource MenuItemHoverTextColor}" >
53:                </Setter>
54:                <Setter Property="Background"
55:                          TargetName="ImageWrapper"
56:                         Value="{StaticResource MenuItemTextColor}" >
57:                 </Setter>
58:             </Trigger.Setters>
59:           </Trigger>
60:           <Trigger Property="IsEnabled" Value="false" SourceName="Root">
61:               <Trigger.Setters>
62:                  <Setter Property="Opacity"
63:                          TargetName="DisabledOverlay"
64:                          Value="0.7" ></Setter>
65:                  <Setter Property="Foreground"
66:                          TargetName="Name"
67:                          Value="{StaticResource ContextMenuBorderColor}" >
68:                   </Setter>
69:               </Trigger.Setters>
70:           </Trigger>
71:         </ControlTemplate.Triggers>
72:        </ControlTemplate>
73:      </Setter.Value>
74:    </Setter>
75: </Style>

Some of you will as why is the Converter a Singleton, There is a good reason for that and I will explain it in a post later on.

To download the Sample Project Subscribe to our feed and get access to our Freebies Page

If you have any questions please comment.

Enjoy

Amit

Update: Take alook at the better WPF Custom ContextMenu

Tags :

6 Responses to “How To Create a WPF Custom Context Menu”


  1. Michael

    Said on June 20, 2008 :

    Good work, Amit.

    I think that this post http://weblogs.asp.net/okloeten/archive/2007/11/14/5149692.aspx may complete the picture.

  2. Moon

    Said on January 28, 2011 :

    Good tuto. Thanks

  3. Itamar

    Said on October 26, 2013 :

    1. you didn’t provided the source for URI2ImageConverter.
    2. is there a way to put another FrameworkElement instead of MenuItem Header ?

    thanks

3 Trackback(s)

  1. Sep 18, 2008: A BETTER WF CUSTOM CONTEXT MENU | Dev102.com
  2. Apr 17, 2009: Dev102.com March 2009 Roundup | Dev102.com
  3. Sep 6, 2009: WPF: 90+ Miejsc kt√≥re warto zna? « Dawid Po?li?ski

Post a Comment