WPF Reusability Factor – UI with a “Style

In my previous post I discussed about User Controls and Custom Controls with the help of examples and mentioned about the customization using Styles. In today's post I’ll focus my discussion on Styles in WPF in a fairly detailed manner. [digg]

“Style is primarily a matter of instinct.”
Bill Blass

“Only great minds afford a simple style.”
Stendal

Styles are around us for a long time, like from the era of Word Star or Ventura Publishers or so, where you define some styles in a style sheet and can be reused/applied  for Headings, Body Texts etc. It was further got popularity in the web development in the form of CSS style sheets and is used the same way in order to achieve a unified look & feel of the user interfaces. WPF has further this concept in defining not only just the appearance like background, outlining, setting fonts etc. but also went one step further, in redefining/customizing the control’s visual tree with the aid of Control Templates and also the behavior/actions with the help of Triggers. We can attach Property Triggers, Data Triggers as well Event Triggers, that we’ll see in upcoming example scenarios. A Style is actually an aggregate, that is basically composed of a set of properties that you set while defining it and actions taken when some property got changed. Styles in WPF supports templates, data bindings, command bindings and also animations. Styles can be defined in the local scope of itself as well as scope of the control or on the global level resources. When you considering global scope consider packaging it into a Theme. A Theme is actually Resource Dictionary, that provides a common repository for the resources. If a user want to give a completely different look and feel of an application, the theme provides a convenient way of providing any sort of customization.  

Using Style in WPF

Lets see some practical scenarios of applying the styles:

Scenario#1: Defining a simple style for a control like button.

We are looking for a button that has gray background and white foreground, without using a style, we can achieve like this :

  • <Button x:Name="TestButton" Width="100" Margin="2"
                  Background="Silver"
                  Foreground="White"
                  Content="Test Button">
    </Button>

    Here is the corresponding output for it: 
    image

 

Now if we need the same type of styles for three buttons and only difference is the content should be changed. We’ll achieve this with the help of defining a style, and here is how we achieve this:

* Define a named style in the resource section as refer it using key word {StaticResouce key }, here is how it looks like:

Code Snippet
  1. <Window x:Class="Shams.WpfTestApp.Window1"
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    Title="Test Window" Height="151" Width="371">
  5.     <Window.Resources>
  6.         <Style x:Key="MyButtonStyleKey">
  7.             <Setter Property="Control.Background" Value="Silver" />
  8.             <Setter Property="Control.Foreground" Value="White" />
  9.             <Setter Property="Control.Width" Value="100" />
  10.             <Setter Property="Control.Margin" Value="2" />
  11.             <Setter Property="Control.FontSize" Value="16" />
  12.         </Style>
  13.     </Window.Resources>
  14.     <Grid Margin="3">
  15.         <Grid.RowDefinitions>
  16.             <RowDefinition Height="31"/>
  17.             <RowDefinition />
  18.         </Grid.RowDefinitions>
  19.         <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
  20.             <Button x:Name="Button1" Width="100" Content="Start" Style="{StaticResource MyButtonStyleKey}"/>
  21.             <Button x:Name="Button2" Width="100" Content="Pause" Style="{StaticResource MyButtonStyleKey}"/>
  22.             <Button x:Name="Button3" Width="100" Content="Stop" Style="{StaticResource MyButtonStyleKey}"/>
  23.         </StackPanel>
  24.     </Grid>
  25. </Window>

If you look at the above XAML, you’ll notice that the Properties for the Control is defined once in the Style and Reused in all three buttons shown buttons with a Style set to the Key MyButtonStyle, This type of Style is known as Named Style. In this way all the buttons have the same look-N-feel. Here is the corresponding output for it.
Note: The Control-prefix is used above instead of Button, so that it can be reused for other controls too.

image Figure-1

Scenario#2: Defining a simple style by using TargetType for the control

If we want to define a Style for a specific Control Type, we set the attribute TargetType of the Style. The above example can be rewritten as follows by changing the style as below:

  • <Style x:Key="MyButtonStyleKey2" TargetType="{x:Type Button}">
        <Setter Property="Background" Value="Silver" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="Width" Value="100" />
        <Setter Property="Margin" Value="2" />
        <Setter Property="FontSize" Value="16" />
    </Style>

