WPF Simplified Part 11: XAML Tricks

The Extensible Application Markup Language (aka XAML) is a declarative programming language that was specifically built for WPF to encourage separation of front-end appearance and back-end logic.

While a full explanation of XAML is beyond the scope of this post (see here for an introduction), we’ll focus on some uncommon features of XAML,

1. Escaping curly braces: Say you want to display curly braces in XAML,

        <!-- Error! -->
        <TextBlock Text="{test string}" />

will fail because the parser thinks that the string is a markup extension. There are two ways to fix this,

    a. Escape the curly braces:

    <TextBlock Text="{}{test string}" />

    b. Use the property element syntax:

    <TextBlock>{test string}</TextBlock>

2. Procedural code inside XAML: You can embed code inside your XAML file, though its not recommended.

    <Button x:Name="MyButton" Height="100" Width="100" Click="MyButton_Click" />
    <x:Code>
        <![CDATA[ 
            private void MyButton_Click(Object sender, RoutedEventArgs e) { 
                MessageBox.Show("Button click!"); 
            } 
        ]]>
    </x:Code>

3. Preserve white space: Say you want to preserve white spaces between text,

     <TextBlock>Text     spacing</TextBlock>

this will collapse the space between ‘Text’ and ‘spacing’, to preserve the spaces try this,

     <TextBlock xml:space="preserve">Text     spacing</TextBlock>

or simply,

    <TextBlock Text="Text     spacing" /> 

4. Use null in XAML: You can specify null in XAML using the x:Null markup extension,

    <Grid Background="{x:Null}">

5. Use StringFormat in XAML: Very useful for quick formatting,

    <TextBlock Text="{Binding Name, StringFormat=Hello {0}!!}" />
    <TextBlock Text="{Binding Date, StringFormat=Today\'s date is: {0:MM/dd/yyyy}}" />

6. Use MultiBinding with StringFormat:

    <TextBlock> 
        <TextBlock.Text> 
            <MultiBinding StringFormat="Name:{0}, Age:{1}"> 
                <Binding Path="Name" /> 
                <Binding Path="Age" /> 
            </MultiBinding> 
        </TextBlock.Text>
    </TextBlock>

7. Create an array in XAML: You can create an array in XAML,

    <ListBox>
        <ListBox.ItemsSource>
            <x:Array Type="{x:Type sys:String}">
                <sys:String>John</sys:String>
                <sys:String>Paul</sys:String>
                <sys:String>Andy</sys:String>
            </x:Array>
        </ListBox.ItemsSource>
    </ListBox>

where,

    xmlns:sys="clr-namespace:System;assembly=mscorlib"

In fact you can use other data types from the System namespace, like Int32, Boolean, and even DateTime,

    <ListBox>
        <ListBox.ItemsSource>
            <x:Array Type="{x:Type sys:DateTime}">
                <sys:DateTime>1/1/10</sys:DateTime>
                <sys:DateTime>3/3/10 03:21 PM</sys:DateTime>
            </x:Array>
        </ListBox.ItemsSource>
    </ListBox>

8. Specify an empty string in XAML:

    <ListBox>
        <ListBox.ItemsSource>
            <x:Array Type="{x:Type sys:String}">
                <sys:String>John</sys:String>
                <x:Static Member="sys:String.Empty" />
                <sys:String>Andy</sys:String>
            </x:Array>
        </ListBox.ItemsSource>
    </ListBox>
 

where,

    xmlns:sys="clr-namespace:System;assembly=mscorlib"

9. Put constants in XAML: Say you have a constant in code,

    public static string Extension = ".xaml"  //In class Window1
 

You can use this in XAML via the x:Static markup extension, like this,

    <TextBlock Text="{x:Static local:Window1.Extension}" />

where,

    xmlns:local="clr-namespace:WpfApplication1"

In fact, you can assign any static value,

    <Label Content="{x:Static sys:DateTime.Now}" />
    <Label Foreground="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />

10. Instantiate a class in XAML: Say you have the following class in your code,

    public class Person {
        public string Name { get; set; }
        public int Age { get; set; }
    }

You can instantiate an array in XAML, populate it with Person type objects and bind to a listbox,

    <Grid.Resources>
        <!-- Create a array of Person objects -->
        <x:Array x:Key="Office" Type="{x:Type local:Person}">
            <!-- Instantiate a Person and add to the array -->
            <local:Person Name="Michael" Age="40"/>
            <local:Person Name="Jim" Age="30"/>
            <local:Person Name="Dwight" Age="30"/>
        </x:Array>
    </Grid.Resources>
    
    <!-- Bind the array to the listbox -->
    <ListBox ItemsSource="{Binding Source={StaticResource Office}}" DisplayMemberPath="Name" />

If you have a collection,

    public class Family : ObservableCollection<Person> {
        public Family() {
            Add(new Person() { Name = "Jim", Age = 30 });
            Add(new Person() { Name = "Pam", Age = 30 });
        }
    }

we can instantiate the collection in XAML and then bind to it,

    <Grid.Resources>
        <!-- Instantiate the Family class -->
        <local:Family x:Key="Family" />
    </Grid.Resources>
    <!-- Bind to the collection -->
    <ListBox ItemsSource="{Binding Source={StaticResource Family}}" DisplayMemberPath="Name" />

See more XAML tricks here.

Digg This

WPF Simplified Part 10: WPF Framework Class Hierarchy

The WPF framework contains three major components, the PresentationFramework, the PresentationCore, and the milcore (Media Integration Layer Core).

image

While PresentationFramework and PresentationCore form the managed WPF programming model, MSDN says this about the milcore,

Milcore is written in unmanaged code in order to enable tight integration with DirectX. All display in WPF is done through the DirectX engine, allowing for efficient hardware and software rendering. WPF also required fine control over memory and execution. The composition engine in milcore is extremely performance sensitive, and required giving up many advantages of the CLR to gain performance.

The WPF framework contains many classes and its useful to get an overall view of the class hierarchy. The diagrams below show some of the major class in the framework (not all classes are included) in the WPF class hierarchy.

image

The FrameworkElement contains most of the UI controls that we usually work with, and is expanded below,

image

Some notes about the class hierarchy,

1. All classes in the WPF framework derive from System.Object.

2. Deriving from System.Threading.DispatcherObject gives us a CLR object that has single thread affinity (STA) behavior. Since all the UI controls derive from DispatcherObject, all WPF UI controls can be accessed only on the thread that created it, and are thus inherently thread-unsafe. Some of the methods are of the class are,

    public Dispatcher Dispatcher { get; }
    public bool CheckAccess();
    public void VerifyAccess();
 

