#176 – Two Kinds of Templates

In WPF, there are two different kinds of templatescontrol templates and data templates.

Control templates allow you to exactly specify the visual tree of a control, i.e. its constituent controls.  The example below shows a Button control being constructed out of a Button and two Label controls.

        <Button Name="btnWithTemplate" Content="Recreate Me" Foreground="Blue">
            <Button.Template>
                <ControlTemplate TargetType="{x:Type Button}">
                    <StackPanel Orientation="Horizontal">
                        <Label Content="**" Foreground="{TemplateBinding Foreground}"/>
                        <Button Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}"/>
                        <Label Content="**" Foreground="{TemplateBinding Foreground}"/>
                    </StackPanel>
                </ControlTemplate>
            </Button.Template>
        </Button>

 

Data templates allow you to specify how a bound data object will be mapped to one or more controls.  The example below maps properties of a Person object to several labels.

        <Label Name="lblPerson" Content="{Binding}">
            <Label.ContentTemplate>
                <DataTemplate>
                    <Border BorderThickness="2" BorderBrush="DarkBlue">
                        <StackPanel Orientation="Vertical">
                            <StackPanel Orientation="Horizontal">
                                <Label Content="{Binding Path=FirstName}"/>
                                <Label Content="{Binding Path=LastName}" FontWeight="Bold"/>
                            </StackPanel>
                            <Label Content="{Binding Path=BirthYear}" FontStyle="Italic"/>
                        </StackPanel>
                    </Border>
                </DataTemplate>
            </Label.ContentTemplate>
        </Label>

#175 – Freeze Graphical Objects That You Don’t Intend to Modify

For performance purposes, it’s best to freeze graphical objects (e.g. brushes) if you don’t intend to modify them.  You must also freeze an object if you intend to reference it from a thread other than the thread that created it.

You can freeze an object in code, using the Freeze method.

            // SolidColorBrush, created in XAML, not frozen
            bool frozen = tealBrush.IsFrozen;    // frozen = false

            if (tealBrush.CanFreeze)
                tealBrush.Freeze();

            frozen = tealBrush.IsFrozen;         // frozen = true

You can also freeze an object in XAML, when it is declared, with the addition of an XML namespace.

<Window x:Class="WpfApplication4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:po="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
        Title="MainWindow" Height="350" Width="525" >
    <Window.Resources>
        <SolidColorBrush x:Key="tealBrush" Color="Teal" po:Freeze="True"/>
    </Window.Resources>

#173 – You Can Put Freezable Objects Into a Read-Only State

The idea of an object deriving from Freezable is that it normally is in a read/write state, but can be explicitly put into a read-only state using the Freeze method.  A frozen object can be used more efficiently in WPF because it doesn’t need to notify consumers of the object that its value has changed.

Graphical objects in WPF like brushes and 3D geometries derive from Freezable.  Initially unfrozen, a change to one of these objects results in consumers of the objects being notified of the change.

If you have an object deriving from Freezable that you don’t plan to change, you can make the object read-only using the Freeze method.

            // Freeze this object, making it read-only (since we don't plan on changing it)
            if (theBrush.CanFreeze)
                theBrush.Freeze();

After freezing the object, if you try modifying it, you’ll get an InvalidOperationException.  But WPF will be more efficient in its use of the object.

#171 – Other Base Classes – Freezable and Animatable

We can add two other important base classes to our class hierarchy–Freezable and Animatable.

  • Freezable - Implements a “freezable” pattern, in that the object can provide a “frozen” read-only copy of itself.
  • Animatable – Derives from Freezable and adds the ability to animate properties

#170 – Functionality That The Base Element Classes Provide

