A Beginners Article about XAML and the WPF Engine

Preface

This article overviews WPF's graphics capabilities, including two-dimensional and three-dimensional shapes, fonts and animations. The writer would like to stress that the level of this article is for beginners. The reason why I am writing this article is because WPF integrates drawing and animation features that were previously available only in special libraries (such as Microsoft GDI+ and DirectX). The graphics system in WPF is designed to your computer graphics hardware to reduce the load on the CPU and in many cases speed up graphics rendering. So taking WPF for what it is, it is actually a remarkable technology that, apparently, can read and assess what your machine's graphics capabilities are via the machine's video interface card. Moreover, WPF graphics uses a resolution-independent measurement in units to make applications more uniform and portable across devices. For instance, the size properties of elements in WPF are measured in resolution-independent pixels, where one pixel represents 1/96 of an inch--but this depends on the computer's DPI (dots per inch) setting. The graphics engine determines the correct pixel count so that all users see elements of the same size on all devices. To emphasize the importance of graphics sketching, I also introduce the Microsoft Blend SDK version 4.

Graphic elements are rendered on screen using a vector-based system in which calculations determine how to size and scale each element in order to prevent an oversized element against a regular-size foreground. The basic 2-D shapes are Lines, Rectangles, and Ellipses. WPF, as we all know, has controls that can be used to create custom shapes or curves. Brushes can be used to fill an element with solid colors, complex patterns, images, and even videos.


Tell Me What XAML Is

In Windows Presentation Foundation, the screens are built with a dialect of XML called Extensible Markup Language (XAML). XML is data centric and is viewed as a technology in its own right. Data on certain web pages or a web service request (response message) is in the form of an XML document. Any data (any alphanumeric character) is tagged, as the rules for XML are very strict. That is, controls, images, or any item that are marked up on a page via HTML comprises the presentation content. Alphabetic letters or numbers are separately tagged via XML. The idea is to separate the presentation content on a web page from the data. This outputs a more dazzling-looking page. XAML is more declarative than imperative. By declarative, I mean that a control is specified as an XML element with attributes and sub-elements to describe what the control will look like. You do not have to be concerned with how this done because when you tell WPF what you want, it will figure out how to build the controls (instantiating objects and assigning properties) under the hood. This parallels dragging and dropping a control onto a Windows Form. The partial classes enable code to be generated, where the developer is only responsible for writing the event handler code. In addition, the designer is split between a graphical design surface on top and XAML on the bottom. The designer works the same as Windows Forms in that you can drag and drop controls onto it and edit them in the Properties window. But XAML is new, and we therefore must take a close look at XAML and define some terms that are used in WPF.

In Windows Forms, everything translates directly into code. However, WPF translates its user interface to XAML, which gets compiled into code later. This is a key distinction: XAML compiles into a binary form called BAML. At this point, however, it is not necessary to go into a description of BAML Just note that when working in the UI designer for a WPF project, VS2008 produces XAML, instead of code for all UI elements.

<Window x:Class="MainWindow1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<
Grid>
</
Grid>
</
Window>

The XAML defines a window with a title, dimensions, and an empty grid. A later section on layout explains the grid. Because it's XML, you also have namespaces: xmlns and xmlns:x. Windows are often associated with a code-behind file, which is C#. But where does the C# code come into play? WPF uses events, just like Windows Forms, so you need a place for the handlers to go, which is the code-behind.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
         }
    }

Notice that the Window1 class in the code-behind is partial and that its constructor calls InitializeComponent. In Windows Forms, the InititalizeComponent was in a separate file that you could get to. However, WPF generates InitializeComponent from the XAML. You see, WPF will ensure that the XAML creates a partial class named Window1 with an InitializeComponent method, and then C# will combine and compile the partials. If you get an error message that underlines the InitializeMethod, then drag your mouse over that underline noting the error. A box should appear that when clicked, gives you the option to generate a "stub method" to correct the error.

Controls in XAML

