Lecture 3: WPF architecture
August 14, 2011 at 11:06 am Leave a comment
This is a third lecture about WPF. In first one we discussed about the Windows graphical interfaces (namely GDI and DirectX), in the second we talked about different frameworks wrapping these interfaces (MFC, Winforms and WPF). I explained why WPF is so different from any previous technologies. In this lecture we will talk about WPF architecture.
WPF architecture
The following diagram shows the high level view of the WPF architecture:
- PresentationFramework.dll holds the top-level WPF types, such as buttons, windows and other controls. In WPF, all controls re-implemented from scratch, first time from 1985. They are not wrappers around user32 GDI controls anymore, instead they rendering by using DirectX.
- PresentationCore.dll holds base types, from which all shapes and controls derive.
- Millcore.dll is the core of WPF rendering system. This is where rendering thread runs, going over defined visual trees and composes all elements together. DWM uses milcore.dll to compose desktop from off-screen memory buffers. Milcore is written in unmanaged code in order to enable tight integration with DirectX.
In order to demonstrate composition in action, I took the sample WPF application (taken from http://www.codeproject.com/KB/WPF/GuidedTourWPF_1.aspx). Its main window description is very simple, and shown above:
<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>
<Grid.Resources>
<ResourceDictionary>
<!-- Import the resource dictionary which contains the DataTemplate for the RaceHorse class. -->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/RaceHorseDataTemplate.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- This RotateTransform is referenced by the ItemsControl (race track) and Slider. -->
<RotateTransform x:Key="RaceTrackRotateTrans" Angle="0" />
</ResourceDictionary>
</Grid.Resources>
<!-- The 'Race Track' area. -->
<ItemsControl x:Name="raceTrack"
Grid.Row="0"
HorizontalAlignment="Stretch"
LayoutTransform="{StaticResource RaceTrackRotateTrans}"
Margin="8"
VerticalAlignment="Center"
/>
<!-- The 'Command Strip' area -->
<Border Grid.Row="1" BorderBrush="Gray" BorderThickness="1" CornerRadius="8" Margin="12,4">
<!-- The background of the command strip area is set via an inline resource. -->
<Border.Background>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1" Opacity="0.8">
<GradientStop Color="#FFFFFF" Offset="0" />
<GradientStop Color="#22AA22" Offset="0.7" />
<GradientStop Color="#448844" Offset="0.95" />
<GradientStop Color="#EEEEEE" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<Grid>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<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>
<local:DoubleToIntegerConverter x:Key="RotationDisplayConv" />
</StackPanel.Resources>
<TextBlock Margin="10,4">Rotation: </TextBlock>
<!-- This Slider is bound to the angle of rotation applied to the ItemsControl 'race track.' -->
<Slider x:Name="rotationSlider"
Margin="2,0"
Minimum="-90" Maximum="90"
Width="120"
Value="{Binding Source={StaticResource RaceTrackRotateTrans}, Path=Angle}"
/>
<TextBlock Text="{Binding ElementName=rotationSlider, Path=Value, Converter={StaticResource RotationDisplayConv}}" />
<TextBlock xml:space="preserve"> degrees</TextBlock>
</StackPanel>
<TextBlock Margin="10,4" HorizontalAlignment="Right">
<Hyperlink x:Name="lnkStartNewRace">Start new race</Hyperlink>
</TextBlock>
</Grid>
</Border>
</Grid>
It describes a main grid, which contains two main parts: in first row it has a collection of racetracks, and in second row it has a stack panel, which in turn contains a slider and hyperlink controls with a text around. The main grid has a background picture.
The following picture demonstrates a snapshot of this application:
We can see a simple UI, with a picture in background and bars atop on it. In bottom it has a hyperlink and a slider controls.
As a next step I used a Snoop application, which allows viewing all elements separated (kind of Spy++ application for WPF). Also, it has a nice feature viewing the whole rendered tree in a 3D space. Since everything is retained, every visual item from the tree is available for manipulations, such as 3D projections.
Here is a snapshot of this application, slightly rotated to the side:
You can notice the difference in the z-order: bottom controls have higher order than top ones. This is exactly how rendering works: it goes over the visual tree, and renders element by element. Each next element in tree is rendered on top of the previous one (unless z order specified other than default).
Here is another snapshot of the same application, rotated even more:
Now it’s time to dig deeper into WPF architecture. The following diagram representing the hierarchy of WPF objects:
System.Threading.DispatcherObject
In WPF, Dispatcher object associated with any UI thread. Its job is to coordinate messages, such as keyboard and mouse events. Objects that derive from DispatcherObject have thread affinity, and can be accessed directly from thread that the Dispatcher was created on. DispatcherObject provide methods to determine whether it accessed on the right thread.
System.Windows.DependencyObject
One of the primary architectural philosophies used in WPF was a preference for properties over methods or events. Properties are declarative and allow you to more easily specify intent instead of action. This also supported a model driven, or data driven, system for displaying user interface content. In order to support this, WPF introduced concept of “dependency properties”, which are properties with change notifications, default values, callbacks and more. In order to declare dependency properties, class has to derive from DependencyObject class.
System.Windows.Media.Visual
With a system defined, the next step is getting pixels drawn to the screen. The Visual class provides for building a tree of visual objects, each optionally containing drawing instructions and metadata about how to render those instructions (clipping, transformation, etc.). Visual is designed to be extremely lightweight and flexible, so most of the features have no public API exposure and rely heavily on protected callback functions.
Visual is really the entry point to the WPF composition system. Visual is the point of connection between these two subsystems, the managed API and the unmanaged milcore.
WPF displays data by traversing the unmanaged data structures managed by the milcore. These structures, called composition nodes, represent a hierarchical display tree with rendering instructions at each node. This tree, illustrated on the right hand side of the figure below, is only accessible through a messaging protocol.
When programming WPF, you create Visual elements, and derived types, which internally communicate to the composition tree through this messaging protocol. Each Visual in WPF may create one, none, or several composition nodes.
System.Windows.UIElement
UIElement defines core subsystems including Layout, Input, and Events.
Layout is a core concept in WPF. In many systems there is either a fixed set of layout models (HTML supports three models for layout; flow, absolute, and tables) or no model for layout (User32 really only supports absolute positioning). WPF started with the assumption that developers and designers wanted a flexible, extensible layout model, which could be driven by property values rather than imperative logic. At the UIElement level, the basic contract for layout is introduced – a two phase model with Measure and Arrange passes.
Measure allows a component to determine how much size it would like to take. This is a separate phase from Arrange because there are many situations where a parent element will ask a child to measure several times to determine its optimal position and size. The fact that parent elements ask child elements to measure demonstrates another key philosophy of WPF – size to content. All controls in WPF support the ability to size to the natural size of their content. This makes localization much easier, and allows for dynamic layout of elements as things resize. The Arrange phase allows a parent to position and determine the final size of each child.
System.Windows.FrameworkElement
The two most critical things that FrameworkElement introduces are data binding and styles. The data binding subsystem in WPF should be relatively familiar to anyone that has used Windows Forms or ASP.NET for creating an application user interface (UI). In each of these systems, there is a simple way to express that you want one or more properties from a given element to be bound to a piece of data. WPF has full support for property binding, transformation, and list binding.
One of the most interesting features of data binding in WPF is the introduction of data templates. Data templates allow you to declaratively specify how a piece of data should be visualized. Instead of creating a custom user interface that can be bound to data, you can instead turn the problem around and let the data determine the display that will be created.
Styling is really a lightweight form of data binding. Using styling you can bind a set of properties from a shared definition to one or more instances of an element. Styles get applied to an element either by explicit reference (by setting the Style property) or implicitly by associating a style with the CLR type of the element.
System.Windows.Controls.Control
Control’s most significant feature is templating. If you think about WPF’s composition system as a retained mode rendering system, templating allows a control to describe its rendering in a parameterized, declarative manner. A ControlTemplate is a piece of XAML, describing look and feel of control. When designer alters presentation of control, it actually alters its template.
That’s all. I hope after these three lectures you will be more familiar with WPF. WPF is great technology which solves many problems (and creates a new ones). From my experience, developing using WPF (in avarage) takes more time than using WinForms. The concept of visual trees introducing time consuming problems, related to binding and routed events. In addition, WPF suffers from bad scalability (in terms of amount of objects). However, WPF brings many cool features to the table, such as truly separated presentation and functionality layers, skinning and customization abilities and much more.
Right now I have no planned future lectures, but I will do something based on FAQ and feedback.






Trackback this post | Subscribe to the comments via RSS Feed