3. System.Windows.Dependency is the base class that supports dependency and attached properties. Some of the methods are of the class are,

    public void SetValue(DependencyProperty dp, object value);
    public object GetValue(DependencyProperty dp);
    public void ClearValue(DependencyProperty dp);
 

4. System.Windows.Media.Visual is the entry point to the WPF composition system. This is the base class for all objects that have their own visual representation. Some of the methods are of the class are,

    protected DependencyObject VisualParent { get; }
    protected void AddVisualChild(Visual child);
    protected void RemoveVisualChild(Visual child);
 

5. System.Windows.UIElement adds support for core features like, Events, Input, Layout, and CommandBindings. Some of the methods are of the class are,

    public event MouseButtonEventHandler PreviewMouseLeftButtonDown;
    public event MouseButtonEventHandler MouseLeftButtonDown;
    public static readonly DependencyProperty IsEnabledProperty;
    public bool IsMouseOver { get; }
 

6. System.Windows.FrameworkElement extends the layout features of UIElement and adds support for features like, styles, data binding, resources, data templates, tooltips, and animation. Some of the methods are of the class are,

    public double MinHeight { get; set; }
    public Style Style { get; set; }
    public ResourceDictionary Resources { get; set; }
    public object FindResource(object resourceKey);
    public object ToolTip { get; set; }
    public void BeginStoryboard(Storyboard storyboard);
 

7. System.Windows.Controls.Control adds support for templating and is the base class for the familiar UI controls like Button, Grid, and TextBox. Some of the methods are of the class are,

    public ControlTemplate Template { get; set; }
    public Brush Background { get; set; }
    public FontFamily FontFamily { get; set; }
Digg This

WPF Simplified Part 9: Logical Resources

There are two types of resources in WPF – binary and logical. Binary resources are plain old .NET resources like bitmaps, fonts, and strings that can either be embedded inside the assembly or packaged in a resource file. We’ll discuss this in another post.

Logical resources, henceforth called resources are new to WPF. In XAML, resources are simply objects stored in an element’s Resource property so that they can be shared by child elements. Just about any object in WPF can be a resource, like a brush, a color, some text, some style etc.

Consider the simple XAML,

<Grid>
    <Button Height="100" Width="100" Content="Some Text" FontStyle="Italic" />
    <RadioButton Content="Some Text" FontStyle="Italic" />

</Grid>

Since we want both the child elements of Grid to have the same FontStyle we could define a FontStyle resource, put it in the Grid’s Resource property and point the FontStyles of the Button and RadioButton to the resource, like below,

<Grid>
     <Grid.Resources>
         <!-- Define a FontStyle resource -->
         <FontStyle x:Key="GridFontStyle">Italic</FontStyle>
     </Grid.Resources>

     <Button Height="100" Width="100" Content="Some Text" 
             FontStyle="{StaticResource GridFontStyle}" />
     <RadioButton Content="Some Text" FontStyle="{StaticResource GridFontStyle}" />     
 </Grid>

Doing this in code is simple enough,

<Grid x:Name="MyGrid">
    <Button x:Name="MyButton"  Height="100" Width="100" Content="Some Text" />
    <RadioButton x:Name="MyRadioButton" Content="Some Text" />    
</Grid>

// And in code...
this.MyGrid.Resources.Add("GridFontStyle", FontStyles.Italic);
this.MyButton.FontStyle = (FontStyle)this.MyGrid.FindResource("GridFontStyle");
this.MyRadioButton.FontStyle = (FontStyle)this.MyGrid.FindResource("GridFontStyle");

Every resource in the element’s Resource property must be unique, this is because internally WPF uses a ResourceDictionary to implement the resources. Thus, the x:Key that you specify in XAML, or equivalently the first parameter to Add, becomes the key in the dictionary for that resource. Note that although we have used a String object as the key any object can be used as a key. However, we don’t have to explicitly set the x:Key on all resources, for example in the case of Styles setting the TargetType is sufficient (for an example see here), and WPF implicitly creates an x:Key for the resource.

Resources are scoped hierarchically, like this,imageSo when we use FindResource to get a resource like in the above example, FindResource will start looking at the parent’s resources, then the parent’s parent’s resources etc., traversing the logical tree upward all the way to Window’s resources, then the entire Application’s resources and eventually the System’s resources! FindResource is quite a hard worker,

[In App.xaml]

<Application.Resources>
    <FontStyle x:Key="ApplicationFontStyle">Italic</FontStyle>
</Application.Resources>

[In Window1.xaml]

<Window.Resources>
    <FontStyle x:Key="WindowFontStyle">Italic</FontStyle>
</Window.Resources>
    
<Grid x:Name="MyGrid">
    <Button Height="100" Width="100" Content="Some Text" FontStyle="{StaticResource WindowFontStyle}" />
    <RadioButton Content="Some Text" FontStyle="{StaticResource ApplicationFontStyle}" />
</Grid>

We can use FrameworkElement.Resources to search the element’s resource dictionary directly, which will return null if the resource isn’t found.

// Looks up the Grid's ResourceDictionary directly using a key and returns null
this.MyButton.FontStyle = (FontStyle)this.MyGrid.Resources["WindowFontStyle"];

// Looks up the logical tree and finds the resource in Window's resources
this.MyButton.FontStyle = (FontStyle)this.MyGrid.FindResource("WindowFontStyle");

In addtion to using StaticResource in XAML, we could also use DynamicResource, the differences being,

                                                  StaticResource                                                DynamicResource
The reference must appear after the resource to which it refers. Forward references are allowed.
The resource is applied only once – the first time it is needed. The resource is reapplied every time the resource changes.
Can be used on regular .NET properties and almost anywhere. Can only be used to set dependency property values.
Resources are loaded when the Window or Page loads. Not loaded until the resource is actually used.
Code equivalent: FindReference Code equivalent: SetResourceReference

As an example, consider the XAML,

<Window.Resources>
    <RadioButton x:Key="MyRadioButton" x:Shared="False" Content="Some Text" />
</Window.Resources>
    
<Grid>
    <!-- The button’s background is set to a System resource -->
    <Button x:Name="MyButton" Height="100" Width="100" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" >

        <!-- The static resource can be used anywhere... --> 
        <StaticResource ResourceKey="MyRadioButton" />
    </Button>

    <!-- We can use the same resource here because we set x:Shared to False -->
    <StaticResource ResourceKey="MyRadioButton" />        
</Grid>

A few notes about the XAML above,

1. The button’s Background dependency property is set to a system resource. Setting the property like this,

    Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"

is interpreted as a dynamic reference to a resource named by the string SystemColors.ControlBrushKey. Setting x:Static means the text should be treated as the name of a static property not a string. The equivalent in code is,

    this.MyButton.SetResourceReference(Button.BackgroundProperty, SystemColors.ControlBrushKey);

