Custom WPF check box with inner shadow effect.

By Mirek on (tags: CheckBox, inner shadow, styling, WPF, categories: code)

Today I will show you how to override the CheckBox control template to style it in custom way and add a sexy looking shadow effect.

We want to replace the standard windows checkbox look

standard_checkbox

with something cooler and fit to our application design

inner_shadow_checkbox

Windows Presentation Foundation controls are based on Styles and templates. When you want to change the look of the control you have to style it. In our case we want to change the the look of standard CheckBox control. Replace steely, glossy tile with regular square with inner shadow effect and standard check mark with gray cross.

First we have to do is download the default CheckBox control template from MSDN and put it as a resource in our application resources or in separate assembly. Next we apply needed changes. I will try to describe the most important parts of the template

   1: <Style TargetType="CheckBox">
   2:            <Setter Property="SnapsToDevicePixels" Value="False" />
   3:            <Setter Property="OverridesDefaultStyle" Value="true" />
   4:            <Setter Property="FocusVisualStyle" Value="{x:Null}" />
   5:            <Setter Property="BorderBrush" Value="LightGray" />
   6:            <Setter Property="Template">
   7:                <Setter.Value>
   8:                    <ControlTemplate TargetType="CheckBox">
   9:                        <BulletDecorator Background="Transparent">
  10:                            <BulletDecorator.Bullet>
  11:                                <Border x:Name="Border"
  12:                                        Width="15"
  13:                                        Height="15"
  14:                                        Background="{TemplateBinding Background}"
  15:                                        BorderBrush="{TemplateBinding BorderBrush}"
  16:                                        BorderThickness="1"
  17:                                        ClipToBounds="True">
  18:                                    <Border.Effect>
  19:                                        <DropShadowEffect BlurRadius="5" ShadowDepth="2" />
  20:                                    </Border.Effect>
  21:                                    <Path x:Name="CheckMark"
  22:                                          Width="8"
  23:                                          Height="8"
  24:                                          HorizontalAlignment="Center"
  25:                                          VerticalAlignment="Center"
  26:                                          Data="M 0 0 L 8 8 M 0 8 L 8 0"
  27:                                          Stretch="Fill"
  28:                                          Stroke="LightGray"
  29:                                          StrokeEndLineCap="Round"
  30:                                          StrokeStartLineCap="Round"
  31:                                          StrokeThickness="2" />
  32:                                </Border>
  33:                            </BulletDecorator.Bullet>
  34:                            <ContentPresenter Margin="4,0,0,0"
  35:                                              HorizontalAlignment="Left"
  36:                                              VerticalAlignment="Center"
  37:                                              RecognizesAccessKey="True" />
  38:                        </BulletDecorator>
  39:                        <ControlTemplate.Triggers>
  40:                            <Trigger Property="IsChecked" Value="false">
  41:                                <Setter TargetName="CheckMark" Property="Visibility" Value="Collapsed" />
  42:                            </Trigger>
  43:                            <Trigger Property="IsFocused" Value="true">
  44:                                <Setter Property="BorderBrush" Value="White" />
  45:                            </Trigger>
  46:                            <Trigger Property="IsEnabled" Value="false">
  47:                                <Setter Property="BorderBrush" Value="Gray" />
  48:                                <Setter TargetName="CheckMark" Property="Stroke" Value="Gray" />
  49:                            </Trigger>
  50:                        </ControlTemplate.Triggers>
  51:                    </ControlTemplate>
  52:                </Setter.Value>
  53:            </Setter>
  54:        </Style>

Line 3 – OverridesDefaultStyle – means we want to totally override the default style, so we have to provide the complete look and feel of the control including user action triggers. This is required as long as we want to change something that is not exposed as a dependency property of the control. For instance in our case we want to change the shape of the check mark. Since the Data property is not exposed and we can change it only by overriding the whole control style.
As you can see the check box consists of BulletDecorator which holds the border with path geometry and content presenter which shows the check box label. In Line 26 we change the standard shape of check mark and replace it with cross. For more information about using path geometries go to Geometry Overview,  the syntax we use here is called Path Markup Syntax.

To achieve the inner shadow effect we use border with special manipulations on it (Line 11 to 20). The requirement here is that the border has transparent background. Ok lets see it step by step:

First we have a regular border with 1 pixel thickness

   1: <Border x:Name="Border"
   2:         Width="15"
   3:         Height="15"
   4:         Background="{TemplateBinding Background}"
   5:         BorderBrush="{TemplateBinding BorderBrush}"
   6:         BorderThickness="1">

cb01

Then we add a DropShaddowEffect and make the border’s background transparent

   1: <Border x:Name="Border"
   2:         Width="15"
   3:         Height="15"
   4:         Background="Transparent"
   5:         BorderBrush="{TemplateBinding BorderBrush}"
   6:         BorderThickness="1">
   7:     <Border.Effect>
   8:         <DropShadowEffect BlurRadius="5" ShadowDepth="2" />
   9:     </Border.Effect>

cb02

The transparent background prevents the border from inheriting background color from its parents. We need it to the inner shadow to be visible.

The last step is enabling ClipToBounds property on the border

   1: ClipToBounds="True"

Which causes that border is rendered only within its bounds. For us it means that the outer part of the shadow will be hidden

cb03

And done, we got  the look we wanted

cb04

Cheers