Windows Presentation Foundation

Hello, WPF

WPF from Scratch
Navigation Applications
Content Model
Layout
Controls
Data Binding
Dependency Properties
Resources
Styles and Control Templates
Graphics
Application Deployment
Where Are We?

Layout

Introduction
Layout Basics
DockPanel
StackPanel
Grid
Canvas
Viewbox
Text Layout
Common Layout Properties
When Content Doesn't Fit
Custom Layout
Where Are We?

Controls

Introduction
What Are Controls?
Handling Input
Built-In Controls
Where Are We?

Data Binding

Introduction
Without Data Binding
Data Binding
Binding to List Data
Data Sources
Master-Detail Binding
Where Are We?

Styles and Control Templates

Introduction
Without Styles
Inline Styles
Named Styles
Element-Typed Styles
Data Templates and Styles
Triggers
Control Templates
Where Are We?

Resources

Introduction
Creating and Using Resources
Resources and Styles
Binary Resources
Global Applications
Where Are We?

Graphics

Introduction
Graphics Fundamentals
Shapes
Brushes and Pens
Transformations
Visual-Layer Programming
Video and 3-D
Where Are We?

Animation

Animation Fundamentals
Timelines
Storyboards
Key Frame Animations
Creating Animations Procedurally
Where Are We?

Custom Controls

Introduction
Custom Control Basics
Choosing a Base Class
Custom Functionality
Templates
Default Visuals
Where Are We?

ClickOnce Deployment

A Brief History of Windows Deployment
ClickOnce: Local Install
The Pieces of ClickOnce
Publish Properties
Deploying Updates
ClickOnce: Express Applications
Choosing Local Install versus Express
Signing ClickOnce Applications
Programming for ClickOnce
Security Considerations
Where Are We?

Named Styles

5.3. Named Styles

By hoisting the same inline style into a resource (as introduced in Chapter 1), we can award it a name and use it by name in our button instances, as shown in Example 5-5.

Example 5-5. Setting a named style
<!-- Window1.xaml -->
<Window ...>

  <Window.Resources>

    <Style x:Key="CellTextStyle">
      <Setter Property="Control.FontSize" Value="32" />
      <Setter Property="Control.FontWeight" Value="Bold" />
    </Style>
  </Window.Resources>
  ...
  <Button Style="{StaticResource CellTextStyle}" ... x:Name="cell00" />

  ...
</Window>

In Example 5-5, we've used the Control prefix on our properties instead of the Button prefix to allow the style to be used more broadly, as we'll soon see.

5.3.1. The TargetType Attribute

As a convenience, if all of the properties can be set on a shared class, like Control in our example, we can promote the class prefix into the TargetType attribute and remove it from the name of the property, as in Example 5-6.

Example 5-6. A target-typed style
<Style x:Key="CellTextStyle" TargetType="{x:Type Control}">

  <Setter Property="FontSize" Value="32" />
  <Setter Property="FontWeight" Value="Bold" />
</Style>

When providing a TargetType attribute, you can only set properties available on that type. If you'd like to expand to a greater set of properties down the inheritance tree, you can do so by using a more derived type, as in Example 5-7.

Example 5-7. A more derived target-typed style
<Style x:Key="CellTextStyle" TargetType="{x:Type Button}">

  <!-- IsCancel is a Button-specific property -->
  <Setter Property="IsCancel" Value="False" />
  <Setter Property="FontSize" Value="32" />

  <Setter Property="FontWeight" Value="Bold" />
</Style>

In this case, the IsCancel property is only available on Button, so to set it, we need to switch the TargetType attribute for the style.

You may be wondering why I'm setting the FontSize to "32" instead of "32pt" when the latter is more in line with how font sizes are specified and the two representations are definitely not equivalent (the former is pixels, while the latter is points). I'm using pixels because as of this writing, WPF styles using a non-prefixed property allow "32pt" to be specified for FontSize, while prefixed properties do not. For example, the following works (assuming a TargetType is set):

    <Setter Property="FontSize" Value="32pt" />

whereas the following does not (regardless of whether a TargetType is set or not):

    <Setter Property="Control.FontSize" Value="32pt" />


Hopefully this problem will have been fixed by the time you read this (and not replaced with others).


5.3.2. Reusing Styles

In addition to saving you from typing out the name of the class prefix for every property name, the TargetType attribute will also check that all classes that have the style applied are an instance of that type (or derived type). What that means is that if we leave TargetType set to Control, we can apply it to a Button element, but not to a TextBlock element, as the former derives ultimately from Control but the latter does not.

