Chapter 19. XAML (Rhymes with Camel)


This is a valid snippet of Extensible Markup Language (XML):

<Button Foreground="LightSeaGreen" FontSize="24pt">     Hello, XAML! </Button> 


These three lines comprise a single XML element: a start tag, an end tag, and content between the two tags. The element type is Button. The start tag includes two attribute specifications with attribute names of Foreground and FontSize. These are assigned attribute values, which XML requires to be enclosed in single or double quotation marks. Between the start tag and end tag is the element content, which in this case is some character data (to use the XML terminology).

XML was designed as a general-purpose markup language that would have a wide range of applications, and the Extensible Application Markup Language (or XAML) is one of those applications.

XAML (pronounced "zammel") is a supplementary programming interface for the Window Presentation Foundation. As you may have surmised, that snippet of XML is also a valid snippet of XAML. Button is a class defined in the System.Windows.Controls namespace, and Foreground and FontSize are properties of that class. The text "Hello, XAML!" is the text that you would normally assign to the Content property of the Button object.

XAML is designed mostly for object creation and initialization. The XAML snippet shown above corresponds to the following equivalent (but somewhat wordier) C# code:

Button btn = new Button(); btn.Foreground = Brushes.LightSeaGreen; btn.FontSize = 32; btn.Content = "Hello, XAML!" 


Notice that the XAML does not require that LightSeaGreen be explicitly identified as a member of the Brushes class, and that the string "24pt" is acceptable as an expression of 24 points. A typographical point is 1/72 inch, so 24 points corresponds to 32 device-independent units. Although XML can often be somewhat verbose (and XAML increases the verbosity in some respects), XAML is often more concise than the equivalent procedural code.

The layout of a program's window is often a hierarchy of panels, controls, and other elements. This hierarchy is paralleled by nested elements in XAML:

<StackPanel>     <Button Foreground="LightSeaGreen" FontSize="24pt">         Hello, XAML!     </Button>    <Ellipse Fill="Brown" Width="200" Height="100" />     <Button>        <Image Source="http://www.charlespetzold.com/PetzoldTattoo.jpg"               Stretch="None" />     </Button> </StackPanel> 


In this snippet of XAML, the StackPanel has three children: a Button, an Ellipse, and another Button. The first Button has text content. The other Button has an Image for its content. Notice that the Ellipse and Image elements have no content, so the elements can be written with the special XML empty-element syntax, where the end tag is replaced by a slash before the closing angle bracket of the start tag. Also notice that the Stretch attribute of the Image element is assigned a member of the Stretch enumeration simply by referring to the member name.

A XAML file can often replace an entire constructor of a class that derives from Window, which is the part of the class that generally performs layout and attaches event handlers. The event handlers themselves must be written in procedural code such as C#. However, if you can replace an event handler with a data binding, that binding can usually go right into the XAML.

The use of XAML separates the visual appearance of an application from its functionality. This separation allows designers to work with XAML files to create an attractive user interface, while the programmers focus more on the run-time interactions among the elements and controls. Design tools that generate XAML are already becoming available.

Even for programmers who don't have access to a graphics design artist conveniently occupying an adjacent cubicle, Visual Studio has its own built-in designer that generates XAML. Obviously a designer that generates XML is much preferred to a designer that generates C# code, as was the case with the Visual Studio designer for Windows Forms. A designer that generates procedural code must later read that generated code, and it often relies on the code being in a particular format. For that reason, the human programmer isn't allowed to mess with it. XML, however, was specifically designed to be editable by both computer and human. As long as each editor leaves the XAML in a syntactically correct state, there should be no problem.

Despite the availability of designers that generate XAML, you as a programmer will benefit greatly by learning the syntax of XAML, and the best way to learn is by doing. I believe that every WPF programmer should be fluent in XAML and adept at coding XAML by hand, and that's what I'm going to show you how to do.

Although the snippets of XAML shown so far in this chapter might be found in the context of some larger XAML document, they are not ready to stand by themselves. A certain ambiguity exists. What is that Button element? Could it be a shirt button? An electrical button? A campaign button? It is very desirable that XML documents not be ambiguous. If two XML documents use the same element name for different purposes, something should clearly distinguish the two documents.

For this reason, the XML namespace was devised. A XAML document created by a WPF programmer has a different namespace than XML documents created by a manufacturer of shirt buttons.

You declare a default XML namespace in the document with an attribute of xmlns. The namespace applies to the element in which the namespace declaration appears, along with all child elements. XML namespace names must be unique and persistent, and it is very common to use URLs for this purpose. For the XAML used in Windows Presentation Foundation programs, the URL is:

http://schemas.microsoft.com/winfx/2006/xaml/presentation

Don't bother going to your Web browser to look at that location. There's nothing there. It's just a namespace name that Microsoft has devised to uniquely identify XAML elements such as Button and StackPanel and Image.

The snippet of XAML shown at the beginning of this chapter can become a full-fledged XAML document by adding the xmlns attribute and the proper namespace:

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         Foreground="LightSeaGreen" FontSize="24pt">     Hello, XAML! </Button> 


That XAML is now ready to be put into a little file, perhaps created with Notepad or NotepadClone, and perhaps with an XML comment at the top to identify the file. You can save the following file to your hard drive.

XamlButton.xaml

[View full width]

<!-- ============================================= XamlButton.xaml (c) 2006 by Charles Petzold ============================================= --> <Button xmlns="http://schemas.microsoft.com/winfx/ 2006/xaml/presentation" Foreground="LightSeaGreen" FontSize="24pt"> Hello, XAML! </Button>



The shaded background of this file listing means that the file is among the source code for this book that you can download from the Microsoft Press Web site. If you don't feel like typing it in, you'll find this file in the Chapter 19 directory.

However you obtain the file, if you have the WinFx extensions to .NET installed or you're running Microsoft Vista, you can launch the file just like a program by double-clicking the file name in Windows Explorer or, if you're so inclined, by running it off the command-line prompt. You'll see Microsoft Internet Explorer come up, and the button will fill the bulk of Internet Explorer's client area except for a couple of navigation buttons below the Address Bar. The navigation buttons will be disabled because there's no place for them to go from here. (If you're running Microsoft Vista, the navigation buttons will not appear in the client area; their functionality is subsumed by Internet Explorer's own navigation buttons.)

A file such as XamlButton.xaml is known as "loose" XAML or "stand-alone" XAML. The .xaml file name extension is associated with a program named PresentationHost.exe. Launching the XAML causes PresentationHost.exe to run, and this program is responsible for creating an object of type Page (a class derived from FrameworkElement but somewhat similar to Window), which can then be hosted by Internet Explorer. The PresentationHost.exe program also converts the loaded XAML into an actual Button object, and sets that object to the Content property of the Page.

