More on Triggers and Styles and Control Templates

Completing this four-part series, we continue our discussion of triggers and deepen our knowledge of templates. This article is excerpted from chapter five of the book Programming Windows Presentation Foundation, written by Chris Sells and Ian Griffiths (O'Reilly; ISBN: 0596101139). Copyright © 2006 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Contributed by
Rating: 4 stars4 stars4 stars4 stars4 stars / 4
July 03, 2007
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

Multi-Condition Data Triggers

Just as property triggers can be combined into “and” conditions using the MultiTrigger element, data triggers can be combined using the MultiDataTrigger element. For example, if we wanted to watch for the 10th move of the game, make sure it’s player “O,” and do something special, we can do so as shown in Example 5-29.

Example 5-29.  A multi-data trigger

<?Mapping XmlNamespace="sys" ClrNamespace="System" Assembly="mscorlib" ?>
...
<Window ...
xmlns:sys="sys">
  <Style x:Key="MoveNumberStyle" TargetType="{x:Type TextBlock}">
    ...
    <Style.Triggers> 
      <MultiDataTrigger>
       
<MultiDataTrigger.Conditions>
          <Condition Binding="{Binding Path=PlayerName}" Value="O" />
          <Condition Binding="{Binding Path=MoveNumber}">
           
<Condition.Value> 
             <sys:Int32>10</sys:Int32>
            </Condition.Value>
          </Condition>
        </MultiDataTrigger.Conditions>
        <Setter Property="Background" Value="Yellow" / >
      </MultiDataTrigger>
    
</Style.Triggers>
  </Style>
  ...
</Window>

The only thing about Example 5-29 that may seem a little strange is the use of the mapping syntax to bring in theSystem namespace from the mscorlib .NET assembly. We do this so that we can pass10as anInt32instead of as a string; otherwise, the multi-condition data trigger won’t match ourMoveNumber property correctly. The multi-condition data trigger in Example 5-29 sets the background of the move
number to yellow to connote a cause for celebration for this special move that regular tic-tac-toe doesn’t have, but you can use multi-condition data triggers for celebrations of your own kinds.

Event Triggers

While property triggers check for values on dependency properties and data triggers check for values on CLR properties, event triggers watch for events. When an event happens, such as a Click event, an event trigger responds by raising an animation-related action. While animation is challenging enough to deserve its own chapter (Chapter 8), Example 5-30 illustrates a simple animation that will transition a cell from solid yellow to white over five seconds when an empty cell is clicked.

Example 5-30.  An event trigger

<Style TargetType="{x:Type Button}">
 
...
 
<Setter Property="Background" Value="White" />
 
<Style.Storyboards>
   
<ParallelTimeline Name="CellClickedTimeline" BeginTime="{x:Null}">
      <SetterTimeline Path="(Button.Background).(SolidColorBrush.Color)">
        <ColorAnimation From="Yellow" To="White" Duration="0:0:5" />
      </SetterTimeline>
    </ParallelTimeline>
  </Style.Storyboards>
 
<Style.Triggers>
   
<EventTrigger RoutedEvent="Click">
      <EventTrigger.Actions> 
        <BeginAction TargetName="CellClickedTimeline" /> 
      </EventTrigger.Actions>
    </EventTrigger>
 
</Style.Triggers>
</Style>

Adding an animation to a style requires two things. The first is a storyboard with a named timeline that describes what you want to happen. In our case, we’re animating the button’s background brush color from yellow to white over five seconds.

For any property being animated with a nested path, there needs to be an explicit property setting that creates the top level of the nesting. In Example 5-30, this means that we need aSetterelement for theBackgroundproperty. If the top level of the nesting isn’t created, there won’t be anything to animate at runtime.

The second thing you need is an event trigger to start the timeline. In our case, when the user clicks on a button with theCellButtonStyle style applied (all of them, in our case), we begin the action described by the named timeline in the storyboard.

As of this writing, if you have an event trigger and a multi-condition property trigger animating the same property—e.g., theBackgroundof aButton, make sure you put the multi-data trigger in the XAML file before the event trigger; otherwise, you’ll get a nonsensical error at runtime.

The results of the animation, showing various shades of yellow, through past clicks can be seen in Figure 5-13.


Figure 5-13.  The event trigger and our fading yellow animation (Color Plate 10)

Property and data triggers let you set properties when properties change. Event triggers let you trigger events when events happen. While both are pretty different—e.g., you can’t set a property with an event trigger or raise an event with a property or data trigger—both let you add a degree of interactivity to your applications in a wonderfully declarative way with little or no code.

For the full scoop on event triggers, you’ll want to read Chapter 8.

Control Templates

If we take a closer look at our current tic-tac-toe game, we’ll see that the Button objects aren’t quite doing the job for us. What tic-tac-toe board has rounded inset corners (Figure 5-14)?

What we really want here is to be able to keep the behavior of the button—i.e., holding content and firing click events—but we want to take over the look of the


Figure 5-14.  Tic-tac-toe boards don't have rounded insets!

button. WPF allows this kind of thing because the intrinsic controls are built to be lookless—i.e., they provide behavior, but the look can be swapped out completely by the client of the control.

Remember how we used data templates to provide the look of a non-visual object? We can do the same to a control using a control template, which is a set of storyboards, triggers, and, most importantly, elements that provide the look of a control.

To fix our buttons’ looks, we’ll build ourselves a control-template resource. Let’s start things off in Example 5-31 with a simple rectangle and worry about showing the actual button content later.

Example 5-31. A minimal control template

<Window.Resources>
 
<ControlTemplate x:Key="ButtonTemplate">
    <Rectangle />
  </ControlTemplate>
 
...
  <!-- let's just try one button for now... -->
  <Button Template="{StaticResource ButtonTemplate}" ... />
  ...

</Window.Resources>

Figure 5-15 shows the results of setting a single button’sTemplate property.

Notice that no vestiges of what the button used to look like remain in Figure 5-15. Unfortunately, no vestige of our rectangles can be seen, either. The problem is that, without a fill explicitly set, the rectangle’s fill defaults to transparent, showing the grid’s black background. Let’s set it to our other favorite Halloween color instead:

  <ControlTemplate x:Key="ButtonTemplate">
   
<Rectangle Fill="Orange" />
 
</ControlTemplate>

 


Figure 5-15.  Replacing the control template with something less visual than we'd like...

Now we’re getting somewhere, as Figure 5-16 shows.


Figure 5-16.   Replacing the button’s control template with an orange rectangle (Color Plate 11)

Notice how square the corners are? Also, if you click, you won’t get the depression that normally happens with a button (and I don’t mean “a sad feeling”).

Control Templates and Styles

Now that we’re making some progress on the control template, let’s replicate it to the other buttons. We can do so by setting each button’s Template property by hand or, as is most common, we can bundle the control template with the button’s style, as in Example 5-32.

Example 5-32.  Putting a control template into a style

<Window.Resources>
  <Style TargetType="{x:Type Button}">
    ...
   
<Setter Property="Template">
      <Setter.Value>
        <ControlTemplate>
          <Rectangle Fill="Orange" />
       </ControlTemplate>
      </Setter.Value>
   
</Setter>
 
</Style>
 
...
</Window.Resources>
...
<!-- No need to set the Template property for each button -->
<Button ... x:Name="cell00" />
...

As Example 5-32 shows, theTemplateproperty is the same as any other and can be set with a style. Figure 5-17 shows the results.


Figure 5-17.  Spreading the orange (Color Plate 12)

Still, the orange is kind of jarring, especially since the settings on the style call for a white background. We can solve this problem with template bindings.

Template Binding

To get back to our white buttons, we could hardcode the rectangle’s fill to be white, but what happens when a style wants to change it (such as in the animation we’ve now broken)? Instead of hardcoding the fill of the rectangle, we can reach out of the template into the properties of the control using template binding, as in Example 5-33.

Example 5-33.  Template binding to the Background property

<Style TargetType="{x:Type Button}">
 
<Setter Property="Background" Value="White" />
  ...
  <Setter Property="Template">
    <Setter.Value>
     
<ControlTemplate x:Key="ButtonTemplate">
       
<Rectangle Fill="{TemplateBinding Property=Background}" />
     
</ControlTemplate>
   
</Setter.Value>
 
</Setter>
 
...
</Style>

A template binding is like a data binding, except that the properties to bind come from the control whose template you’re replacing (called the templated parent). In our case, things likeBackground,HorizontalContentAlignment, and so on, are fair game for template binding from the parent. And, like data binding, template bindings are smart enough to keep the properties of the items inside the template up to date with changing properties on the outside, as set by styles, animations, etc. For example, Figure 5-18 shows the effect of aliasing the rectangle’sFillproperty to the button’sBackgroundproperty with our click animation and mouse-over behavior still in place.


Figure 5-18.  Setting the rectangle’s fill using property aliasing (Color Plate 13)

We’re not quite through yet, however. If we’re going to change the paint swatch that Figure 5-18 has become into a playable game, we have to show the moves. To do so, we’ll need a content presenter.

Content Presenters

If you’ve ever driven by a billboard or a bench at a bus stop that says “Your advertisement here!” then that’s all you need to know to understand content presenters. A content presenter is the WPF equivalent of “your content here” that allows content held by a ContentContainer control to be plugged in at runtime.

In our case, the content is the visualization of ourPlayerMoveobject. Instead of reproducing all of that work inside of the button’s new control template, we’d just like to drop it in at the right spot. The job of the content presenter is to take the content provided by the templated parent and do all of the things necessary to get it to show up properly, including styles, triggers, etc. The content presenter itself can be dropped into your template wherever you’d like to see it (including multiple times, if it tickles your fancy—e.g., to produce a drop shadow). In our case, we’ll compose a content presenter in Example 5-34 with the rectangle inside a grid using techniques from Chapter 2.

Example 5-34.  A content presenter

<Style TargetType="{x:Type Button}">
 
<Setter Property="Background" Value="White" />
 
...
 
<Setter Property="Template">
   
<Setter.Value>
     
<ControlTemplate>
       
<Grid>
         
<Rectangle Fill="{TemplateBinding Property=Background}" />
         
<ContentPresenter 
           Content="{TemplateBinding Property=ContentControl.Content}" />

        </Grid>
     
</ControlTemplate>
   
</Setter.Value>
 
</Setter>
 
...
</Style>

In Example 5-34, the content presenter’sContent property is bound to theContentControl.Contentproperty so that content comes through. As with styles, we can avoid prefixing template binding property names with classes by setting theTargetType attribute on theContentTemplate element:

  <ControlTemplate TargetType="{x:Type Button}">
   
<Grid>
     
<Rectangle Fill="{TemplateBinding Property=Background}" />
     
<ContentPresenter
       
Content="{TemplateBinding Property=Content}" />
    </Grid>
  </ControlTemplate>

Further, with theTargetType property in place, you can drop the explicit template binding on theContent property altogether, as it’s now set automatically:

  <ControlTemplate TargetType="{x:Type Button}">
    <Grid>
      <Rectangle Fill="{TemplateBinding Property=Background}" />
     
<!-- with TargetType set, the template binding for the -->
     
<!-- Content property is no longer required --
>
      <ContentPresenter />
    </Grid>
 
</ControlTemplate>

The content presenter is all we need to get our game back to being functional, as shown in Figure 5-19.


Figure 5-19.  Adding a content presenter to our control template (Color Plate 14)

The Real Work

The last little bit of work is getting the padding right. Since the content presenter doesn’t have its own Padding property, we can’t bind the Padding property directly (it doesn’t have a Background property, either, which is why we usedRectangleand itsFillproperty). For properties that don’t have a match on the content presenter, you have to find mappings or compose the elements that provide the functionality that you’re looking for. For example,Paddingis an amount of space inside of a control.Margin, on the other hand, is the amount of space around the outside of a control. Since they’re both of the same type,System.Windows.Thickness, if we could map thePaddingfrom the inside of our button to the outside of the content control, our game would look very nice:

  <Style TargetType="{x:Type Button}">
    <Setter Property="Background" Value="White" />
   
<Setter Property="Padding" Value="10,5" />
   
...
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type Button}">
          <Grid>
           