On the other hand, while Control and TextBlock both share the common ancestor FrameworkElement, the FrameworkElement class doesn't define a FontSize dependency property, so a style with a TargetType of FrameworkElement won't let us set the FontSize property because its not there, despite the fact that both Control and TextBlock have a FontSize property.

Even with the TargetType set to Control, we gain a measure of reuse of our style across classes that derive from Controle.g., Button, Label, Window, etc. However, if we drop the TargetType attribute from the style altogether, we gain a measure of reuse of styles across controls that don't have a common base but share a dependency-property implementation. In my experimentation, I've found that dependency properties that share the same name across classes, such as Control.FontSize and TextBlock.FontSize, also share an implementation. What that means is that even though Control and TextBlock each define their own FontSize property, at runtime they share the implementation of this property, so I can write code like Example 5-8.

Example 5-8. Reusing a style between different element types
...
<Style x:Key="CellTextStyle">
  <Setter Property="Control.FontSize" Value="32" />
</Style>
...
<!-- derives from Control -->

<Button Style="{StaticResource CellTextStyle}" ... />

<!-- does *not* derive from Control -->
<TextBlock Style="{StaticResource CellTextStyle}" ... />
...

In Example 5-8, I've dropped the TargetType attribute from the style definition, using instead the class prefix on each property the style sets. This style can be applied to a Button element, as you'd expect, but can also be applied to a TextBlock control, with the FontSize set as specified by the style. The reason this works is that both the Button, which gets its FontSize dependency property definition from the Control class, and the TextBlock, which provides it's own FontSize dependency property definition, share the FontSize dependency property implementation with the TextElement class. Figure 5-2 shows the relationship of elements to their dependency-property implementations.

As Figure 5-2 shows, if we wanted to, we could redefine our style in terms of the TextElement class, even though it falls into the inheritance tree of neither Control nor TextBlock, as in Example 5-9.

Figure 5-2. Elements and dependency properties


Example 5-9. Depending on the implementation of dependency properties
<Style x:Key="CellTextStyle">
  <Setter Property="TextElement.FontSize" Value="32" />
</Style>
...
<Button Style="{StaticResource CellTextStyle}" ... />

<TextBlock Style="{StaticResource CellTextStyle}" ... />

Taking this further, if we'd like to define a style that contains properties not shared by every element we apply them to, we can do that, too, as in Example 5-10.

Example 5-10. Styles can have properties that targets don't have
<Style x:Key="CellTextStyle">

  <Setter Property="TextElement.FontSize" Value="32" />
  <Setter Property="Button.IsCancel" Value="False" />
</Style>
...
<!-- has an IsCancel property -->
<Button Style="{StaticResource CellTextStyle}" ... />

<!-- does *not* have an IsCancel property -->
<TextBlock Style="{StaticResource CellTextStyle}" ... />

In Example 5-10, we've added the Button.IsCancel property to the CellTextStyle and applied it to the Button element, which has this property, and the TextBlock element, which doesn't. This is OK. At runtime, WPF will apply the dependency properties that exist on the elements that have them and silently swallow the ones that aren't present.

WPF's ability to apply styles to objects that don't have all of the properties defined in the style is analogous to applying the Word Normal style, which includes a font size property of its own, to both a range of text and an image. Even though Word knows that images don't have a font size, it applies the portions of the Normal style that do make sense (such as the justification property), ignoring the rest.


Getting back to our sample, we can use the CellTextStyle on a TextBlock in a new row to show whose turn it is, as in Example 5-11.

Example 5-11. Applying a style to Button and TextBlock elements
<Window.Resources>

  <Style x:Key="CellTextStyle">
    <Setter Property="TextElement.FontSize" Value="32" />
    <Setter Property="TextElement.FontWeight" Value="Bold" />
  </Style>
</Window.Resources>
<Grid Background="Black">
  <Grid.RowDefinitions>

    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
    <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>

    <ColumnDefinition />
    <ColumnDefinition />
    <ColumnDefinition />
  </Grid.ColumnDefinitions>
  <Button Style="{StaticResource CellTextStyle}" ... />

  ...
  <TextBlock
    Style="{StaticResource CellTextStyle}"
    
    Foreground="White"
    Grid.Row="3"
    Grid.ColumnSpan="3"
    x:Name="statusTextBlock" />
</Grid>
</Window>


This reuse of the style across controls of different types gives me a consistent look in my application, as shown in Figure 5-3.

One thing you'll notice is that the status text in Figure 5-3 is white, while the text in the buttons is black. Since black is the default text color, if we want the status text to show up against a black background, we have to change the color to something else, hence the need to set the Foreground property to white on the TextBlock. Setting per-instance properties works just fine in combination with the style, and you can combine the two techniques of setting property values as you see fit.