2. The RadioButton resource is used to instantiate a radio button inside the button and also inside the grid. By default, when a resource is applied in multiple places, the same object instance is used, so a resource can only be applied once in an element tree because each resource application is the same instance. This can be overridden by setting x:Shared to false. Note that you’ll get a VisualStudio designer error if you’re trying to use x:Shared and instantiate a new copy of the resource, x:Shared will work fine in a compiled XAML.

image

Digg This

WPF Simplified Part 8: Data Templates

In WPF, a control can specify how it wants to display a .NET object, by specifying a UI template for the object. Thus, when the object is bound to the control, the object is displayed using the (data) template for it that the control has specified. This data template overrides the control’s visual tree just like a control template. The DataTemplate can determine both the visual aspect of how the data is presented and also how data binding accesses different properties of the data object. You can store the data template for an object as a resource and have different controls use the same data template to display the object, so that the object’s UI representation is similar across different controls. Also, by applying different data templates to the same data, you can flexibly change the visual appearance of the data in your application.

Consider the simple XAML,

<Window x:Class="WpfApplication3.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <Button Height="100" Width="100" />
    </Grid>
</Window>

And say we want to display this simple .NET object,

public class Person {
    public string Name { get; set; }
    public int Age { get; set; }
}

We can do this by defining a DataTemplate and assigning to the button’s ContentTemplate,

<Button Height="100" Width="100" Content="{Binding}">
    <Button.ContentTemplate>
        <!-- Define a DataTemplate for the Person type... --> 
        <DataTemplate DataType="{x:Type local:Person}">
            <!-- ...with a custom visual tree for the data object... -->
            <TextBlock>
                <!-- ...and binding to different properties of the data object -->
                <TextBlock Text="{Binding Name}" />
                (age:<TextBlock Text="{Binding Age}" />)
            </TextBlock>
        </DataTemplate>
    </Button.ContentTemplate>
</Button>
 
// And in code, do this,
Person person = new Person() { Name = "John", Age = 32 };
this.DataContext = person;
 

The XAML above creates a DataTemplate asociated with the Person type. This data template has its own visual tree (consisting of TextBlocks or anything else we could want). Now when the button’s Content is bound to an object of type Person, the data template will handle the way the button displays the Person object.

Say we want to reuse this DataTemplate (that is associated with the Person type), we can declare it as a resource and any control can then use the data template to display the Person object.

<Grid>
    <Grid.Resources>       
        <!-- Define a DataTemplate for the Person type -->
        <DataTemplate DataType="{x:Type local:Person}">
            <TextBlock>
                <TextBlock Text="{Binding Name}" />
                (age:<TextBlock Text="{Binding Age}" />)
            </TextBlock>
        </DataTemplate>
    </Grid.Resources>
    
    <!-- The button's Content is bound to a Person type -->
    <Button Height="100" Width="100" Content="{Binding}" />
    
    <!-- The radiobutton's Content is bound to a Person type -–> 
    <RadioButton>
        <RadioButton.Content>
            <Binding />
        </RadioButton.Content>
    </RadioButton>

</Grid>
 

// And in code, do this, 
Person person = new Person() { Name = "John", Age = 32 }; 
this.DataContext = person; 
 

DataTemplates can be applied to both content controls and item controls. For content controls, the ContentTemplate property is set to a DataTemplate, and for item controls the ItemTemplate property is set to a DataTemplate. The following is a partial list of the controls that support DataTemplates,

   Content controls using the  
   ContentTemplate property
     Item controls using the

     ItemTemplate property
     Other properties that can

     be set to a DataTemplate
                        Button                   ComboBox               CellTemplate
                     CheckBox                 ContextMenu           HeaderTemplate
                         Label                      ListBox      ColumnHeaderTemplate
                 RadioButton                     ListView     SelectedContentTemplate
                  TabControl                    StatusBar    SelectionBoxItemTemplate
                     ToolTip                     ToolBar
                     Window                    TreeView  

 

There are three ways we can change the data template based on properties of the bound data object:

1. Data triggers:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication3"
    xmlns:System="clr-namespace:System;assembly=mscorlib"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <Grid.Resources>
           
            <!-- Define a named DataTemplate -->
            <DataTemplate DataType="{x:Type local:Person}">
                <!-- Custom visual tree of how the data will be presented -->
                <TextBlock>
                    <!-- Binding to different properties of the data object -->
                    <TextBlock Text="{Binding Name}" />
                    (age:<TextBlock x:Name="AgeTextBlock" Text="{Binding Age}" />)
                </TextBlock>
    
                <!-- Add a trigger to the template... –>
                <DataTemplate.Triggers>
                    <!-- ...that binds to the Age property of the data -->
                    <DataTrigger Binding="{Binding Path=Age}">
                        <!-- ...and if the Age is 32 –>
                        <DataTrigger.Value>
                            <System:Int32>32</System:Int32>
                        </DataTrigger.Value>
                        <!-- ...we’ll change the font color to red -->
                        <Setter TargetName="AgeTextBlock" Property="Foreground" Value="Red" />
                    </DataTrigger>
                </DataTemplate.Triggers>

            </DataTemplate>
        </Grid.Resources>
 
        <!-- The button's Content is bound to a Person type via code -->
        <Button Height="100" Width="100" Content="{Binding}" />
    </Grid>
</Window>
 
// And in code, do this,
Person person = new Person() { Name = "John", Age = 32 };
this.DataContext = person;

We could also use a trigger inside a Style to change the DataTemplate of the button,

<Grid>
    <Grid.Resources>
 
        <!-- Define a Style element... -->
        <Style TargetType="{x:Type Button}">
            <Style.Triggers>
                <!-- ...with a watch on the binding object’s Name property -->
                <DataTrigger Binding="{Binding Path=Name}" Value="John">
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <!-- define any custom UI here -->
                                <TextBlock Text="{Binding Path=Name}" Foreground="Red" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>

    </Grid.Resources>
    <!-- The button's Content is bound to a Person type via code -->
    <Button Height="100" Width="100" Content="{Binding}" />
</Grid>

 
// And in code, do this,
Person person = new Person() { Name = "John", Age = 32 };
this.DataContext = person;

or we could set the ContentTemplate to a named DataTemplate,

