Windows Presentation Foundation (hereafter referred to as WPF) is a new API for creating graphical user interfaces for the Windows platform. It is an alternative to WinForms that further empowers developers by providing an API capable of taking full advantage of the multimedia facilities of the modern PC. Unlike WinForms, it does not wrap Win32 but is completely new, built from the ground up using .NET. The fundamental power of WPF comes from the fact that it is vector based, hardware accelerated and resolution independent.
Reminiscent of WinForms, you’ll find many of the controls you are used to when building applications: Button,ComboBox, ListBox, etc. However, in WPF, your UI design is represented in a completely different fashion. Instead of using a designer generated code file or a resource file as the source of a UI definition, WPF uses XML. Specifically, it uses XAML. Please, don’t let this frighten or dissuade you. It’s surprisingly intuitive and much easier than you would think (not to mention fun). There are many other new features in WPF, too many to discuss in this tutorial. Below, you can see a chart depicting what I consider to be some of the most important features/innovations in WPF.
Figure 1: Important WPF Features
The right column of the chart lists some technologies that you may be familiar with that parallel the ideas in WPF. Hopefully this gives you a starting point for understanding the terms on the left. Don’t take the parallels too literally; there are differences. We’ll talk about each of these areas over the course of this article and I will provide additional resources for going into greater depth as well.
Besides the above mentioned features, there is a new type of application. The standard Windows application is alive and kicking, but you now have an additional option: the XAML Browser Application (.xbap). When you deploy this type of application to a web server, users can navigate to a URL and run your program from within the IE sandbox. The experience is similar to using a Flash application, but is restricted to users of IE (versions 6 and 7 and any browser that supports the WebControl, such as Firefox through its IE tab). You can begin to imagine the types of rich intranet applications you could build for a company if you were given IE and WPF as the platform. At the same time, it’s too bad that it only works in IE. To address this issue, Microsoft is currently working on Silverlight(formerly WPF/E), a cross-platform, cross-browser subset of the .NET Framework and WPF.
If you want to work with WPF (part of .NET 3.0) there are a few things you are going to need. First off, .NET 3.0 was designed to work on Vista, XP (SP2) and Windows Server 2003 (SP1). Any one of these OS’s and the .NET 3.0 runtime will allow you to run applications built with WPF. You can get the current version of the runtime here. Installing the runtime will add .NET 2.0 if you don’t have it and if you do, it will just add the new assemblies (marked with a 3.0 version designation). I’m guessing that most of you will want to do some development; for that you will need a few more things. The necessities are a version of Visual Studio 2005 and the Windows SDK. (Watch out! The download size is rather large.) If you want to have a more pleasant development experience, you should install the Visual Studio S2005 extensions. Once you get up and running with these pieces I recommend you explore theofficial site a little bit.
Before I dig into more technical issues I would like to mention a few general items available for learning and working with WPF. If you have installed the VS extensions for .NET 3.0, then you now have the Orcas designer available to you. This is a tool much like the WinForms designer, but it is obviously for creating WPF UIs. It has a tabbed design, so that you can switch back and forth between the visual designer and XAML markup. The powerhouse design tool isBlend (formerly Expression Interactive Designer), which you can get here. If you are more graphically inclined, you are going to want to check this out. It is similar to using a tool like Adobe Illustrator, but it generates XAML and integrates with the VS solution and MSBuild. This is a landmark tool because it makes designers fully fledged members of the development team. No longer do they need to do a UI mockup and deliver it to a developer to implement (or turn down because of its complexity). They can simply build the UI using a set of tools that they are familiar with. A similar third party tool is Mobiform’s Aurora. I highly recommend investigating it. There are also numerous plug-ins for exporting 2D/3D art to XAML from most popular programs. Regardless of whether you are approaching WPF from a developer or designer’s perspective, you are probably going to want a good book on the technology. Currently, the two best resources I know of are Charles Petzold’s book Applications = Code + Markupand Adam Nathan’s book Windows Presentation Foundation Unleashed.
XAML
XAML stands for eXtensible Application Markup Language. You can think of it as HTML for Windows applications, but it is really quite a bit more expressive and powerful. For those of you code junkies out there, XAML is really no more than a special type of CLR object serialization. It was intentionally architected to serialize CLR object graphs into anXML representation that is both verbose and human readable. The reasoning behind this was to make it possible for developers to easily edit the markup by hand and enable the creation of powerful graphical tools that could generate markup for you behind the scenes. When you compile an application that contains XAML files, the markup gets converted into BAML. BAML is a tokenized, binary representation of XAML. This binary representation is then stored inside the application’s resources and loaded as needed by WPF during the execution of your program. The main advantage of this approach is that you get a faster UI load time by reading a binary stream than through parsing XML.
The best way to get to know XAML is by looking at some examples. So without further ado, let’s begin by comparing two markup fragments:
HTML Fragment
1. <div style="border: solid 5px black; margin: 10px; padding: 5px">
2. <input type="button" value="Click Me!" />
3. </div>
XAML Fragment
1. <Border BorderBrush="Black" BorderThickness="5" Margin="10" Padding="5">
2. <Button>Click Me!</Button>
3. </Border>
Figure 2: A Button with Border
Both of these fragments accomplish the same effect on their respective platforms: creating a button with a border around it (Note: Declaring styles inline is not a best practice in XAML any more than HTML; this is for demonstration purposes only.). I started with this example so that we could compare XAML to something more familiar, HTML. I think that if you are honest, however, you will agree that the XAML fragment is much more expressive than the HTMLfragment. It is intuitive. One of the reasons for this is that, generally speaking, the element tags map directly toTypes and the attributes map to Properties on those Types. Since WPF’s class hierarchy was very carefully designed, you’ll find that many elements have the same available attributes. This consistency makes for smooth, natural learning.
Using XAML, you can represent a wide variety of UI related constructs. These include things like controls, layout panels, graphics primitives, 3D content and even animations. Let’s look at another example. This time we will draw a graphics primitive:
1. <Ellipse Width="200" Height="150">
2. <Ellipse.Fill>
3. <LinearGradientBrush>
4. <GradientStop Offset="0" Color="Teal"/>
5. <GradientStop Offset="1" Color="Aqua"/>
6. </LinearGradientBrush>
7. </Ellipse.Fill>
8. </Ellipse>
Figure 3: An Ellipse
The first line of this example is pretty simple. We have an Ellipse whose Height and Width properties are being set. The child element of Ellipse, on the other hand, is something different than what we have previously seen. In this case we wanted to represent a linear gradient brush for filling the Ellipse. But how could we represent all of that information using the standard attribute syntax? It turns out that this is a very common scenario, and to solve the problem we have something called Property Element Syntax. In XAML, you can turn any attribute (property on the class) into a child element by using this pattern: ElementName.AttributeName (TypeName.PropertyName). So in this case we are assigning the enclosed linear gradient brush to the Fill property of the Ellipse. If you’re very keen, you’ll notice that there is a discrepancy between what I just told you and what is happening with the child nodes ofLinearGradientBrush. I have defined a set of gradient stops for the brush, but what property are they being assigned to on the brush? In fact they are being assigned to the GradientStops property, but where is the element name LinearGradientBrush.GradientStops? How does the XAML parser know to assign to this property? Again, we have a common scenario. Any object can declare a default content property to which its child nodes will be assigned automatically. In this case GradientStops is the default content property. You have actually seen this in action in the very first example:
1. <Button>Click Me!</Button>
On the button there is a property called Content that is set as the default content property for the button. This is how the XAML parser knows what to do with the text Click Me! Let’s do one final example. This time we’ll do something different with the button’s content, since it is a bit of a special property:
1. <Button>
2. <Ellipse Width="200" Height="150">
3. <Ellipse.Fill>
4. <LinearGradientBrush>
5. <GradientStop Offset="0" Color="Teal"/>
6. <GradientStop Offset="1" Color="Aqua" />
7. </LinearGradientBrush>
8. </Ellipse.Fill>
9. </Ellipse>
10. </Button>
Figure 4: A Button with Ellipse content
It happens that the Button (and many other controls) inherits from a class called ContentControl. This is where it gets its Content property, mentioned earlier. The type of Content is object, which allows you to fill it with any CLRtype. For most objects, WPF will simply call ToString() and render the value. However, you can also set the content to any renderable WPF object. This means that the button’s content could just as easily have been an image, a 3D animation or video. This is not a feature of XAML, but is part of the design of WPF, sometimes referred to as theContent Model. The flexibility of this architecture makes for seemingly endless possibilities when composing WPF UI’s.
If XAML hasn’t totally clicked yet, don’t worry. You are going to see many more examples. The more you look at it, the more it will make sense. Nevertheless, the best way to learn XAML is to play with it yourself. For that, I would recommend XamlPad; a text editor that renders your XAML on the fly. It comes with the SDK. There are several other similar tools that can be found on the web.
UI Layout
When building a UI, one of the first issues you will deal with is how to arrange all of the UI pieces on screen. In previous MS technologies we have had limited support for layout. .NET 2.0 offers WinForms developers some long awaited options in this area. WPF, however, has made layout a first class citizen from the beginning. There is quite a variety of layout options to choose from. I can only cover a few here, so be sure to look in the SDK for things that inherit from Panel in order to see more of what is available. Let’s begin with an example:
1. <StackPanel>
2. <TextBlock>My UI</TextBlock>
3. <ListBox>
4. <ListBoxItem>Item 1</ListBoxItem>
5. <ListBoxItem>Item 2</ListBoxItem>
6. </ListBox>
7. <RichTextBox/>
8. </StackPanel>
Figure 5: A StackPanel layout
StackPanel is one of the simplest layout options available. It does exactly what its name implies: stack elements, either vertically or horizontally. Vertical is the default, but you can specify horizontal by setting the Orientationproperty. Most layouts will require more advanced functionality. A typical application will likely use DockPanel as its root layout element:
1. <DockPanel>
2. <TextBlock DockPanel.Dock="Top">My UI</TextBlock>
3. <ListBox DockPanel.Dock="Right">
4. <ListBoxItem>Item 1</ListBoxItem>
5. <ListBoxItem>Item 2</ListBoxItem>
6. </ListBox>
7. <RichTextBox/>
8. </DockPanel>
Figure 6: A DockPanel layout
The DockPanel allows you to dock elements to the top, bottom, left or right of the container. The last element will, by default, fill the remaining space. This XAML sample presents a new markup construct that I intentionally did not discuss in the previous section: attached properties. Notice that both TextBlock and ListBox have a DockPanel.Dockattribute on them. If you examine these classes, you will find that neither has a property that looks like this. What you will find is that DockPanel declares a DependencyProperty called DockProperty. This special type ofDependencyProperty, called an Attached Property, allows a parent control to store information with its children (We don’t have time to get into details on DependencyProperties. For a great discussion, see Charles Petzold’s book). In this case, the DockPanel is storing information it needs for layout with its child controls. In XAML this manifests itself as an attribute on the child element in the form: ParentName.AttachedPropertyName. Let’s look at another example of this syntax used by Grid:
1. <Grid>
2. <Grid.RowDefinitions>
3. <RowDefinition Height="*"/>
4. <RowDefinition Height="3*"/>
5. </Grid.RowDefinitions>
6.
7. <Grid.ColumnDefinitions>
8. <ColumnDefinition/>
9. <ColumnDefinition/>
10. </Grid.ColumnDefinitions>
11.
12. <TextBlock Grid.ColumnSpan="2">My UI</TextBlock>
13. <ListBox Grid.Row="1" Grid.Column="0">
14. <ListBoxItem>Item 1</ListBoxItem>
15. <ListBoxItem>Item 2</ListBoxItem>
16. </ListBox>
17. <RichTextBox Grid.Row="1" Grid.Column="1"/>
18. </Grid>
Figure 7: A Grid layout
The Grid is perhaps the most common layout option and it is the most complex that we have looked at. First, notice that inside the Grid, we have to declare our rows and columns. In this grid we have two of each. I have used a grid specific height notation for the rows. In this case I have said “split the space such that the second row takes up three times as much as the first row.” Because I have not declared a width on the columns, the space will be split evenly between the two. Once I have declared the grid’s structure, I use attached properties to place my controls in the appropriate location. This is mostly self explanatory except for the first child (TextBlock), which is missing column and row specifications. The markup works because the grid will default its location to column and row zero.
In a real world application, your layouts are likely to be formed by using various combinations of layout controls nested one in the other. Try to avoid hard coding widths and heights on elements. Let the controls do the work for you, adapting your UI to fit the available space. With a little practice, you will begin to realize the flexibility of WPF’s layout system. There are quite a lot of layout options. Below is a list of other common layout panels that we don’t have time to look at markup for:
- WrapPanel: Similar to stack panel, but wraps elements to the next line when it cannot fit them.
- GroupBox: Groups elements in a titled bounding box.
- Viewport: Scales its child to fit the available space.
- Border: Wraps its child in a border.
- Canvas: Positions child elements exactly according to top and left coordinates (relative to the container).
In addition to these out-of-box options, you can always inherit from Panel and create your own. If you search the web, you will find several examples of this. One great resource is Kevin’s Bag-O-Tricks. He has a couple of custom panels to learn from as well as some good examples of how to build other custom controls. You should also know that there are Margin and/or Padding properties available on most elements for fine grained control of layout. With a little research you will find properties such as VerticalAlignment and HorizontalAlignment, among others, that make WPF’s layout mechanism quite flexible and able to handle real world application needs.
Databinding
Databinding and DataTemplates are perhaps the most powerful features of WPF. Whatever your experiences with databinding in previous technologies, MS or otherwise, I think you will be impressed with the new goods. To begin with, support for datadinding is built into WPF from its core. Almost every graphics/UI object that you will work with in WPF inherits from DependencyObject. The functionality supported by this base is what powers animation, styling and databinding. Without going into too much detail, objects that inherit from DependencyObject support a special type of property called a DependencyProperty. Most of the properties you will work with, such as Text, Content,Width, Height, etc, are DependencyProperties. Any DependencyProperty can be animated, styled and databound. WPFsupport for databinding is so rich, that I will only be able to demonstrate the most basic of examples here. The best resource for digging deeper is Beatriz Costa’s blog. I recommend that you start from the very first post and read everything up to the present. This may seem like quite a task, but the time you will save by gaining a thorough understanding of databinding is worth it.
The first example I would like to show is a very common scenario. You have some object representing a Person,Customer, Employee, etc. and you want to display their information in the UI for editing purposes. Here’s some markup:
1. <Grid>
2. <Grid.RowDefinitions>
3. <RowDefinition/>
4. <RowDefinition/>
5. <RowDefinition/>
6. <RowDefinition/>
7. <RowDefinition/>
8. </Grid.RowDefinitions>
9.
10. <Grid.ColumnDefinitions>
11. <ColumnDefinition Width="75"/>
12. <ColumnDefinition/>
13. </Grid.ColumnDefinitions>
14.
15. <Label>First Name:</Label>
16. <TextBox Grid.Column="1" Text="{Binding Path=FirstName}"/>
17.
18. <Label Grid.Row="1">Last Name:</Label>
19. <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding LastName}"/>
20.
21. <Label Grid.Row="2">Street:</Label>
22. <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Address.Street}"/>
23.
24. <Label Grid.Row="3">City:</Label>
25. <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Address.City}"/>
26.
27. <Label Grid.Row="4">State:</Label>
28. <TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Address.State}"/>
29. </Grid>
Figure 8: A basic databinding example
The screenshot is not very impressive, but you get the idea. I have created a Person object with FirstName andLastName public properties. There is also a public property called Address of type Address which has Street, City andState properties. I have assigned an instance of the Person object to the DataContext property of the window in the code beside. Every FrameworkElement has this property. When binding WPF searches from the current element up the UI tree until it finds a DataContext property it can bind to. Notice that the markup syntax is enclosed in curly braces and begins with the word Binding. You will see other Markup Extensions like this, but Binding is the most common. Notice the Path expression and the difference between how I have defined the binding to FirstName and LastName. These statements are equivalent; LastName demonstrates the abbreviated version. Also, notice how I have accessed the Address object. If you were to run this program, you would notice that all changes to the UI were propagated to the Person object, but changes to the Person object are not propagated back to the UI. For this to work you must implement the INotifyPropertyChanged interface, a trivial task.
Besides editing the Person object, let’s say that we want to display the information elsewhere in our application, in a Contact Card fashion. Let’s also say that we want to display this information in a consistent way in several different places. Using previous technologies we would likely create a custom control or user control to meet this need (or custom rendering logic for an existing control). In WPF we have both of these options, but additionally we can create a DataTemplate:
1. <Grid>
2. <Grid.Resources>
3. <DataTemplate DataType="{x:Type local:Person}">
4. <Grid Margin="3">
5. <Grid.BitmapEffect>
6. <DropShadowBitmapEffect />
7. </Grid.BitmapEffect>
8. <Rectangle Opacity="1" RadiusX="9" RadiusY="9" Fill="Blue" StrokeThickness="0.35">
9. <Rectangle.Stroke>
10. <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
11. <GradientStop Color="White" Offset="0" />
12. <GradientStop Color="#666666" Offset="1" />
13. </LinearGradientBrush>
14. </Rectangle.Stroke>
15. </Rectangle>
16. <Rectangle Margin="2,2,2,0"
17. VerticalAlignment="Top"
18. RadiusX="6"
19. RadiusY="6"
20. Stroke="Transparent"
21. Height="15px">
22. <Rectangle.Fill>
23. <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
24. <GradientStop Color="#ccffffff" Offset="0" />
25. <GradientStop Color="transparent" Offset="1" />
26. </LinearGradientBrush>
27. </Rectangle.Fill>
28. </Rectangle>
29. <Grid Margin="5">
30. <Grid.RowDefinitions>
31. <RowDefinition Height="auto"/>
32. <RowDefinition Height="auto"/>
33. <RowDefinition Height="auto"/>
34. </Grid.RowDefinitions>
35. <Grid.ColumnDefinitions>
36. <ColumnDefinition />
37. </Grid.ColumnDefinitions>
38.
39. <StackPanel Grid.Row="0" Orientation="Horizontal">
40. <TextBlock FontSize="16" Foreground="White" Text="{Binding LastName}" />
41. <TextBlock FontSize="16" Foreground="White" xml:space="preserve">, </TextBlock>
42. <TextBlock FontSize="16" Foreground="White" Text="{Binding FirstName}" />
43. </StackPanel>
44.
45. <TextBlock Grid.Row="1" FontSize="16" Foreground="White"
46. Text="{Binding Address.Street}" />
47. <StackPanel Orientation="Horizontal" Grid.Row="2">
48. <TextBlock FontSize="16" Foreground="White" Text="{Binding Address.City}" />
49. <TextBlock FontSize="16" Foreground="White" xml:space="preserve">, </TextBlock>
50. <TextBlock FontSize="16" Foreground="White" Text="{Binding Address.State}" />
51. </StackPanel>
52. </Grid>
53. </Grid>
54. </DataTemplate>
55. </Grid.Resources>
56.
57. <ListBox Name="personList" />
58.
59. </Grid>
Figure 9: A contact card DataTemplate sample
I got a little more advanced with this sample. I wanted to do something that looked decent at least once during this article. To the explanation:
A DataTemplate is a visualization of something nonvisual. In this case we have a Person class, but what does that look like? Here, I have created a DataTemplate and set its DataType to person. Now WPF knows how to render this type of class when databinding occurs. You can also assign a key to a template and reference the template by this rather than having it automatically applied. This is the same idea as using CSS to apply a style to all DIV elements as apposed to applying the style based on class name. The difference is that here we are defining how .NET classes should be rendered. DataTemplates can also be scoped at any level of the application. Notice that the template is defined inside of a Grid.Resources tag. Every FrameworkElement has a resource collection. This means that you can store DataTemplate at virtually any level of the UI hierarchy. So I could define what Person looks like at the Application, Window, Panel or Control level. Here, I have defined the appearance of Person and scoped it to all controls within the Grid. The ListBox contained within the Grid picks up the template automatically when I set itsItemsSource to a collection of Person objects in the code beside. DataTemplates are also aware of .NET inheritance. So if I have Employee and Manager classes that both derive from Person, they will be rendered with this template as well. But, if I define a DataTemplate specifically for the Manager class, WPF will use the more specific template. Consider the possibilities.
There is so much more to databinding and DataTemplates than I have said here. I am quite embarrassed at the lack of coverage I have been able to give. But I have to move on. As food for thought, here are some other things supported out-of-box with WPF databinding: You can have conditional binding based on business rule validation, conditional template selection based on business rules, converters that shape data when binding to and from the UI, DataTriggers that change the appearance of the template based on values in the data, binding between different UI elements based on name or relative position in the UI hierarchy, a choice of four different binding modes and a choice of what type of event triggers the binding, and bindings to data sources that pass in other bindings as parameters, etc. The list goes on and on. Again, let me mention Beatriz Costa’s blog as a great source of info. Once you have learned the basics, go there and read everything.
Styles and Control Templates
Frequently, when building a UI, one finds him or herself setting various appearance related properties over and over again. For example, you may want all the Label text in your app to be a Bold Trebuchet 12px font. This is easy to do with CSS in a web app, but not as straight forward with WinForms. WPF recognizes this need and satisfies it with the introduction of the Style element. Let’s see how we would implement the above example in XAML:
1. <StackPanel>
2. <StackPanel.Resources>
3. <Style TargetType="{x:Type Label}">
4. <Setter Property="FontFamily" Value="Trebuchet" />
5. <Setter Property="FontSize" Value="12" />
6. <Setter Property="FontWeight" Value="Bold" />
7. </Style>
8. </StackPanel.Resources>
9.
10. <Label>Here is some text.</Label>
11. <Label>More text.</Label>
12. <Label>The last bit of text.</Label>
13. </StackPanel>
The basic and most common part of a Style is its Setters. Simply declare the property name and its value and it will be applied. Easy, isn’t it? In the last section, I discussed how DataTemplates could be applied by type or by key. The same applies to styles. In this case, I have scoped the style to the StackPanel; any Label in that panel will pick up the style. I can, of course, override that style on the individual elements if I need. You can also inherit styles one from another by setting the BasedOn attribute of the style element:
1. <StackPanel>
2. <StackPanel.Resources>
3. <Style x:Key="baseStyle" TargetType="{x:Type Control}">
4. <Setter Property="FontFamily" Value="Trebuchet" />
5. <Setter Property="FontSize" Value="12" />
6. <Setter Property="FontWeight" Value="Bold" />
7. </Style>
8. <Style BasedOn="{StaticResource baseStyle}" TargetType="{x:Type Label}">
9. <Style.Triggers>
10. <Trigger Property="IsMouseOver" Value="True">
11. <Setter Property="Foreground" Value="Red" />
12. </Trigger>
13. </Style.Triggers>
14. </Style>
15. </StackPanel.Resources>
16.
17. <Label>Here is some text.</Label>
18. <Label>More text.</Label>
19. <Label>The last bit of text.</Label>
20. </StackPanel>
Notice that I now have two styles; one that applies to all elements inherited from Control, and a second one based on that Style that extends it – the second Style applies only to Labels. I have taken this opportunity to show the second most common element you will find in a Style: its Triggers collection. A Trigger is a stateful aspect of theStyle. In this case, any of the Labels in the StackPanel will appear red only when the mouse is hovering over them.
By taking advantage of styles, you can create a consistent and easily maintainable appearance for your UI. There is a lot of power in this concept. However, sometimes you need to go further. Suppose you want all of the buttons in your application to have a gel appearance. You cannot accomplish this with only basic style setters. In this case you need to use a ControlTemplate. In order to understand what a ControlTemplate is, you need to think of controls more abstractly. In WPF, a Button control is something akin to the platonic idea of a Button. In your development experience you have seen many buttons of different shapes and styles, but they were all buttons. They typically all had some mouse over effect and triggered something when they were clicked. With WPF, all of the buttons you have seen could be implemented with the same Button control, but with different ControlTemplates. WPF, by way ofControlTemplates, allows you to switch out the appearance of any control, without having to write any code related to its functionality. Some controls have simple templates and some are complex. Here is a basic gel button template straight out of the SDK:
1. <StackPanel>
2. <StackPanel.Resources>
3. <Style TargetType="{x:Type Button}">
4. <Setter Property="Foreground" Value="white" />
5. <Setter Property="Margin" Value="1" />
6. <Setter Property="Template">
7. <Setter.Value>
8. <ControlTemplate TargetType="{x:Type Button}">
9. <Grid>
10. <Rectangle x:Name="GelBackground"
11. Opacity="1" RadiusX="9"
12. RadiusY="9"
13. Fill="{TemplateBinding Background}"
14. StrokeThickness="0.35">
15. <Rectangle.Stroke>
16. <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
17. <GradientStop Color="White" Offset="0" />
18. <GradientStop Color="#666666" Offset="1" />
19. </LinearGradientBrush>
20. </Rectangle.Stroke>
21. </Rectangle>
22. <Rectangle x:Name="GelShine"
23. Margin="2,2,2,0"
24. VerticalAlignment="Top"
25. RadiusX="6"
26. RadiusY="6"
27. Opacity="1"
28. Stroke="Transparent"
29. Height="15px">
30. <Rectangle.Fill>
31. <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
32. <GradientStop Color="#ccffffff" Offset="0"/>
33. <GradientStop Color="Transparent" Offset="1"/>
34. </LinearGradientBrush>
35. </Rectangle.Fill>
36. </Rectangle>
37. <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
38. </Grid>
39. <ControlTemplate.Triggers>
40. <Trigger Property="IsMouseOver" Value="True">
41. <Setter Property="Fill" TargetName="GelBackground">
42. <Setter.Value>
43. <RadialGradientBrush>
44. <GradientStop Color="Lime" Offset="0" />
45. <GradientStop Color="DarkGreen" Offset="1" />
46. </RadialGradientBrush>
47. </Setter.Value>
48. </Setter>
49. </Trigger>
50. <Trigger Property="IsPressed" Value="true">
51. <Setter Property="Fill" TargetName="GelBackground">
52. <Setter.Value>
53. <RadialGradientBrush>
54. <GradientStop Color="#ffcc00" Offset="0"/>
55. <GradientStop Color="#cc9900" Offset="1"/>
56. </RadialGradientBrush>
57. </Setter.Value>
58. </Setter>
59. </Trigger>
60. </ControlTemplate.Triggers>
61. </ControlTemplate>
62. </Setter.Value>
63. </Setter>
64. <Style.Triggers>
65. <Trigger Property="IsMouseOver" Value="True">
66. <Setter Property="Foreground" Value="Black"/>
67. </Trigger>
68. <Trigger Property="IsPressed" Value="True">
69. <Setter Property="Foreground" Value="Black"/>
70. </Trigger>
71. </Style.Triggers>
72. </Style>
73. </StackPanel.Resources>
74.
75. <Button Height="35" Width="125" Background="Black">Normal</Button>
76. <Button Height="35" Width="125" Background="Black">Mouse Over</Button>
77. </StackPanel>
Figure 10: A templated button
This ControlTemplate is embedded within a Style, but it does not have to be done this way. If you examine theControlTemplate element, you will see where the look of the Button is being defined. In this case it is simply a layering of rectangles with various gradients, but you have access to the full power of WPF in defining these templates (yes that includes 3D and animation if you so desire). Similar to Styles, ControlTemplates can be assigned by type or key, but be careful that whatever template you are applying matches the control type you are applying it to. Every Control expects to find certain pieces in its Template in order to make the control work right. You should study a working Template of a Control before attempting to create your own. One of the best resources for learning about control templates is Simple Styles. Here you will find basic ControlTemplates for all of the major controls inWPF. They are accessible through the sidebar in Kaxaml (a tool you should try out anyway) and can also now be found in Blend. You can also take advantage of control templating support in your own custom controls. A discussion of this is beyond the scope of this article.
Animation and 3D
Both animation and 3D are large areas when examined individually. In this article I would like to cover them both generally. Mostly, I will point out a number of great resources that you can use to develop skills in these areas.
When working with animations in WPF, you should begin by making sure to have a good understanding ofDependencyProperties. It is these properties that WPF supports animation for. As mentioned before, a good resource for learning about DependencyProperties and Animation is Charles Petzold’s book. He has thorough explanations of both of these topics.
To begin with, WPF uses a Storyboard to represent a hierarchical organization of animations. This sort of structure obviously lends itself well to XAML and makes it possible to compose increasingly complex animations out of simpler pieces. A typical animation has a BeginStoryboard element at its root and consists of one or more children:
1. <BeginStoryboard>
2. <Storyboard TargetProperty="Opacity">
3. <DoubleAnimation From="1" To="0" Duration="0:0:1" />
4. </Storyboard>
5. </BeginStoryboard>
In the above example we are animating the Opacity property of some element from 1 to 0 over the course of one second. But what object is having its Opacity animated and what triggers the animation to begin? Observe the XAMLin its proper context:
1. <Button Height="40" Width="125">
2. <Button.Triggers>
3. <EventTrigger RoutedEvent="Button.Click">
4. <BeginStoryboard>
5. <Storyboard TargetProperty="Opacity">
6. <DoubleAnimation From="1" To="0" Duration="0:0:1" />
7. </Storyboard>
8. </BeginStoryboard>
9. </EventTrigger>
10. </Button.Triggers>
11. </Button>
Now it should be clear that we are causing a Button to fade out whenever it is clicked. This sample demonstrates an animation being triggered by a RoutedEvent. They can also be embedded in a Style and triggered based on the value of any DependencyProperty.
1. <Window.Resources>
2. <Style TargetType="{x:Type RadioButton}">
3. <Style.Triggers>
4. <Trigger Property="IsChecked" Value="True">
5. <Trigger.EnterActions>
6. <BeginStoryboard>
7. <Storyboard TargetProperty="Opacity">
8. <DoubleAnimation From="1" To="0" Duration="0:0:1" />
9. </Storyboard>
10. </BeginStoryboard>
11. </Trigger.EnterActions>
12. </Trigger>
13. </Style.Triggers>
14. </Style>
15. </Window.Resources>
16.
17. <RadioButton Height="40" Width="125"/>
This causes the animation to run whenever the IsChecked property of any RadioButton enters the True state. You can also use Trigger.ExitActions to specify an animation for a move from the True state. Notice that the animation is called a DoubleAnimation. Double refers to the type of the property being animated. WPF defines animation classes for 22 different types (ColorAnimation, VectorAnimation, PointAnimation, etc). There are many properties on the basic animation classes that allow the developer more fine grained control over how animations occur. These properties include: AccelerationRatio, DecelerationRatio, SpeedRatio and RepeatBehavior among several others. In addition to these basic animations, the types Double, Matrix and Point have coresponding AnimationUsingPathclasses that allow the developer to use a Path to control the animated values. If either of these scenarios does not provide enough control for you, all 22 types have matching AnimationUsingKeyFrames classes which offer varying support for discrete, linear and spline based interpolation. Together, this set of classes provides quite a bit of animation power. If you are interested in animation, then I would recommend checking out two blogs in particular:Charles Petzold (search through the archives) and theWPFblog. You will also find several examples of 3D in these blogs as well. Speaking of 3D, I would like to note that the animation classes were designed to provide a consistent animation framework regardless of whether the item being animated is 2D or 3D.
If you are interested in working with WPF’s 3D features, there are a few resources you should know about. Windows Presentation Foundation Unleashed has an extensive chapter on 3D written by Daniel Lehenbauer. This is probably the best resource on WPF 3D currently in print. If you can hold out for a while, Petzold is planning on releasing an entire book devoted to WPF 3D. Both of the blogs mentioned above have some good 3D examples, but you’ll also want to check these sites: The WPF3D Team Blog, Five Great WPF 3D Nuggets and 3D Tutorial. The first thing you will want to know about 3D in WPF is that all 3D content must exist within a Viewport3D. You’ll want to put a camera in that scene. The WPF offers two main options: PerspectiveCamera and OrthographicCamera. Go here for a discussion of these two choices. Of course, if you want to see anything, you will need a light: AmbientLight and/orDirectionalLight.
1. <Viewport3D>
2. <Viewport3D.Camera>
3. <PerspectiveCamera
4. FarPlaneDistance="20"
5. LookDirection="5,-2,-3"
6. UpDirection="0,1,0"
7. NearPlaneDistance="1"
8. Position="-5,2,3"
9. FieldOfView="45" />
10. </Viewport3D.Camera>
11. <ModelVisual3D>
12. <ModelVisual3D.Content>
13. <Model3DGroup>
14. <DirectionalLight Color="White" Direction="-3,-4,-5" />
15. <GeometryModel3D>
16. <GeometryModel3D.Geometry>
17. <MeshGeometry3D
18. Positions="-1 -1 0 1 -1 0 -1 1 0 1 1 0"
19. Normals="0 0 1 0 0 1 0 0 1 0 0 1"
20. TextureCoordinates="0 1 1 1 0 0 1 0"
21. TriangleIndices="0 1 2 1 3 2" />
22. </GeometryModel3D.Geometry>
23. <GeometryModel3D.Material>
24. <DiffuseMaterial>
25. <DiffuseMaterial.Brush>
26. <SolidColorBrush Color="Blue"/>
27. </DiffuseMaterial.Brush>
28. </DiffuseMaterial>
29. </GeometryModel3D.Material>
30. </GeometryModel3D>
31. </Model3DGroup>
32. </ModelVisual3D.Content>
33. </ModelVisual3D>
34. </Viewport3D>
Of course, you have to add your own 3D content. The above demonstrates a very simple mesh: a plane. There is a lot of information to be covered regarding what is happening in this simple example, but I don’t have time to go into it all here. The main thing I want to talk about is material. If you have worked with 3D before, you know that every 3D object must be covered with some sort of material. The above example uses a DiffuseMaterial, but WPF also offers EmmisiveMaterial and SpecularMaterial for your use. Here’s the really powerful thing about materials: As you can see above, the material architecture takes advantage of the same Brush system that the rest of WPF uses. This means that you can pretty much put anything on the surface of a 3D object, including 2D UI components. With thisrecently released solution, you can even make all of the 2D UI completely interactive! Just don’t get carried way.
WPF: Present and Future
WPF is a very powerful framework for building UI. My hat goes off to the team that built it. I truly believe it is the best UI framework to date, on any platform. Since it is only a version one, it still has room to grow quite a bit. I wanted to conclude this article with a few thoughts on what I would like to see in future versions:
- We need a set of Common Dialog Boxes that have a look and feel that is consistent with WPF.
- WPF Databinding is awesome and is one of my favorite features. Upon using Reflector, I discovered that is makes extensive use of reflection (no big surprise there). I would recommend that the WPF team research using fast property setters/getters and LCG for future versions. I don’t know if this is feasible or would reasonably improve performance, but it is worth looking into.
- We need more common controls. The important ones that are missing are: DatePicker, Calendar, MaskedTextbox, DataGrid and PropertyGrid. I would also like to see some more advanced components, such as a window docking system similar to the one in Blend.
- On the whole, I would like to see better 3D performance. We need more material types and support for pixel shaders to do next gen stuff. I like the add-ons for 2D and 3D, so just add those to the SDK.
- When it comes to XBAPs, we need support for WCF minimum and hopefully WF as well. I expect the WPF team to gradually move more and more WPF features into the XBAP realm as time passes.
With the release of Orcas coming closer and closer, I expect several of these to find there way into the next release.
Looking into the future, Microsoft is extending the reach of WPF and the .NET Framework to other platforms with the introduction of Silverlight. At MIX07 it was announced that a cross-platform version of the CLR and scaled down BCL would be shipped as part of Silverlight 1.1. This is very exciting news because it means that developers will be able to use the .NET language of their choice to program Rich Internet Applications that run in the browser on both Mac and PC. Silverlight 1.0 will not contain the CLR and is in Beta 1 at the time of this article. If you wish to author Silverlight applications in a .NET language you will need the Silverlight 1.1 Alpha bits. The presence of the BCL in version 1.1 makes available many great features such as: Multithreading, Databinding, Web Services, etc. I'm looking forward to the future of this technology and firmly believe that Microsoft is moving in the right direction.
Summary
It is my hope that this article has aided you in learning what WPF is all about. It is a new, rich UI Framework for Windows with a reach that is extending to the web. Inspired by many existing UI technologies, it evolves those ideas and binds them together in a consistent model that empowers developers to build more visually stunning and usable applications with great agility.
Resources
Prerequisites
Tools
Recommended Books
Articles
Sites/Blogs
- Silverlight
- The Official .NET 3.0 Home
- Beatriz Costa’s Blog
- Charles Petzold's Blog
- The WPF Blog
- The WPF3D Team Blog
No comments:
Post a Comment