Many people will use the drag-and-drop capabilities of the WPF designer to build their UIs, but others will want to work directly in XAML. To try out the designer, drag and drop a Label control onto the window in the designer. If you have the split window open, you'll see the XAML for the Label come into view. You can see the Label control in the XAML code and how the text inside of the Label tag has been changed. There are also attributes on the Label that match properties that you'll see in the Properties window. The Name attribute is the variable name you use in code to access this label if you need to, and VerticalAlignment describes how this control will align within its parent element, which means that this Label will align inside the top border of the grid. The Margin in a later comes later when talking about layout (it means that the top, left, right, and bottom borders are the specified distance from the containing control, which is the Grid). Notice that the Content property matches the Label text, but the XAML doesn't show a Content property. The XAML we used was a shortcut for the following:

<Label Height="28" Margin="80,94,78,0"
Name="label1" VerticalAlignment="Top">
<
Label.Content>
What the Heck is XAML?
</Label.Content>
</
Label>

OUTPUT:

Untitled.jpg

If you want to use text only, which is the most typical content, you don't need to wrap the text in the property-specific content element. However, one of the cool things about XAML controls is their composability. Whether it is a label, button, text box, or any other type of control, you can add any type of content to them that you want. You can put only one item inside of the Content element, but that one element can be a layout control that can hold many controls.

Managing Layout

One of the subjects in the previous section was how WPF determined where the Label control should appear on the page, its size, and its relationship to other controls. While Windows Forms applications have absolute positioning and layout support via anchoring and docking, WPF is more sophisticated and has more options. For WPF, you need to know about control alignment and sizing, borders and margins, and layout controls for defining the relationship of containing controls and where they appear. With WPF controls, you can control their alignment and size. You can also manage their content, borders, and margins, which are often referred to as the box model. The default behavior of a WPF control is to stretch to fill its contents. If you are using a ListBox or Grid, filling the entire area might be the desired appearance, but you might not want this to happen with Button or Label controls. The following Label control demonstrates stretching behavior:

<Label Name="label1" Background="AliceBlue">What the Heck is XAML?</Label>

This Label doesn't have any attributes that specify its size or position, so the default is to stretch. In any event, a common way to lay out controls is for them to be in-line horizontally or vertically. You can use the StackPanel layout control to accomplish this. Here's an example:

<StackPanel Orientation="Horizontal">
<
Label>One</Label>
<
Label>After</Label>
<
Label>the Other</Label>
</
StackPanel>

Untitled1.jpg

You can add numerous items to the StackPanel, as you saw previously with the Label controls, and it will line them up. the XAML code below shows what this looks like. The example sets the Orientation property to Horizontal, but Orientation will default to Vertical if you omit it. The UniformGrid control divides the size of each cell evenly for all of its controls. The following example shows how this works:

<UniformGrid Height="100"
Name="uniformGrid1"
Width="200">
<
Label>O</Label>
<
Label>X</Label>
<
Label>O</Label>
<
Label>X</Label>
<
Label>O</Label>
<
Label>X</Label>
<
Label>O</Label>
<
Label>X</Label>
<
Label>O</Label>
</
UniformGrid>

Because there are nine items, the grid will look like a tic-tac-toe board. If you need more explicit control of grid layout, you should use the Grid layout control. So another layout arrangement you'll need is to place items in tabular format with rows and columns, which is the role of the Grid layout. That Grid was empty, but you'll often want to create rows and columns. The code below shows you how to use a Grid.

<Window x:Class="Main.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="400">
<Grid
ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="125"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition
Height="25"
/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" HorizontalAlignment="Center">Grid Facts</Label>
<Label Grid.Row="1" Grid.Column="0">Width="125"</Label>
<Label
Grid.Row="1"
Grid.Column="1">Absolute
Column Width</Label>
<Label Grid.Row="1" Grid.Column="2">Grid.Column</Label>
<Label
Grid.Row="2"
Grid.Column="0">Width="2*"</Label>
<Label
Grid.Row="2"
Grid.Column="1">Proportional
Spacing</Label>
<Label
Grid.Row="2"
Grid.Column="2">Grid.Column</Label>
<Label
Grid.Row="3"
Grid.Column="0">Width="Auto"></Label>
<Label
Grid.Row="3"
Grid.Column="1">Fill
Remaining Space</Label>
<Label
Grid.Row="3"
Grid.Column="2">Grid.Column</Label>
<Label
Grid.Row="4"
Grid.Column="0">Height="25"</Label>
<Label
Grid.Row="4" Grid.Column="1">Absolute Row Height</Label>
<Label Grid.Row="4" Grid.Column="2">Grid.Row</Label
<Label
Grid.Row="5"
Grid.Column="0">Grid.ColumnSpan="3"</Label>
<Label Grid.Row="5" Grid.Column="1">Cover 3 Columns</Label>
<Label
Grid.Row="5"
Grid.Column="2">Control
Attribute</Label>
</Grid>
</Window>

