Ugrás a tartalomhoz

.NET Programming Technologies

Gergely Kovásznai, Csaba Biró

Eszterházy Károly College

14. fejezet - Templates (written by Gergely Kovásznai)

14. fejezet - Templates (written by Gergely Kovásznai)

Styles, as introduced in Section 0, provide uniform format setting for controls and code reuse. In this section, we are going to introduce templates, which serve the same purposes in an even higher level. Furthermore, the use of templates gives us even more power, since they are primarily suitable for customizing controls, besides preserving their functionality. This can be done in a direct way by applying control templates, as introduced in Section Hiba! A hivatkozási forrás nem található., or in an indirect way, via bound data objects, by using data templates, as shown in Section Hiba! A hivatkozási forrás nem található..[5]

Control Templates

A control template can be realized by using the ControlTemplate XAML element. It has an important attribute called TargetType, which is for specifying the control type to which the given template can be applied. Since in the next example we are going to create a button that has special outlook and behavior, one has to assign TargetType to Button. In the body of the ControlTemplate, one has to give the XAML code that defines the control. For the sake of example, let the shape of our button be rather irregular; therefore, it is worth to use a Path (Section VII.2).[6] Besides defining all the visual elements (in our example there is only one such element), it is an important task to place the content assigned to the button (Content). For this reason we need to use the ContentPresenter XAML element, and place it to where Content should be displayed, based on the design we apply.

<Window.Resources>

        <ControlTemplate TargetType="Button" x:Key="buttonTemplate">

            <Canvas x:Name="canvas">

                        <Path Data="M 11,23C 11,11 23,11 23,11L 116,11L 125,58L 80,59L

                                                75,45L 67,59L 14,60L 11,23" Stretch="Fill" x:Name="path">

                                ...

                        </Path>

                        <ContentPresenter Canvas.Left="15" Canvas.Top="8"

                                                                TextBlock.FontFamily="Vivaldi"/>

                </Canvas>

        </ControlTemplate>

</Window.Resources>

...

<Button Template="{StaticResource buttonTemplate}" FontSize="20">

        Get Started

</Button>

As can be seen, the template can be specified as a resource, therefore a key (called buttonTemplate now) has to be assigned to it. If one would like to apply the template to a certain GUI control (to the Button now), then the key has to be used in the Template property of the control. In the above example, the Content of the button is set to „Get Started” (it could be anything else), therefore this text is going to be displayed at the location of the ContentPresenter.

As can be seen, we have assigned fixed values to certain properties of the template, e.g., Vivaldi to the text font, which makes all the buttons that apply this template use this font to display texts.[7] Nevertheless, other attributes of a Button can be customized on demand (e.g. FontSize); however, sometimes it is complicated to specify which property of a template is “bound” to which property of a control. E.g., one should rather bind the Width of the Button to the Width of the Path (instead of the one of the default ContentPresenter). This can be achieved by a special variant of data binding (Section Hiba! A hivatkozási forrás nem található.) called TemplateBinding. The properties of a template can be bound to certain properties of a control by the use of TemplateBinding. Below such an example is shown, which binds the contour brush (Stroke) of the Path to the contour brush (BorderBrush) of the Button. The result can be seen in Figure 1.

<ControlTemplate TargetType="Button" x:Key="buttonTemplate">

        ...

        <Path ... Width="{TemplateBinding Width}" Stroke="{TemplateBinding BorderBrush}">

                ...

        </Path>

        ...

</ControlTemplate>

...

<Button Template="{StaticResource buttonTemplate}" FontSize="20" Width="150">

        Get Started

</Button>

<Button Template="{StaticResource buttonTemplate}" FontSize="16" BorderBrush="Red">

        Help

</Button>

XIV.1. The usage of a control template

The result is however not completely satisfactory since these buttons are static, do not move, and do not react to mouse operations, as buttons are expected to do. It would be so nice if one were able to specify in our template the behavior of buttons as well! How fortunate that this is also possible, by adding triggers (Section Hiba! A hivatkozási forrás nem található.) to a template (ControlTemplate.Triggers). For the sake of example, let us show two such triggers. The first one is going to be a simple Trigger, which starts to react whenever the mouse pointer enters the area of the control. It sets, for instance, the contour of the Path thicker and colored:

<ControlTemplate TargetType="Button" x:Key="buttonTemplate">

        ...

        <ControlTemplate.Triggers>

                <Trigger Property="IsMouseOver" Value="True">

                        <Setter TargetName="path" Property="Stroke" Value="Orange"/>

                        <Setter TargetName="path" Property="StrokeThickness" Value="3"/>

                </Trigger>

        </ControlTemplate.Triggers>

</ControlTemplate>

Let the other trigger be an EventTrigger, which observes the Click event of the button, and starts to play an animation (Storyboard) (Section Hiba! A hivatkozási forrás nem található.). The illusion of the button descending and ascending is going to be realized by using a translation (TranslateTransform), and, furthermore, we are going to add shadow effect (DropShadowBitmapEffect) to the button and decrease the shadow depth when clicking. Below it can be seen how to realize this, and the flow of animation in Figure 2.

<ControlTemplate TargetType="Button" x:Key="buttonTemplate">

        <Canvas x:Name="canvas">

                ...

                <Canvas.RenderTransform>

                        <TranslateTransform x:Name="translate"/>

                </Canvas.RenderTransform>

                <Canvas.BitmapEffect>

                        <DropShadowBitmapEffect Color="{StaticResource purplish}"

                                                                         ShadowDepth="20" x:Name="shadow"/>

                </Canvas.BitmapEffect>

        </Canvas>

        <ControlTemplate.Triggers>

                ...

                <EventTrigger RoutedEvent="Button.Click">

                        <BeginStoryboard>

                                <Storyboard AutoReverse="True">

                                        <DoubleAnimation Storyboard.TargetName="shadow"

                                                                         Storyboard.TargetProperty="ShadowDepth"

                                                                         To="5" Duration="{StaticResource duration}"/>

                                        <DoubleAnimation Storyboard.TargetName="translate"

                                                                         Storyboard.TargetProperty="X"

                                                                         To="15" Duration="{StaticResource duration}"/>

                                        <DoubleAnimation Storyboard.TargetName="translate"

                                                                         Storyboard.TargetProperty="Y"

                                                                         To="20" Duration="{StaticResource duration}"/>

                                </Storyboard>

                        </BeginStoryboard>

                </EventTrigger>

        </ControlTemplate.Triggers>

</ControlTemplate>

XIV.2. Control template with triggers



[5] We do not give details about two other WPF templates, HierarchicalDataTemplate and ItemsPanelTemplate.

[6] This is a rather hard manual task. It is advantageous to use Microsoft’s Expression Design (Section XVIII.3), which can even export XAML code (and this is exactly how the example was done).

[7] Templates can be used very well for unifying our GUI design.