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

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
Michael Said on Jun 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.