WPF Guided Tour Josh Smith Table of contents Part 1 (XAML): Learn about XAML and how it is used in WPF applications. Part 2 (Layout): Learn about layout panels and how they are used to construct user interfaces. Part 3 (Data binding): Learn how WPF data binding works and how it can be used. Part 4 (Data templates and triggers): Learn how data templates and triggers work and how they can be used. Part 5 (Styles): Learn about how UIs can be styled in WPF. Part 1 - XAML Introduction This article is the first in a series which reviews some important features of Microsoft's Windows Presentation Foundation (WPF). The entire series barely scratches the surface of the immense WPF platform, and it does not dive too deeply into any particular topic. The purpose of this series is to familiarize the reader with the basic WPF programming model to the point where he/she is comfortable enough with WPF to fully understand the, rather silly, WPF Horse Race application (available for download via a link at the top of this page). The subject matter is presented in the context of a completely impractical horse racing application, as seen in the screenshot above. The application was designed with the intention of being used as the focal point of this series. I tried to find the right balance between making the demo application simple enough to be understandable to a WPF newcomer, yet complicated enough to actually do something interesting (or at least mildly amusing). Prerequisites In order to run a WPF application you need the .NET Framework version 3.0, or greater. Windows Vista has the .NET Framework v3.0 installed by default, so you only need to install it if you have Windows XP SP2. To develop WPF applications you should have Visual Studio 2005 with the "Orcas" extensions, or a later version of Visual Studio, and also the Windows SDK. Refer to the External Links section at the end of this article for links to relevant resources. What is XAML? XAML stands for eXtensible Application Markup Language. It is an all-purpose XMLbased language used for declaring object graphs, which are instantiated at runtime. XAML is used by WPF developers to declare the layout of a user interface (UI), and the resources used in that UI. It is not at all necessary to use XAML when programming in WPF. Anything that can be done in XAML can also be done in code. Using XAML makes many UI development scenarios much easier and faster, such as creating the layout of a UI and configuring styles, templates, and other WPF-specific entities discussed later in this series. Performance impact When you compile a WPF application in Visual Studio it will compile your XAML files into a compressed representation known as Binary Application Markup Language (BAML). The BAML is then saved as a resource in the resultant assembly. When that assembly is loaded and the resource is requested, the BAML is streamed out and very quickly turned into the object graph described by the original XAML. This two-step process allows the XAML parsing to occur at compile time, thus mitigating the performance overhead of using a text-based object instantiation technology. With that said, it is possible to load XAML programmatically via the XamlReader.Load method. Dynamically loading XAML can be useful in a variety of situations, such as when a volatile portion of your UI is periodically downloaded from a server via XML Web services. Basic syntax Since XAML is an XML-based language, it should be quite simple and straightforward for anyone with XML experience. There are two different uses for XML elements in XAML; elements that represent objects and elements that represent properties of objects. An XML attribute always represents a property or event of an object. That's it; it is that simple. There are a few other concepts built on top of those fundamentals, but XAML never becomes complicated. For example: Collapse <Button Content="Click Me" Click="OnButtonClick"> <Button.Background> <LinearGradientBrush> <GradientStop Color="Yellow" Offset="0" /> <GradientStop Color="Green" Offset="1" /> </LinearGradientBrush> </Button.Background> </Button> That XAML results in a funky little button, which is rendered like this: Let's dissect that XAML to see how it works. The <Button> element declares that an instance of the Button class will be created. That Button object will have its Content property set to the string "Click Me". The Button's Click event will be handled by a method called OnButtonClick in the code-behind file (we'll get into code-behind files soon). The next element is <Button.Background>, which is said to use the "property element syntax". That XML element represents the Background property of the Button being configured. The child element of a property element is the value to which the property is set. In this case, the Background property is set to a LinearGradientBrush object. That brush has two <GradientStop> child elements, which might seem confusing at first. What are those GradientStop objects doing there? What are they being assigned or added to? The answer lies in the fact that XAML has some "shortcuts" built in which allow you to specify a property on a class which is considered to be the "content property." You do not need to use the property element syntax to assign a value to the "content property" of a class. Since LinearGradientBrush's content property is the GradientStops property, the following XAML is equivalent to that seen above: Collapse <Button Content="Click Me" Click="OnButtonClick"> <Button.Background> <LinearGradientBrush> <LinearGradientBrush.GradientStops> <GradientStop Color="Yellow" Offset="0" /> <GradientStop Color="Green" Offset="1" /> <LinearGradientBrush.GradientStops> </LinearGradientBrush> </Button.Background> </Button> The C# version In case you are curious about what that XAML would look like if it was written in C#, here's the translation: Collapse Button b = new Button(); b.Content = "Click Me"; b.Click += this.OnButtonClick; LinearGradientBrush lgb = new LinearGradientBrush(); lgb.GradientStops.Add( new GradientStop( Colors.Yellow, 0 ) ); lgb.GradientStops.Add( new GradientStop( Colors.Green, 1 ) ); b.Background = lgb; Markup extensions The XAML parser also knows how to work with a special construct known as the "markup extension." Markup extensions allow you to compactly configure objects and reference other objects defined elsewhere in the application. They are used when setting a property on an object, either via an XML attribute or the property element syntax. Markup extensions are used for things such as specifying a null value, declaring an array, referencing an object held in a resource dictionary, binding one property to another, and much more. The following XAML is roughly equivalent to the example shown previously (with a few minor changes): Collapse <Grid> <Grid.Resources> <LinearGradientBrush x:Key="FunkyBrush"> <GradientStop Color="Yellow" Offset="0" /> <GradientStop Color="Green" Offset="1" /> </LinearGradientBrush> </Grid.Resources> <Button Background="{StaticResource FunkyBrush}">Click Me</Button> </Grid> Notice that the Button's Background property is set, via an XML attribute, to {StaticResource FunkyBrush}. When an attribute is set to a value which begins with { and ends with }, then a markup extension is being used. In this case the StaticResourceExtension class is being used to reference a LinearGradientBrush stored in the Grid's resource dictionary. (Note: Grid is a layout panel in WPF, not a tabular data presentation control like you might expect.) Code-behind files XAML is compiled down into a class. Unless you specify the class name it should use, the compiler will generate a class name for you. However, when you apply the x:Class attribute to the root XML element in a XAML file, you can create a partial class in C# or VB.NET which will be merged with the XAML partial. This is how you can associate behavior with the layout and visuals declared in XAML. For example, in the demonstration above we created a Button in XAML and assigned an event handling method to its Click event. The OnButtonClick method would be defined in the code-behind file which contains another part of the class associated with the partial class generated by the XAML. Let's suppose that the Button we've been discussing is in a Window subclass called MyWindow. Here would be the code-behind file (MyWindow.xaml.cs): Collapse public partial class MyWindow : Window { public MyWindow() { InitializeComponent(); } void OnButtonClick( object sender, RoutedEventArgs e ) { MessageBox.Show( "Click me again. That felt good." ); } } How the WPF Horse Race uses XAML The WPF Horse Race application is almost entirely written in XAML. The application uses three C# files: the code-behind for the main Window, a file for the RaceHorse class, and another which contains some value converters (more on those in the article about data binding). The rest of the application is all in XAML. I encourage you to explore the WPF Horse Race source files and check out what XAML looks like when it's being used to create a real program (as opposed to just a simple demonstration of it). The rest of the articles in this series will examine how that entire application works, but there is no substitute for just digging into the source files now and exploring how it works on your own. External links Prerequisites Microsoft .NET Framework v3.0 Visual Studio "Orcas" March 2007 CTP XAML XAML Overview Markup Extensions and XAML XamlPadX General WPF Tips Tips on how to debug and learn about WPF History April 1, 2007 - Created the article. April 3, 2007 - Removed superfluous ColumnDefinitions from the Grid in RaceHorseDataTemplate.xaml and updated the demo project download file. April 18, 2007 - Fixed an incorrect statement about markup extensions. Originally I asserted that they are only used in conjunction with XML attributes, but they can be used with the property element syntax, as well. License This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL) About the Author Josh Smith Josh creates software, mostly with C# and XAML. He works at IdentityMine as a Senior UX Developer. He plays the music of J.S. Bach on the piano, but has started branching into other composers recently. Member Get his runtime debugging and scripting tool, called Crack.NET, right here[^]. Download his WPF.JoshSmith library here[^] You can check out his WPF blog here[^]. You can take his guided tour of WPF here[^]. You can check out a powerful debugger visualizer he worked on called Mole for Visual Studio here[^]. His Microsoft MVP profile can be viewed here[^]. Occupation: Software Developer (Senior) Company: IdentityMine, Inc. Location: United States Part 2 - Layout Introduction This is the second article in an introductory series about the Windows Presentation Foundation. In the previous article, we discussed XAML and how it is used in WPF application development. This article shifts focus to WPF's rich support for layout panels, and how they are used in the WPF Horse Race demo application (which is available for download at the top of the first article in this series). This article is not intended to provide an encyclopedic review of the entire layout system in WPF. The Windows SDK documentation provides ample material on how to use and extend the WPF layout system, so there is no point in repeating it. Instead, we will briefly cover the basics and then examine how the WPF Horse Race application makes use of the two most common layout panels, Grid and StackPanel. Background Traditional desktop application user interface development is mostly based on absolute positioning of visual elements, namely setting properties on controls to affect their location and size. There are some higher-level concepts one can use to make it easier to create UIs that adapt to a changing Window size, such as docking and anchoring of controls to their respective containers. However, as of Windows Forms 2.0 there was still much to be desired in the realm of automatic UI layout. WPF addresses those issues very well, in many cases by borrowing some of the better layout concepts from the world of HTML. Layout via panels The Panel class is the abstract base for all layout panels in WPF. It has a Children property which contains references to the UIElements within the panel. If you add an element to the Children of a panel, that element's size and/or location will be "managed" by the panel. All but one of the Panel subclasses provide automatic positioning of its children; Canvas being the exception to that rule. This basically means that as the size of the Window changes, panels automatically update the location of their child elements. Some panels, such as DockPanel and Grid, can also affect the display size of their child elements. That behavior is useful when you want visual elements to occupy as much screen space as possible, such as when displaying photographs or a line chart. If you are interested in learning about all of the built-in Panel subclasses and what they have to offer, refer to the External links section at the end of this article for more information. Attached layout settings The WPF architects decided that since the layout system is extensible (i.e. you can subclass Panel) there needs to be a flexible way of communicating layout settings between a panel and its children. Having all of the various layout properties on a base class, such as UIElement, would pollute its API and make it impossible for custom panels to follow suit (you can't add properties to the UIElement class). In short, they needed a way to set a layout property on a visual element in a panel without that element ever knowing about the panel or its properties. This problem was solved by introducing attached properties into the WPF framework. An attached property can be set on any object; it does not have to expose the property being set. For example: Collapse <DockPanel> <Button DockPanel.Dock="Left">Alf Was Here</Button> </DockPanel> The XAML seen above puts a Button inside a DockPanel. The Button is docked to the left side of the DockPanel because the Dock attached property is set to the 'Left' value of the Dock enumeration. For more information about how attached properties work refer to the pages mentioned in the External links section at the bottom of this article. In the next section, we will see attached properties in action. How the WPF Horse Race uses panels The WPF Horse Race explicitly uses two layout panels: Grid and StackPanel. I say 'explicitly' because it is entirely possible that the controls used in the application themselves use panels other than those two. In fact, a relatively simple WPF user interface will usually contain many panels, both small and large. Let's take a look at a simplified version of the main Window's XAML file: Collapse <Window> <Grid> <Grid.Background> <ImageBrush ImageSource="Resources/Background.jpg" Opacity="0.25" /> </Grid.Background> <Grid.RowDefinitions> <!-- The top row is for the race track. --> <RowDefinition Height="*" /> <!-- The bottom row is for the command strip. --> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <!-- The 'Race Track' area. --> <ItemsControl Grid.Row="0" ... /> <!-- The 'Command Strip' area --> <Border Grid.Row="1"> <Grid> <StackPanel Orientation="Horizontal" VerticalAlignment="Center"> <TextBlock Margin="10,4">Rotation: </TextBlock> <Slider ... /> <TextBlock ... /> <TextBlock> degrees</TextBlock> </StackPanel> <TextBlock HorizontalAlignment="Right" Margin="10,4"> <Hyperlink>Start new race</Hyperlink> </TextBlock> </Grid> </Border> </Grid> </Window> The XAML seen above contains two Grids and one StackPanel. Below is a diagram which shows the spatial relationships between those panels, and the elements they contain: The main layout The outermost Grid in the Window is colored red. Notice that it has two rows, separated in the diagram above, by a black line. Those two rows were declared with the following markup: Collapse <Grid.RowDefinitions> <!-- The top row is for the race track. --> <RowDefinition Height="*" /> <!-- The bottom row is for the command strip. --> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> The first row's Height is set to '*' which means that it will try to be as tall as possible. The second row's Height is set to 'Auto' so that it auto-sizes to the elements it contains. This setup makes sense because the command strip on the bottom should only take up as much space as it needs to be fully visible. The rest of the screen real estate should be given to the "race track" above. Notice that the row heights are not explicitly set to a numeric value. The Height property can be set to a numeric value if necessary, but it is usually better to let the Grid class handle as many metrics calculations as possible. Doing so allows the Grid to intelligently resize the rows as needed. Next we can see how to indicate in which row we want visual elements to be placed. Grid exposes several attached properties, one of which is called Row. Here's how that attached property is used: Collapse <!-- The 'Race Track' area. --> <ItemsControl Grid.Row="0" ... /> <!-- The 'Command Strip' area --> <Border Grid.Row="1">...</Border> The ItemsControl does not need to have the Row attached property set on it because the default value for that property is zero. However, the Border does need to have Row set on it so that it does not overlap with the ItemsControl in the first row. The command strip area Referring back to the diagram seen above, we can now venture into the nested panels seen toward the bottom of the UI. Here is the XAML which describes that "command strip" area: Collapse <Border Grid.Row="1"> <Grid> <StackPanel Orientation="Horizontal" VerticalAlignment="Center"> <TextBlock Margin="10,4">Rotation: </TextBlock> <Slider ... /> <TextBlock ... /> <TextBlock> degrees</TextBlock> </StackPanel> <TextBlock HorizontalAlignment="Right" Margin="10,4"> <Hyperlink>Start new race</Hyperlink> </TextBlock> </Grid> </Border> That abridged XAML results in the following visual entity: The entire command strip area is contained within a Border element, which is in the bottom row of the main Grid panel. The Border's Child is another Grid panel, which is represented by the yellow rectangle in the diagram above. That Grid contains two UIElements; a StackPanel and TextBlock. Not all panels can have more than one child element, which is why Grid was used. On a side note, Grid by default has one row and one column, which is why the XAML which declares that Grid does not specify them. The StackPanel, represented by a blue rectangle in the diagram, is an interesting panel in that it can have any number of child elements, but they all will be tightly arranged next to each other vertically or horizontally (hence, it "stacks" the child elements). I chose to use StackPanel to contain the Slider, and surrounding TextBlocks, because it's an easy way to create a horizontal arrangement of related elements. The StackPanel automatically sizes to the elements contained within it. That explains why in the diagram above, the blue rectangle does not stretch all the way to the righthand side of its parent Grid. It is just wide enough to allow the TextBlocks and Slider to be fully visible. Positioning elements within a panel There are two common ways to fine-tune the location of an element within a panel. One way is to set the element's Margin property and the other is to set its HorizontalAlignment and/or VerticalAlignment properties. We can see both of those techniques put to use on the TextBlock which contains the Hyperlink on the right-hand side of the command strip: Collapse <TextBlock HorizontalAlignment="Right" Margin="10,4"> <Hyperlink>Start new race</Hyperlink> </TextBlock> Setting the HorizontalAlignment property to 'Right' will force the TextBlock to be "pushed" over to the right-hand side of the Grid. Setting the Margin property to "10,4" is shorthand for saying that it should be at least 10 device-independent pixels (DIPs) away on its left and right edges from any surrounding elements, and at least 4 DIPs away on its top and bottom edges from any surrounding elements. The net effect of those two property settings is that the Hyperlink will be displayed on the right-hand side of the Grid, but with a little space between its right edge and the Grid's right edge. The concept to take away from this is that panels will perform the "broad strokes" for positioning child elements, but the child elements can perform the "finishing touches" to position themselves exactly where they need to be. External links The Layout System Panels Overview Panel Panel Hierarchy Attached Properties Overview History April 2, 2007 - Created the article. Part 3 - Data Binding Introduction This is the third article in an introductory series about the Windows Presentation Foundation. In the previous article we examined layout panels and how they are used to create WPF user interfaces. In this article we will explore the world of data binding, and how it is put to use in the WPF Horse Race demo application (which is available for download at the top of the first article in this series). For a comprehensive review of WPF data binding be sure to refer to the links listed in the External links section at the bottom of the page. This article covers the bare essentials of WPF data binding, and demonstrates various ways in which the WPF Horse Race application uses data binding. Background Data binding in the user interface layer is nothing new. It has been around for quite some time, in various UI platforms, both for desktop and Web applications. The basic idea is that you "bind" the visual elements (controls) in a user interface to the data objects they are meant to display. The binding infrastructure then manages the data interactions from then on, so that modifications to the UI controls are reflected in the data objects, and vice versa. The major benefit of using data binding is that it reduces the amount of code the application developer needs to write. The architects of some earlier UI platforms have done a good job integrating data binding with the rest of their framework. WPF's architects have done an amazing job integrating data binding with all aspects of their framework. Data binding in WPF is ubiquitous and seamless. It is so powerful and flexible that it literally forces you to change the way you think about designing and developing user interfaces. With one simple API you can bind to domain/business objects, XML data, visual elements, ADO.NET data containers, collections, and basically anything else you can think of. You can use value converters to execute arbitrary data manipulation operations when bound values are passed back and forth. You can perform data validation by creating custom validation rules and applying them to a binding. The list goes on. Data binding in WPF is really a huge step forward. Dependency properties Before diving into the guts and glory of WPF data binding, it is necessary to take a detour and briefly discuss another fundamental feature of WPF which makes it all possible. In WPF a property can only be bound if it is a dependency property. Dependency properties are like normal .NET properties on steroids. There is a whole dependency property infrastructure in place, which provides an array of features for application developers to make their lives easier. For our purposes in this article it is important to know the following things about dependency properties: 1. They can determine their value by retrieving it from a Binding object (i.e. they can be bound). 2. They can participate in property value inheritance, meaning that if a dependency property on an element does not have a value it will use the value of that property on an ancestor element (in the logical tree). This is somewhat analogous to ambient properties in the Windows Forms world. 3. They can be set in XAML, just like a normal property. The reasons why those aspects of dependency properties are important will become clear later on, as we examine how data binding uses them. For more information about dependency properties, refer to the Dependency properties sub-section in the External links section at the bottom of this page. DataContext User interface elements in WPF have a DataContext dependency property. That property has the aforementioned "value inheritance" feature enabled, so if you set the DataContext on an element to a Foo object, the DataContext property on all of its logical descendant elements will reference that Foo object too. This means that all data bindings contained within that root element's element tree will automatically bind against the Foo object, unless explicitly told to bind against something else. This feature is extremely useful, as we will soon see. The Binding class Data binding in WPF revolves around the Binding class. Binding is the sun of the data binding solar system, so to speak. That class has a fairly simple and intuitive API, but it is very powerful. The WPF Horse Race demo application does not nearly use all of the features of the Binding class, but it does make use of some common ones. Let's take a look at the Binding members we will see in action later on in this article. Source - references a Binding's data source. By default this object references the element's DataContext value, which might be inherited from an ancestor element's DataContext, as discussed in the previous section. If you set this property to a non-null value, then the data binding operation will treat that value as the place where data is pushed to and pulled from. Path - is used to indicate from which property on the source object to get and set the bound data value. It is a property of type PropertyPath, which allows it to support a complex range of path expressions. ElementName - can be used as an alternative to the Source property described earlier. It allows you to specify the name of an element to use as a data source. This can be useful when binding a property on one element to a property on another element, particularly when the binding is declared in XAML. Converter - of type IValueConverter. You can set this property to an instance of a class which implements that interface to intercept any movement of data from the binding source to the binding target, or vice versa. Value converters are very convenient and are used quite often. How the WPF Horse Race uses data binding Now that we have a general idea of how data binding is structured, it's about time to see how the WPF Horse Race demo application uses it. We won't examine each place that data binding is used in the application; some of them are better saved for a later article in this series. All of the data binding logic in the application is expressed in XAML, but keep in mind that it is entirely possible to do all of this in the code-behind as well. Displaying a racehorse's name Open the RaceHorseDataTemplate.xaml file, which contains the DataTemplate for the RaceHorse class (if you don't know what a DataTemplate is yet, don't worry, the next article in this series covers that topic). In that file there is a TextBlock named 'horseName' which is bound to the Name property of a RaceHorse object. Here's an abridged version of the XAML I'm referring to: Collapse <TextBlock x:Name="horseName" Text="{Binding Name}" /> When a RaceHorse named 'Sweet Fate' is displayed in the UI, that TextBlock displays its name as seen below: Notice that the binding statement in the XAML snippet above does not explicitly mention that it is setting the Binding's Path property. Markup extensions, such as Binding, can have one property which acts as the "default" property when being set in XAML. Binding's default property is Path. If you are not in favor of using implicit notation like that, the XAML seen below is equivalent to the previous snippet: Collapse <TextBlock x:Name="horseName" Text="{Binding Path=Name}" /> Rotating the race track In the main Window's XAML file (Window1.xaml) there is a Slider control which affects the rotation angle of the "race track" ItemsControl. The ItemsControl has its LayoutTransform property set to a RotateTransform resource, so the Slider must bind to that RotateTransform in order to affect the rotation of the ItemsControl. The result of this binding is that when the user moves the thumb in the Slider, the "race track" rotates, like this: The following XAML is a skeletal outline of the Window, showing only the markup relevant to this particular binding: Collapse <Window> <Grid> <Grid.RowDefinitions> <!-- The top row is for the race track. --> <RowDefinition Height="*" /> <!-- The bottom row is for the command strip. --> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.Resources> <RotateTransform x:Key="trans" Angle="0" /> </Grid.Resources> <!-- The 'Race Track' area. --> <ItemsControl LayoutTransform="{StaticResource trans}" /> <!-- The 'Command Strip' area --> <Border Grid.Row="1"> <Slider Value="{Binding Source={StaticResource trans}, Path=Angle}" /> </Border> </Grid> </Window> Displaying the angle of rotation Directly beneath the Slider in the main Window's XAML is a TextBlock whose Text property is bound to the Slider's Value property. The purpose of this TextBlock is to show the angle of rotation applied to the race track. Since this should display a userfriendly number, and the Slider's Value property is a double, a value converter is used to remove all of the decimal places (basically it casts the double to an int). Here's how that works: Collapse <StackPanel> <StackPanel.Resources> <local:DoubleToIntegerConverter x:Key="conv" /> </StackPanel.Resources> ... <Slider x:Name="rotationSlider" /> <TextBlock Text="{Binding ElementName=rotationSlider, Path=Value, Converter={StaticResource conv}}" /> ... </StackPanel> That binding uses the ElementName property to refer to the Slider element. It also uses a custom value converter to convert the Value property of the Slider to an integer. Below is the code for the value converter: Collapse public class DoubleToIntegerConverter : IValueConverter { public object Convert( object value, Type targetType, object parameter, CultureInfo culture ) { return (int)(double)value; } public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture ) { throw new NotSupportedException( "Cannot convert back" ); } } Calculating the width of a racehorse's progress indicator As a racehorse "runs the race" it is followed by a green progress indicator. That indicator's width represents what percent of the race the horse has completed as seen below: Determining the width of the progress indicator requires two pieces of information: the percent of the race completed by the racehorse, and the total distance which the racehorse must travel to reach the finish line. Based on what we have seen of data binding in WPF so far, this seems like a problem. How can you bind the width of an element to a value whose calculation requires two numbers? That's where the MultiBinding and IMultiValueConverter types come into play. Here's an approximation of how that works: Collapse <Border x:Name="racePit"> <Grid> <StackPanel> <StackPanel.Resources> <local:RaceHorseProgressIndicatorWidthConverter x:Key="WidthConv" /> </StackPanel.Resources> <!-- This Rectangle "follows" a horse as it runs the race. --> <Rectangle x:Name="progressIndicator" Fill="{StaticResource RaceInProgressBrush}" > <!-- The progress indicator width is calculated by an instance of the RaceHorseProgressIndicatorWidthConverter class. --> <Rectangle.Width> <MultiBinding Converter="{StaticResource WidthConv}"> <Binding Path="PercentComplete" /> <Binding ElementName="racePit" Path="ActualWidth" /> </MultiBinding> </Rectangle.Width> </Rectangle> </StackPanel> </Grid> </Border> The multi-value converter used to calculate the progress indicator's width is seen below: Collapse public class RaceHorseProgressIndicatorWidthConverter : IMultiValueConverter { public object Convert( object[] values, Type targetType, object parameter, CultureInfo culture ) { int percentComplete = (int)values[0]; double availableWidth = (double)values[1]; return availableWidth * (percentComplete / 100.0); } public object[] ConvertBack( object value, Type[] targetTypes, object parameter, CultureInfo culture ) { throw new NotSupportedException( "Cannot convert back" ); } } External links Dependency properties Dependency Properties Overview Property Value Inheritance Data binding Data Binding Overview � highly recommended Binding Sources Overview Binding Class Piping Value Converters in WPF History April 3, 2007 � Created the article. Part 4 - Data Templates and Triggers Introduction This is the fourth article in an introductory series about the Windows Presentation Foundation. In the previous article we examined data binding and how it is used to display information in WPF user interfaces. In this article we examine data templating and triggers, and how they are used in the WPF Horse Race demo application (which is available for download at the top of the first article in this series). Just like the other articles in this series, this article does not cover its subject matter in exhaustive detail. Instead we will examine just enough of the basics so that we can see how those features are put to use in the demo app. If you want to learn more about how data templating and triggers can be used, refer to the External links section for additional information. Background So far in this series of articles, we have seen how XAML, layout panels, and data binding are used to create user interfaces that display simple data. Those fundamental building blocks, however, serve as the foundation upon which more powerful and compelling features of WPF depend. One of those higher-level features allows you to easily describe how an object of any type can be rendered. In other words, it enables you to create a template, in XAML, of visual elements which can be "expanded" at runtime to render a data object. Of course, the feature I'm referring to is known as "data templating". Data templates are only one type of template in WPF. This article, in fact this entire series of articles, will not examine the other types of templates because the WPF Horse Race demo application does not use them. In case you are interested, there are also control templates, items panel templates, and hierarchical data templates. Refer to the Other templates sub-section in the External links section toward the bottom of this article for links to information about them. Another feature of WPF covered in this article is called "triggers". Triggers are another fundamental building block in WPF, upon which many parts of the framework depend. They are, in general, a means of conditionally applying values to properties. Triggers are especially useful when you are writing XAML because they provide a means of evaluating properties at runtime and taking certain actions based on their values. In that sense triggers are a gray area somewhere between the declarative world of XAML markup and the imperative world of the code-behind. The DataTemplate class All of WPF's templating functionality is based on the FrameworkTemplate class. DataTemplate derives from FrameworkTemplate, as do all other classes used for templating purposes. This familial relationship between the templating classes is largely irrelevant for typical WPF development scenarios because in practice, you will create templates in XAML, and will not use them in a polymorphic manner. It is far more straightforward and expedient to create a DataTemplate in XAML than it is to create one programmatically. The code for creating templates is actually very cumbersome, while the XAML for creating the same template is clean and simple. It is technically possible to construct data templates programmatically, but even Microsoft does not recommend it. Without a DataTemplate Before we dive into the data template used in the WPF Horse Race application let's see how a simple example works. First take a look at a simple class which represents a river: Collapse namespace WPF_Test { public class River { string name; int milesLong; public string Name { get { return name; } set { name = value; } } public int MilesLong { get { return milesLong; } set { milesLong = value; } } } } If we were to display an instance of this class in a ContentControl, the XAML would be something like this: Collapse <StackPanel> <StackPanel.Resources> <local:River x:Key="theRiver" Name="Colorado River" MilesLong="1450" /> </StackPanel.Resources> <ContentControl Content="{StaticResource theRiver }" /> </StackPanel> The UI created by that XAML looks like the following: That certainly isn't too impressive. What you see above is what gets displayed as a result of calling ToString() on the River object referenced by the ContentControl. Let's take a moment to examine what is going on here. The example above creates an instance of the River class, which represents the Colorado River. It also creates a ContentControl which is told to display that River object somehow. The ContentControl examines the River object and tries to figure out how to render it, but since River does not derive from UIElement it has no way of knowing how to do so. Once ContentControl is devoid of options, it ends up calling ToString() on the River object and then displays that text. With a DataTemplate Now that we've seen how boring a River object looks in the absence of data templates, it's time to add one into the mix. Here's the same XAML used before, only this time there is a template for the River class: Collapse <StackPanel> <StackPanel.Resources> <DataTemplate DataType="{x:Type local:River}"> <Border BorderBrush="Blue" BorderThickness="3" CornerRadius="12"> <Grid Margin="4"> <TextBlock> <Run Text="The"/> <TextBlock Text="{Binding Name}"/> <Run Text="is"/> <TextBlock Text="{Binding MilesLong}" /> <Run Text="miles long." /> </TextBlock> </Grid> </Border> </DataTemplate> <local:River x:Key="theRiver" Name="Colorado River" MilesLong="1450" /> </StackPanel.Resources> <ContentControl Content="{StaticResource theRiver}" /> </StackPanel> This is how that XAML renders: The River object is displayed much more intelligently when a data template is applied. The information held within it is now displayed as part of a sentence, and that sentence is wrapped in a curved blue border. Keep in mind that the rendition of a River object seen above is completely arbitrary. It can be displayed in whatever way is considered appropriate for the application in which it exists. Triggers Another feature of WPF often used in conjunction with templates is known as "triggers". In general a trigger is somewhat like an if block in procedural code; it only executes what it contains when some condition evaluates to true. For example, here is a modified version of the XAML seen in the previous section. This time the DataTemplate has a Trigger in it which sets the Border's Background property to 'LightGray' and the TextBlock's Foreground property to 'Red' when the mouse cursor is anywhere over the Border. Collapse <StackPanel> <StackPanel.Resources> <DataTemplate DataType="{x:Type local:River}"> <Border x:Name="bdr" BorderBrush="Blue" BorderThickness="3" CornerRadius="12" > <Grid Margin="4"> <TextBlock x:Name="txt"> <Run Text="The"/> <TextBlock Text="{Binding Name}"/> <Run Text="is"/> <TextBlock Text="{Binding MilesLong}" /> <Run Text="miles long." /> </TextBlock> </Grid> </Border> <DataTemplate.Triggers> <Trigger SourceName="bdr" Property="IsMouseOver" Value="True"> <Setter TargetName="bdr" Property="Background" Value="LightGray"/> <Setter TargetName="txt" Property="Foreground" Value="Red"/> </Trigger> </DataTemplate.Triggers> </DataTemplate> <local:River x:Key="theRiver" Name="Colorado River" MilesLong="1450" /> </StackPanel.Resources> <ContentControl Content="{StaticResource theRiver}" /> </StackPanel> When that XAML is used, the UI looks like this when the cursor is over the River visuals: When the cursor is moved away from the River visuals, the background and foreground colors automatically revert to their previous values. Triggers handle all of that functionality for us, as seen below: There are several kinds of triggers, each has a different way of determining when it should execute. In the remainder of this article, we will only focus on the two types of triggers used in the WPF Horse Race demo application: DataTrigger and MultiDataTrigger. Triggers can be used in other places outside of templates, such as within Styles and on any FrameworkElement subclass, but this article only shows them being used within a DataTemplate. For more information about triggers, refer to the Triggers sub-section of the External links section toward the bottom of this article. How the WPF Horse Race uses Data Templates and Triggers The WPF Horse Race demo application has one custom DataTemplate, which is used to render instances of the RaceHorse class. The template declaration can be found in the RaceHorseDataTemplate.xaml file. The full template XAML is not shown in this article because it is rather lengthy and would serve no purpose being shown in its entirety. Instead we will examine it piecemeal. The Visuals for a RaceHorse The visuals elements in the RaceHorse data template can be distilled down to this basic structure: Collapse <Border> <Grid> <StackPanel Orientation="Horizontal"> <!-- This Rectangle is the "progress indicator" which follows the horse. --> <Rectangle /> <!--This Image displays the picture of a race horse. --> <Image /> </StackPanel> <!-- Displays the horse's name. --> <TextBlock /> </Grid> </Border> Here is a visual explanation for how the elements in the template correspond to what you see as a RaceHorse runs a race: (Note: The yellow rectangle in the image above, which represents the StackPanel, was added just for the sake of clarity. It does not actually appear on screen when the application is running.) The root Border element defines the boundaries of the RaceHorse's "race pit" (the area in which it runs). The Border contains a Grid panel, which, in turn, contains a StackPanel and a TextBlock. The StackPanel holds the RaceHorse's progress indicator and a picture of a horse. A StackPanel was used to hold those two elements so that as the Rectangle (i.e. the progress indicator) becomes wider, the Image will be moved to the right along with it. The TextBlock displays the RaceHorse's name. Since the TextBlock is declared beneath the StackPanel (lexically), it will be rendered on top of the StackPanel, with respect to the z-order. This ensures that the horse's name is always visible. Setting properties on the winner of a race After the template's visuals are declared, there are also some trigger declarations. One of them is a DataTrigger. A DataTrigger executes its contents when the supplied Binding returns a specific value. Here is the XAML for that trigger: Collapse <!-- Set special values for the winner of the race. --> <DataTrigger Binding="{Binding IsWinner}" Value="True"> <Setter TargetName="progressIndicator" Property="Fill" Value="{StaticResource WinnerBrush}" /> <Setter TargetName="horseName" Property="Foreground" Value="Black" /> <Setter TargetName="horseName" Property="FontWeight" Value="Bold" /> <Setter TargetName="horseName" Property="HorizontalAlignment" Value="Center" /> </DataTrigger> That DataTrigger waits for the templated RaceHorse's IsWinner property to change from false to true. When that happens (i.e. the RaceHorse wins a race) all Setters contained within it are executed. The Setter class provides a way to assign a value to a property. It is especially convenient when used in XAML, but can be used in the code-behind as well. When a RaceHorse wins a race the Setters modify the template's visuals, so that the progress indicator is rendered with a golden brush and its name stands out visually. In the screenshot below, the RaceHorse named 'Fresh Spice' won the race: When the next race starts and Fresh Spice's IsWinner property is set back to false, the properties affected by that trigger will automatically be reverted back to their previous values. In case you are curious as to how the DataTrigger knows when the IsWinner property value changes, the answer lies in the fact that RaceHorse implements INotifyPropertyChanged. A RaceHorse object's PropertyChanged event is raised when its IsWinner property value changes. The Binding class is aware of the INotifyPropertyChanged interface and is notified when the PropertyChanged event has been raised for the IsWinner property. Fading Away Losing RaceHorses When a RaceHorse completes a race but does not finish first, it turns red and then fades away. By "fades away," I mean that the Opacity property of the RaceHorse's Border element is animated down to 60% over three quarters of a second. This article does not cover WPF's support for animation, but it is important to know that animations can be started in response to a trigger being executed. In order to change the color of and fade away a RaceHorse which lost a race, a trigger must know two pieces of information: whether the RaceHorse finished the race yet, and if it lost the race. This type of "AND" expression can be implemented with a MultiDataTrigger, as seen below: Collapse <!-- This MultiDataTrigger affects losers of the race. --> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsFinished}" Value="True" /> <Condition Binding="{Binding IsWinner}" Value="False" /> </MultiDataTrigger.Conditions> <!-- Apply the "finished the race" brush to the horse's progress indicator. --> <Setter TargetName="progressIndicator" Property="Fill" Value="{StaticResource FinishedBrush}" /> <!-- Fade the race pit in and out if the horse lost the race. --> <MultiDataTrigger.EnterActions> <!-- Fade away the RaceHorse's Border element when it loses a race. --> </MultiDataTrigger.EnterActions> <MultiDataTrigger.ExitActions> <!-- Fade in the RaceHorse's Border element when a new race starts. --> </MultiDataTrigger.ExitActions> </MultiDataTrigger> External links DataTemplates Data Templating Overview Styling and Templating DataTemplate Class FrameworkTemplate Class Triggers Trigger Class DataTrigger Class MultiDataTrigger Class EventTrigger Class Other templates ControlTemplate Class ItemsPanelTemplate Class HierarchicalDataTemplate Class History April 6, 2007 – Created the article. License This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL) Part 5 - Styles Introduction This is the fifth article in an introductory series about the Windows Presentation Foundation. In the previous article we examined data templates and triggers to see how the rendering of data objects can be described in XAML. In this article we examine styles, and how they are used in the WPF Horse Race demo application (which is available for download at the top of the first article in this series). Just like the other articles in this series, this article does not cover its subject matter in exhaustive detail. Instead we will examine just enough of the basics so that we can see how those features are put to use in the demo app. If you want to learn more about how styles can be used, refer to the External links section for additional information. Background WPF borrows many concepts from both the Web and desktop programming worlds. The separation of user interface layout from behavior (i.e. XAML vs. code-behind) is a tip of the hat to ASP.NET. The extensive set of event-driven APIs for detailed control over user interaction is reminiscent of the Windows Forms programming model. WPF can be thought of as a distillation of the best features found in various UI platforms, plus a wide range of new features as well. One major contribution made to WPF by the world of Web development was the concept of styles. Styling in WPF is somewhat similar to how Cascading Style Sheets (CSS) is used by Web developers. The basic idea of styling in WPF is that you can define a Style object which is applied to a specific type of element in the UI, such as a Button or TextBox. That style can be applied to every instance of the element type, or you can selectively apply it to certain instances. Styling in WPF really just boils down to a convenient way to apply property settings to one or more visual elements. Style vs. theme Before we start looking into styles, it is important to draw a distinction between styles and themes. Operating systems have "themes", WPF applications have "styles". Themes are an OS-level concept: such as the blue, green, and silver themes seen in Windows XP, or the Aero theme in Vista. WPF applications automatically detect the current OS theme and use that color scheme wherever possible, and can even programmatically choose which theme to use. A style exists only within one WPF application, or just one Window in a WPF application, or even just one portion of a Window for that matter. Styles cannot be applied to anything outside of the WPF application in which they exist, nor can they be applied to any WinForms controls hosted within a WPF Window. For more information about theme support in WPF, refer to the External links section at the end of this article. The Style class The entire styling infrastructure in WPF is based on the Style class. It has a relatively small set of public members, which makes it easy to grasp how styling works. Instances of the Style class are almost always created in XAML, but it is possible to create them in code if necessary. Here are some of the properties of Style we will see in use later: Resources � is a ResourceDictionary where you can put objects used only within the Style, such as brushes, value converters, etc. Setters � a collection of Setter and EventSetter objects that apply values to properties, or assign handlers to events. This is the content property of the Style class, which makes it very easy to use in XAML. TargetType � indicates on what type of element the Style will be applied, such as TreeView or Button. The styles created in the WPF Horse Race demo application are very simple. There are other common properties of the Style class which they do not use, such as: BasedOn � allows for style inheritance. You can derive one Style from another Style to create customizations without duplicating the core Style's XAML. Triggers � just like in the DataTemplates seen in the previous article in this series, this property contains a collection of triggers that can be used to conditionally apply values to properties. A Style can be applied to any object which derives from FrameworkElement or FrameworkContentElement, both of which expose a public property named Style. Without styles If WPF did not provide a way to stylize elements, you would have to be out of your mind to even consider creating a distinctive, branded look and feel for an application's user interface. To create a visual consistency across all Windows in an application would quickly turn into a maintenance nightmare, especially once changes needed to be made to certain aspects of the visual style. For example, consider the following XAML: Collapse <Border BorderBrush="Black" BorderThickness="2"> <StackPanel> <TextBlock Background="Tan" Margin="2,4">Bike</TextBlock> <TextBlock Background="Tan" Margin="2,4">Car</TextBlock> <TextBlock Background="Tan" Margin="2,4">Truck</TextBlock> </StackPanel> </Border> That simple markup results in something which looks like this: Suppose that the application we are building used to have a rule that TextBlocks must always have a tan background color, but one day a big wig at our imaginary company decides that tan is no longer a good color. Instead, now all TextBlocks should have a light gray background color. If our application only had the three TextBlocks seen in the snippet above, that wouldn't be too much trouble. All we would have to do is update those three property settings: Collapse <Border BorderBrush="Black" BorderThickness="2" Margin="10"> <StackPanel> <TextBlock Background="LightGray" Margin="2,4">Bike</TextBlock> <TextBlock Background="LightGray" Margin="2,4">Car</TextBlock> <TextBlock Background="LightGray" Margin="2,4">Truck</TextBlock> </StackPanel> </Border> But if our application happens to contain hundreds or even thousands of TextBlocks, then we are in trouble. Suddenly we would be wishing that there was an easy way to set every TextBlock's Background property to light gray. With styles Fortunately there is an easy way to set properties on any number of elements: "styles". Let's see how a Style can be used to solve the problem described in the previous section. Here's one way to do it: Collapse <Border BorderBrush="Black" BorderThickness="2"> <StackPanel> <StackPanel.Resources> <Style x:Key="TxtBlkStyle" TargetType="{x:Type TextBlock}"> <Setter Property="Background" Value="LightGray" /> <Setter Property="Margin" Value="2,4" /> </Style> </StackPanel.Resources> <TextBlock Style="{StaticResource TxtBlkStyle}">Bike</TextBlock> <TextBlock Style="{StaticResource TxtBlkStyle}">Car</TextBlock> <TextBlock Style="{StaticResource TxtBlkStyle}">Truck</TextBlock> </StackPanel> </Border> The XAML seen above creates a Style in the StackPanel's Resources collection. That Style targets elements of type TextBlock, and sets their Background and Margin properties. Notice that the Style has a key, 'TxtBlkStyle'. The Style property on each TextBlock is then explicitly set to reference that Style. It results in the following UI: It turns out that there's an even easier way to accomplish this task. If you do not give a Style a key, then it will automatically be applied to all elements whose type matches the TargetType of the Style. Here's an example: Collapse <Border BorderBrush="Black" BorderThickness="2"> <StackPanel> <StackPanel.Resources> <Style TargetType="{x:Type TextBlock}"> <Setter Property="Background" Value="LightGray" /> <Setter Property="Margin" Value="2,4" /> </Style> </StackPanel.Resources> <TextBlock>Bike</TextBlock> <TextBlock>Car</TextBlock> <TextBlock>Truck</TextBlock> </StackPanel> </Border> The solutions seen so far do not really solve the overall problem. If we want the entire application to contain light gray TextBlocks, then we need to move our Style to a higher place in the resource tree, such as the Application's resources (read more about that here and here). This way, all TextBlocks in the app will be able to use the Style we created. Here is an example of this technique: Collapse <!-- App.xaml --> <Application x:Class="WPF_Test.MyApp" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Window1.xaml" > <Application.Resources> <Style TargetType="{x:Type TextBlock}"> <Setter Property="Background" Value="LightGray" /> <Setter Property="Margin" Value="2,4" /> </Style> </Application.Resources> </Application> <!--Somewhere inside of SomeWindow.xaml --> <Border BorderBrush="Black" BorderThickness="2"> <StackPanel> <TextBlock>Bike</TextBlock> <TextBlock>Car</TextBlock> <TextBlock>Truck</TextBlock> </StackPanel> </Border> Since the Application's resource collection is the most visible place to put a Style, all TextBlocks in every Window in the app will use that Style. It is possible to override that Style in any Window or anywhere in a Window's element tree, but by default that Style will be used by all TextBlocks in the application. How the WPF Horse Race uses styles The WPF Horse Race demo application creates two Styles. One of them is used to affect the Border element in the data template for the RaceHorse class (as seen in the previous article in this series). The other provides common values for properties of TextBlocks in the application's "Command Strip" area, toward the bottom of the Window. Race pit border style If the RaceHorse data template's root Border element did not have a Style applied to it, the UI would look like this: With the Style applied, it looks like this: In the RacePitBorderStyle.xaml file, you will find a ResourceDictionary which contains a Style whose key is 'RacePitBorderStyle'. That file contains the following XAML: Collapse <!-- This resource dictionary contains the Style applied to each horse's race pit. --> <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > <Style x:Key="RacePitBorderStyle" TargetType="Border"> <Style.Resources> <LinearGradientBrush x:Key="BackBrush" StartPoint="0.5,0" EndPoint="0.5,1" > <GradientStop Color="#88000000" Offset="0.1" /> <GradientStop Color="#CC000000" Offset="0.9" /> </LinearGradientBrush> <LinearGradientBrush x:Key="BorderBrush" StartPoint="0.5,0" EndPoint="0.5,1" > <GradientStop Color="#18000000" Offset="0.1" /> <GradientStop Color="#08000000" Offset="0.9" /> </LinearGradientBrush> </Style.Resources> <Setter Property="Background" Value="{StaticResource BackBrush}"/> <Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/> <Setter Property="BorderThickness" Value="1" /> <Setter Property="CornerRadius" Value="8" /> <Setter Property="Margin" Value="2,4" /> </Style> </ResourceDictionary> That Style is applied to the root Border element in the RaceHorse data template, as seen below: Collapse <!-- RaceHorseDataTemplate.xaml --> <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfHorseRace" > <!-- Import the resource dictionary which contains the Style applied to Border of each horse's "pit". --> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="RacePitBorderStyle.xaml" /> </ResourceDictionary.MergedDictionaries> <DataTemplate DataType="{x:Type local:RaceHorse}"> <Border x:Name="racePit" Style="{StaticResource RacePitBorderStyle}"> <!-- Other elements omitted for clarity. --> </Border> </DataTemplate> </ResourceDictionary> Command strip text style The other Style used in the demo application is implicitly applied to its target elements. This Style can be seen in Window1.xaml, in the "Command Strip" section of the XAML. Collapse <StackPanel Orientation="Horizontal"> <StackPanel.Resources> <!-- This Style is applied to all TextBlock elements in the command strip area. --> <Style TargetType="TextBlock"> <Setter Property="VerticalAlignment" Value="Center" /> <Setter Property="Foreground" Value="#EE000000" /> </Style> </StackPanel.Resources> <TextBlock>Rotation: </TextBlock> <Slider ... /> <TextBlock ... /> <TextBlock> degrees</TextBlock> </StackPanel> Notice that this Style's TargetType is set to "TextBlock". When the TargetType is set to a type that is part of the WPF framework, you do not have to use the more cumbersome {x:Type TypeName} syntax seen previously in this article. Since the Style seen above does not have a key, it is automatically applied to all TextBlock elements in the StackPanel. External links Styles and themes Default Templates in WPF � highly recommended Styles Style Class Styling and Templating Introduction to Styling and Templating Sample Nick's Twisted Perspectives on Styles and Templates Styles and Triggers in WPF Themes Create and apply custom themes Theme support in Windows Presentation Foundation Vista look on Non-Aero themes Microsoft.Windows.Themes Namespace History April 12, 2007 - Created the article. License This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)