<Grid>
    <Grid.Resources>
 
        <!-- Define a named data template -->        
        <DataTemplate x:Key="FirstTemplate">
            <TextBlock Text="{Binding Path=Name}" Foreground="Red" />
        </DataTemplate>

        <!-- Define a Style element -->
        <Style TargetType="{x:Type Button}">
            <Style.Triggers>
                <!-- ...with a watch on the binding object’s Name property -->
                <DataTrigger Binding="{Binding Path=Name}" Value="John">
                    <!-- apply the named data template -->
                    <Setter Property="ContentTemplate" Value="{StaticResource FirstTemplate}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>

    </Grid.Resources>
    <!-- The button's Content is bound to a Person type via code -->
    <Button Height="100" Width="100" Content="{Binding}" />
</Grid>

 
// And in code, do this,
Person person = new Person() { Name = "John", Age = 32 };
this.DataContext = person;

2. Template Selector: Use the DataTemplateSelector class to choose the template you want to apply based on the bound object’s properties. In XAML,

<Grid>
    <Grid.Resources>
        <!-- Define a named DataTemplate -->
        <DataTemplate x:Key="firstTemplate">
            <TextBlock>
                <TextBlock Text="{Binding Name}" />
                (age:<TextBlock Text="{Binding Age}" />)
            </TextBlock>
        </DataTemplate>

        <!-- Define a named DataTemplate -->
        <DataTemplate x:Key="secondTemplate">
            <TextBlock>
                <TextBlock Text="{Binding Age}" />
                (name:<TextBlock Text="{Binding Name}" />)
            </TextBlock>
        </DataTemplate>
 
        <!-- Select the template based on the code behind -->
        <local:PersonTemplateSelector x:Key="templateSelector"
            FirstTemplate="{StaticResource firstTemplate}"
            SecondTemplate="{StaticResource secondTemplate}" />

    </Grid.Resources>
    
    <!-- Add the TemplateSelector property -->
    <Button Height="100" Width="100" Content="{Binding}" 
                         ContentTemplateSelector="{StaticResource templateSelector}" />
</Grid>
 

And in the code behind,

/// <summary>
/// The template selector class
/// </summary>
public class PersonTemplateSelector : DataTemplateSelector {
    // Define a set of properties that represent all the templates 
    // that this selector can return.
    public DataTemplate FirstTemplate { get; set; }
    public DataTemplate SecondTemplate { get; set; }

    public override DataTemplate SelectTemplate (object item, DependencyObject container) {

        if (item != null) {
            Person person = (Person)item;

            // Return a template based on the object’s properties,
            if (person.Age < 32)
                return FirstTemplate;
            else
                return SecondTemplate;
        }
        return null;
    }
}
 

3. Value Converter: Takes an incoming bound object property value and returns a changed UI property value. This is the topic of a future post.

Digg This

WPF Simplified Part 7: Routed Events

In plain old .NET, an event is based on the idea of pub-sub model, the publisher publishes an “event” and the subscriber is notified of the “event”. In WPF the idea is still the same, but updated to take into account the rather complex visual trees that often occur in WPF.

In WPF, when an event is raised on an element in the visual tree, the event will either travel up the visual tree (from the element till it reaches the root) or down the tree (from the root to the element). Which direction it will take depends on the event’s predefined “routing strategy”. What is meant by travel is that the same event will fire on the element’s parent and the parent’s parent and so on, if the event is moving up. That is, if you have a Button inside a Grid, and if a MouseRightButonDown event is raised on the Button, a MouseRightButtonDown event is then raised by the WPF framework on the Grid, because the MouseRightButonDown event moves up (“bubbles up”).

The advantage here is that a high-level visual element need not explicitly hook the same event on all of its descendants, it can hook into the event on itself and wait for the framework to raise the event on itself (just like the Grid hooks into it’s own MouseRightButonDown event and waits for the framework to raise a MouseRightButonDown event on the Grid). Descendants don’t need to explicitly notify parents when an event occurs.

Take this simple XAML,

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">

    <Grid>
        <Button Height="100" Width="100">
            <StackPanel Height="80" Width="80">
                <RadioButton Content="Click Me"/>
            </StackPanel>
        </Button>
    </Grid>
</Window>
 

Adding routed event handlers to this code,

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    MouseRightButtonDown="WindowMouseLeftButtonDownHandler"
    PreviewMouseRightButtonDown="WindowPreviewMouseLeftButtonDownHandler"
    Title="Window1" Height="300" Width="300">

    <Grid MouseRightButtonDown="GridMouseLeftButtonDownHandler"
        PreviewMouseRightButtonDown="GridPreviewMouseLeftButtonDownHandler">
 
        <Button Height="100" Width="100" 
            MouseRightButtonDown="ButtonMouseLeftButtonDownHandler"
            PreviewMouseRightButtonDown="ButtonPreviewMouseLeftButtonDownHandler">
 
            <StackPanel Height="80" Width="80" 
                MouseRightButtonDown="StackPanelMouseLeftButtonDownHandler"
                PreviewMouseRightButtonDown="StackPanelPreviewMouseLeftButtonDownHandler">
 
                <RadioButton Content="Click Me" 
                  MouseRightButtonDown="RadioButtonMouseLeftButtonDownHandler"
                  PreviewMouseRightButtonDown="RadioButtonPreviewMouseLeftButtonDownHandler"/>
            </StackPanel>
        </Button>
    </Grid>
</Window>
 

And in the code behind class add handlers that look like this,

private void WindowMouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e){
    Debug.WriteLine("Window caught event from " + e.OriginalSource.ToString());
}

private void GridMouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e){
    Debug.WriteLine("Grid caught event from " + e.OriginalSource.ToString());
}
 
// Add similar handlers for Button, StackPanel, and RadioButton

 
private void WindowPreviewMouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e){
    Debug.WriteLine("Window caught preview event from " + e.OriginalSource.ToString());
}

private void GridPreviewMouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e){
    Debug.WriteLine("Grid caught preview event from " + e.OriginalSource.ToString());
}
 
// Add similar handlers for Button, StackPanel, and RadioButton
 

Now if you run this example, and click on the radio button’s circle, you’ll see the output look like this,

Window caught preview event from Microsoft.Windows.Themes.BulletChrome
Grid caught preview event from Microsoft.Windows.Themes.BulletChrome
Button caught preview event from Microsoft.Windows.Themes.BulletChrome
StackPanel caught preview event from Microsoft.Windows.Themes.BulletChrome
RadioButton caught preview event from Microsoft.Windows.Themes.BulletChrome
RadioButton caught event from Microsoft.Windows.Themes.BulletChrome
StackPanel caught event from Microsoft.Windows.Themes.BulletChrome
Button caught event from Microsoft.Windows.Themes.BulletChrome
Grid caught event from Microsoft.Windows.Themes.BulletChrome
Window caught event from Microsoft.Windows.Themes.BulletChrome
 

