
Pro CSharp 2008 And The .NET 3.5 Platform [eng]
.pdf
1022 CHAPTER 28 ■ INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML
While tools can generate a good deal of XAML on your behalf, it is important for you to understand the basic workings of XAML syntax and how this markup is eventually transformed into a valid .NET assembly. To illustrate XAML in action, in our next example we’ll build a WPF application using nothing more than a pair of *.xaml files.
Defining MainWindow in XAML
Our first Window-derived class (MainWindow) was defined in C# as a class type that extends the System.Windows.Window base class. This class contains a single Button type that calls a registered event handler when clicked. Defining this same Window type in the grammar of XAML can be achieved as so (assume this markup has been defined in a file named MainWindow.xaml):
<!-- |
Here is our Window definition -- |
> |
<Window x:Class="SimpleXamlApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="My Xaml App" Height="200" Width="300" WindowStartupLocation ="CenterScreen">
<!--Set the content of this window -- |
> |
<Button Width="133" Height="24" Name="btnExitApp" Click ="btnExitApp_Clicked"> Exit Application
</Button>
<!--The implementation of our button's Click event handler! -->
<x:Code>
<![CDATA[
private void btnExitApp_Clicked(object sender, RoutedEventArgs e)
{
// Get a handle to the current app and shut it down. Application.Current.Shutdown();
}
]]>
</x:Code>
</Window>
First of all, notice that the root element, <Window>, defines the name of the derived type via the Class attribute. The x prefix is used to denote that this attribute is defined within the XAML-centric XML namespace, http://schemas.microsoft.com/winfx/2006/xaml (more details on these XML namespaces later in this chapter). Within the scope of the opening <Window> element we have specified values for the Title, Height, Width, and WindowsStartupLocation attributes, which as you can see are a direct mapping to properties of the same name supported by the System.Windows.Window type.
Next up, notice that within the scope of the window’s definition, we have authored markup to describe the look and feel of the Button instance, which will be used to implicitly set the Content property of the window. Beyond setting up the variable name and its overall dimensions, we have also handled the Click event of the Button type by assigning the method to delegate to when the Click event occurs.
The final aspect of this XAML file is the <Code> element, which allows us to author event handlers and other methods of this class directly within an *.xaml file. As a safety measure, the code itself is wrapped within a CDATA scope, to prevent XML parsers from attempting to directly interpret the data (although this is not strictly required for the current example).
It is important to point out that authoring functionality within a <Code> element is not recommended. Although this “single-file approach” isolates all the action to one location, inline code does

CHAPTER 28 ■ INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML |
1023 |
not provide us with a clear separation of concerns between UI markup and programming logic. In most WPF applications, “real code” will be found within an associated C# code file (which we will do eventually).
Defining the Application Object in XAML
Remember that XAML can be used to define in markup any nonabstract .NET class that supports a default constructor. Given this, we could most certainly define our application object in markup as well. Consider the following content within a new file, MyApp.xaml:
<!-- The Main() method seems to be missing! However, the StartupUri attribute is the functional equivalent -->
<Application x:Class="SimpleXamlApp.MyApp" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml">
</Application>
Here, you might agree, the mapping between the Application-derived C# class type and its XAML description is not as clear-cut as was the case for our MainWindow’s XAML definition. Specifically, there does not seem to be any trace of a Main() method. Given that any .NET executable must have a program entry point, you are correct to assume it is generated at compile time, based in part on the StartupUrl property. The assigned *.xaml file will be used to determine which Window- derived class to create when this application starts up.
Although the Main() method is automatically created at compile time, we are free to use the <Code> element to establish our Exit event handler if we so choose, as follows (notice this method is no longer static, as it will be translated into an instance-level member in the MyApp class):
<Application x:Class="SimpleXamlApp.MyApp" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml" Exit ="AppExit">
<x:Code>
<![CDATA[
private void AppExit(object sender, ExitEventArgs e)
{
MessageBox.Show("App has exited");
}
]]>
</x:Code>
</Application>
Processing the XAML Files via msbuild.exe
At this point, we are ready to transform our markup into a valid .NET assembly. When doing so, we cannot make direct use of the C# compiler and a response file. To date, the C# compiler does not have a direct understanding of XAML markup. However, the msbuild.exe command-line utility does understand how to transform XAML into C# code and compile this code on the fly when it is informed of the correct *.targets files.
msbuild.exe is a tool that allows you to define complex build scripts via (surprise, surprise) an XML grammar. One interesting aspect of these XML definitions is that they are the same format as Visual Studio *.csproj files. Given this, we are able to define a single file for automated commandline builds as well as a Visual Studio 2008 project. Consider the following minimalist build file,
SimpleXamlApp.csproj:

1024 CHAPTER 28 ■ INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup>
<RootNamespace>SimpleXamlApp</RootNamespace>
<AssemblyName>SimpleXamlApp</AssemblyName>
<OutputType>winexe</OutputType>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" /> <Reference Include="WindowsBase" /> <Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" /> </ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="MyApp.xaml" /> <Page Include="MainWindow.xaml" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.WinFX.targets" />
</Project>
Here, the <PropertyGroup> element is used to specify some basic aspects of the build, such as the root namespace, the name of the resulting assembly, and the output type (the equivalent of the
/target:winexe option of csc.exe).
The first <ItemGroup> specifies the set of external assemblies to reference with the current build, which as you can see are the core WPF assemblies examined earlier in this chapter. The second <ItemGroup> is much more interesting. Notice that the <ApplicationDefinition> element’s Include attribute is assigned to the *.xaml file that defines our application object. The <Page>’s Include element can be used to list each of the remaining *.xaml files that define the windows (and pages, which are often used when building XAML browser applications) processed by the application object.
However, the “magic” of this *.csproj file is the final <Import> subelements. Notice that our build script is referencing two *.targets files, each of which contains numerous other instructions used during the build process. The Microsoft.WinFX.targets file contains the necessary build settings to transform the XAML definitions into equivalent C# code files, while Microsoft.CSharp. Targets contains data to interact with the C# compiler itself.
■Note A full examination of the msbuild.exe utility is beyond the scope of this text. If you’d like to learn more, perform a search for the topic “MSBuild” in the .NET Framework 3.5 SDK documentation.
At this point, we can pass our SimpleXamlApp.csproj file into msbuild.exe for processing:
msbuild SimpleXamlApp.csproj
Once the build has completed, you should be able to find your assembly within the generated \bin\Debug folder. At this point, you can launch your WPF application as expected. As you may agree, it is quite bizarre to generate valid .NET assemblies by authoring a few lines of markup. However, to be sure, if you open SimpleXamlApp.exe in ildasm.exe, you can see that (somehow) your XAML has been transmogrified into an executable application (see Figure 28-6).