If there's an error in the XAML, Internet Explorer will let you know, and you can click a "More information" button in Internet Explorer that reveals the presence of PresentationHost.exe and also shows a stack trace. Among references to many other methods in the stack trace, you can spot a particular static method named XamlReader.Load in the System.Windows.Markup namespace. This is the method that converts XAML into objects, and I'll show you how to use it shortly.

Besides running XamlButton.xaml from your hard drive, you can also put the file on your Web site and launch it from there. You might need to register the MIME type of the .xaml file name extension, however. On some servers, this is accomplished by adding the following line to a file named .htaccess:

AddType application/xaml+xml xaml 


Here's another "stand-alone" XAML file that creates a StackPanel with three children: a Button, an Ellipse, and a ListBox.

XamlStackPanel.xaml

[View full width]

<!-- ================================================= XamlStackPanel.xaml (c) 2006 by Charles Petzold ============================================= ==== --> <StackPanel xmlns="http://schemas.microsoft.com /winfx/2006/xaml/presentation"> <Button HorizontalAlignment="Center" Margin="24"> Hello, XAML! </Button> <Ellipse Width="200" Height="100" Margin="24" Stroke="Red" StrokeThickness="10" /> <ListBox Width="100" Height="100" Margin="24"> <ListBoxItem>Sunday</ListBoxItem> <ListBoxItem>Monday</ListBoxItem> <ListBoxItem>Tuesday</ListBoxItem> <ListBoxItem>Wednesday</ListBoxItem> <ListBoxItem>Thursday</ListBoxItem> <ListBoxItem>Friday</ListBoxItem> <ListBoxItem>Saturday</ListBoxItem> </ListBox> </StackPanel>



XML files are required to have only one root element, and in this file that root element is a StackPanel. Between the StackPanel start tags and end tags is the content of the StackPanelits three children. The Button element looks pretty similar to the one you've already seen. The Ellipse element includes five attributes (corresponding to properties of the Ellipse class) but no content, so it uses the empty-element syntax. The ListBox element has seven children, which are ListBoxItem elements. The content of each ListBoxItem is a text string.

In general, XAML files represent an entire element tree. When PresentationHost.exe loads a XAML file, not only is each element in the tree created and initialized, but the elements are also assembled into the visual tree.

When launching these stand-alone XAML files, you may have noticed that the title bar of Internet Explorer displays the path name of the file. In a real-life application, you probably want to control the text that appears there. You can do that by making the root element a Page, setting the WindowTitle property, and making the StackPanel a child of Page, as in this stand-alone XAML file.

XamlPage.xaml

[View full width]

<!-- =========================================== XamlPage.xaml (c) 2006 by Charles Petzold =========================================== --> <Page xmlns="http://schemas.microsoft.com/winfx /2006/xaml/presentation" WindowTitle="Xaml Page"> <StackPanel> <Button HorizontalAlignment="Center" Margin="24"> Hello, XAML! </Button> <Ellipse Width="200" Height="100" Margin="24" Stroke="Red" StrokeThickness="10" /> <ListBox Width="100" Height="100" Margin="24"> <ListBoxItem>Sunday</ListBoxItem> <ListBoxItem>Monday</ListBoxItem> <ListBoxItem>Tuesday</ListBoxItem> <ListBoxItem>Wednesday</ListBoxItem> <ListBoxItem>Thursday</ListBoxItem> <ListBoxItem>Friday</ListBoxItem> <ListBoxItem>Saturday</ListBoxItem> </ListBox> </StackPanel> </Page>



You might be inclined to try a stand-alone XAML file with a root element of Window. That won't work, because PresentationHost.exe wants to make the root element a child of something, and a Window object can't be a child of anything. The root element of a stand-alone XAML file can be anything that derives from FrameworkElement except for Window.

Suppose you have a C# program that defines a string variable (named strXaml, for example) containing a small but complete XAML document:

string strXaml =     "<Button xmlns='http://schemas.microsoft.com/winfx/2006/presentation'" +     "        Foreground='LightSeaGreen' FontSize='24pt'>" +     "    Click me!" +     "</Button>"; 


To make the string more readable, I've used single quotes rather than double quotes on the attributes. Could you write a program that parses this string to create and initialize a Button object? You'd certainly be using reflection a lot, and making certain assumptions regarding the data used to set the Foreground and FontSize properties. It's entirely conceivable that such a parser would be possible, so it will be no surprise that one already exists. The System.Windows.Markup namespace contains a class named XamlReader with a static method named Load that can parse XAML and turn it into an initialized object. (In addition, the static XamlWriter.Save method goes in the opposite direction. It generates XAML from objects.)

A Windows Presentation Foundation program can use XamlReader.Load to convert a chunk of XAML into an object. If the root element of the XAML has child elements, those elements are converted as well and put together in the visual tree implied by the hierarchy of the XAML.

To use XamlReader.Load, you'll want a using directive for System.Windows.Markup, of course, but you'll also need a reference to the System.Xml.dll assembly, which contains XML-related classes that XamlReader.Load obviously requires. Unfortunately, XamlReader.Load can't directly accept a string argument. Otherwise, you could just pass some XAML directly to the method and cast the result to an object of the desired type:

Button btn = (Button) XamlReader.Load(strXaml);  // Won't work! 