As you can see, the PreviewMouseRightButtonDown routed event is a tunneling event, which means it starts from the root (Window) and goes to the source (RadioButon), whereas the MouseRightButtonDown routed event is a bubbling event, which means that it starts at the source and goes to the root of the tree. By convention tunneling events have a prefix of Preview. There is also a third “routing strategy”, called “direct”, which means that the event is only raised on the element and does not travel either up or down the visual tree.

Input events are usually in pairs, like the MouseRightButtonDown and the PreviewMouseRightButtonDown both occur on a single user input. First the tunneling event is raised and travels its route (to the element from the root) and then the bubbling event is raised and it travels its route.

If you want to stop the tunneling or bubbling at any point (say at the Button level), simply set the Handled to true.

private void ButtonMouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e){
    Debug.WriteLine("Button caught " + e.OriginalSource.ToString());
    e.Handled = true;
}
 

Notice in the output that Grid and Window don’t receive the bubbling MouseLeftButtonDown routed event.

To get all the routed events raised by a class, use the EventManager class,

var events = EventManager.GetRoutedEvents();
foreach (var routedEvent in events)
{
    EventManager.RegisterClassHandler(typeof(Window1), routedEvent, new RoutedEventHandler(handler));
}

internal static void handler(object sender, RoutedEventArgs e)
{
    Debug.WriteLine(e.OriginalSource + "=>" + e.RoutedEvent);
}
 

Writing a custom routed event is similar to writing a dependency property,

public static readonly RoutedEvent MyRoutedEvent = EventManager.RegisterRoutedEvent(
                               "MyRoutedEvent",            // Name of the custom routed event
                               RoutingStrategy.Bubble,     // The routing strategy
                               typeof(RoutedEventHandler), // Type of the event handler
                               typeof(MyControl));         // The type of the owner of this routed event 

// Provide CLR property wrapper for the routed event
public event RoutedEventHandler MyEvent
{
    add { AddHandler(MyRoutedEvent, value); }
    remove { RemoveHandler(MyRoutedEvent, value); }
}
 

The tunneling and bubbling of a routed event occurs when every element in the route exposes that event. But WPF supports tunneling and bubbling of routed events through elements that don’t even define that event – this is possible via attached properties. Attached events operate much like attached properties (and their use with tunneling or bubbling is very similar to using attached properties with property value inheritance), elements can handle events that are declared in a different element.

<Grid Button.Click="ButtonClickHandler">
    <Button Height="100" Width="100" Content="Some Text"/>
</Grid>
 

Grid doesn’t have a Click event, but WPF allows the Button.Click event to be raised on the Grid. The Grid can then handle this event in it’s handler “ButtonClickHandler”. Every routed event can be used as an attached event. We can also hook up the attached event in code,

<Grid x:Name="MyGrid">
    <Button Height="100" Width="100" Content="Some Text"/>
</Grid>
 
// In code...
this.MyGrid.AddHandler(Button.ClickEvent, new RoutedEventHandler(ButtonClickHandler));

Digg This

WPF Simplified Part 6: Attached Properties

Attached properties is a new construct in WPF that allows you to attach a property defined for an object to another  object when the second object has no definition or declaration of the property.

For example, consider the simple XAML,

    <Grid>
        <!-- A simple button -->
        <Button Height="100" Width="100" Content="Some Text" />
    </Grid>

 

Now say, we’d like to set the FontSize on the Button, this is simple,

    <Grid>
        <!-- Setting FontSize on the button -->
        <Button Height="100" Width="100" FontSize="20" Content="Some Text" />
    </Grid>
 

This was easy because Button has a dependency property called FontSize. Now say we have many buttons inside the Grid and we’d like to set all their FontSizes. This could be done with Styles, but for the purpose of this explanation we’ll do it via attached properties. The following gives a compile time error, because Grid does not have any Font related properties.

    <!-- ERROR: The property 'FontSize' was not found in type 'Grid' -->
    <Grid FontSize="20">
        <Button Height="100" Width="100" Content="Some Text" />
    </Grid>

 

But this works,

    <Grid TextBlock.FontSize="20">    
        <Button Height="100" Width="100" Content="Some Text" />
    </Grid>

 

How? FontSize is defined in WPF as an attached (read attachable) property of the TextBlock class. This attachable property defined in TextBlock (attached property provider) is attached to a completely different class (Grid) here. The end result is that Grid now effectively has a new property (FontSize) that the Button inherits. The code behind is a little bit more explanatory,

Grid grid = new Grid();
TextBlock.SetFontSize(grid, 20);
Button button = new Button() { Height = 100, Width = 100, Content = "Some Text" };
grid.Children.Add(button);

 

As you can see, the attached property is just a method call that associates an object with an otherwise-unrelated property. But why get the attached property FontSize from TextBlock? Why not from Control or Button or TextElement? We could have used TextElement in place of TextBlock in the examples above, the reason is that TextBlock and TextElement register FontSize as an attached property, while Control and Button do not.

Attached properties can be used as an extensibility mechanism,

// Define a class that derives from DependencyObject
public class MyClass : DependencyObject { }
 
// The attach the 'Tag' property to it
MyClass myClass = new MyClass();
myClass.SetValue(FrameworkElement.TagProperty, "My Data");

// Retrieve the 'Tag' property from it
string tagValue = (string)myClass.GetValue(FrameworkElement.TagProperty);
 

Declaring a custom attached property is similar to declaring a dependency property,

public static void SetMyAttachedProperty(DependencyObject target, int value)
{
    // Calls DependencyObject.SetValue on the passed in DependencyObject
    target.SetValue(MyAttachedProperty, value);
}

public static int GetMyAttachedProperty(DependencyObject target)
{
    // Calls the DependencyObject.GetValue
    return (int)target.GetValue(MyAttachedProperty);
}

public static readonly DependencyProperty MyAttachedProperty = DependencyProperty.RegisterAttached(
        "MyAttached",           // Name of the attached property
        typeof(int),            // Type of the attached property
        typeof(MyControl),      // Type of the owner
        new PropertyMetadata(0,
            new PropertyChangedCallback(OnValueChanged),  // Callback when the property changes
            new CoerceValueCallback(CoerceValue)),        // Callback when value coercion is required
        new ValidateValueCallback(IsValidValue));     // Callback for custom validation
 
// The validation callback
private static bool IsValidValue(object value)
{ /* Validate the set value */ } 
        
// The coercion callback
private static object CoerceValue(DependencyObject d, object value)
{ /* Adjust the value without throwing an exception */ } 
        
// The value changed callback 
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ /* Called every time the dependency property value changes */ }

 