Figure 5-3. A tic-tac-toe game with style


5.3.3. Overriding Style Properties

Further, if we want to override a style property on a specific instance, we can do so by setting the property on the instance, as in Example 5-12.

Example 5-12. Overriding the FontWeight property from the style
<Style x:Key="CellTextStyle">
  <Setter Property="TextElement.FontSize" Value="32" />
  <Setter Property="TextElement.FontWeight" Value="Bold" />
</Style>
...
<TextBlock
  Style="{StaticResource CellTextStyle}"
  FontWeight="Thin" ... />

In Example 5-12, the TextBlock instance property setting of FontWeight take precedence over the style property settings of FontWeight.

5.3.4. Inheriting Style Properties

To complete the object-oriented triumvirate of reuse, override, and inheritance, you can inherit a style from a base style, adding new properties or overriding existing ones, as in Example 5-13.

Example 5-13. Style inheritance
<Style x:Key="CellTextStyle">
  <Setter Property="TextElement.FontSize" Value="32" />
  <Setter Property="TextElement.FontWeight" Value="Bold" />
</Style>
<Style x:Key="StatusTextStyle" BasedOn="{StaticResource CellTextStyle}">

  <Setter Property="TextElement.FontWeight" Value="Thin" />
  <Setter Property="TextElement.Foreground" Value="White" />
  <Setter Property="TextBlock.HorizontalAlignment" Value="Center" />
</Style>

The BasedOn style attribute is used to designate the base style. In Example 5-13, the StatusTextStyle style inherits all of the CellTextStyle property setters, overrides the FontWeight, and adds setters for Foreground and HorizontalAlignment. Notice that the HorizontalAlignment property uses a TextBlock prefix; this is because TextElement doesn't have a HorizontalAlignment property.

Our current use of styles causes our tic-tac-toe game to look like Figure 5-4.

Figure 5-4. A tic-tac-toe game with more style


Our application so far is pretty good, especially with the thin font weight on the status text, but we can do better.

5.3.5. Setting Styles Programmatically

Once a style has a name, it's easily available from our code. For example, we might decide that we'd like each player to have their own style. In this case, using named styles in XAML at compile time won't do the trick, since we want to set the style based on the content, which isn't known until runtime. However, there's nothing that requires us to set the Style property of a control statically; we can set it programmatically as well, as we do in Example 5-14.

Example 5-14. Setting styles programmatically
public partial class Window1 : Window {
  void cell_Click(object sender, RoutedEventArgs e) {
    Button button = (Button)sender;
    ...
    // Set button content
    button.Content = this.CurrentPlayer;
    ...
    if( this.CurrentPlayer == "X" ) {
      button.Style = (Style)FindResource("XStyle");
      this.CurrentPlayer == "O";

    }
    else {
      button.Style = (Style)FindResource("OStyle");
      this.CurrentPlayer == "X";
    }
    ...
  }
  ...
}

In Example 5-14, whenever the player clicks, in addition to setting the button's content, we pull a named style out of the window's resources and use that to set the button's style. This assumes a pair of named styles defined in the window's scope, as in Example 5-15.

Example 5-15. Styles pulled out via FindResource
<Window.Resources>
  <Style x:Key="CellTextStyle">
    <Setter Property="TextElement.FontSize" Value="32" />
    <Setter Property="TextElement.FontWeight" Value="Bold" />
  </Style>

  <Style x:Key="XStyle" BasedOn="{StaticResource CellTextStyle}">
    <Setter Property="TextElement.Foreground" Value="Red" />
  
  </Style>
  <Style x:Key="OStyle" BasedOn="{StaticResource CellTextStyle}">
    <Setter Property="TextElement.Foreground" Value="Green" />

  </Style>
</Window.Resources>

With these styles in place and the code to set the button style along with content, we get Figure 5-5.

Notice that the Xs and Os are colored according to the named player styles. In this particular case (and in many other cases, too), data triggers (discussed in "Data Triggers," later in this chapter) should be preferred to setting styles programmatically, but you never know when you're going to have to jam.

As with all XAML constructs, you are free to create styles themselves programmatically. Appendix A is a good introduction on how to think about going back and forth between XAML and code.


Figure 5-5. Setting styles programmatically based on an object's content (Color Plate 9)



Previous Page
Next Page

©2008 FAQ - WPF Labs - Discuss - Terms of Use - Privacy Policy - About WPF
- Interview Questions - Sharepoint Articles - Interview Questions Resource Library - All about LINQ - MS Knowledgebase Articles - Electronics and Hardware discussions