Untitled2.jpg

The first two items to notice in this code are Grid.ColumnDefinitions and Grid.RowDefinitions, which define the columns and rows, respectively. Combined with the Label controls, the code below shows grid lines so that you can see the effects of sizing on rows and columns. Here's the line that makes this happen:

<Grid ShowGridLines="True">

You can use three settings to control the size of columns: absolute, proportional, and auto fill. Here's how they're used:

<Grid.ColumnDefinitions>
<ColumnDefinition Width="125" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

Absolute sets an explicit width, and auto fills in the rest of the width of the Grid layout's containing control. Proportional is a little different because it is proportional to the size of the other controls. The second column in the preceding example is two times the current average size of a column. Therefore, if the width of the Grid's container is 400 and there are three columns, the average size of a column is approximately 133. Because the proportional coefficient is 2, the width of the second column is 2 x 133 = 266. You can use absolute, proportional, and auto-fill spacing on the Height property of RowDefinition elements, too. After you've defined columns and rows, you must specify which column and row that controls go into. This example uses all Label controls, but you can put any WPF controls into the grid. Here's the control for the first column of the second row:

<Label Grid.Row="1" Grid.Column="0">Width="125"</Label>

See how the attributes Grid.Row and Grid.Column reference the row and column to put this control into. You can look at each of these controls and see how they appear. The first row has a single Label that spans all columns of the Grid, shown here:

<Label Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
HorizontalAlignment="Center">
Grid Stuff
</Label
>
 

Notice the Grid.ColumnSpan, which means that this control covers all three columns in the grid. If you need a control to cover multiple rows, you can use Grid.RowSpan the same way. You can also set horizontal alignment of content with HorizontalAlignment or set vertical alignment with VerticalAlignment. With IntelliSense and auto-formatting in VS2008, it's a lot easier. One useful feature is whenever a block of XAML gets out of whack, you can highlight the block and press Ctrl+K+F to auto-format. If you don't like the default auto-format, you can change it yourself. Just select Tools, Options, Text Editor, and then select the XAML branch. You'll notice a few sub-branches that give you many options for how to format XAML. If you've been using Windows Forms and have grown accustomed to docking layout, WPF keeps you in your comfort zone with the DockPanel. You can use the DockPanel to position controls onto the top, left, bottom, right, and center of the client area. Here's an example:

<DockPanel>
<Label DockPanel.Dock="Top"
Background="CornflowerBlue">Top</Label>
<Label DockPanel.Dock="Bottom"
Background="Fuchsia">Bottom</Label>
<Label DockPanel.Dock="Left">Left</Label>
<Label DockPanel.Dock="Right">Right</Label>
<Label Background="Chartreuse">Center</Label>
</DockPanel>
Untitled3.jpg
Each control, except Center, in the DockPanel has its DockPanel.Dock set to the side of the client area it occupies. Controls, such as Center, without DockPanel.Dock will fill in the remaining space. The image figure below shows what this looks like. Background colors show what space is occupied by each control. Not too good but not bad. Top and Bottom span the width of the client area because they are the first controls entered. Left and Right occupy remaining space. If Right had been placed in the listing before Bottom, it would have extended downward to the bottom of the client area, and Bottom would have stopped at Right's left border. The concept of managing layout is sort of similar to web page design, in the HTML (markup) places the presentation content within the borders of the web page that loads within the frame of the web browser. In WPF, we are merely managing the layout that will determine where either data or other controls can be placed, etc.

So Where Are We Now? Let's Start with Fonts