Unlike dependency properties, attached properties are not wrapped in CLR properties. And in the case of attached properties the property values are stored on the element the consumes the property instead of the element that declares the property (which is where the value is stored for dependency properties).

Although setting attached properties via XAML require the presence of the static Set[AttachedProperty] method, in code we can call DependencyObject.SetValue directly. This means that we can use any dependency property as an attached property in procedural code, as we did above for the Tag dependency property.

WPF Simplified Part 5: Control Templates

Recall from my previous post, that elements in the WPF object tree which derive from Visual constitute the visual tree. The visual tree is thus all the WPF elements that come together to render a control’s (or a set of controls’) UI to the screen. Control templates are XAML declarations of the visual tree of a control.

Consider the simple case of a Button control,

<Grid>
        <!-- A simple button -->
        <Button Height="100" Width="100" Content="Some Text" />
    </Grid>

The Button has a default visual template, ie, a visual tree supplied by WPF. We want to override this implementation and provide our own custom UI for the button. To do this we’ll customize the Button control’s UI template (and thus it’s visual tree).

This is simple enough,

<Grid>    
    <Button Height="100" Width="100" Content="Some Text">

        <!-- Override the Button's UI template -->
        <Button.Template>
 
            <!-- Create a new ControlTemplate that targets a Button -->
            <ControlTemplate TargetType="{x:Type Button}">
 
                <!-- Override the default UI of the control with this custom UI -->
                <Grid>
                    <Ellipse Height="60" Width="60" Stroke="Red" Fill="LightGray" />
                    <!-- Take the control's Content (which is “Some Text”) and place here -->
                    <ContentPresenter Content="{TemplateBinding Content}" 
                           VerticalAlignment="Center" HorizontalAlignment="Center" />
                </Grid>
            </ControlTemplate>
        </Button.Template>
 
    </Button>
</Grid>
 

As you can see from the XAML above, we’ve overridden the button’s UI template and provided our own implementation which consists of a Grid, with an Ellipse inside it and the Button’s Content inside the Ellipse. Even the height and width of the button get overridden to the new values of 60.

image image
           Default WPF Button       After providing a custom UI 
      template for the button

 

Another way to do the same thing is similar to the way we define styles in WPF, like below,

<Grid>
    <Grid.Resources>
        <!-- Define a named template, with a target type -->
        <ControlTemplate x:Key="RoundButtonTemplate" TargetType="{x:Type Button}">
            <!-- Override the default UI of the control with this custom UI -->
            <Grid>
                <Ellipse Height="60" Width="60" Stroke="Red" Fill="LightGray" />
                <!-- Take the control's Content and place here -->
                <ContentPresenter Content="{TemplateBinding Content}" 
                        VerticalAlignment="Center" HorizontalAlignment="Center" />
            </Grid>
        </ControlTemplate>
    </Grid.Resources>

    <!—- Apply the 'RoundButtonTemplate' template to this button -->
    <Button Height="100" Width="100" Content="Some Text"
            Template="{DynamicResource RoundButtonTemplate}" />

    </Grid>

Note that although we have overridden the UI, the button’s functionality remains the same, all the same events are raised and all the same properties are still available.

You should use TemplateBinding to allow the control to specify property values as much as possible. For example,

<Grid>
    <Grid.Resources>
        <ControlTemplate x:Key="RoundButtonTemplate" TargetType="{x:Type Button}">
            <Grid>
                <!-- The Stroke and Fill properties are bound to the button's BorderBrush and Background -->
                <Ellipse Height="60" Width="60"
                         Stroke="{TemplateBinding BorderBrush}"
                         Fill="{TemplateBinding Background}" />

                  <ContentPresenter Content="{TemplateBinding Content}" 
                        VerticalAlignment="Center" HorizontalAlignment="Center" />
            </Grid>
        </ControlTemplate>
    </Grid.Resources>

    <!-- Apply the 'RoundButtonTemplate' template to this button -->
    <Button Height="100" Width="100" Content="Some Text"
            Template="{DynamicResource RoundButtonTemplate}"
            BorderBrush="Red" Background="LightGray" />

</Grid>

 

Control templates also work with styles and triggers. The syntax is straightforward,

<Grid>
    <Grid.Resources>

        <!-- First ControlTemplate definition for normal button state UI -->

        <ControlTemplate x:Key="RoundButtonTemplate" TargetType="{x:Type Button}">
            <Grid>
                <Ellipse Height="60" Width="60" x:Name="MyEllipse"
                         Stroke="{TemplateBinding BorderBrush}"
                         Fill="{TemplateBinding Background}" />
                <ContentPresenter Content="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center" />
            </Grid>
            <!-- Set a trigger to activate on mouseover -->
            <ControlTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter TargetName="MyEllipse" Property="Fill" Value="Blue" />
                </Trigger>
            </ControlTemplate.Triggers>
         </ControlTemplate>

        <!-- Second ControlTemplate definition for clicked button state UI -–> 
        <ControlTemplate x:Key="RoundButtonPressedTemplate" TargetType="{x:Type Button}">
            <Grid>
                <Ellipse Height="60" Width="60"
                     Stroke="{TemplateBinding BorderBrush}"
                     Fill="Red" />
                <ContentPresenter Content="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center" />
            </Grid>
        </ControlTemplate>

        <!-- Create a style definition for the button -->

        <Style x:Key="RoundButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="Template" Value="{DynamicResource RoundButtonTemplate}"/>
            <Style.Triggers>
                <Trigger Property="IsPressed" Value="True">
                    <Setter Property="Template" Value="{DynamicResource RoundButtonPressedTemplate}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Grid.Resources>

    <!-- Apply the 'RoundButtonStyle' to this button -->
    <Button Height="100" Width="100" Content="Text"
            Style="{DynamicResource RoundButtonStyle}"
            BorderBrush="Red" Background="LightGray" />

</Grid>

The WPF  content model allows the Button’s content to be a tree of elements. In our examples, we have set the button’s content to “Some Text” like this,

<Button Height="100" Width="100" Content="Some Text" />

But the button’s Content could be set to a tree, like this,

<Button Height="100" Width="100">
    <Button.Content>
        <Grid>
            <StackPanel>
                <TextBlock Text="Some Text"/>
            </StackPanel>
        </Grid>
    </Button.Content>
</Button>

WPF Simplified Part 4: Logical and Visual trees

To be clear at the outset, there is only one object tree in WPF, which is created based on the nested relationships of the elements in the markup (the markup may be built via XAML or code). The logical and visual tree are two different ways of looking at this object tree. They are both subsets of the object tree.

When you’re creating some UI in XAML, for example,

