Styling A ContextMenu In Expression Blend

The other day we had a requirement to style a ContextMenu for our large WPF app. Now this sounds easy enough to do, and there are even some pointer on a Microsoft site which show you the alleged Styles applied to the standard controls, for the ContextMenu though, the example on the Microsoft site which is as follows:

<Style TargetType="ContextMenu">
  <Setter Property="SnapsToDevicePixels" Value="True"/>
  <Setter Property="OverridesDefaultStyle" Value="True"/>
  <Setter Property="Grid.IsSharedSizeScope" Value="true"/>
  <Setter Property="HasDropShadow" Value="True"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="ContextMenu">
        <Border 
          Name="Border"
          Background="{StaticResource WindowBackgroundBrush}"
          BorderBrush="{StaticResource SolidBorderBrush}"
          BorderThickness="1" >
          <StackPanel IsItemsHost="True"
                      KeyboardNavigation.DirectionalNavigation="Cycle"/>
        </Border>
        <ControlTemplate.Triggers>
          <Trigger Property="HasDropShadow" Value="true">
            <Setter TargetName="Border" Property="Padding" Value="0,3,0,3"/>
            <Setter TargetName="Border" Property="CornerRadius" Value="4"/>
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Turned out to be way wrong, you can clearly see that the screen shot below which uses the standard ContextMenu with no additional styling has a ScrollViewer to present items that do not fit in a certain amount of space. There clearly is no ScrollViewer in the Microsoft example shown above.

http://dl.dropbox.com/u/2600965/Blogposts/2010/11/ContextMenu/1.png

So I thought ok, so I need to Style a ContextMenu myself, fine. How I would normally tackle this in Expression Blend is to place some control on the work surface, right click, and just  choose to edit the current Template. This is shown below, where I will edit a Button Template.

http://dl.dropbox.com/u/2600965/Blogposts/2010/11/ContextMenu/2.png

Thing is this approach does not work for ContextMenu at all. mmmm So what can you do. Well luckily Expression Blend is awesome enough to cope with this, so what you do is as follows assuming you have a ContextMenu (or even have a dummy one created just so you can test the Styling)

I have this dummy one created:

<Grid x:Name="LayoutRoot" Background="CornflowerBlue">
    <Grid.ContextMenu>
        <ContextMenu>
            <MenuItem Header="MenuItem"/>
            <MenuItem Header="MenuItem"/>
            <MenuItem Header="MenuItem"/>
            <MenuItem Header="MenuItem"/>
            ....
            ....
            ....
            ....
            <MenuItem Header="MenuItem"/>
            <MenuItem Header="MenuItem"/>
            <MenuItem Header="MenuItem"/>
        </ContextMenu>
    </Grid.ContextMenu>
</Grid>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

So lets assume you have a similar setup. So how can we get into what the ContextMenu style looks like. Well in Blend find you control that has the ContextMenu (real or dummy one) applied, in the VisualTree in Blend. For me that is the Grid called “LayoutRoot”. So here is me doing that

http://dl.dropbox.com/u/2600965/Blogposts/2010/11/ContextMenu/3.png

Next I look at the selected Grid (“LayoutRoot”) properties, and I find its ContextMenu property, which is under the Miscellaneous group.

http://dl.dropbox.com/u/2600965/Blogposts/2010/11/ContextMenu/4.png

Now you can expand the ContextMenu as I have above, and scrolldown until you find the ContextMenu Style property.

http://dl.dropbox.com/u/2600965/Blogposts/2010/11/ContextMenu/6.png

Then what you can do is click on the small rectangle on the right, and select the “Convert to New Resource” option as shown below

http://dl.dropbox.com/u/2600965/Blogposts/2010/11/ContextMenu/6.png

Ok so now what we get if we go to our XAML is a new ContextMenu style that looks something like this, which Expression Blend just generated for us

<!-- This is what Blend produced when you let it create 
     a local resource for you for the ContextMenu -->
