Click or drag to resize

Theming Concept

This page explains the theming concept used in the xServer .NET Framework.

This page contains the following sections:

Basics

The themes used by the map and its elements are a standard WPF mechanism. There are several ways to apply themes. How to set a theme from a stream (like for example from a file or an embedded resource) or to use the built-in default theme is described in the sections above.

WPF themes are (usually) defined with XAML. Some sample themes are included in the Demo Center sources. They can be the base for customizations. From the demo themes the parts can be identified which have to be defined for full theming of the map and its children.

There is a hierarchy in which theming is applied to controls. Setting a theme on a higher level usually means that all of the lower levels inherit these styles. If a style is set on a certain level (below the application level), the corresponding style settings (which have been inherited from a higer level) will be overridden. That is, the new style is set beginning from the current level down the hierarchy.

The styling hierarchy is defined as follows (from higher to lower levels):

  • Application (App.xaml)

  • MainWindow of the Application (MainWindow.xaml)

  • MapControl in the Map DLL (WPFMap.xaml)

  • MapGadget XAML (e.g. NavigationGadget.xaml)

Setting the theme on the highest (application) level in the file App.xaml looks for example like this:

Setting the application theme.
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="WPF Themes\ExpressionDark.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

Furthermore, there is an order in which the styles within a level are applied. For example, if a style and/or a property of a WPF element is set on the MainWindow.xaml level, it depends on the code part which setting will be applied. This are the sections (from highest to lowest priority):

  • Definition in the C# code part

  • Definition in the XAML part

  • Definition in the theme XAML (MapGadget XAML before MapControl XAML before MainWindow XAML before App.xaml)

Defining the shape

The following explanations refer to buttons because they are a commonly used control type. But most of the details also apply to other control types.

To simply change a property in XAML, the following can be done:

Setting the button's background color.
<Button Background="Green" />

To change the shape the Clip property can be used. A GeometryGroup object has to be defined, which can be assigned to the Clip property. By doing so, geometric primitives (lines, arcs, etc.) can be used.

Setting the button's shape.
<GeometryGroup x:Key="UpButtonGeometry">
<PathGeometry>
<PathFigure StartPoint="5,20">
<ArcSegment Size="40,40" RotationAngle="0" IsLargeArc="False" SweepDirection="Clockwise" Point="60,20" IsSmoothJoin="True"/>
<LineSegment Point="45,35" IsSmoothJoin="True"/>
<ArcSegment Size="20,20" RotationAngle="0" IsLargeArc="False" SweepDirection="Counterclockwise" Point="20,35" IsSmoothJoin="True"/>
<LineSegment Point="5,20" IsSmoothJoin="True"/>
</PathFigure>
</PathGeometry>
</GeometryGroup>

This will result in such a shape:

Geometry Group Shape
Defining the behavior

The following explanations refer to buttons because they are a commonly used control type. But most of the details also apply to other control types.

WPF allows the definition of a control's behavior. For example, an animation of properties (position, color, opacity, etc.) on mouse events (mouse enter, leave, etc.) can be implemented. To achieve custom behavior, a ControlTemplate has to be defined.

The drawback of defining a ControlTemplate is that it has to be defined fully. This means, if the hover action should be defined, the click, enter, leave, focused, etc. actions have to be defined too. Each control has a default behavior, which then has to be redefined. If not, simply nothing will happen, especially not the defaults, which might be expected in this case.

Furthermore, a custom ControlTemplate might also override styles defined by a theme (XAML before theme). See the sections above for more information about the theming/styling hierarchy in WPF.

The ControlTemplate can be defined in the resources and assigned by using the Template property of a control instance.

Setting the button's behavior.
<Storyboard x:Key="HoverOn">
<DoubleAnimation Duration="00:00:00.1000000" Storyboard.TargetName="BackgroundOver" Storyboard.TargetProperty="Opacity" To="1" />
</Storyboard>
<Storyboard x:Key="HoverOff">
<DoubleAnimation Duration="00:00:00.4000000" Storyboard.TargetName="BackgroundOver" Storyboard.TargetProperty="Opacity" To="0" />
</Storyboard>
<Storyboard x:Key="PressedOn">
<DoubleAnimation Duration="00:00:00.1000000" Storyboard.TargetName="BackgroundPressed" Storyboard.TargetProperty="Opacity" To="0.84" />
</Storyboard>
<Storyboard x:Key="PressedOff">
<DoubleAnimation Duration="00:00:00.4000000" Storyboard.TargetName="BackgroundPressed" Storyboard.TargetProperty="Opacity" To="0" />
</Storyboard>
<Storyboard x:Key="DisabledOn">
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="DisabledVisualElement" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00.1000000" Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="FocusedOn">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="FocusedOff">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>

<ControlTemplate x:Key="UpButton" TargetType="{x:Type Button}">
<Grid x:Name="Grid">
<Path x:Name="BackgroundNorm" Fill="{Binding ElementName=Navigation, Path=BackgroundColorBrush}" Opacity="1" Data="{StaticResource UpButtonGeometry}" />
<Path x:Name="BackgroundOver" Stroke="{Binding ElementName=Navigation, Path=ForegroundColorBrush}" StrokeThickness="1" Opacity="0" Data="{StaticResource UpButtonGeometry}" />
<Path x:Name="BackgroundPressed" Fill="{Binding ElementName=Navigation, Path=ForegroundColorBrush}" Opacity="0" Data="{StaticResource UpButtonGeometry}" />
<Path x:Name="DisabledVisualElement" Fill="DarkGray" Opacity="0" Data="{StaticResource UpButtonGeometry}" />
<ContentPresenter x:Name="ContentPresenter" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" />
<Path x:Name="FocusVisualElement" Stroke="{Binding ElementName=Navigation, Path=ForegroundColorBrush}" StrokeThickness="1" Opacity="0" Data="{StaticResource UpButtonGeometry}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Trigger.EnterActions>
<BeginStoryboard x:Name="FocusedOn_BeginStoryboard" Storyboard="{StaticResource FocusedOn}" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard x:Name="FocusedOff_BeginStoryboard" Storyboard="{StaticResource FocusedOff}" />
</Trigger.ExitActions>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Trigger.ExitActions>
<BeginStoryboard x:Name="FocusedOff_BeginStoryboard1" Storyboard="{StaticResource FocusedOff}" />
</Trigger.ExitActions>
<Trigger.EnterActions>
<BeginStoryboard x:Name="FocusedOn_BeginStoryboard1" Storyboard="{StaticResource FocusedOn}" />
</Trigger.EnterActions>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard" />
</Trigger.ExitActions>
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource HoverOn}" />
</Trigger.EnterActions>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource PressedOff}" />
</Trigger.ExitActions>
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource PressedOn}" />
</Trigger.EnterActions>
</Trigger>
<Trigger Property="IsEnabled" Value="true" />
<Trigger Property="IsEnabled" Value="false">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource DisabledOn}" />
</Trigger.EnterActions>
<Setter Property="Foreground" Value="{Binding ElementName=Navigation, Path=ForegroundColorBrush}" />
<Setter Property="Opacity" TargetName="DisabledVisualElement" Value="1" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
See Also