XamlReader.Load requires either a Stream object or an XmlReader object. Here's one approach I've seen that uses a MemoryStream object for the job. (You'll need a using directive for the System.IO namespace for this code.) A StreamWriter writes the string into a MemoryStream, and that MemoryStream is passed to XamlReader.Load:

MemoryStream memory = new MemoryStream(strXaml.Length); StreamWriter writer = new StreamWriter(memory); writer.Write(strXaml); writer.Flush(); memory.Seek(0, SeekOrigin.Begin); object obj = XamlReader.Load(memory); 


Here's a somewhat smoother approach that requires using directives for both System.Xml and System.IO:

StringReader strreader = new StringReader(strXaml); XmlTextReader xmlreader = new XmlTextReader(strreader); object obj = XamlReader.Load(xmlreader); 


You could actually use this approach with one almost-unreadable statement:

object obj = XamlReader.Load(new XmlTextReader(new StringReader(strXaml))); 


The following program defines the strXaml string as shown above, converts that short XAML document into an object, and sets it to the Content property of the window:

LoadEmbeddedXaml.cs

[View full width]

//------------------------------------------------- // LoadEmbeddedXaml.cs (c) 2006 by Charles Petzold //------------------------------------------------- using System; using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Markup; using System.Xml; namespace Petzold.LoadEmbeddedXaml { public class LoadEmbeddedXaml : Window { [STAThread] public static void Main() { Application app = new Application(); app.Run(new LoadEmbeddedXaml()); } public LoadEmbeddedXaml() { Title = "Load Embedded Xaml"; string strXaml = "<Button xmlns='http://schemas .microsoft.com/" + "winfx/2006 /xaml/presentation'" + " Foreground='LightSeaGreen' FontSize='24pt'>" + " Click me!" + "</Button>"; StringReader strreader = new StringReader(strXaml); XmlTextReader xmlreader = new XmlTextReader(strreader); object obj = XamlReader.Load(xmlreader); Content = obj; } } }



Because this program happens to know that the object returned from XamlReader.Load is actually a Button object, the program could cast it to a Button:

Button btn = (Button) XamlReader.Load(xmlreader); 


The program could then attach an event handler if you had one in the code:

btn.Click += ButtonOnClick; 


You could do anything with this Button that you could do if your program contained explicit code to create and initialize it.

Of course, defining some XAML as a string variable is a little awkward. Perhaps a better approach to converting a chunk of XAML into an object at run time is loading the XAML as a resource stored in the executable file of the program.

Let's begin with an empty project as usual, perhaps named LoadXamlResource. Add a reference to the System.Xml assembly along with the other WPF assemblies. Select Add New Item from the Project menu (or right-click the project name and select Add New Item). Select a template of XML File and a file name of LoadXamlResource.xml. (What I'm going to show you here is somewhat easier if you give this file an extension of .xml rather than .xaml. If you use the .xaml extension, Visual Studio will want to load the XAML designer and make certain assumptions about the file that aren't quite appropriate yet.) Here's the XML file.

LoadXamlResource.xml

[View full width]

<!-- === =============================================== LoadXamlResource.xml (c) 2006 by Charles Petzold ============================================= ===== --> <StackPanel xmlns="http://schemas.microsoft.com /winfx/2006/xaml/presentation"> <Button Name="MyButton" HorizontalAlignment="Center" Margin="24"> Hello, XAML! </Button> <Ellipse Width="200" Height="100" Margin="24" Stroke="Red" StrokeThickness="10" /> <ListBox Width="100" Height="100" Margin="24"> <ListBoxItem>Sunday</ListBoxItem> <ListBoxItem>Monday</ListBoxItem> <ListBoxItem>Tuesday</ListBoxItem> <ListBoxItem>Wednesday</ListBoxItem> <ListBoxItem>Thursday</ListBoxItem> <ListBoxItem>Friday</ListBoxItem> <ListBoxItem>Saturday</ListBoxItem> </ListBox> </StackPanel>



As you can see, this file is very similar to the stand-alone XamlStack.xaml file. The big difference is that I've also included a Name attribute for the Button object. The Name property is defined by FrameworkElement.

Very important: Right-click the LoadXamlResource.xml file in Visual Studio and select Properties. Make sure the Build Action is set to Resource or the program won't be able to load it as a resource.

The LoadXamlResource project also contains a rather normal-looking C# file with a class that inherits from Window.

LoadXamlResource.cs

[View full width]

//------------------------------------------------- // LoadXamlResource.cs (c) 2006 by Charles Petzold //------------------------------------------------- using System; using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Markup; namespace Petzold.LoadXamlResource { public class LoadXamlResource : Window { [STAThread] public static void Main() { Application app = new Application(); app.Run(new LoadXamlResource()); } public LoadXamlResource() { Title = "Load Xaml Resource"; Uri uri = new Uri("pack://application: ,,,/LoadXamlResource.xml"); Stream stream = Application .GetResourceStream(uri).Stream; FrameworkElement el = XamlReader.Load (stream) as FrameworkElement; Content = el; Button btn = el.FindName("MyButton") as Button; if (btn != null) btn.Click += ButtonOnClick; } void ButtonOnClick(object sender, RoutedEventArgs args) { MessageBox.Show("The button labeled '" + (args.Source as Button).Content + "' has been clicked"); } } }



The constructor creates a Uri object for the XML resource, and uses the static Application.GetResourceStream property to return a StreamResourceInfo object. StreamResourceInfo includes a property named Stream that returns a Stream object for the resource. This Stream object becomes the argument to XamlReader.Load, and the object that property returns (an object of type StackPanel) is assigned to the Content property of the window.

Once the object converted from the XAML has become part of the visual tree of the window, it is possible to use the FindName method to locate an element in the tree with the specified name. This is the Button. The program can then attach an event handler, or do something else with the element. This is perhaps the most straightforward way in which a program can connect event handlers to XAML loaded at run time.

Here's a little variation. The project is named LoadXamlWindow, and like the previous project, this XML file must have a Build Action of Resource:

LoadXamlWindow.xml

[View full width]

<!-- ================================================ LoadXamlWindow.xml (c) 2006 by Charles Petzold ============================================= === --> <Window xmlns="http://schemas.microsoft.com/winfx/ 2006/xaml/presentation" Title="Load Xaml Window" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize"> <StackPanel> <Button HorizontalAlignment="Center" Margin="24"> Hello, XAML! </Button> <Ellipse Width="200" Height="100" Margin="24" Stroke="Red" StrokeThickness="10" /> <ListBox Width="100" Height="100" Margin="24"> <ListBoxItem>Sunday</ListBoxItem> <ListBoxItem>Monday</ListBoxItem> <ListBoxItem>Tuesday</ListBoxItem> <ListBoxItem>Wednesday</ListBoxItem> <ListBoxItem>Thursday</ListBoxItem> <ListBoxItem>Friday</ListBoxItem> <ListBoxItem>Saturday</ListBoxItem> </ListBox> </StackPanel> </Window>



This XAML has a root element of Window. Notice that the attributes on the Window start tag include Title, SizeToContent, and ResizeMode. The latter two are assigned members from the enumeration associated with each property.

A root element of Window isn't allowed for stand-alone XAML files because PresentationHost.exe wants to make the converted XAML a child of something. Fortunately, the following program knows that the XAML resource is a Window object, so it doesn't inherit from Window or directly create a Window object itself.

LoadXamlWindow.cs

[View full width]

//----------------------------------------------- // LoadXamlWindow.cs (c) 2006 by Charles Petzold //----------------------------------------------- using System; using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Markup; namespace Petzold.LoadXamlWindow { public class LoadXamlWindow { [STAThread] public static void Main() { Application app = new Application(); Uri uri = new Uri("pack://application: ,,,/LoadXamlWindow.xml"); Stream stream = Application .GetResourceStream(uri).Stream; Window win = XamlReader.Load(stream) as Window; win.AddHandler(Button.ClickEvent, new RoutedEventHandler (ButtonOnClick)); app.Run(win); } static void ButtonOnClick(object sender, RoutedEventArgs args) { MessageBox.Show("The button labeled '" + (args.Source as Button).Content + "' has been clicked"); } } }



The Main method creates an Application object, loads the XAML, and casts the return value of XamlReader.Load to a Window object. The program installs an event handler for the button's Click event not by finding the button in the visual tree, but by calling the AddHandler method on the window. Finally the Main method passes the Window object to the Run method of Application.

Here's a program that includes an Open File dialog box that lets you load a XAML file from disk. You can use this program to load any of the XAML files shown in this chapter so far, including those with a file name extension of .xml.

LoadXamlFile.cs

[View full width]

//--------------------------------------------- // LoadXamlFile.cs (c) 2006 by Charles Petzold //--------------------------------------------- using Microsoft.Win32; using System; using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Markup; using System.Xml; namespace Petzold.LoadXamlFile { public class LoadXamlFile : Window { Frame frame; [STAThread] public static void Main() { Application app = new Application(); app.Run(new LoadXamlFile()); } public LoadXamlFile() { Title = "Load XAML File"; DockPanel dock = new DockPanel(); Content = dock; // Create button for Open File dialog. Button btn = new Button(); btn.Content = "Open File..."; btn.Margin = new Thickness(12); btn.HorizontalAlignment = HorizontalAlignment.Left; btn.Click += ButtonOnClick; dock.Children.Add(btn); DockPanel.SetDock(btn, Dock.Top); // Create Frame for hosting loaded XAML. frame = new Frame(); dock.Children.Add(frame); } void ButtonOnClick(object sender, RoutedEventArgs args) { OpenFileDialog dlg = new OpenFileDialog(); dlg.Filter = "XAML Files (*.xaml)|* .xaml|All files (*.*)|*.*"; if ((bool)dlg.ShowDialog()) { try { // Read file with XmlTextReader. XmlTextReader xmlreader = new XmlTextReader(dlg.FileName); // Convert XAML to object. object obj = XamlReader.Load (xmlreader); // If it's a Window, call Show. if (obj is Window) { Window win = obj as Window; win.Owner = this; win.Show(); } // Otherwise, set as Content of Frame. else frame.Content = obj; } catch (Exception exc) { MessageBox.Show(exc.Message, Title); } } } } }



As you can see in the ButtonOnClick method, getting a file name from OpenFileDialog makes the XAML loading a bit easier than in the programs that loaded XAML as resources. The file name can be passed directly to the XmlTextReader constructor, and that object is accepted by XamlReader.Load.

The method has some special logic if the object returned from XamlReader.Load is a Window. It sets the Owner property of the Window object to itself and then calls Show as if the loaded window were a modeless dialog box. (I added the code to set the Owner property after I discovered that I could close the main application window, but the window loaded from XAML would still hang around, preventing the application from terminating. That didn't seem quite proper to me. An alternative solution is setting the ShutdownMode property of the Application object to ShutdownMode.OnMainWindowClose, which is the approach taken by the XAML Cruncher program in the next chapter.)

You have now seen a couple of different approaches to loading a XAML element tree at run time, and you've even gotten a feel for how the code that loads the XAML can locate various elements in the tree and attach event handlers to them.

However, in real-life applications, it's much more common to compile the XAML along with the rest of your source code. It's more efficient, certainly, and there are certain desirable things you can do by compiling the XAML that simply cannot be done in stand-alone XAML. One of these desirable things is specifying the name of an event handler right in the XAML. Generally this event handler itself is located in a procedural code file, but you can also embed some C# code right in the XAML. This is possible only when you compile the XAML with the rest of your project. Generally your project will contain one XAML file for every page or window in the application (including dialog boxes), and a code file associated with each XAML file (often called a code behind file). But you can use as little or as much XAML as you want in your projects.

All the XAML you've seen so far has used classes and properties that are part of the Windows Presentation Foundation. But XAML is not a WPF-specific markup language. It is more correct to think of WPF as one possible application of XAML. XAML could be used with other application frameworks that are not WPF at all. (For example, another application that uses XAML is the Windows Workflow Foundation.)

The XAML specification defines several elements and attributes that you can use in any XAML application, including WPF. These elements and attributes are associated with an XML namespace different from the WPF namespace, and if you want to use the XAML-specific elements and attributes (and you will) you need to include a second namespace declaration in your XAML files. This second namespace declaration refers to this URL:

http://schemas.microsoft.com/winfx/2006/xaml

It's the same as the URL for WPF except without the additional path of presentation, which refers to the Windows Presentation Foundation. The WPF namespace declaration will continue to appear in all the XAML files in this book:

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 


The namespace for XAML-specific elements and attributes is customarily declared with a prefix of x:

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 


You can use whatever prefix you want, of course (as long as it doesn't begin with the letters XML), but x has already become entrenched as the convention in many XAML files.

(In theory, it might make more sense for the default namespace in a XAML file to be the namespace associated with XAML itself, and then to use a second namespace declaration for the WPF elements. However, the elements and attributes defined by XAML are very few, so to avoid a lot of "hair" in the XAML, it makes the most practical sense for the default namespace to be the one associated with the WPF elements.)

In this chapter, you'll see examples of the Class attribute and the Code element, both of which belong to the XAML namespace rather than the WPF namespace. Because the XAML namespace is customarily associated with a prefix of x, the Class attribute and Code element usually appear in XAML files as x:Class and x:Code, and that's how I'll refer to them.

The x:Class attribute can only appear in the root element of a XAML file. This attribute is only allowed with XAML that you compile as part of your project. It cannot appear in loose XAML or XAML loaded by a program at run time. The x:Class attribute looks something like this:

x: 


Very often, this x:Class attribute will appear in a root element of Window, so the XAML file might have an overall structure like this:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"         x:         ... >   ... </Window> 


The "MyNamespace" namespace referred to here is the .NET namespace (also called the common language runtime, or CLR, namespace) associated with the application project. Very often you will have a corresponding Window class written in C# that has the same namespace and class name, and which is defined with the partial keyword:

public namespace MyNamespace {     public partial class MyClassName: Window     {         ...     } } 


This is the code-behind file because it contains codevery often event handlers but possibly some initialization code as wellthat supports the controls and elements defined in the XAML file. The XAML file and the code-behind file are essentially each part of the same class, and that's often a class of type Window.

Once again, let's begin with an empty project. Let's call this one CompileXamlWindow. This project will have two files, a XAML file named CompileXamlWindow.xaml and a C# file named CompileXamlWindow.cs. Both files will effectively be part of the same class, which has a fully qualified name of Petzold.CompileXamlWindow.CompileXamlWindow.

Let's create the XAML file first. In the empty project, add a new item of type XML File, and specify the name of the file as CompileXamlWindow.xaml. Visual Studio will load a designer, but try to avoid it. In the lower left corner of the source window, click the Xaml tab rather than the Design tab.

If you check the Properties for the CompileXamlWindow.xaml file, the Build Action should be Page. If not, set it to Page. (Earlier in the LoadXamlResource and the LoadXamlWindow projects, I specified that you should use a file name extension of .xml for the XAML files, and now I'm telling you to use a .xaml extension. It really doesn't matter what extension you use. What matters is the Build Action. In the previous project, we wanted the file to become a resource of the executable. In this current project, we want the file to be compiled, and that's what the Build Action of Page enables.)

The CompileXamlWindow.xaml file is somewhat similar to the LoadXamlWindow.xml file. The first big difference is that this file includes a second namespace declaration for the x prefix, and an x:Class attribute appears in the root element. What we are defining here is a class that inherits from Window with the fully qualified class name of Petzold.CompileXamlWindow.CompileXamlWindow.

CompileXamlWindow.xaml

[View full width]

<!-- === ================================================= CompileXamlWindow.xaml (c) 2006 by Charles Petzold ============================================= ======= --> <Window xmlns="http://schemas.microsoft.com/winfx/ 2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com /winfx/2006/xaml" x:0" width="14" height="9" align="left" src="/books/4/266/1/html/2/images/ccc.gif" />.CompileXamlWindow" Title="Compile XAML Window" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize"> <StackPanel> <Button HorizontalAlignment="Center" Margin="24" Click="ButtonOnClick"> Click the Button </Button> <Ellipse Name="elips" Width="200" Height="100" Margin="24" Stroke="Black"/> <ListBox Name="lstbox" Width="150" Height="150" Margin="24" SelectionChanged="ListBoxOnSelection" /> </StackPanel> </Window>



In effectand, as you'll see, in actualitythis XAML document defines a class that in C# syntax looks something like this:

namespace Petzold.CompileXamlWindow {     public partial class CompileXamlWindow: Window     {         ...     } } 


The partial keyword implies that the CompileXamlWindow class has additional code somewhere else. That's the code in the C# code-behind file.

Notice also that the XAML element for the button includes the Click event as another attribute and assigns the event to a handler named ButtonOnClick. Where is this event handler? It will be in the C# part of the CompileXamlWindow class. The ListBox also requires a handler for the SelectionChanged event.

Also, both the Ellipse and ListBox include Name attributes with values of elips and lstbox, respectively. You saw earlier how a program can locate these elements in a tree using the FindName method. When you compile XAML in the project, the Name attributes play an extremely important role. They become fields of the class, so the class created from the XAML during compilation is more like this:

namespace Petzold.CompileXamlWindow {     public partial class CompileXamlWindow: Window     {         Ellipse elips;         ListBox lstbox;         ...     } } 


In the part of the CompileXamlWindow class that you write in C#, you can refer to these fields directly. Here's the code-behind file that contains the rest of the CompileXamlWindow class:

CompileXamlWindow.cs

[View full width]

//-------------------------------------------------- // CompileXamlWindow.cs (c) 2006 by Charles Petzold //-------------------------------------------------- using System; using System.Reflection; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; namespace Petzold.CompileXamlWindow { public partial class CompileXamlWindow : Window { [STAThread] public static void Main() { Application app = new Application(); app.Run(new CompileXamlWindow()); } public CompileXamlWindow() { // Required method call to hook up event handlers and // initialize fields. InitializeComponent(); // Fill up the ListBox with brush names. foreach (PropertyInfo prop in typeof (Brushes).GetProperties()) lstbox.Items.Add(prop.Name); } // Button event handler just displays MessageBox. void ButtonOnClick(object sender, RoutedEventArgs args) { Button btn = sender as Button; MessageBox.Show("The button labled '" + btn.Content + "' has been clicked."); } // ListBox event handler changes Fill property of Ellipse. void ListBoxOnSelection(object sender, SelectionChangedEventArgs args) { ListBox lstbox = sender as ListBox; string strItem = lstbox.SelectedItem as string; PropertyInfo prop = typeof(Brushes) .GetProperty(strItem); elips.Fill = (Brush)prop.GetValue(null , null); } } }



The class CompileXamlWindow derives from Window, as is normal, but the declaration also contains the partial keyword. The class has a static Main method, as usual. The constructor for CompileXamlWindow, however, begins by calling InitializeComponent. This method appears to be part of the CompileXamlWindow class, but it's nowhere to be seen. You will actually see this method shortly. For now you should know that it performs some vital functions, such as setting the fields named lstbox and elips to the ListBox and Ellipse elements created from the XAML, as well as attaching the event handlers to the Button and ListBox controls.

The constructor of CompileXamlWindow doesn't set the Title property or any content of the window because that's all done in the XAML. But it does need to fill up the list box. The remainder of the code is devoted to the two event handlers. The ButtonOnClick handler simply displays the MessageBox that you're probably tired of by now. The handler for the SelectionChanged event handler of ListBox changes the Fill property of the Ellipse object. Although this event handler obtains the ListBox object from the sender argument to the handler, it could also simply access the lstbox field. You can delete the first statement in the event handler and the program will work just the same.

When you compile and run the project, you'll see that it works, which is the important objective, of course, but at this point you might also crave a few insights into how it works.

Take a look at the obj subdirectory of the project, and either the Release or Debug subdirectory of obj (depending how you've compiled the project). You'll see a file with the name CompileXamlWindow.baml. That file name extension stands for Binary XAML, and yes, it's pronounced "bammel." That's the XAML file, already parsed and tokenized, and converted to a binary form. That file becomes part of the executable as an application resource.

You'll also see a file with the name CompileXamlWindow.g.cs. That's the code generated from the XAML file. (The g stands for generated.) Open it up in Notepad or another text viewer. This is the other part of the CompileXamlWindow class, and it is compiled along with the CompileXamlWindow.cs file. Near the top of the class you'll see two fields declared and named lstbox and elips. You'll also see the InitializeComponent method that loads the BAML file at run time and converts it into the element tree. At the bottom of the file you'll see the method that sets the lstbox and elips fields and attaches the event handlers. (Sometimes Visual Studio will display compilation error messages concerning these generated files. That's something you need to handle without editing the generated code.)

When the constructor of the CompileXamlWindow class begins execution, the Content property of the window is null and all the window properties (such as Title, SizeToWindow, and ResizeMode) have default values. Following the call to InitializeComponent, the Content is the StackPanel and the other properties have been set to values indicated in the XAML file.

The types of connections between XAML and C# code illustrated in the CompileXamlWindow programsharing a single class, specifying event handlers, and setting fieldsare possible only when you compile the XAML along with the code. When you load the XAML at run time by directly (or indirectly) calling XamlReader.Load, your options are more limited. You have access to the objects created by the XAML, but it's not quite as easy to set event handlers, or to save the objects as fields.

One of the first questions programmers often have about XAML is "Can I use my own classes in it?" Yes, you can. To use a custom class defined in a C# file that is compiled along with the rest of your project, all you need in the XAML is another namespace declaration.

Suppose you have a custom control named MyControl in a C# file with a CLR namespace of MyNamespace. You include this C# file as part of the project. In the XAML file, you must associate this CLR namespace with a prefixfor example, stuff, using a namespace declaration like this:

xmlns:stuff="clr-namespace:MyNamespace" 


The text "clr-namespace" must be written in lowercase and followed by a colon. (It resembles the http: part of a common XML namespace declaration. In the next chapter, I'll discuss the more enhanced syntax you use when referring to a namespace in an external dynamic-link library.) This namespace declaration must appear before the first reference to MyControl, or as an attribute in the MyControl element. The MyControl element requires the prefix stuff in front of it:

<stuff:MyControl ... > 


What prefix should you use? (I'm assuming you've already rejected "stuff" as a general-purpose solution.) Short prefixes are customary, but certainly not required. If your project contains source code from multiple CLR namespaces, you'll need a prefix for each of these namespaces, so the prefix should probably resemble the name of the CLR namespace to avoid confusion. If all the custom classes you need are in one namespace, the prefix src (meaning source code) is often seen.

Let's create a new project named UseCustomClass. This project contains a link to the ColorGridBox.cs file from the SelectColorFromGrid project in Chapter 13, "ListBox Selection." The ColorGridBox class is in the namespace Petzold.SelectColorFromGrid, so to use this class in a XAML file, you'll need a namespace declaration like this:

xmlns:src="/books/4/266/1/html/2/clr-namespace:Petzold.SelectColorFromGrid" 


Here's the UseCustomClass.xaml file that contains that namespace declaration to enable the file to reference the ColorGridBox control with a prefix of src.

UseCustomClass.xaml

[View full width]

<!-- ================================================= UseCustomClass.xaml (c) 2006 by Charles Petzold ============================================= ==== --> <Window xmlns="http://schemas.microsoft.com/winfx/ 2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com /winfx/2006/xaml" xmlns:src="/books/4/266/1/html/2/clr-namespace:Petzold .SelectColorFromGrid" x:0" width="14" height="9" align="left" src="/books/4/266/1/html/2/images/ccc.gif" />.UseCustomClass" Title="Use Custom Class" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize"> <StackPanel Orientation="Horizontal"> <Button HorizontalAlignment="Center" VerticalAlignment="Center" Margin="24"> Do-nothing button to test tabbing </Button> <src:ColorGridBox HorizontalAlignment="Center" VerticalAlignment="Center" Margin="24" SelectionChanged="ColorGridBoxOnSelectionChanged" /> <Button HorizontalAlignment="Center" VerticalAlignment="Center" Margin="24"> Do-nothing button to test tabbing </Button> </StackPanel> </Window>



The code-behind file contains Main, a call to InitializeComponent, and the SelectionChanged event handler for the ColorGridBox control.

UseCustomClass.cs

[View full width]

//----------------------------------------------- // UseCustomClass.cs (c) 2006 by Charles Petzold //----------------------------------------------- using Petzold.SelectColorFromGrid; using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; namespace Petzold.UseCustomClass { public partial class UseCustomClass : Window { [STAThread] public static void Main() { Application app = new Application(); app.Run(new UseCustomClass()); } public UseCustomClass() { InitializeComponent(); } void ColorGridBoxOnSelectionChanged(object sender, SelectionChangedEventArgs args) { ColorGridBox clrbox = args.Source as ColorGridBox; Background = (Brush) clrbox.SelectedValue; } } }



The UseCustomClass.cs file requires a using directive for the namespace Petzold.SelectColorFromGrid because the event handler refers to the ColorGridBox class. You could change that reference to just ListBox (from which ColorGridBox derives) and leave out the using directive. It is possible to dispense with the SelectionChanged event handler entirely by defining a data binding right in the XAML, but the syntax is a bit unusual and I'm afraid you'll have to wait until Chapter 23 to see how it's done.

I mentioned earlier that you'll generally have a XAML file and a corresponding code-behind file for every window and dialog box in your application. But don't fall into the trap of believing that XAML files can't be used for elements other than Window. The following project, UseCustomXamlClass, defines a custom class derived from Button (albeit a very simple one) entirely in XAML. You define a custom class in XAML using the x:Class attribute on the root element, which is the only place the attribute can appear. The following XAML file defines this Button derivative as the class CenteredButton. The XAML sets the HorizontalAlignment and VerticalAlignment properties to Center, and gives the button a little margin.

CenteredButton.xaml

[View full width]

<!-- ================================================= CenteredButton.xaml (c) 2006 by Charles Petzold ============================================= ==== --> <Button xmlns="http://schemas.microsoft.com/winfx/ 2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com /winfx/2006/xaml" x:0" width="14" height="9" align="left" src="/books/4/266/1/html/2/images/ccc.gif" />.CenteredButton" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="12" />



The code-behind file is a partial class for CenteredButton that is so simple that no using directives are required.

CenteredButton.cs

//----------------------------------------------- // CenteredButton.cs (c) 2006 by Charles Petzold //----------------------------------------------- namespace Petzold.UseCustomXamlClass {     public partial class CenteredButton     {         public CenteredButton()         {             InitializeComponent();         }     } } 



The project also contains a XAML file for a class that derives from Window. Besides the normal x:Class attribute, this file also contains an XML namespace declaration for the project's namespace so that a StackPanel can include five instances of the CenteredButton class.

UseCustomXamlClass.xaml

[View full width]

<!-- === ================================================== UseCustomXamlClass.xaml (c) 2006 by Charles Petzold ============================================= ======== --> <Window xmlns="http://schemas.microsoft.com/winfx/ 2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com /winfx/2006/xaml" xmlns:src="/books/4/266/1/html/2/clr-namespace:Petzold .UseCustomXamlClass" x:0" width="14" height="9" align="left" src="/books/4/266/1/html/2/images/ccc.gif" />.UseCustomXamlClass" Title = "Use Custom XAML Class"> <StackPanel Name="stack"> <src:CenteredButton>Button A</src :CenteredButton> <src:CenteredButton>Button B</src :CenteredButton> <src:CenteredButton>Button C</src :CenteredButton> <src:CenteredButton>Button D</src :CenteredButton> <src:CenteredButton>Button E</src :CenteredButton> </StackPanel> </Window>



Notice that the StackPanel has a Name attribute with the value of stack. The code-behind file can simply use that name to add five additional buttons (ten total) to the StackPanel.

UseCustomXamlClass.cs

[View full width]

//--------------------------------------------------- // UseCustomXamlClass.cs (c) 2006 by Charles Petzold //--------------------------------------------------- using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; namespace Petzold.UseCustomXamlClass { public partial class UseCustomXamlClass : Window { [STAThread] public static void Main() { Application app = new Application(); app.Run(new UseCustomXamlClass()); } public UseCustomXamlClass() { InitializeComponent(); for (int i = 0; i < 5; i++) { CenteredButton btn = new CenteredButton(); btn.Content = "Button No. " + (i + 1); stack.Children.Add(btn); } } } }



Besides having XAML files for Window and perhaps other elements and controls, it is also common for a project to have a XAML file for the Application object. One interesting outcome of doing this is that your program no longer requires an explicit Main method.

Let's try it. This project is called IncludeApplicationDefinition. It has two XAML files (one for Application and one for Window) and two corresponding code-behind files. The XAML file for Window contains only a button so it's very short:

MyWindow.xaml

[View full width]

<!-- =========================================== MyWindow.xaml (c) 2006 by Charles Petzold =========================================== --> <Window xmlns="http://schemas.microsoft.com/winfx/ 2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com /winfx/2006/xaml" x:0" width="14" height="9" align="left" src="/books/4/266/1/html/2/images/ccc.gif" />.IncludeApplicationDefinition.MyWindow" Title="Include Application Definition" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize"> <Button HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1.5in" Click="ButtonOnClick"> Click the Button </Button> </Window>



This XAML shares a namespace of Petzold.IncludeApplicationDefinition and a class name of MyWindow with the partial class derived from Window and defined in MyWindow.cs. The class's constructor calls InitializeComponent and also includes an event handler for the Click event of the Button.

MyWindow.cs

[View full width]

//----------------------------------------- // MyWindow.cs (c) 2006 by Charles Petzold //----------------------------------------- using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace Petzold.IncludeApplicationDefinition { public partial class MyWindow : Window { public MyWindow() { InitializeComponent(); } void ButtonOnClick(object sender, RoutedEventArgs args) { Button btn = sender as Button; MessageBox.Show("The button labled '" + btn.Content + "' has been clicked."); } } }



The second XAML file is for the Application object. The namespace is still Petzold.IncludeApplicationDefinition, but the class is MyApplication.

MyApplication.xaml

[View full width]

<!-- ================================================ MyApplication.xaml (c) 2006 by Charles Petzold ============================================= === --> <Application xmlns="http://schemas.microsoft.com/ winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com /winfx/2006/xaml" x:0" width="14" height="9" align="left" src="/books/4/266/1/html/2/images/ccc.gif" />.IncludeApplicationDefinition.MyApplication" StartupUri="MyWindow.xaml" />



The Build Action of the MyApplication.xaml file must be ApplicationDefinition. Watch out: Visual Studio might change that Build Action under certain circumstances (such as renaming the file). If you ever use a XAML file to define an Application object and you get an error message about no Main method in the project, check the Build Action of the XAML file for the Application object.

Notice the final attribute of StartupUri, which refers to the MyWindow.xaml file. Of course, by the time the application runs, the MyWindow.xaml file will have been compiled into the MyWindow.baml file and made an application resource, but it's still the initial window that you want the application to display. The StartupUri attribute here takes the place of the Run method normally called in Main.

Finally, here's MyApplication.cs, which doesn't do anything at all. In some applications it might have some event handlers for the Application object, if these were defined by attributes in the MyApplication.xaml file.

MyApplication.cs

//---------------------------------------------- // MyApplication.cs (c) 2006 by Charles Petzold //---------------------------------------------- using System; using System.Windows; namespace Petzold.IncludeApplicationDefinition {     public partial class MyApplication : Application     {     } } 



That's the whole project. There's obviously no Main method, but after you compile the program, you can look at the generated file MyApplication.g.cs, and that's where you'll find Main.

The MyApplication.cs file is so inconsequential that you can delete it entirely from the project, and the project will compile and run just as before. (In fact, when I was first putting this project together I accidentally used a different class name in MyApplication.xaml and MyApplication.cs, and that project compiled and ran fine as well!)

It is also possible (although unlikely for many applications) that the project can include only XAML files and no code files. Such a no-code project makes more sense when the controls actually have some data bindings defined in the XAML, or when you use some XAML animation. This project is named CompileXamlOnly and it has two files. The first is the Application file:

XamlOnlyApp.xaml

[View full width]

<!-- ============================================== XamlOnlyApp.xaml (c) 2006 by Charles Petzold ============================================= = --> <Application xmlns="http://schemas.microsoft.com/ winfx/2006/xaml/presentation" StartupUri="XamlOnlyWindow.xaml" />



As I mentioned earlier, this Application file must have a Build Action of ApplicationDefinition, or nothing will work. Notice that the StartupUri is the XamlOnlyWindow.xaml file, which is this one:

XamlOnlyWindow.xaml

[View full width]

<!-- ================================================= XamlOnlyWindow.xaml (c) 2006 by Charles Petzold ============================================= ==== --> <Window xmlns="http://schemas.microsoft.com/winfx/ 2006/xaml/presentation" Title="Compile XAML Only" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize"> <StackPanel> <Button HorizontalAlignment="Center" Margin="24"> Just a Button </Button> <Ellipse Width="200" Height="100" Margin="24" Stroke="Red" StrokeThickness="10" /> <ListBox Width="100" Height="100" Margin="24"> <ListBoxItem>Sunday</ListBoxItem> <ListBoxItem>Monday</ListBoxItem> <ListBoxItem>Tuesday</ListBoxItem> <ListBoxItem>Wednesday</ListBoxItem> <ListBoxItem>Thursday</ListBoxItem> <ListBoxItem>Friday</ListBoxItem> <ListBoxItem>Saturday</ListBoxItem> </ListBox> </StackPanel> </Window>



Notice that neither file defines a class name. If you check, you'll discover that Visual Studio generates code for only the Application XAML file and gives it a class name of Application__. The generated file includes the Main method. There is no generated code file for the Window XAML file, but Visual Studio compiles the Window into a BAML file, so the whole setup is similar to projects that include explicit code. No BAML file is required for the Application class because it doesn't define an element tree or anything else required during execution.

Suppose you start out with a XAML-only application, and then you decide that the application really needs some C# code. But you'd rather not create a whole new C# file just for that code. I don't know why. (Maybe you can tell me. It's your project!) Fortunately for you, it is entirely possible to embed C# code into a XAML file. It's not pretty, but it works.

This project is called EmbedCodeInXaml, and the first file is the Application class:

EmbeddedCodeApp.xaml

[View full width]

<!-- === =============================================== EmbeddedCodeApp.xaml (c) 2006 by Charles Petzold ============================================= ===== --> <Application xmlns="http://schemas.microsoft.com/ winfx/2006/xaml/presentation" StartupUri="EmbeddedCodeWindow.xaml" />



The StartupUri refers to the EmbeddedCodeWindow.xaml file, which has a Button, Ellipse, and ListBox and also some embedded C# code.

EmbeddedCodeWindow.xaml

[View full width]

<!-- === ================================================== EmbeddedCodeWindow.xaml (c) 2006 by Charles Petzold ============================================= ======== --> <Window xmlns="http://schemas.microsoft.com/winfx/ 2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com /winfx/2006/xaml" x:0" width="14" height="9" align="left" src="/books/4/266/1/html/2/images/ccc.gif" />.EmbeddedCodeWindow" Title="Embed Code in XAML" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize" Loaded="WindowOnLoaded"> <StackPanel> <Button HorizontalAlignment="Center" Margin="24" Click="ButtonOnClick"> Click the Button </Button> <Ellipse Name="elips" Width="200" Height="100" Margin="24" Stroke="Red" StrokeThickness="10" /> <ListBox Name="lstbox" Width="150" Height="150" Margin="24" SelectionChanged="ListBoxOnSelection" /> <x:Code> <![CDATA[ void WindowOnLoaded(object sender, RoutedEventArgs args) { foreach (System.Reflection .PropertyInfo prop in typeof (Brushes).GetProperties()) lstbox.Items.Add(prop.Name); } void ButtonOnClick(object sender, RoutedEventArgs args) { Button btn = sender as Button; MessageBox.Show("The button labeled '" + btn.Content + "' has been clicked."); } void ListBoxOnSelection(object sender, SelectionChangedEventArgs args) { string strItem = lstbox.SelectedItem as string; System.Reflection.PropertyInfo prop = typeof (Brushes).GetProperty(strItem); elips.Fill = (Brush)prop.GetValue(null , null); } ]]> </x:Code> </StackPanel> </Window>



Embedded code requires using the x:Code element and a CDATA section within the x:Code element. The XML specification defines CDATA (which stands for "character data") as a section in an XML file for "blocks of text containing characters which would otherwise be recognized as markup," which is certainly the case for symbols used in C# and other programming languages.

The CDATA section always begins with the string "<![CDATA[" and always ends with the string "]]>". Within the CDATA section, the characters "]]>" must not appear under any circumstances. That may actually be a problem if you write C# code that looks like this:

if (array1[array2[i]]>5) 


Simply insert a little white space somewhere within that inadvertent CDATA delimiter and all will be well.

During compilation of this project, the C# code is transferred into the EmbeddedCodeWindow.g.cs file. This embedded code cannot define fields. The embedded code might require fully qualified namespace names if the generated code file does not automatically include using directives for those namespaces. Notice that the embedded code in EmbeddedCodeWindow.xaml file needs to fully qualify classes in the System.Reflection namespace.

Although embedding C# code in a XAML file is sometimes convenient, it is much too ugly and awkward as a general-purpose solution. You'll probably have a happier, longer, and more fulfilling life if you never use embedded code in your XAML files and forget you ever saw it.

Perhaps you remember the DesignAButton program from Chapter 5, "Stack and Wrap." That program assigned a StackPanel to the Content property of a Button, and then decorated the StackPanel with two Polyline objects, a Label, and an Image showing an icon named BOOK06.ICO that I obtained from the image library included with Visual Studio. Let's try to mimic that program with a XAML-only projector rather, a project that includes only XAML and the icon file.

The icon file must have a Build Action of Resource. The following DesignXamlButtonApp.xaml file must have a Build Action of Application Definition.

DesignXamlButtonApp.xaml

[View full width]

<!-- === =================================================== DesignXamlButtonApp.xaml (c) 2006 by Charles Petzold ============================================= ========= --> <Application xmlns="http://schemas.microsoft.com/ winfx/2006/xaml/presentation" StartupUri="DesignXamlButtonWindow .xaml" />



The final part of the project is the XAML file for the Window with a Build Action of Page.

DesignXamlButtonWindow.xaml

[View full width]

<!-- === ====================================================== DesignXamlButtonWindow.xaml (c) 2006 by Charles Petzold ============================================= ============ --> <Window xmlns="http://schemas.microsoft.com/winfx/ 2006/xaml/presentation" Title="Design XAML Button" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize"> <Button HorizontalAlignment="Center" VerticalAlignment="Center" Margin="24"> <StackPanel> <Polyline Stroke="Black" Points="0 10,10 0,20 10,30 0 ,40 10,50 0, 60 10,70 0,80 10,90 0,100 10" /> <Image Margin="0,10,0,0" Source="BOOK06.ICO" Stretch="None" /> <Label HorizontalAlignment="Center"> _Read Books! </Label> <Polyline Stroke="Black" Points="0 0,10 10,20 0,30 10 ,40 0,50 10, 60 0,70 10,80 0,90 10,100 0" /> </StackPanel> </Button> </Window>



This window contains only a Button, but the Button contains a StackPanel, and the StackPanel contains four other elements. The two Polyline elements specify a series of eleven points each as X and Y pairs. Notice that commas separate the points. You can alternatively use spaces to separate the points and commas to separate the X and Y coordinates of each point. Or you can use spaces for both purposes or commas for both purposes.

The XAML Image element is very elegant as well. Rather than defining a Uri object and then creating a BitmapImage from that Uri, and then assigning the BitmapImage object to the Source property of Imageall of which is required in C# code and which you can examine in the original DesignAButton.cs fileyou can simply set the Source property to the name of the icon file. If you want, you can use the entire URI that references the icon resource ("pack://application:,,/BOOK06.ICO") but I think just the file name looks a lot cleaner.

XAML includes many little shortcuts like these, and I'll begin exploring them in the next chapter. But first we need a program that lets us interactively experiment with XAML and explore its syntax to the fullest extent possible.




Applications = Code + Markup. A Guide to the Microsoft Windows Presentation Foundation
Applications = Code + Markup: A Guide to the Microsoft Windows Presentation Foundation (Pro - Developer)
ISBN: 0735619573
EAN: 2147483647
Year: 2006
Pages: 72

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net