<Style x:Key="ContextMenuStyle1" 
    TargetType="{x:Type ContextMenu}">
    <Setter Property="Background" 
        Value="{DynamicResource MenuBackgroundBrush}"/>
    <Setter Property="BorderThickness" 
        Value="1"/>
    <Setter Property="BorderBrush" 
        Value="{DynamicResource WindowBorderBrush}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ContextMenu}">
                <Border Uid="Border_93">
                    <Border.Style>
                        <Style TargetType="{x:Type Border}">
                            <Setter Property="Tag" 
                                Value="{DynamicResource 
                                    {x:Static SystemParameters.DropShadowKey}}"/>
                            <Style.Triggers>
                                <DataTrigger 
                                        Binding="{Binding Tag, 
                                            RelativeSource={RelativeSource Self}}" 
                                        Value="True">
                                    <Setter Property="Background" 
                                        Value="Transparent"/>
                                    <Setter Property="Padding" 
                                        Value="0,0,5,5"/>
                                    <Setter Property="Effect">
                                        <Setter.Value>
                                            <DropShadowEffect 
                                                BlurRadius="4" 
                                                Opacity="0.8" 
                                                ShadowDepth="1"/>
                                        </Setter.Value>
                                    </Setter>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Border.Style>
                    <Border BorderBrush="{TemplateBinding BorderBrush}" 
                            BorderThickness="{TemplateBinding BorderThickness}" 
                            Background="{TemplateBinding Background}" 
                            Uid="Border_50">
                        <ScrollViewer CanContentScroll="True" 
                            Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, 
                                    TypeInTargetAssembly={x:Type FrameworkElement}}}" 
                                    Uid="ScrollViewer_9">
                            <ItemsPresenter 
                                KeyboardNavigation.DirectionalNavigation="Cycle" 
                                Margin="{TemplateBinding Padding}" 
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                Uid="ItemsPresenter_5"/>
                        </ScrollViewer>
                    </Border>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

Ok so that is what the ContextMenu looks like by default. But what I wanted to do was swap out the standard look and feel of the ContextMenu (not the MenuItem controls they are styled separately), and the look of the Up/Down arrows it obviously has in its Template. So how about that then.

Well looking into it, I could see a ScrollViewer that is declared like this, which seemed to be the key:

<ScrollViewer CanContentScroll="True" 
    Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, 
            TypeInTargetAssembly={x:Type FrameworkElement}}}" 
            Uid="ScrollViewer_9">

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

So all that I really need to do then was create a new Style for the ScrollViewer using that ComponentResourceKey, which I did and here is all the relevant XAML for that.

<!-- These are the extras that you have to create to get your own ScrollViewer in for a custom ContextMenu
     ScrollViewer -->