The output will obviously remain the same, the only difference is this Style is no more generic, rather its more concrete and can only be applied in context to the “Button” Style.

Scenario#3: Defining a default style for a control like Button.

If you want the Style should be set as default style, just remove the x:Key portion of the Style, this way this style will be set for all the Buttons. You can override this behaviour by explicitly providing some style for a button.

  • <Style TargetType="{x:Type Button}">
        <Setter Property="Background" Value="Silver" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="Width" Value="100" />
        <Setter Property="Margin" Value="2" />
        <Setter Property="FontSize" Value="16" />
    </Style>

Here is how I am using the default Style with One button has a different style defined in the resource, here is the XAML for it.

Code Snippet
  1. <Window x:Class="Shams.WpfTestApp.Window1"
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    Title="Test Window" Height="151" Width="371">
  5.     <Window.Resources>
  6.         <Style x:Key="MyButtonStyleKey">
  7.             <Setter Property="Control.Background" Value="Silver" />
  8.             <Setter Property="Control.Foreground" Value="White" />
  9.             <Setter Property="Control.Width" Value="100" />
  10.             <Setter Property="Control.Margin" Value="2" />
  11.             <Setter Property="Control.FontSize" Value="16" />
  12.         </Style>
  13.         <Style x:Key="MyButtonStyleKey2" TargetType="{x:Type Button}">
  14.             <Setter Property="Background" Value="Red" />
  15.             <Setter Property="Foreground" Value="White" />
  16.             <Setter Property="Width" Value="100" />
  17.             <Setter Property="Margin" Value="2" />
  18.             <Setter Property="FontSize" Value="16" />
  19.         </Style>
  20.         <!--Default Button Style-->
  21.         <Style TargetType="{x:Type Button}">
  22.             <Setter Property="Background" Value="Gray" />
  23.             <Setter Property="Foreground" Value="White" />
  24.             <Setter Property="Width" Value="100" />
  25.             <Setter Property="Margin" Value="2" />
  26.             <Setter Property="FontSize" Value="16" />
  27.                 </Style>
  28.     </Window.Resources>
  29.     <Grid Margin="3">
  30.         <Grid.RowDefinitions>
  31.             <RowDefinition Height="31"/>
  32.             <RowDefinition />
  33.         </Grid.RowDefinitions>
  34.         <!--<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
  35.             <Button x:Name="Button1" Width="100" Content="Start" Style="{StaticResource MyButtonStyleKey}"/>
  36.             <Button x:Name="Button2" Width="100" Content="Pause" Style="{StaticResource MyButtonStyleKey}"/>
  37.             <Button x:Name="Button3" Width="100" Content="Stop" Style="{StaticResource MyButtonStyleKey}"/>
  38.         </StackPanel>-->
  39.         <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
  40.             <Button x:Name="Button1" Width="100" Content="Start" />
  41.             <Button x:Name="Button2" Width="100" Content="Pause" Style="{StaticResource MyButtonStyleKey2}"/>
  42.             <Button x:Name="Button3" Width="100" Content="Stop" />
  43.         </StackPanel>
  44.  
  45.     </Grid>
  46. </Window>

Here is the corresponding output for it.

image Figure-2

Scenario#4: Using Inheritance in defining a Style of the Control

Styles can be inherited with the use of “Based On” key word in the style. So what you do you create some base style and you can create derived ones that may have some other properties to be set. And when you apply the derived style, both the styles based + derived will be applied.

  •         <Style x:Key="MyButtonBaseStyleKey" TargetType="{x:Type Button}">
                <Setter Property="Background" Value="Green" />
                <Setter Property="Foreground" Value="White" />
                <Setter Property="FontSize" Value="16" />
            </Style>
            <Style x:Key="MyButtonDerivedStyleKey" BasedOn="{StaticResource MyButtonBaseStyleKey}" >
                <Setter Property="Button.Margin" Value="2" />
                <Setter Property="Button.FontSize" Value="16" />
           </Style>
           
    <!— Or when you are using TargetType you dont need an explicit settings, like Button.Margin or Button.FontSize etc. — >
           
           <Style x:Key="MyButtonDerivedStyleKey2" BasedOn="{StaticResource MyButtonBaseStyleKey}" TargetType="{x:Type Button}">
                <Setter Property="Margin" Value="2" />
                <Setter Property="FontSize" Value="16" />
            </Style>

And here is its usage:
<Button x:Name="Button2" Width="100" Content="Pause" Style="{StaticResource MyButtonDerivedStyleKey2}"/>

And here is the corresponding output:

image Figure-3

Scenario#5a: Attaching behavior to the Style – the EventSetters

Event Setters are setup in the style as part of the other Style.Setters. They invoke the specified event handlers defined in response to only to the routed events, like MouseEnter, MouseLeave etc. And it will be applied to all the elements of the Style. Take a look at the following XAML and the corresponding code-behind for it:
   1: <Window x:Class="WpfTestApp2.MainWindow"
   2:         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:         Title="MainWindow" Height="135" Width="470">
   5:         <Window.Resources>
   6:         <Style TargetType="{x:Type TextBlock}">
   7:             <Style.Setters>
   8:                 <Setter Property="Foreground" Value="Black" />
   9:                 <Setter Property="HorizontalAlignment" Value="Center" />
  10:                 <Setter Property="VerticalAlignment" Value="Center" />
  11:                 <Setter Property="FontFamily" Value="Segoe Black" />
  12:                 <Setter Property="FontSize" Value="32pt" />
  13:                 <EventSetter Event="MouseEnter" Handler="OnMouseEnter"/>
  14:                 <EventSetter Event="MouseLeave" Handler="OnMouseLeave"/>
  15:             </Style.Setters>
  16:             <!--<Style.Triggers>
  17:                 <Trigger Property="IsMouseOver"  Value="true">
  18:                     <Setter Property = "Foreground" Value="Red"/>
  19:                 </Trigger>
  20:             </Style.Triggers>-->
  21:         </Style>
  22:  
  23:     </Window.Resources>
  24:     <Grid>
  25:         <TextBlock x:Name="Tb1" Text="Hello World!"/>
  26:     </Grid>
  27: </Window>

Figure-1 – XAML

   1:      public partial class MainWindow : Window
   2:      {
   3:          Brush prevForeGround = null;
   4:   
   5:          public MainWindow()
   6:          {
   7:              InitializeComponent();
   8:   
   9:              prevForeGround = Tb1.Foreground;
  10:          }
  11:          
  12:          private void OnMouseEnter(object sender, MouseEventArgs e)
  13:          {
  14:              Tb1.Foreground = Brushes.Red;
  15:          }
  16:   
  17:          private void OnMouseLeave(object sender, MouseEventArgs e)
  18:          {
  19:              Tb1.Foreground = prevForeGround;
  20:          }
  21:      }

Figure-2 – C# code

Figure-3 - Output

Take a look at lines 13-14, under XAML above where we setup the MouseEnter and MouseLeave Events and defined the handlers for them that are handled in the C# code-behind shown above. What that means is this strategy of capturing events requires code behind, in other-words you need loose binary XAML (BAML) files. What that means, it means, the Event setters cannot be used in a style that is contained in a theme resource dictionary. This is because a theme resource dictionary at run time is often loose binary XAML (BAML) files, and does not have any scope defined where accompanying code-behind that defines the handlers can exist.

You can read more about this at EventSetter Class at msdn.

There is much better approach that is actually explained in the next section. The same effect can be very easily achieved with this one, no code behind required:

   1: <Window x:Class="WpfTestApp2.MainWindow"
   2:         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:         Title="MainWindow" Height="135" Width="470">
   5:         <Window.Resources>
   6:         <Style TargetType="{x:Type TextBlock}">
   7:             <Style.Setters>
   8:                 <Setter Property="Foreground" Value="Black" />
   9:                 <Setter Property="HorizontalAlignment" Value="Center" />
  10:                 <Setter Property="VerticalAlignment" Value="Center" />
  11:                 <Setter Property="FontFamily" Value="Segoe Black" />
  12:                 <Setter Property="FontSize" Value="32pt" />
  13:             </Style.Setters>
  14:             <Style.Triggers>
  15:                 <Trigger Property="IsMouseOver"  Value="true">
  16:                     <Setter Property = "Foreground" Value="Red"/>
  17:                 </Trigger>
  18:             </Style.Triggers>
  19:         </Style>
  20:  
  21:     </Window.Resources>
  22:     <Grid>
  23:         <TextBlock x:Name="Tb1" Text="Hello World!"/>
  24:     </Grid>
  25: </Window>

All I did was taken the event-setters away and introduced the triggers lines-14-18. that is explained in the next section. See, no Code-Behind required. : )

Scenario#5b: Attaching behavior to the Style – the Property Triggers

If we want some conditions to be imposed / applied while setting up the properties, we use Property Triggers. These triggers have effects on the control’s properties when certain condition is met. Like, when the mouse is over, fires a Trigger that sets the property of the control’s background to something different, like, if it was Silver before the Trigger condition is met, make it Red and when the condition is over, WPF reverts the control back to its previous/normal state.

Let’s take a look at some code examples.

Code Snippet
  1.    <Window.Resources>
  2.     <!--Default Button Style-->
  3.     <Style TargetType="{x:Type Button}">
  4.         <Setter Property="Background" Value="Silver" />
  5.         <Setter Property="Foreground" Value="White" />
  6.         <Setter Property="Width" Value="100" />
  7.         <Setter Property="Margin" Value="2" />
  8.         <Setter Property="FontSize" Value="16" />
  9.         <Style.Triggers>
  10.             <Trigger Property="IsMouseOver" Value="True">
  11.                 <Setter Property="Background" Value="Red" />
  12.             </Trigger>
  13.         </Style.Triggers>
  14.     </Style>
  15. </Window.Resources>

And here is the corresponding output when some one hover on the Start Button:

image

You can have as many triggers as you want in a style and also each trigger can be composed of any number of Setter Properties. Like in our next example, on MouseOver Trigger we’ll set two more properties, Border and Border Brush properties.

We are not limited in the number of triggers included in a Style, and each trigger can have as many Setter elements as we want, like in the next sample.

Code Snippet
  1.    <Window.Resources>
  2.     <!--Default Button Style-->
  3.     <Style TargetType="{x:Type Button}">
  4.         <Setter Property="Background" Value="Silver" />
  5.         <Setter Property="Foreground" Value="White" />
  6.         <Setter Property="Width" Value="100" />
  7.         <Setter Property="Margin" Value="2" />
  8.         <Setter Property="FontSize" Value="16" />
  9.         <Style.Triggers>
  10.             <Trigger Property="IsMouseOver" Value="True">
  11.                 <Setter Property="Background" Value="Red" />
  12.                 <Setter Property="BorderBrush" Value="Yellow" />
  13.                 <Setter Property="BorderThickness" Value="2" />
  14.             </Trigger>
  15.             <Trigger Property="IsEnabled" Value="False">
  16.                 <Setter Property="Background" Value="Gray" />
  17.                 <Setter Property="Foreground" Value="Silver" />
  18.             </Trigger>
  19.         </Style.Triggers>
  20.     </Style>
  21. </Window.Resources>

And here is the corresponding output for it, when we hover on the Start Button and Stop button is disabled:

image

Scenario #5c: Attaching behavior to the Style – the Multiple Property Triggers

If we have more than one condition need to be met/true when trigger action takes place, the Multiple Property Triggers come into the picture. Like an obvious condition in our previous case is, when the mouse is over the Border should only be visible when it is Enabled too. Here’s how we use multi-condition trigger in XAML.

Code Snippet
  1.    <Window.Resources>
  2.     <!--Default Button Style-->
  3.     <Style TargetType="{x:Type Button}">
  4.         <Setter Property="Background" Value="Silver" />
  5.         <Setter Property="Foreground" Value="White" />
  6.         <Setter Property="Width" Value="100" />
  7.         <Setter Property="Margin" Value="2" />
  8.         <Setter Property="FontSize" Value="16" />
  9.         <Style.Triggers>
  10.             <!--<Trigger Property="IsMouseOver" Value="True">
  11.                 <Setter Property="Background" Value="Red" />
  12.                 <Setter Property="BorderBrush" Value="Yellow" />
  13.                 <Setter Property="BorderThickness" Value="2" />
  14.             </Trigger>-->
  15.  
  16.             <MultiTrigger>
  17.                 <MultiTrigger.Conditions>
  18.                     <Condition Property="IsMouseOver" Value="True"/>
  19.                     <Condition Property="IsEnabled" Value="True" />
  20.                 </MultiTrigger.Conditions>
  21.                 <Setter Property="Background" Value="Red" />
  22.                 <Setter Property="BorderBrush" Value="Yellow" />
  23.                 <Setter Property="BorderThickness" Value="2" />
  24.             </MultiTrigger>
  25.             <Trigger Property="IsEnabled" Value="False">
  26.                 <Setter Property="Background" Value="Gray" />
  27.                 <Setter Property="Foreground" Value="Silver" />
  28.             </Trigger>
  29.         </Style.Triggers>
  30.     </Style>
  31. </Window.Resources>

Scenario #5d: Attaching behavior to the Style – the Data Triggers

The Property Triggers we seen before are all related to dependency properties of the controls while the Data Triggers are related to some sort of data associated with it. Say you have a collection of data, and you are populating a data grid view that is data-bounded to this collection. Now you need to fire a Trigger on certain value of the data meets a criteria that you need to highlight it. The Data Trigger comes into Rescue, that means it is associated to some business data, that's why Data Triggers. Data Triggers are mostly used with Data Templates, but they can also be used with Styles.

Lets take a concrete example. We have created a “PersonInfo” data class that actually looks like this:

Code Snippet
  1. /// <summary>
  2. /// This class represents the Person's info
  3. /// </summary>
  4. [Serializable]   
  5. public class PersonInfo
  6. {
  7.     public string FirstName { get; set; }
  8.     public string LastName { get; set; }
  9.     public string Address { get; set; }
  10.  
  11.     public PersonInfo() :
  12.         this("Shams", "Mukhtar", "San Diego") { }
  13.     
  14.     public PersonInfo(string firstName,
  15.         string lastName,
  16.         string address)
  17.     {
  18.         this.FirstName = firstName;
  19.         this.LastName = lastName;
  20.         this.Address = address;
  21.     }
  22. }

And a Collection class, here is how it looks:

Code Snippet
  1. /// <summary>
  2. /// This class represents a collection/data set of Person's Info.
  3. /// </summary>
  4. [Serializable]
  5. public class PersonInfoCollection :
  6.     ObservableCollection<PersonInfo>
  7. {
  8.     public PersonInfoCollection()
  9.         : base()
  10.     {
  11.         // Default recods creation, for demonstration purpose only.
  12.         base.Add(new PersonInfo("Shams",
  13.             "Mukhtar",
  14.             "San Diego"));
  15.         base.Add(new PersonInfo("Aliya",
  16.             "Shams",
  17.             "San Diego"));
  18.         base.Add(new PersonInfo("Maha",
  19.             "Shams",
  20.             "San Diego"));
  21.         base.Add(new PersonInfo("Momal",
  22.             "Shams",
  23.             "San Diego"));
  24.     }
  25. }

Now we want to display this data in a ListView’s, GridView control, with a condition that when the Row/Item meets a criteria, like in our case if it sees “Momal” as a first name highlight it by setting the Row’s background to Gray. Here is how we achieve this using the XAML View, listed below:

Code Snippet
  1. <Window x:Class="Shams.WpfTestApp.Window3"
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    xmlns:local="clr-namespace:Shams.WpfTestApp"
  5.    Title="Window3" Height="164" Width="298">
  6.     <Window.Resources>
  7.         <local:PersonInfoCollection x:Key="personInfoCollection" x:Name="personInfoCollection" />
  8.     </Window.Resources>
  9.     <Grid>
  10.         <ListView ItemsSource="{Binding Source={StaticResource ResourceKey=personInfoCollection}}">
  11.             <ListView.Resources>
  12.                 <Style TargetType="{x:Type ListViewItem}">
  13.                     <Style.Triggers>
  14.                         <DataTrigger Binding="{Binding Path=FirstName}" Value="Momal">
  15.                             <Setter Property="Background" Value="Silver" />
  16.                         </DataTrigger>
  17.                         <Trigger Property="IsMouseOver" Value="True">
  18.                             <Setter Property="Foreground" Value="Red" />
  19.                         </Trigger>
  20.                     </Style.Triggers>
  21.                 </Style>
  22.             </ListView.Resources>
  23.             <ListView.View>
  24.                 <GridView>
  25.                     <GridViewColumn Header="First Name" DisplayMemberBinding="{Binding Path=FirstName}" />
  26.                     <GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding Path=LastName}" />
  27.                     <GridViewColumn Header="Address" DisplayMemberBinding="{Binding Path=Address}" />
  28.                 </GridView>
  29.             </ListView.View>
  30.         </ListView>
  31.     </Grid>
  32. </Window>

Line 7 above creates an instance of the PersonInfoCollection and we setting the ListViews ItemsSource to it @ line#10. We also created a local Style only visible to ListView for the ListView Item (or Grid View Row) , where we defined two types of Triggers. Take a look closely @ line#14-16, where we defined the DataTrigger, that when ever sees the FirstName == Momal, will fire a Trigger and sets the Background to Silver. And a regular Trigger, that fires when you are hovering over the row. Here is the output for it:

image

Scenario #5e: Attaching behavior to the Style – the Event Triggers

Last but not least, the Event Triggers. Event Triggers are raised on some Events instead of some Property change in case of Property Trigger and data change in case of Data Trigger. These events could be mouse events like mouse-click events or mouse-over events. When ever these events are raised. Event Triggers are fired in conjunction with Animations. Here is an example for this concept.

Code Snippet
  1. <Window x:Class="Shams.WpfTestApp.Window5"
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    Title="Window5" Height="200" Width="411">
  5.     <Window.Resources>
  6.         <Style TargetType="TextBlock">
  7.             <Setter Property="FontSize" Value="32" />
  8.             <Style.Triggers>
  9.                 <EventTrigger RoutedEvent="MouseEnter">
  10.                     <BeginStoryboard>
  11.                         <Storyboard>
  12.                             <DoubleAnimation To="72" Duration="0:0:1.0"
  13.                                             AccelerationRatio="0.15"
  14.                                             DecelerationRatio="0.25"
  15.                                             Storyboard.TargetProperty="(FontSize)" />
  16.                         </Storyboard>
  17.                     </BeginStoryboard>
  18.                 </EventTrigger>
  19.                 <EventTrigger RoutedEvent="MouseLeave">
  20.                     <BeginStoryboard>
  21.                         <Storyboard>
  22.                                                                              <DoubleAnimation Duration="0:0:1.0"
  23.                                             AccelerationRatio="0.15"
  24.                                             DecelerationRatio="0.25"
  25.                                             Storyboard.TargetProperty="FontSize" />
  26.                         </Storyboard>
  27.                     </BeginStoryboard>
  28.                 </EventTrigger>
  29.             </Style.Triggers>
  30.         </Style>
  31.  
  32.     </Window.Resources>
  33.     <Grid>
  34.         <TextBlock Text="Hello World!" HorizontalAlignment="Center" VerticalAlignment="Center"/>
  35.             </Grid>
  36. </Window>

In the above example, We have defined a default Style of a TextBlock Control, that has default Font Size of 32 points . The Event Trigger is defined at Line# 9, that actually is fired when MouseEnter Routed Event is Raised (line# 10). What happens here, Font Size is increasing to 72 points and the other EventTrigger is fired When the MouseLeave Routed Event is raised (line# 19) and goes back smoothly to its default state. Here is the output for it:

See the Transition for yourself in this Silverlight Replica – Enter the mouse and you’ll see the Effect:

Get Microsoft Silverlight

Transition From
 image

To
 image

Scenario #6: Redefining the Visual Tree using Style – the Control Template

WPF has an inherent support for compositing of controls with the help of Templates. That means you can declaratively redefine/customize the complete visual tree of the Control it is composed of. In this particular example, we’ll redefine the button’s appearance to a circular look, along with transparency:

Code Snippet
  1. <Window x:Class="Shams.WpfTestApp.Window6"
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    Title="Window6"
  5.    Height="400"
  6.    Width="400"
  7.     
  8.    AllowsTransparency="True"
  9.    WindowStyle="None"
  10.    Background="Transparent"
  11.    PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown">
  12.    
  13.     <Window.ContextMenu>
  14.         <ContextMenu x:Name="WindowContextMenu">
  15.             <MenuItem Header="Exit" Click="MenuItemExit_Click" />
  16.         </ContextMenu>
  17.     </Window.ContextMenu>
  18.     <Window.Resources>
  19.  
  20.         <!--<Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">-->
  21.         <Style TargetType="{x:Type Button}">
  22.             <Style.Resources>
  23.                 <SolidColorBrush x:Key="solidColorBrush" Color="Green" />
  24.                 <LinearGradientBrush x:Key="strokeBrush">
  25.                     <GradientStop Offset="0" Color="White" />
  26.                     <GradientStop Offset="1" Color="Black" />
  27.                 </LinearGradientBrush>
  28.                 <LinearGradientBrush x:Key="fillBrush">
  29.                     <GradientStop Offset="0" Color="White" />
  30.                     <GradientStop Offset="1" Color="Silver" />
  31.                 </LinearGradientBrush>
  32.             </Style.Resources>
  33.  
  34.             <Setter Property="FontSize" Value="42" />
  35.             <Setter Property="Opacity" Value="0.77" />
  36.             <!--<Setter Property="Background" Value="{StaticResource solidColorBrush}" />-->
  37.  
  38.             <!--Redefining the visual tree of the button-->
  39.             <Setter Property="Template">
  40.                 <Setter.Value>
  41.                     <ControlTemplate TargetType="{x:Type Button}">
  42.                         <Grid>
  43.                             <Ellipse StrokeThickness="5"
  44.                                     Stroke="{StaticResource strokeBrush}"
  45.                                     Fill="{StaticResource fillBrush}" />
  46.                             <ContentPresenter
  47.                                Margin="3"
  48.                                HorizontalAlignment="Center"
  49.                                VerticalAlignment="Center" />
  50.                         </Grid>
  51.                     </ControlTemplate>
  52.                 </Setter.Value>
  53.             </Setter>
  54.         </Style>
  55.     </Window.Resources>
  56.     <Grid SnapsToDevicePixels="True">
  57.         <Button >
  58.             <StackPanel >
  59.                 <TextBlock Text="Hello World!" HorizontalAlignment="Center"/>
  60.                 <TextBlock Text="Happy New Year!" HorizontalAlignment="Center"/>
  61.                 <TextBlock Text="2010 :)" HorizontalAlignment="Center"/>
  62.             </StackPanel>
  63.         </Button>
  64.     </Grid>
  65. </Window>

Lets take a look at the above XAML. We started with a default style for a button and defined some resources in the Styles resource sections. They will be further referred in the Control Template, that is defined in Lines 39-51. Control Template here is of type Button, obviously, We created two layers, one is an Ellipse, to give a circular look while Content Presenter, is the Presenter for Button’s/Control’s Content. In this scenario will present the “Hello World!” content. Here is the final output of it.

image

That's all for now folks, I hope it was helpful. In the next session I’ll talk more about Themes and Skinning feature of WPF, so stay tuned. I appreciate that you please leave your valuable feedbacks. Enjoy :)

If you enjoyed reading this blog, leave your valuable feedback and consider subscribing to the RSS feed. You can also subscribe to it by email. Also, you can follow me on Twitter. Thank you!

Technorati Tags: ,,
learn colors with nursery rhymes songs
10/17/2017 11:07:27 AM #

Currently it appears like BlogEngine is the top blogging platform out there right now. (from what I've read) Is that what you are using on your blog?

Add comment