This section of code introduces how to control fonts by modifying the font properties of a TextBlock control in the XAML code. The XAML code shows how to use TextBlocks and how to change the properties to control the appearance of the displayed text. The manipulations shown at this point can also be applied to other text-based controls such as a TextBox.

<Window x:Class="UsingFonts.UsingFontsWindow"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    Title="UsingFonts" Height="120" Width="400">
 
   <StackPanel>
 
      <TextBlock FontFamily="Arial" FontSize="12" FontWeight="Bold">Arial 12 point bold.</TextBlock>
 
      <TextBlock FontFamily="Times New Roman">Times New Roman plain, default size.</TextBlock>
 
      <TextBlock FontFamily="Courier New" FontSize="16"          FontStyle="Italic" FontWeight
="Bold">
Courier New 16 point bold and italic.</TextBlock>      <TextBlock>         <TextBlock.TextDecorations>            <TextDecoration Location="Overline" />            <TextDecoration Location="Baseline" />           </TextBlock.TextDecorations>Default font with overline and baseline.</TextBlock>
 
      <TextBlock>
 
         <TextBlock.TextDecorations>
 
            <TextDecoration Location="Strikethrough" />
 
            <TextDecoration Location="Underline" />
 
         </TextBlock.TextDecorations
>Default font with strikethrough and underline.</TextBlock>
 
   </StackPanel>
</
Window
>

The code behind is the same as the above: there are no event handlers, or more importantly, no "routed events".

With this output, we can see why it works, but we should know how it works. The text that you want to display in the TextBlock is placed between the TextBlock tags. The FontFamily property defines the font of the displayed text. This property can be set to any font. The lines define (that separate the TextBlock fonts) to be Arial, Times New Roman, and Courier New, respectively. The FontSize property defines the text size measured in points. When no FontSize is specified, the property is set to the system's default value. TextBlocks have various properties to further modify the font. The FontWeight property can be set to bold to make the font thicker. This property can be set either to a numeric value (1-999) or to a predefined descriptive value--such as Light or UltraBold--to define the thickness of the text. You can use the FontStyle property to make the text Italic or Oblique (which is a more emphasized italic. Finally, you can also define TextDecorations for a TextBlock to draw a horizontal line through the text. Overline and Baseline, shown in the fourth TextBlock create lines above and below the text, respectively.

4.jpg

With this output, we can see why it works, but we should know how it works. The text that you want to display in the TextBlock is placed between the TextBlock tags. The FontFamily property defines the font of the displayed text. This property can be set to any font. The lines define (that separate the TextBlock fonts) to be Arial, Times New Roman, and Courier New, respectively. The FontSize property defines the text size measured in points. When no FontSize is specified, the property is set to the system's default value. TextBlocks have various properties to further modify the font. The FontWeight property can be set to bold to make the font thicker. This property can be set either to a numeric value (1-999) or to a predefined descriptive value--such as Light or UltraBold--to define the thickness of the text. You can use the FontStyle property to make the text Italic or Oblique (which is a more emphasized italic. Finally, you can also define TextDecorations for a TextBlock to draw a horizontal line through the text. Overline and Baseline, shown in the fourth TextBlock create lines above and below the text, respectively.

With this output, we can see why it works, but we should know how it works. The text that you want to display in the TextBlock is placed between the TextBlock tags. The FontFamily property defines the font of the displayed text. This property can be set to any font. The lines define (that separate the TextBlock fonts) to be Arial, Times New Roman, and Courier New, respectively. The FontSize property defines the text size measured in points. When no FontSize is specified, the property is set to the system's default value. TextBlocks have various properties to further modify the font. The FontWeight property can be set to bold to make the font thicker. This property can be set either to a numeric value (1-999) or to a predefined descriptive value--such as Light or UltraBold--to define the thickness of the text. You can use the FontStyle property to make the text Italic or Oblique (which is a more emphasized italic. Finally, you can also define TextDecorations for a TextBlock to draw a horizontal line through the text. Overline and Baseline, shown in the fourth TextBlock create lines above and below the text, respectively.

WPF has several built-in shapes. The example XAML code will demonstrate how to display Lines, Rectangles, and Ellipses. Examine the XAML code:

<Window x:Class="BasicShapes.BasicShapesWindow"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Title="BasicShapes" Height="200" Width="500">
   <Canvas>
      <Rectangle Canvas.Left="90" Canvas.Top="30" Width="150" Height="90"
         Fill="Blue"/>
            <Line X1="90" Y1="30" X2="110" Y2="40" Stroke="Black" />
      <Line X1="90" Y1="120" X2="110" Y2="130" Stroke="Black" />
      <Line X1="240" Y1="30" X2="260" Y2="40" Stroke="Black" />
      <Line X1="240" Y1="120" X2="260" Y2="130" Stroke="Black" />
      <Rectangle Canvas.Left="110" Canvas.Top="40" Width="150"
         Height="90" Stroke="Black" />
      <Ellipse Canvas.Left="280" Canvas.Top="75" Width="100" Height="50"
         Fill="Red" />
      <Line X1="380" Y1="55" X2="380" Y2="100" Stroke="Black" />
      <Line X1="280" Y1="55" X2="280" Y2="100" Stroke="Black" />
      <Ellipse Canvas.Left="280" Canvas.Top="30" Width="100" Height="50"
         Stroke="Black" />
   </Canvas>
</
Window> 

For the sake being complete here is the standard code-behind file that is placed here (not to fill space up but to exemplify the class construction of BasicShapes):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace BasicShapes
{
   public partial class BasicShapesWindow : Window
   {
      public BasicShapesWindow()
      {
         InitializeComponent();
      }
   }
}

This XAML code outputs some basic shapes:

5.jpg

Notice that we used the Rectangle element to create a filled rectangle in the window. Also notice that layout control is a Canvas allowing us to use coordinates to position the shapes. To specify the upper-left corner of the Rectangle, we set the attached properties Canvas.Left and Canvas.Top to 90 and 30, respectively. We then set the Width and Height properties to 150 and 90, respectively, to specify the size. To define the Rectangle's color, we use the Fill property.

A Vector Based Drawing via WPF

A transform can be applied to any element to reposition or reorient the graphic. There are four types of transforms: TranslateTransform, RotateTransform, SkewTransform, and ScaleTransform. A TranslateTransform moves an object to another location. A RotateTransform rotates the object around a point and by a specified RotationAngle. A SkewTransform shears (or skews) the object. A ScaleTransform scales the object's x and y coordinate points by different specified amounts. A star shape is a polygon. If we want to draw a start, we use the Polygon element and use RotateTransforms to create a circle of randomly colored stars. Notice that we use the word random. This is the class used to generate random numbers, and therefore uses the same methods. Now the points property of the Polygon in this example is defined in a new syntax. Examine the XAML:

<Window x:Class="DrawStars.DrawStarsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DrawStars" Height="330" Width="330" Name="DrawStars">
<Canvas Name="mainCanvas">
<Polygon Name="star"
Fill="Green" Points="205,150 217,186 259,186
223,204 233,246 205,222 177,246
187,204 151,186 193,186" />
</
Canvas>
</
Window>

And here is the code-behind file that uses the Random class of the System namespace to generate the random number of coordinates. This, in combination with rotating the stars, creates an pretty nice on image on a user interface: 

using System;
using
System.Windows;
using
System.Windows.Media;
using
System.Windows.Shapes;
namespace
DrawStars
{
public partial class
DrawStarsWindow : Window
{
public DrawStarsWindow()
{
InitializeComponent();
Random random =
new Random();
// now let's
create 18 more random stars

for ( int count = 0; count < 18; count++ )
{
Polygon newStar =
new Polygon();
newStar.Points = star.Points;
byte[] colorValues = new byte[ 4 ];
random.NextBytes( colorValues );
newStar.Fill =
new SolidColorBrush( Color.FromArgb(colorValues[ 0 ], colorValues[ 1 ], colorValues[ 2 ],colorValues[ 3 ] ) );
RotateTransform rotate =
new RotateTransform( count * 20, 150, 150 );
newStar.RenderTransform = rotate;
mainCanvas.Children.Add( newStar );
}
}
}
}

Here is the output:

Untitled6.jpg

Admittedly, this was a very basic look at the WPF engine and the XAML markup. This article is strictly meant for the beginner, who may not have grasped the basics of XAML.