<MenuScrollingVisibilityConverter x:Key="MenuScrollingVisibilityConverter" />

    <Style x:Key="MenuScrollButton"
           BasedOn="{x:Null}"
           TargetType="{x:Type RepeatButton}">
        <Setter Property="ClickMode"
                Value="Hover" />
        <Setter Property="MinWidth"
                Value="0" />
        <Setter Property="MinHeight"
                Value="0" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type RepeatButton}">
                    <DockPanel SnapsToDevicePixels="true" Width="Auto" 
                           Height="30"
                               Background="CornflowerBlue">
                        <Rectangle x:Name="R1"
                                   Width="2"
                                   Fill="DarkBlue"
                                   DockPanel.Dock="Right" />
                        <Rectangle x:Name="B1"
                                   Height="2"
                                   Fill="DarkBlue"
                                   DockPanel.Dock="Bottom" />
                        <Rectangle x:Name="L1"
                                   Width="2"
                                   Fill="DarkBlue"
                                   DockPanel.Dock="Left" />
                        <Rectangle x:Name="T1"
                                   Height="2"
                                   Fill="DarkBlue"
                                   DockPanel.Dock="Top" />
                        <ContentPresenter HorizontalAlignment="Center"
                                          Margin="2,2,2,2"
                                          x:Name="ContentContainer"
                                          VerticalAlignment="Center" />
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>        



      <Geometry x:Key="UpArrow">M 0,4 L 3.5,0 L 7,4 Z</Geometry>
    <Geometry x:Key="DownArrow">M 0,0 L 3.5,4 L 7,0 Z</Geometry>

    <Style x:Key="{ComponentResourceKey ResourceId=MenuScrollViewer, 
        TypeInTargetAssembly={x:Type FrameworkElement}}"
           BasedOn="{x:Null}"
           TargetType="{x:Type ScrollViewer}">
        <Setter Property="HorizontalScrollBarVisibility"
                Value="Hidden" />
        <Setter Property="VerticalScrollBarVisibility"
                Value="Auto" />
        <Setter Property="MaxHeight"
                Value="200" />        
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ScrollViewer}">
                    <Grid SnapsToDevicePixels="true">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="*" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
                        <Border Grid.Column="0"
                                Grid.Row="1">
                            <ScrollContentPresenter Margin="{TemplateBinding Padding}" />
                        </Border>
                        <RepeatButton Focusable="false"
                                      Style="{StaticResource MenuScrollButton}"
                                      Grid.Column="0"
                                      Grid.Row="0"
                                      Command="{x:Static ScrollBar.LineUpCommand}"
                                      CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}">
                            <RepeatButton.Visibility>
                                <MultiBinding FallbackValue="Visibility.Collapsed"
                                              Converter="{StaticResource MenuScrollingVisibilityConverter}"
                                              ConverterParameter="0">
                                    <Binding Path="ComputedVerticalScrollBarVisibility"
                                             RelativeSource="{RelativeSource TemplatedParent}" />
                                    <Binding Path="VerticalOffset"
                                             RelativeSource="{RelativeSource TemplatedParent}" />
                                    <Binding Path="ExtentHeight"
                                             RelativeSource="{RelativeSource TemplatedParent}" />
                                    <Binding Path="ViewportHeight"
                                             RelativeSource="{RelativeSource TemplatedParent}" />
                                </MultiBinding>
                            </RepeatButton.Visibility>
                            <Path Fill="{DynamicResource {x:Static SystemColors.MenuTextBrushKey}}"
                                  Data="{StaticResource UpArrow}" />
                        </RepeatButton>
                        <RepeatButton Focusable="false"
                                      Style="{StaticResource MenuScrollButton}"
                                      Grid.Column="0"
                                      Grid.Row="2"
                                      Command="{x:Static ScrollBar.LineDownCommand}"
                                      CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}">
                            <RepeatButton.Visibility>
                                <MultiBinding FallbackValue="Visibility.Collapsed"
                                              Converter="{StaticResource MenuScrollingVisibilityConverter}"
                                              ConverterParameter="100">
                                    <Binding Path="ComputedVerticalScrollBarVisibility"
                                             RelativeSource="{RelativeSource TemplatedParent}" />
                                    <Binding Path="VerticalOffset"
                                             RelativeSource="{RelativeSource TemplatedParent}" />
                                    <Binding Path="ExtentHeight"
                                             RelativeSource="{RelativeSource TemplatedParent}" />
                                    <Binding Path="ViewportHeight"
                                             RelativeSource="{RelativeSource TemplatedParent}" />
                                </MultiBinding>
                            </RepeatButton.Visibility>
                            <Path Fill="{DynamicResource {x:Static SystemColors.MenuTextBrushKey}}"
                                  Data="{StaticResource DownArrow}" />
                        </RepeatButton>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

And having done that, I ended up with this. Ok I have over styled it for this blog post, but that is easily changed by the 2 RepeatButtons with the key “MenuScrollButton” that the ScrollViewer is using

http://dl.dropbox.com/u/2600965/Blogposts/2010/11/ContextMenu/7.png

 

Additional Data That May Be Of Interest

You may actually find these additional links of use, as they do show you how to style Menu/MenuItem controls quite well. I trust these 2 links

Demo App:

As usual here is a small demo app : Demo App

About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s