<!-- Note: no x:Class, load this XAML using XmlReader.Create -->

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Width="300" Height="300">
  <Grid>
        <Button Height="100" Width="100" Content="Text" />
    </Grid>
</Window>
 

You may intuitively think of a tree structure that looks like the one below (arrows point to parent). In reality, the object tree is far more complex and has many more elements. This simplified subset of the object tree, which most closely matches the elements declared in the XAML, is called the logical tree.

VisualTree
                                                      Logical Tree
 

The elements in the object tree that derive from the Visual or Visual3D base class form the visual tree subset of the object tree. The visual tree consists of all the drawing primitives and other controls that are used to implement the UI. As you can see below, a lot more elements are included in the visual tree, even though you didn’t explicitly declare them (in the XAML above).

VisualLogicalTree
                                                              Visual Tree

The way to read the tree diagram above is: the TextBlock’s visual parent’s type is ContentPresenter, while it’s logical parent is null. Both of ContentPresenter’s visual and logical parent’s types are ButtonChrome. ButtonChrome’s visual parent’s type is Button, while it’s logical parent is null, and so on.

The Button’s visual tree comprises of a TextBlock, a ContentPresenter, a ButtonChorme, and the Button itself. Similarly, the Window’s visual tree comprises of a Border, an AdornerDecorator, a ContentPresenter, a Grid, and the Button along with it’s visual tree.

Setting visual properties on a visual parent propagates to visual descendants. For example, setting opacity, the IsEnabled property, or setting a transform on the parent applies to the children as well.

But why have two subsets of the full object tree? The purpose is that WPF uses the logical tree to iterate over child objects, propagate certain notifications, and also for resource lookup, and the visual tree to render visual objects, and for routed events.

WPF gives us a way to traverse both the logical and the visual trees using the LogicalTreeHelper and the VisualTreeHelper respectively. Using the these are simple enough,

// Traverse up the visual tree 
private void TraverseVisualTreeUp(DependencyObject element)
{
    while (element != null)
    {
        Console.WriteLine(element.GetType());
        element = VisualTreeHelper.GetParent(element);
    }
}

and,

// Traverse up the logical tree
private void TraverseLogicalTreeUp(DependencyObject element)
{
    while (element != null)
    {
        Console.WriteLine(element.GetType());
        element = LogicalTreeHelper.GetParent(element);
    }
}
 

The FrameworkElement.Parent property also returns the logical parent. Elements in the object tree that derive from the ContentElement base class show up in the logical tree but not in the visual tree.

Another good article about logical/visual trees in WPF is here.

WPF Simplified Part 3: Dependency Properties

If you’ve been coding with C# for some time, you’re no doubt familiar with the CLR’s property construct. A typical CLR property looks like this,

// Private field
private int statusId;

// Public property that wraps the private field
public int StatusId {
    get {
        // Do stuff... 
        return this.statusId;
    }
    set {
        // Do stuff...
        this.statusId = value;
    }
}
 

Note how, for a regular CLR property, the property value is read directly from a private class member. WPF introduces a new construct called a dependency property, which can get it’s value from different sources and the actual value resolution occurs dynamically at runtime based on some precedence rules.

For example, a TextBox‘s Text property, which is a dependency property, can get its value from a style, a trigger, an animation, or locally (among others). Consider the following XAML,

<Grid.Resources>
    <Style x:Key="TextBoxBaseStyle" TargetType="TextBox">
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Text" Value="Trigger setter text" />
            </Trigger>
        </Style.Triggers>
        <Setter Property="Text" Value="Property setter text" />
    </Style>
</Grid.Resources>

<!-- The local value of the Text property is set here -->
<TextBox x:Name="MyTextBox" Height="30" Width="100" 
    Text="Local value text" Style="{StaticResource TextBoxBaseStyle}" />

 

If you look closely the TextBox‘s Text (dependency) property is set in three places. So which one applies? If you run this, the TextBox starts out with the text "Local value text", even though we’ve set a Style that says Text should be "Property setter text". Why? This is because when the WPF property system tried to resolve the Text value at runtime the local value has precedence over a Style setter.

The precedence rules are (higher to lower),

    1. Active animations

    2. Local value

    3. Style triggers

    4. Style setters

    5. Theme style

    6. Inheritance

    7. Default value (from dependency property metadata)

As you can see, a local value has higher precedence than a Style trigger or a Style setter. The Text dependency property depends on these different property value providers, each of which is checked in turn at runtime to get the property value. This also explains how triggers are able to revert back the value when they are done (see this post). When the trigger stops changing the value, the WPF property system looks down the list for the next value provider that can supply a value, the trigger changes are thus temporary.

Now say we want to remove the dependency property’s local value so that the Text value can come from elsewhere (like a Style). The local value can be erased by,

    this.MyTextBox.ClearValue(TextBox.TextProperty);
 
    // Note that these, will not remove the local value because
    // we're still setting a local value (to empty or null).
    //this.MyTextBox.Text = string.Empty;
    //this.MyTextBox.Text = null;

 

Once the local value is erased, you’ll see the Text changed to "Property setter text", and mouse over text to "Trigger setter text". Thus, the Text (dependency) property can get it’s value from different sources (local, style, trigger), and the runtime value depends a set of precedence rules.

// A dependency property is usually wrapped in a regular CLR property. This wrapper is not necessary,
// we can directly call GetValue/SetValue, but its useful if the dependency property needs to be set via XAML.
// Note that at runtime WPF calls the underlying GetValue/SetValue methods directly, bypassing the wrappers.
public int MyValue
{
    get { return (int)GetValue(MyValueProperty); }
    set { SetValue(MyValueProperty, value); }
}

// A dependency property definition
public static readonly DependencyProperty MyValueProperty =
            DependencyProperty.Register(
                "MyValue",              // Name of the dependency property
                typeof(int),            // Type of the dependency property
                typeof(MyClass),        // Type of the owner
                new PropertyMetadata(
                    0,                                            // The default value of the dependency property
                    new PropertyChangedCallback(OnValueChanged),  // Callback when the property changes
                    new CoerceValueCallback(CoerceValue)),        // Callback when value coercion is required
                new ValidateValueCallback(IsValidValue));     // Callback for custom validation

// The validation callback
private static bool IsValidValue(object value) { /* Validate the set value */ }
 
// The coercion callback
private static object CoerceValue(DependencyObject d, object value) { /* Adjust the value without throwing an exception */ }
 
// The value changed callback 
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ /* Called every time the dependency property value changes */ }

 