CHAPTER 28 ■ INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML |
1025 |
Figure 28-6. Transforming markup into a .NET assembly? Interesting . . .
Transforming Markup into a .NET Assembly
To understand exactly how our markup was transformed into a .NET assembly, we need to dig a bit deeper into the msbuild.exe process and examine a number of compiler-generated files, including a particular binary resource embedded within the assembly at compile time.
Mapping XAML to C# Code
As mentioned, the *.targets files specified in an MSBuild script define numerous instructions to translate XAML elements into C# code for compilation. When msbuild.exe processed our *.csproj file, it produced two files with the form *.g.cs (where g denotes autogenerated), which were saved into the \obj\Debug directory. Based on the names of our *.xaml file names, the C# files in question are MainWindow.g.cs and MyApp.g.cs.
If you open the MainWindow.g.cs file, you will find your class extends the Window base class and contains the btnExitApp_Clicked() method as expected. Also, this class defines a member variable of type System.Windows.Controls.Button. Strangely enough, there does not appear to be any code that establishes the property settings for the Button or Window type (Height, Width, Title, etc.). This part of the mystery will become clear in just a moment.
Finally, note that this class defines a private member variable of type bool (named _contentLoaded), which was not directly accounted for in the XAML markup. Here is a partial definition of the generated MainWindow type:
public partial class MainWindow :
System.Windows.Window, System.Windows.Markup.IComponentConnector
{
internal System.Windows.Controls.Button btnExitApp;
// This member variable will be explained soon enough.

1026 CHAPTER 28 ■ INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML
private bool _contentLoaded;
private void btnExitApp_Clicked(object sender, RoutedEventArgs e)
{
// Get a handle to the current application and shut it down. Application.Current.Shutdown();
}
...
}
This Windows-derived class also explicitly implements the WPF IComponentConnector interface defined in the System.Windows.Markup namespace. This interface defines a single method, Connect(), which has been implemented to rig up the event logic as specified within the original
MainWindow.xaml file:
void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target)
{
switch (connectionId)
{
case 1:
this.btnExitApp = ((System.Windows.Controls.Button)(target)); this.btnExitApp.Click += new
System.Windows.RoutedEventHandler(this.btnExitApp_Clicked); return;
}
this._contentLoaded = true;
}
Finally, the MainWindow class also implements a method named InitializeComponent(). This method ultimately resolves the location of an embedded resource within the assembly, given the name of the original *.xaml file. Once the resource is located, it is loaded into the current application object via a call to Application.LoadComponent(). Finally, the private bool member variable (mentioned previously) is set to true, to ensure the requested resource is loaded exactly once during the lifetime of this application:
public void InitializeComponent() { if (_contentLoaded) {
return;
}
_contentLoaded = true;
System.Uri resourceLocater = new System.Uri("/SimpleXamlApp;component/mainwindow.xaml", System.UriKind.RelativeOrAbsolute);
System.Windows.Application.LoadComponent(this, resourceLocater);
}
At this point, the question becomes, what exactly is this embedded resource?
The Role of BAML
When msbuild.exe processed our *.csproj file, it generated a file with a *.baml file extension, which is named based on the initial MainWindow.xaml file. As you might have guessed from the name, Binary Application Markup Language (BAML) is a binary representation of XAML. This *.baml file is embedded as a resource (via a generated *.g.resources file) into the compiled assembly. Using BAML, WPF assemblies contain within them their complete XAML definition (in a much more

CHAPTER 28 ■ INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML |
1027 |
compact format). You can verify this for yourself by opening your assembly using reflector.exe, as shown in Figure 28-7.
Figure 28-7. Viewing the embedded *.baml resource via Lutz Roeder’s .NET Reflector
The call to Application.LoadComponent() reads the embedded BAML resource and populates the tree of defined objects with their correct state (again, such as the window’s Height and Width properties). In fact, if you open the *.baml or *.g.resources file via Visual Studio, you can see traces of the initial XAML attributes. As an example, Figure 28-8 highlights the StartupLocation. CenterScreen property.
Figure 28-8. Behold the BAML!
The final piece of the autogenerated code puzzle occurs in the MyApp.g.cs file. Here we see our Application-derived class with a proper Main() entry point method. The implementation of this method calls InitializeComponent() on the Application-derived type, which in turn sets the


CHAPTER 28 ■ INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML |
1029 |
It is also important to point out that once the compiler has processed all of your *.xaml files in order to build the related C# code and binary resource, they are technically no longer required (and would never need to be shipped along with your executable). However, as shown at the end of this chapter, it is possible to dynamically create a Window object by reading a *.xaml file programmatically. In this case, the physical *.xaml file would indeed need to be shipped with the application itself.
■Source Code The SimpleXamlApp project can be found under the Chapter 28 subdirectory.
Separation of Concerns Using Code-Behind Files
Before we truly begin digging into the details of XAML, we have one final aspect of the basic programming model to address: the separation of concerns. Recall that one of the major motivations for WPF was to separate UI content from programming logic, which our current examples have not done.
Rather than directly embedding our event handlers (and other custom methods) within the scope of the XAML <Code> element, it is preferable to define a separate C# file to define the implementation logic, leaving the *.xaml files to contain nothing but UI markup content. Assume the following code-behind file, MainWindow.xaml.cs (by convention, the name of a C# code-behind file takes the form *.xaml.cs):
// MainWindow.xaml.cs using System;
using System.Windows;
using System.Windows.Controls;
namespace SimpleXamlApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
//Remember! This method is defined
//within the generated MainWindow.g.cs file.
InitializeComponent();
}
private void btnExitApp_Clicked(object sender, RoutedEventArgs e)
{
// Get a handle to the current application and shut it down. Application.Current.Shutdown();
}
}
}
Here, we have defined a partial class (to contain the event handling logic) that will be merged with the partial class definition of the same type in the *.g.cs file. Given that InitializeComponent() is defined within the MainWindow.g.cs file, our window’s constructor makes a call in order to load and process the embedded BAML resource.