The four main base element classes in WPF each provide slightly different functionality, above and beyond DependencyObject, which they all inherit from, directly or indirectly.

  • ContentElement adds (to DependencyObject)
    • Input events and commanding
    • Focus
    • Raise and respond to routed events
    • Animation support
  • FrameworkContentElement adds (to ContentElement)
    • Additional input elements (e.g. tooltips, context menus)
    • Storyboards
    • Data binding
    • Styles
    • Property value inheritance
  • UIElement adds (to DependencyObject)
    • via Visual
      • Hit testing
      • Clipping and coordinate transformations
      • Participation in visual tree via parent/child relationships
    • Layout behavior (measure/arrange)
    • Input events and commanding
    • Focus
    • Raise and respond to routed Events
    • Animation support
  • FrameworkElement adds (to UIElement)
    • Additional input elements (e.g. tooltips, context menus)
    • Storyboards
    • Data binding
    • Styles
    • Property value inheritance
    • Support for the logical tree

 

#169 – The Base Element Classes

There are four main classes that that serve as base classes for many of the other classes in WPF.  They also often serve as base classes for new custom classes that you write.

The four base element classes are:

  • ContentElement
  • FrameworkContentElement
  • UIElement
  • FrameworkElement

#168 – A More Complete Class Hierarchy

Post #27 presented a class hierarchy that shows the most common classes used in WPF.  Here is an updated view, with ContentElement and FrameworkContentElement added.  These two classes are sometimes used as base classes for creating new elements, so it’s important to know where they fit in the class hierarchy.

(Click on the image to see the diagram full-sized).

 

#132 – Inherited Property Values Pass Through All Elements

When an element looks up the element tree for an inherited property value, it doesn’t stop when it hits an element that doesn’t define that particular property.  The element “looks” all of the way up the tree, to the root element.

In the example below, we set a value for the FontStyle in the top-level Window element.  That value is inherited by the Button elements further down the tree, even though their immediate parent–the StackPanel–does not have a FontStyle property.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:m="clr-namespace:PersonLib;assembly=PersonLib"
        Title="MainWindow" Height="350" Width="525" FontStyle="Italic">
    <StackPanel Orientation="Vertical">
        <Button Content="Run" Height="23" Width="75" />
        <Button Content="Skip" Height="23" Width="75" />
    </StackPanel>
</Window>

#131 – Dependency Properties Inherit Values from Higher Up in the Logical Tree

The value of a dependency property can come from a number of different sources, but the property often inherits its value from an element further up the logical tree.

This means that when you set a property value in XAML or in code, that value often “trickles down” the element tree and is applied to other elements that have a property of the same name.

Here’s an example.  The value of FontStyle for several controls is inherited from the top-level Window element.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:m="clr-namespace:PersonLib;assembly=PersonLib"
        Title="MainWindow" Height="350" Width="525" FontStyle="Italic">
    <StackPanel Orientation="Vertical">
        <Button Content="Run" Height="23" Width="75" />
        <Button Content="Skip" Height="23" Width="75" />
        <StackPanel Orientation="Horizontal">
            <Label Content="Inside 2nd StackPanel"/>
            <Label Content="I do my own FontStyle" FontStyle="Normal"/>
        </StackPanel>
    </StackPanel>
</Window>

Here’s what the window looks like:

The value for a dependency property will not be inherited if that value is set explicitly (locally) in an element.

#130 – WPF Supports Three Types of Triggers

WPF supports three different types of triggers:

  • Property triggers
    • Fire when the value of a dependency property changes
    • Specifies trigger property using property name
    • Actions
      • Setter elements set values for one or more dependency properties
      • One or more TriggerAction classes fire, when trigger becomes active or becomes inactive
  • Data triggers
    • Fire when the value of a CLR property changes
    • Specifies property using Binding keyword
    • Actions
      • Setter elements sets values for one or more dependency properties
      • One or more TriggerAction classes fire, when trigger becomes active or becomes inactive
  • Event triggers
    • Fire when a routed event is raised
    • Action: Class that derives from TriggerAction fires, e.g. BeginStoryboard or SoundPlayerAction
    • Often used for animations
Follow

Get every new post delivered to your Inbox.

Join 129 other followers