<Rectangle Fill="{TemplateBinding Property=Background}" />
           
<ContentPresenter
             
Content="{TemplateBinding Property=Content}" 
           Margin="{TemplateBinding Property=Padding}" /> 
         
</Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
    ...
 
</Style>

Figure 5-20 shows our completed tic-tac-toe variation.


Figure 5-20.  Binding the Padding property to the Margin property (Color Plate 15)

Like the mapping betweenPaddingandMargin, building up the elements that give you the look you want and binding the appropriate properties from the templated parent is going to be a lot of the work of creating your own control templates.

Where Are We?

Styles enable you to define a policy for setting the dependency properties of visual elements. The sets of properties can be named and applied manually or programmatically by name, or applied automatically using element-typed styles. In addition to providing constant dependency-property values, styles can contain condition-based property values based on other dependency properties, data properties, or events. And, if setting properties isn’t enough to get the look you’re after, you can replace a lookless control’s entire rendering behavior using a control template.

But that’s not all there is to styles. For information about how animations work, you’ll want to read Chapter 8, and for information about styles as related to resources, themes, and skins, you’ll want to read Chapter 6.


* Moving groups of settings into styles also allows for easier skinning and theming, as described in Chapter 6.

* The null value is set via a XAML markup extension, which you can read more about in Appendix A.

blog comments powered by Disqus
XML ARTICLES

- More on Triggers and Styles and Control Temp...
- Looking at Triggers with Styles and Control ...
- A Closer Look at Styles and Control Templates
- Styles and Control Templates
- Properties and More in XAML
- Elements and Attributes in XAML
- XAML in a Nutshell
- Importing XML Files into Access 2007
- Using MSXML3.0 with VB 6.0
- MSXML, concluded
- MSXML, continued
- MSXML Tutorial
- Generating XML Schema Dynamically Using VB.N...
- XSL Transformations using ASP.NET
- Applying XSLT to XML Using ASP.NET

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
 
 
 

ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 7 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials