WPF (Windows Presentation Foundation)
Introduction:
The Windows Presentation Foundation is Microsoft’s next generation UI framework to create applications with a rich user experience. WPF combines application UIs, 2D graphics, 3D graphics, documents and multimedia into one single framework. Its vector based rendering engine which uses hardware acceleration of modern graphic cards. This makes the UI faster, scalable and resolution independent.
WPF separates the appearance of a user interface from its behavior. The appearance is generally specified in the Extensible Application Markup Language (XAML), the behavior is implemented in a managed programming language like C# or Visual Basic. The two parts are tied together by data binding, events and commands. The separation of appearance and behavior brings the following benefits:
- Appearance and behavior are loosely coupled
- Designers and developers can work on separate models.
- Graphical design tools can work on simple XAML documents.
Rich composition
Controls in WPF are extremely compostable. You can define almost any type of controls as content of another. Although this flexibility sounds horrible to designers, it’s a very powerful feature if you use it appropriate. Put an image into a button to create an image button.
<Button>
< StackPanel Orientation="Horizontal">
< Image Source="speaker.png" Stretch="Uniform"/>
< TextBlock Text="Play Sound" />
< /StackPanel>
</Button>
Highly customizable
Because of the strict separation of appearance and behavior you can easily change the look of a control. The concept of styles let you skin controls almost like CSS in HTML. Templates let you replace the entire appearance of a control.
The following example shows a default WPF button and a customized button.
Resolution independence
All measures in WPF are logical units - not pixels. A logical unit is a 1/96 of an inch. If you increase the resolution of your screen, the user interface stays the same size - if just gets crispier. Since WPF builds on a vector based rendering engine it's incredibly easy to build scalable user interfaces.
Introduction to XAML
XAML stands for Extensible Application Markup Language. It’s a simple language based on XML to create and initialize .NET objects with hierarchical relations. Today XAML is used to create user interfaces in WPF, Silver light, declare workflows in WF and for electronic paper in the XPS standard.
Advantages of XAML
All you can do in XAML can also be done in code. XAML is just another way to create and initialize objects. You can use WPF without using XAML. It's up to you if you want to declare it in XAML or write it in code. Declare your UI in XAML has some advantages:
- XAML code is short and clear to read and maintain.
- Graphical design tools like Expression Blend require XAML as source.
- The separation of XAML and UI logic allows it to clearly separate the roles of designer and developer.
XAML vs. Code
As an example we build a simple StackPanel with a textblock and a button in XAML and compare it to the same code in C#.
<StackPanel>
<TextBlock Margin="20">Welcome to the World of XAML</TextBlock>
<Button Margin="10" HorizontalAlignment="Right">OK</Button>
</StackPanel>
The same expressed in C# will look like this:
// Create the StackPanel
StackPanel stackPanel = new StackPanel();
this.Content = stackPanel;
// Create the TextBlock
TextBlock textBlock = new TextBlock();
textBlock.Margin = new Thickness(10);
textBlock.Text = "Welcome to the World of XAML";
stackPanel.Children.Add (textBlock);
// Create the Button
Button button = new Button ();
button.Margin= new Thickness (20);
button.Content = "OK";
stackPanel.Children.Add(button);
As you can see is the XAML version much shorter and clearer to read. And that's the power of XAMLs expressiveness.
The followings are the benefit of using XAML:
Properties as Elements
Properties are normally written inline as known from XML <Button Content="OK" />. But what if we want to put a more complex object as content like an image that has properties itself? .To do that we can use the property element syntax. This allows us to extract the property as an own child element.
<Button>
<Button.Content>
<Image Source="Images/OK.png" Width="50" Height="50" />
</Button.Content>
</Button>
Implicit Type conversion
A very powerful construct of WPF are implicit type converters. They do their work silently in the background. When you declare a Border Brush, the word "Blue" is only a string. The implicit Brush Converter makes a System.Windows.Media.Brushes.Blue out of it.
<Border BorderBrush="Blue" BorderThickness="0, 10">
</Border>
Markup Extensions
Markup extensions are dynamic placeholders for attribute values in XAML. They resolve the value of a property at runtime. Markup extensions are surrounded by curly braces. The first identifier within a pair of curly braces is the name of the extension. All preceding identifiers are named parameters in the form of Property=Value. (Example: Background="{StaticResource NormalBackgroundBrush}").
WPF has some built-in markup extensions, but you can write your own, by deriving from Markup Extension. These are the built-in markup extensions:
WPF has some built-in markup extensions, but you can write your own, by deriving from Markup Extension. These are the built-in markup extensions:
- Binding: To bind the values of two properties together.
- Static Resource: One time lookup of a resource entry
- Dynamic Resource: Auto updating lookup of a resource entry
- Template Binding: To bind a property of a control template to a dependency property of the control
- x:Static: Resolve the value of a static property.
- x:Null: Return null.
- x:Name
- x:UId
The following example shows a label whose Content is bound to the Text of the textbox. When you type a text into the text box, the text property changes and the binding markup extension automatically updates the content of the label.
<TextBox x:Name="textBox"/>
<Label Content="{Binding Text, ElementName=textBox}"/>
Namespaces
At the beginning of every XAML file you need to include two namespaces.
The first is http://schemas.microsoft.com/winfx/2006/xaml/presentation.
It is mapped to all WPF controls in System.Windows.Controls.
The second is http://schemas.microsoft.com/winfx/2006/xaml
it is mapped to System.Windows.Markup that defines the XAML keywords.
The first is http://schemas.microsoft.com/winfx/2006/xaml/presentation.
It is mapped to all WPF controls in System.Windows.Controls.
The second is http://schemas.microsoft.com/winfx/2006/xaml
it is mapped to System.Windows.Markup that defines the XAML keywords.
The mapping between an XML namespace and a CLR namespace is done by the Xmlns Definition attribute at assembly level. You can also directly include a CLR namespace in XAML by using the clr-namespace: prefix.
<Window xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”>
xmlns:vm=”clr-namespace: SampleProject.UI.ViewModel”
xmlns:vm=”clr-namespace: SampleProject.UI.ViewModel”
</Window>
Create simple WPF application
WPF Application uses the following assembly;
- PresentationCore
- PresentationFramework
- WindowsBase
- System.Xaml
Open Visual Studio 2008 and choose "File", "New", "Project..." in the main menu. Choose "WPF Application" as project type.
Choose a folder for your project and give it a name. Then press "OK"
Visual Studio creates the project and automatically adds some files to the solution. A Window1.xaml and an App.xaml.
We can design our UI in Window1.xaml file and put our logic in Window1.xaml.cs code behind file. The code behind file contains InitializeComponent() method .Application loads the xaml by using this method. In App.xaml file we can place the Style that is the Resource Dictionary and also mention the startup window /page [StartupUri=”Window1.xaml”]. During application startup, first App.xaml is loaded/ called.
Open the Window1.xaml file in the WPF designer and drag a Button and a TextBox from the toolbox to the Window
Select the Button and switch to the event view in the properties window (click on the little yellow lightning icon). Double-click on the "Click" event to create a method in the code behind that is called, when the user clicks on the button. Visual Studio automatically creates a method in the code-behind file that gets called when the button is clicked.
Select the Button and switch to the event view in the properties window (click on the little yellow lightning icon). Double-click on the "Click" event to create a method in the code behind that is called, when the user clicks on the button. Visual Studio automatically creates a method in the code-behind file that gets called when the button is clicked.
private void button1_Click(object sender, RoutedEventArgs e)
{
textBox1.Text = "Hello WPF!”;
}
The textbox has automatically become assigned the name textBox1 by the WPF designer. Set text Text to "Hello WPF!" when the button gets clicked and we are done! Start the application by hit [F5] on your keyboard.
Development Tools
Microsoft provides two development tools for WPF applications. One is Visual Studio, made for developers and the other is Expression Blend made for designers. While Visual Studio is good in code and XAML editing, it has a rare support for all the graphical stuff like gradients, template editing, animation, etc. This is the point where Expression Blend comes in. Blend covers the graphical part very well but it has (still) rare support for code and XAML editing.
So the conclusion is that you will need both of them.
Layout of Controls
Layout of controls is critical to applications usability. Arranging controls based on fixed pixel coordinates may work for a limited environment, but as soon as you want to use it on different screen resolutions or with different font size it will fail. WPF provides a rich set built-in layout panels that help you to avoid the common pitfalls.
These are the layout panels of WPF:
- Canvas - for specific (X,Y) positioning
- Stack Panel - for stacking elements horizontally or vertically
- Wrap Panel - automatically handles wrapping elements to a new row as needed
- Dock Panel - for familiar docking functionality
- Grid - for a row and column based layout
- Uniform Grid - a specialized form of Grid where all cells are the same size
Canvas
The Canvas panel is used for absolute positioning(X,Y) by specifying pixels relative to the edges of the canvas, In the example below, notice the Canvas.Left , Canvas.Right , Canvas.Bottom and Canvas.Top attached properties on the Textboxes and the and Button.
<Canvas Width="Auto" Height="Auto">
<TextBox Width="159" Height="26" Text="Name" Canvas.Left="36" Canvas.Top="12"/>
<TextBox Width="159" Height="26" Text="Password" Canvas.Left="36" Canvas.Top="53"/>
<Button Width="159" Height="23" Content="Submit" Canvas.Left="36" Canvas.Top="101"/>
</Canvas>
StackPanel
The StackPanel, as the name implies, arranges content either horizontally or vertically. Vertical is the default, but this can be changed using the Orientation property. Content is automatically stretched based on the orientation (see screenshot below), and this can be controlled by changing the HorizontalAlignment or VerticalAlignment properties.
<StackPanel Width="Auto" Height="Auto">
<Button Content="Button" />
<Button Content="Button" HorizontalAlignment="Left" />
<Button Content="Button" HorizontalAlignment="Center" />
<Button Content="Button" HorizontalAlignment="Right" />
</StackPanel>
The StackPanel can also be oriented horizontally.
<StackPanel Width="Auto" Height="Auto" Orientation="Horizontal">
<Button Content="Button" />
<Button Content="Button" VerticalAlignment="Top" />
<Button Content="Button" VerticalAlignment="Center" />
<Button Content="Button" VerticalAlignment="Bottom" />
</StackPanel>
WrapPanel
The WrapPanel is similar to the StackPanel in that it arranges item sequentially. The WrapPanel will wrap content based on the available space in the panel. Like the StackPanel, the Orientation property controls the primary direction of the layout.
<WrapPanel Width="Auto" Height="Auto">
<Button Content="Button"/><Button Content="Button"/>
<Button Content="Button"/><Button Content="Button"/>
<Button Content="Button"/><Button Content="Button"/>
<Button Content="Button"/><Button Content="Button"/>
</WrapPanel>
DockPanel
The DockPanel is used to anchor elements to the edges of the container, and is a good choice to set up the overall structure of the application UI. Elements are docked using the DockPanel.Dock attached property. The order that elements are docked determines the layout. Here is an example that sets up a typical navigation system: a top menu, a bottom status bar, left and right navigational or informational panes, and a center area that fills the remaining space.
<DockPanel HorizontalAlignment="Stretch" Margin="0, 0, 0, 0" Width="Auto">
<! -- DOCKED ON BOTTOM TO FILL ENTIRE HORIZONTAL SPACE -->
<StatusBar Width="Auto" Height="25" Background="#FF451B1B" DockPanel.Dock="Bottom">
<TextBlock Width="Auto" Height="Auto" Foreground="#FFFEFEFE" Text="BottomContent"/>
</StatusBar>
<! -- DOCKED ON TOP TO FILL ENTIRE HORIZONTAL SPACE -->
<Menu Width="Auto" Height="25" DockPanel.Dock="Top">
<MenuItem Header="Top Content"/>
</Menu>
<! -- DOCKED TO LEFT AND RIGHT TO SIMULATE NAVIGATION PANES -->
<GroupBox DockPanel.Dock="Left" Width="100" Height="Auto" Header="Left Content"/>
<GroupBox DockPanel.Dock="Right" Width="100" Height="Auto" Header="Right Content" />
<! -- FINAL ELEMENT FILLS REMAINING SPACE -->
<Canvas Width="Auto" Height="Auto">
<TextBlock Width="Auto" Height="Auto" Text="Center Content" TextWrapping="Wrap"
Canvas.Left="85" Canvas.Top="51"/>
</Canvas>
</DockPanel>
Grid
The Grid is used to create a table-style layout of rows and columns. Columns and rows are defined using two property elements: Grid.ColumnDefinitions and Grid.RowDefinitions. The Grid supports column and row spanning, and either absolute, proportional, or auto sizing.
Child elements are assigned to rows and columns using the Grid.Row and Grid.Column attached properties. The following XAML uses a grid to arrange two labels, two textboxes, and a button.
Child elements are assigned to rows and columns using the Grid.Row and Grid.Column attached properties. The following XAML uses a grid to arrange two labels, two textboxes, and a button.
<Grid Width="Auto" Height="Auto">
<Grid.ColumnDefinitions>
< ColumnDefinition Width="80" />
< ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
< /Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Name:" />
<TextBox Grid.Row="0" Grid.Column="1" VerticalAlignment="Top" Height="25" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Password:" />
<TextBox Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" Height="25" />
<Button Grid.ColumnSpan="2" Grid.Row="2" HorizontalAlignment="Center"
VerticalAlignment="Center" Width="100" Height="25" Content="Submit" />
</Grid>
UniformGrid
The UniformGrid is a limited version of the grid where all rows and columns are the same size and where a cell can only hold one control (determined by the grid). Because it's unnecessary to declare each row and column, the UniformGrid contains two properties, Rows and Columns, for setting the number of rows and columns. Controls are added to the grid in the order that they are declared.
The UniformGrid is useful for simple scenarios, but for the most amount of control, you are better off using the Grid.
The UniformGrid is useful for simple scenarios, but for the most amount of control, you are better off using the Grid.
<UniformGrid Rows="5" Columns="1">
<TextBlock Text="Name:" />
<TextBox VerticalAlignment="Top" />
<TextBlock Text="Password:" />
<TextBox VerticalAlignment="Top" />
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Width="100" Content="Submit" />
</UniformGrid>
Styles
Styles are a means to define a common look and feel to controls. They are similar to CSS style sheets for web pages, but are defined using XAML syntax. Styles are typically defined in the Resources section of a control, panel, or the parent window. They can also be collected into what are known as Resource Dictionaries, and hosted in external files.
To implement a style, follow these steps:
Ø Create a< style> within a Resources collection and give it a key.
Ø Create <setter> elements within the style to set properties to specific values.
- Use the style from an element using the <Style> attribute along with static resource or dynamic resource.
For example, here is a demo which defines two styles as part of a Canvas' resources collection. The style changes the default display of buttons to be 25x100, with a black background and white text. The named style is derived from the first style (style inheritance) and sets the background of the button to Dark Blue instead. The third button references the named style via the Style attribute.
<Canvas>
<Canvas. Resources>
<! -- No x: Key specified, so this becomes the default for buttons -->
<Style TargetType="{x: Type Button}">
< Setter Property="Background" Value="Black" />
< Setter Property="Foreground" Value="White" />
< Setter Property="Height" Value="25" />
< Setter Property="Width" Value="100" />
</Style>
<! -- A style with a key must be referenced explicitly using the Style attribute -->
<! -- Note also that the BasedOn attribute can be used for style inheritance -->
<Style x: Key="SpecificStyle" TargetType="{x: Type Button}" BasedOn="{StaticResource {x:Type Button}}">
< Setter Property="Background" Value="DarkBlue" />
</Style>
</Canvas.Resources>
<Button Canvas.Left="10" Canvas.Top="10">Button 1</Button>
<Button Canvas.Left="10" Canvas.Top="44">Button 2</Button>
<Button Style="{StaticResource SpecificStyle}" Canvas.Left="10" Canvas.Top="77">Button 3</Button>
</Canvas>
Effects of TargetType and Key
When creating a style, including or omitting the TargetType and Key have the following effects:
- TargetType only - Style is automatically applied as the default for all elements of that type within scope.
- Key only - Style can be applied to different elements. The Setter properties must include "Control." (Eg. Property="Control.Background")
- Key and TargetType - Style can be applied to the target type via explicit use of the Style attribute on the element.
Triggers
When applying a style to a control, all properties defined in setter elements are unconditionally applied to the control. If that is not the desired behavior, we can use triggers.
Triggers allow us to define several Setter elements in a style. But, contrary to the previous situation, not all these Setter elements will be applied to the controls using the particular style. The trigger includes a condition, and when that condition is true, the Setters defined in the trigger will be applied. When the condition is false, the Setters are ignored.
There are three types of triggers in WPF:
- Property Triggers - run when the value of a dependency property changes
- Data Triggers - run when the value of any .NET property changes, using data binding
- Event Triggers - run when a routed event occurs
Property triggers:
Modern applications make intensive use of interactive UI elements: highlighting buttons when the mouse hovers over them, rollover images. Most of the time, these effects are achieved through the use of some scripting code, like JavaScript in the case of web applications.
In WPF, it’s not necessary to write scripting code. Instead, property triggers can help us create the same compelling effects. Property triggers are, like all triggers defined inside a style, but when applied to a certain object, they fire when a certain property of that object itself changes. This way, one or more properties of the object can be changed.
Let’s take a look at some code examples.
< Style TargetType="{x: Type Button}">
<Setter Property="Button.Background" Value="AliceBlue" />
...
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Button.Background" Value="Yellow" />
</Trigger>
</Style.Triggers>
In WPF, it’s not necessary to write scripting code. Instead, property triggers can help us create the same compelling effects. Property triggers are, like all triggers defined inside a style, but when applied to a certain object, they fire when a certain property of that object itself changes. This way, one or more properties of the object can be changed.
Let’s take a look at some code examples.
< Style TargetType="{x: Type Button}">
<Setter Property="Button.Background" Value="AliceBlue" />
...
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Button.Background" Value="Yellow" />
</Trigger>
</Style.Triggers>
</Style>
In this style, we have added the Style.Triggers element, which contains one Setter element, which is not applied by default to the control. The property being checked is the IsMouseOver, and when true, the trigger fires and properties defined in its Setters are applied. In this case, this results in a button that changes color whenever the mouse hovers over it.
There are 2 important things to note. First, we did not have to write any code, it’s all handled by WPF. Secondly, there is no Setter to counteract the first one. When the condition is no longer valid, WPF reverts the control back to its previous state.
There are 2 important things to note. First, we did not have to write any code, it’s all handled by WPF. Secondly, there is no Setter to counteract the first one. When the condition is no longer valid, WPF reverts the control back to its previous state.
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.
< Style TargetType="{x: Type Button}">
<Setter Property="Button.Background" Value="AliceBlue" />
<Setter Property="Button.Opacity" Value="0.5" /> ...
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Button.Opacity" Value="1"></Setter>
<Setter Property="Button.Background" Value="Green"></Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Button.Background" Value="Yellow"></Setter>
</Trigger>
</Style.Triggers>
</Style>
Here, we now have 2 triggers, one of which contains 2 Setter elements. The conditions of both triggers are checked and when true, the properties are applied to the control.
So far we’ve had more than one trigger inside a style, but each of them only had one condition to check. What happens when the trigger should only fire when 2 or more conditions are true? For example: only change the background of a button when the mouse is over it AND the button is enabled.
For this purpose, we have so-called multi-condition property triggers.
Here’s some sample code using a multi-condition trigger.
< Page.Resources>
<Style TargetType="{x: Type Button}">
<Setter Property="Button.Background" Value="AliceBlue" />
...
<Style.Triggers>
< MultiTrigger>
<MultiTrigger.Conditions>
< Condition Property="IsMouseOver" Value="True"/>
< Condition Property="IsEnabled" Value="True" />
< /MultiTrigger.Conditions>
< Setter Property="Button.Background" Value="Yellow" />
< /MultiTrigger>
</Style.Triggers>
</Style>
< /Page.Resources>
< StackPanel>
<Button x: Name="WelcomeButton” Content="Welcome user!" ></Button>
</StackPanel>
So far we’ve had more than one trigger inside a style, but each of them only had one condition to check. What happens when the trigger should only fire when 2 or more conditions are true? For example: only change the background of a button when the mouse is over it AND the button is enabled.
For this purpose, we have so-called multi-condition property triggers.
Here’s some sample code using a multi-condition trigger.
< Page.Resources>
<Style TargetType="{x: Type Button}">
<Setter Property="Button.Background" Value="AliceBlue" />
...
<Style.Triggers>
< MultiTrigger>
<MultiTrigger.Conditions>
< Condition Property="IsMouseOver" Value="True"/>
< Condition Property="IsEnabled" Value="True" />
< /MultiTrigger.Conditions>
< Setter Property="Button.Background" Value="Yellow" />
< /MultiTrigger>
</Style.Triggers>
</Style>
< /Page.Resources>
< StackPanel>
<Button x: Name="WelcomeButton” Content="Welcome user!" ></Button>
</StackPanel>
We used a MultiTrigger element here, in which we specify both the conditions that have to be checked. If needed, more can be added in an analogue manner. One or more normal Setters are applied when all of these conditions are true.
As can be seen, property triggers are a very easy-to-use way to change our application’s interface based on one or more conditions and they help increase the user experience with effects which in the past, cost a lot more effort to create.
Data triggers:
As we’ve seen, property triggers are used to check on WPF dependency properties, like the IsMouseOver property. However, there will be times when we have to check the value of a property of a non-visual .NET object, like a self-created User object for example. For this purpose, data triggers come in handy.
In the following sample, we’ll be creating and filling a list box. Each item represents a user, an instance of the User class. Based on the value of the Role property, the user will receive another color. To accomplish this, I’ll use a style with a data trigger that fires whenever the specified condition is true.
Let’s create a User class, with 2 string properties: name and role.
namespace Demo {
public class User {
private string name;
private string role;
public User(string name, string role) {
this.name = name;
this.role = role;
}
public string Name {
get {return name ;}
set {name = value;}
}
public string Role {
get { return role; }
set { role = value; }
}
}
}
Note the namespace Demo; we’ll need it later on.
Within the same namespace, let’s create a class Users.
public class Users : ObservableCollection<User>
{
public Users()
{
this.Add(new User("Gill Cleeren", "Admin"));
this.Add(new User("Steve Smith", "Contributor"));
this.Add(new User("John Miller", "User"));
}
}
This class inherits from ObservableCollection, which represents a dynamic data collection. ObservableCollection provides methods to notify when items are added to or removed from the collection. Since we provide User as type parameter, only User instances can be added to the collection.
Now, let’s get back to the XAML-code. As said earlier, we want to display the users in a list box.
< Page x: Class="Demo.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Page1"
Xmlns: clr="clr-namespace: Demo"
>
<Page.Resources>
<clr:Users x:Key="myUsers" />
<DataTemplate DataType="{x:Type clr:User}">
< TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
...
</Page.Resources>
<StackPanel>
<ListBox Width="200"
ItemsSource="{Binding Source={StaticResource myUsers}}" />
</StackPanel>
< /Page>
Since we’ll be working with the Users collection in the XAML code, it has to be made available in that context. Therefore, we have to create a mapping between a CLR namespace and a namespace in WPF, using the following syntax.
Xmlns: clr="clr-namespace: Demo"
The namespace Demo is mapped to a namespace in WPF, which is called clr. Note that the name clr is not obligatory.
Within the resources of the Page in the XAML code, we can now use the clr-prefix to have WPF create an instance of the Users class, using the default constructor. The value myUsers, specified in the x:Key attribute, can be used to access the instance.
The DataTemplate is used to tell WPF how it should display a non-visual object like a User. In this case, a TextBlock containing the name will be used.
In the StackPanel, I declare a ListBox. This ListBox will bind its ItemsSource to the Users instance, myUsers, which is a collection of User instances.
Now, let’s add the trigger functionality.
Within the resources of the Page in the XAML code, we can now use the clr-prefix to have WPF create an instance of the Users class, using the default constructor. The value myUsers, specified in the x:Key attribute, can be used to access the instance.
The DataTemplate is used to tell WPF how it should display a non-visual object like a User. In this case, a TextBlock containing the name will be used.
In the StackPanel, I declare a ListBox. This ListBox will bind its ItemsSource to the Users instance, myUsers, which is a collection of User instances.
Now, let’s add the trigger functionality.
< Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Role}" Value="Admin">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
< /Style>
Here a data trigger is used. The condition whether the trigger should fire is based on the value of the Role property of the specified User object: if it has the value “Admin”, the trigger’s condition will become true, and the property declared within the Setter will be applied.
The TargetType of the Style is ListBoxItem. Whenever the Role property of the User instance to which a ListBoxItem is bound, has the value “Admin”, the foreground will be set to red.
And this is the result, with the first user colored red:
To finish our trigger-tour, we have to make a stop at the event triggers. Event triggers are used to watch for events to happen, like a click-event or a mouse-over event. They are used in combination with animation: whenever the event is raised, the event trigger fires and starts an animation action.
In the sample code, we’ll use an event trigger to react to the mouse-over event for a button.
< Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Width" Value="200" />
<Setter Property="Height" Value="100" />
<Setter Property="Margin" Value="20" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Style.Triggers>
< EventTrigger RoutedEvent="Button.MouseEnter">
< EventTrigger.Actions>
< BeginStoryboard>
< Storyboard>
< DoubleAnimation To="300" Duration="0:0:3"
Storyboard.TargetProperty="(Button.Width)" />
< DoubleAnimation To="200" Duration="0:0:3"
Storyboard.TargetProperty="(Button.Height)" />
< /Storyboard>
</BeginStoryboard>
< /EventTrigger.Actions>
< /EventTrigger>
< EventTrigger RoutedEvent="Button.MouseLeave">
< EventTrigger.Actions>
< BeginStoryboard>
< Storyboard>
< DoubleAnimation Duration="0:0:3"
Storyboard.TargetProperty="(Button.Width)" />
< DoubleAnimation Duration="0:0:3"
Storyboard.TargetProperty="(Button.Height)" />
< /Storyboard>
< /BeginStoryboard>
< /EventTrigger.Actions>
< /EventTrigger>
</Style.Triggers>
</Style>
< /Window.Resources>
< StackPanel>
<Button x:Name="GrowButton" Content="Hello MSDN" />
< /StackPanel>
The style now includes 2 event triggers. The first one will react to the mouse starting to hover over the Button; the second one will react to the mouse leaving the button surface.
When either one of the events is raised, the actions specified for the trigger that reacts to the event, will be executed.
The result is a button that expands both in width and height when hovering over it.
Note that we also have to include code that will return the button to its original state here, which wasn’t necessary for the other types of triggers. This is due to the fact that event triggers have no concept of termination of state, so the property won’t be changed back to the state it was in before the trigger fired.
As you can see, using triggers is fairly easy and they make your applications much more interactive, without having to write much code.
As you can see, using triggers is fairly easy and they make your applications much more interactive, without having to write much code.
Dependency Property:
The main difference between dependency property and normal property is the value of a normal .NET property is read directly from a private member in your class, whereas the value of a DependencyProperty is resolved dynamically when calling the
GetValue() method that is inherited from DependencyObject.When you set a value of a dependency property it is not stored in a field of your object, but in a dictionary of keys and values provided by the base class
DependencyObject. The key of an entry is the name of the property and the value is the value you want to set.The advantages of dependency properties are
- Reduced memory footprint
it’s a huge dissipation to store a field for each property when you think that over 90% of the properties of a UI control typically stay at its initial values. Dependency properties solve these problems by only store modified properties in the instance. The default values are stored once within the dependency property. - Change notification
Dependency properties have a built-in change notification mechanism. By registering a callback in the property metadata you get notified, when the value of the property has been changed. This is also used by the databinding.
How to create a DependencyProperty:
To create a DependencyProperty, add a static field of type
DepdencyProperty to your type and call DependencyProperty.Register() to create an instance of a dependency property. The name of the DependendyProperty must always end with ...Property. This is a naming convention in WPF.To make it accessable as a normal .NET property you need to add a property wrapper. This wrapper does nothing else than internally getting and setting the value by using the GetValue() and SetValue() Methods inherited from DependencyObject and passing the DependencyProperty as key.
If you set the property from XAML the SetValue() method is called directly.
If you are using Visual Studio, you can type
propdp and hit 2x tab to create a dependency property.
// Dependency Property
public static readonly DependencyProperty CurrentTimeProperty =
DependencyProperty.Register( "CurrentTime", typeof(DateTime),
typeof(MyClockControl), new FrameworkPropertyMetadata(DateTime.Now));
// .NET Property wrapper
public DateTime CurrentTime
{
get { return (DateTime)GetValue(CurrentTimeProperty); }
set { SetValue(CurrentTimeProperty, value); }
}
Each DependencyProperty provides callbacks for change notification, value coercion and validation. These callbacks are registered on the dependency property.
new FrameworkPropertyMetadata( DateTime.Now,
OnCurrentTimePropertyChanged,
OnCoerceCurrentTimeProperty ),
OnValidateCurrentTimeProperty );
Value Changed Callback:
The change notification callback is a static method, that is called everytime when the value of the TimeProperty changes. The new value is passed in the EventArgs, the object on which the value changed is passed as the source.
private static void OnCurrentTimePropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
MyClockControl control = source as MyClockControl;
DateTime time = (DateTime)e.NewValue;
// Put some update logic here...
}
Coerce Value Callback:
The coerce callback allows you to adjust the value if it’s outside the boundaries without throwing an exception. A good example is a progress bar with a Value set below the Minimum or above the Maximum. In this case we can coerce the value within the allowed boundaries. In the following example we limit the time to be in the past.
private static object OnCoerceTimeProperty( DependencyObject sender, object data )
{
if ((DateTime)data > DateTime.Now )
{
data = DateTime.Now;
}
return data;
}
Validation Callback:
In the validate callback you check if the set value is valid. If you return false, an ArgumentException will be thrown. In our example demand, that the data is an instance of a
DateTime.
private static bool OnValidateTimeProperty(object data)
{
return data is DateTime;
}
Some dependency property of WPF controls are readonly. They are often used to report the state of a control, like the
IsMouseOver property. Is does not make sense to provide a setter for this value.Maybe you ask yourself, why not just use a normal .NET property? One important reason is that you cannot set triggers on normal .NET propeties.
Creating a read only property is similar to creating a regular DependencyProperty. Instead of calling
DependencyProperty.Register() you call DependencyProperty.RegisterReadonly(). This returns you a DependencyPropertyKey. This key should be stored in a private or protected static readonly field of your class. The key gives you access to set the value from within your class and use it like a normal dependency property.Second thing to do is registering a public dependency property that is assigned to
DependencyPropertyKey.DependencyProperty. This property is the readonly property that can be accessed from external.
// Register the private key to set the value
private static readonly DependencyPropertyKey IsMouseOverPropertyKey =
DependencyProperty.RegisterReadOnly("IsMouseOver",
typeof(bool), typeof(MyClass),
new FrameworkPropertyMetadata(false));
// Register the public property to get the value
public static readonly DependencyProperty IsMouseoverProperty =
IsMouseOverPropertyKey.DependencyProperty;
// .NET Property wrapper
public int IsMouseOver
{
get { return (bool)GetValue(IsMouseoverProperty); }
private set { SetValue(IsMouseOverPropertyKey, value); }
}
Attached Properties:
Attached properties are a special kind of DependencyProperties. They allow you to attach a value to an object that does not know anything about this value.
A good example for this concept are layout panels. Each layout panel needs different data to align its child elements. The Canvas needs
Top and Left, The DockPanel needs Dock, etc. Since you can write your own layout panel, the list is infinite. So you see, it's not possible to have all those properties on all WPF controls.The solution are attached properties. They are defined by the control that needs the data from another control in a specific context. For example an element that is aligned by a parent layout panel.
To set the value of an attached property, add an attribute in XAML with a prefix of the element that provides the attached property. To set the the Canvas.Top and Canvas.Left property of a button aligned within a Canvas panel, you write it like this:
<Canvas>
<Button Canvas.Top="20" Canvas.Left="20" Content="Click me!"/>
</Canvas>
public static readonly DependencyProperty TopProperty =
DependencyProperty.RegisterAttached("Top",
typeof(double), typeof(Canvas),
new FrameworkPropertyMetadata(0d,
FrameworkPropertyMetadataOptions.Inherits));
public static void SetTop(UIElement element, double value)
{
element.SetValue(TopProperty, value);
}
public static double GetTop(UIElement element)
{
return (double)element.GetValue(TopProperty);
}
Difference between Attached property and Dependency property:
Attached properties are basically meant for the container elements.like if you have a grid and you have grid.row now this is considered to be an attached property of a grid element.also you can use this property in texbox,button etc to set its place in the grid.
Dependency property is like the property basically belongs to some other class and is used in other class. eg: like you have a rectangle here height and width are regula properties of rectangle,but left and top are the dependency property as it belongs to Canvass class.
Attached properties are a special kind of DependencyProperties. Register is used to register dependency property but RegisterAttached is used in attached property.
That's all folks! Hope you felt interesting to read it.
Please like and comment J
No comments:
Post a Comment