Dependency properties can also be read-only. A dependency property can only be created on a class which derives from the DependencyObject class, but since this class is very high up the WPF class heirarchy, this is not much of a concern. Some of the advantages of dependency properties are,

    1. XAML friendly: Dependency properties can be set in XAML (via a CLR property wrapper)

    2. Change notifications and validation: Get change notifications and validate property values by registering callbacks.

    3. Value inheritance: Dependency property values can be inherited from up the logical tree.

    4. Reduced memory: Since dependency properties are static, they save per-instance memory compared to regular CLR properties (which are based on per instance fields).

You can also listen in to dependency property changes,

// We want to hook into TextBox’s Text property... 
DependencyPropertyDescriptor textDescr = 
                  DependencyPropertyDescriptor.FromProperty(TextBox.TextProperty, typeof(TextBox));
 
if (textDescr != null)
{
    textDescr.AddValueChanged(myTextBox, delegate
        {
            // Add your property changed logic here...
        });
} 


Digg This

WPF Simplified Part 2: Triggers

In Part I of this series, I discussed Styles, Triggers are similar to styles, the difference being Styles are applied to controls unconditionally whereas Triggers as based on one or more conditions.

There are four types of Triggers,

        1. Property triggers are invoked when a dependency property changes.

        2. Event triggers are invoked when a routed event is raised.

        3. Data triggers are invoked when a regular .NET property changes.

        4. Multi triggers (and MultiData triggers) represent a logical AND relationship between triggers.

 

1. Property triggers: A property trigger executes a collection of Setters when a specific dependency property changes to a specific value. When the dependency property no longer has the specified value, the Setters are undone with no additional code required.

For example, if we want to change the background of a TextBox when it is moused-over we can simply do this,

<Grid>
    <Grid.Resources>
        <!-- Define a named style... -->
        <Style x:Key="TextBoxBaseStyle">
            <!-- with a triggers collection... -->
            <Style.Triggers>
                <!-- and a property trigger – when the textbox is moused-over... -->
                <Trigger Property="TextBox.IsMouseOver" Value="True">
                    <!-- set the background (color) property  to LightGray -->
                    <Setter Property="TextBox.Background" Value="LightGray" />
                </Trigger>
                <!-- Note that we don’t have to define a property trigger for when 
                IsMouseOver is false, this is automatically handled by the framework -->
            </Style.Triggers>
            <!-- These Setters are not triggered by a dependency property --> 
            <Setter Property="Control.Height" Value="30" />
            <Setter Property="Control.Width" Value="60" />
        </Style>
    </Grid.Resources>

    <!-- Apply the style to this textbox -->
    <TextBox Style="{StaticResource TextBoxBaseStyle}" />
</Grid>

2. Event triggers: An event trigger executes a collection of Setters when a routed event is raised. Starting an animation is the most common action for an event trigger.

<Grid>
    <Grid.Resources>
        <!-- Define a named style... -->
        <Style x:Key="TextBoxBaseStyle">
            <!-- with a triggers collection -->
            <Style.Triggers>
                <!-- Define an event trigger that fires on mouseenter... -->
                <EventTrigger RoutedEvent="TextBox.MouseEnter">
                    <BeginStoryboard Storyboard="{StaticResource BeginAnimation}" />
                </EventTrigger>
                <!-- and another event trigger which fires on mouseleave -->
                <EventTrigger RoutedEvent="TextBox.MouseLeave">
                    <BeginStoryboard Storyboard="{StaticResource EndAnimation}" />
                </EventTrigger>
            </Style.Triggers>
            <!-- These Setters are not triggered by an event –>
            <Setter Property="Control.Height" Value="30" />
            <Setter Property="Control.Width" Value="60" />
        </Style>
        <Storyboard x:Key="BeginAnimation">
            <!-- In this animation we'll change the background's color property -->
            <ColorAnimation 
                Storyboard.TargetProperty="(TextBox.Background).(SolidColorBrush.Color)" 
                To="LightGray" Duration="0:0:0.1"/>
        </Storyboard>
        <Storyboard x:Key="EndAnimation">
            <!-- In this animation we'll change the background's color property -->
            <ColorAnimation 
                Storyboard.TargetProperty="(TextBox.Background).(SolidColorBrush.Color)" 
                To="White" Duration="0:0:0.1"/>
        </Storyboard>
    </Grid.Resources>

    <!-- Apply the style to this textbox -->
    <TextBox Style="{StaticResource TextBoxBaseStyle}" />
</Grid>
 

3. Data Triggers: Similar to Property triggers except that any .NET property can invoke these. Note that the Setters are still restricted to setting dependency properties.

<Grid>
    <Grid.Resources>
        <!-- Define a named style... -->
        <Style x:Key="TextBoxBaseStyle">
            <!-- with a triggers collection... -->
            <Style.Triggers>
                <!-- Define a data trigger that fires when the Role property is set to Admin -->
                <DataTrigger Binding="{Binding Path=Role}" Value="Admin">
                    <Setter Property="TextBox.Foreground" Value="Red" />
                </DataTrigger>
            </Style.Triggers>
            <Setter Property="Control.Height" Value="30" />
            <Setter Property="Control.Width" Value="60" />
        </Style>
    </Grid.Resources>

    <!-- Apply the style to this textbox -->
    <TextBox x:Name="MyTextBox" Style="{StaticResource TextBoxBaseStyle}" />

</Grid>

and in the code behind,

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
 
        // When the role is Admin the foreground text will be red colored.
        this.MyTextBox.DataContext = new Person { Role = "Admin" };

        // Otherwise the foreground text color is default (black)
        //this.MyTextBox.DataContext = new Person { Role = "User" };

    }
}

public class Person
{
    public string Role { get; set; }
}
 

4. Multi triggers (and MultiData triggers): Since Style.Triggers can contain multiple triggers, if you have the exact same Setters then a logical OR relationship forms between them, for example,

<Style.Triggers>
    <Trigger Property="TextBox.IsMouseOver" Value="True">
        <Setter Property="TextBox.Background" Value="Red" />
    </Trigger>
    <!-- OR --> 
    <Trigger Property="TextBox.IsFocused" Value="True">
        <Setter Property="TextBox.Background" Value="Red" />
    </Trigger>
</Style.Triggers>

 

This means that “If IsMouseOver is true OR if IsFocused is true – set Background color to Red”.

Correspondingly, a logical AND relationship is expressed using a Multi trigger (or MultiData trigger).

<Style.Triggers>
    <MultiTrigger>
        <MultiTrigger.Conditions>
            <Condition Property="TextBox.IsMouseOver" Value="True" />
            <!-- AND -->
            <Condition Property="TextBox.IsFocused" Value="True" />
        </MultiTrigger.Conditions>
        <Setter Property="TextBox.Background" Value="Red" />
     </MultiTrigger>
</Style.Triggers>

Follow

Get every new post delivered to your Inbox.