Appendix B. XAML Reference

XAML is a relatively simple and general-purpose declarative programming language suitable for constructing and initializing objects. XAML is just XML, but with a set of rules about its elements and attributes and their mapping to objects, their properties, and the values of those properties (among other things).

Despite popular belief, XAML is not inherently about user interfaces. In Silverlight, XAML is mostly used to define visual elements, but it’s also used for nonvisual purposes, such as the application definition inside App.xaml. Also, although an app’s navigation scheme relies on XAML files, almost everything done in XAML can be done entirely in C# instead. (But note that the reverse is not true.) Practically speaking, however, XAML is heavily used in Silverlight apps for Windows Phone.

Because XAML is just a mechanism for using .NET APIs, attempts to compare it to HTML, Scalable Vector Graphics (SVG), or other domain-specific formats/languages are misguided. XAML consists of rules for how parsers/compilers must treat XML and has some keywords, but it doesn’t define any interesting elements by itself. So, talking about XAML absent of any context (such as Silverlight) is like talking about C# without the .NET Framework.

This appendix explores the mechanics of XAML, examining its syntax in depth and showing how it relates to C# code.

Elements and Attributes

The XAML specification defines rules that map .NET namespaces, types, properties, and events into XML namespaces, elements, and attributes. You can see this by examining the following simple (but complete) XAML file that declares a button and comparing it to the equivalent C# code:

XAML:

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  Content="OK"/>

C#:

System.Windows.Controls.Button b = new System.Windows.Controls.Button();
b.Content = "OK";

or

System.Windows.Controls.Button b =
  new System.Windows.Controls.Button { Content = "OK" };

Declaring an XML element in XAML (known as an object element) is equivalent to instantiating the corresponding .NET object via a default constructor. Setting an attribute on the object element is equivalent to setting a property of the same name (called a property attribute) or hooking up an event handler of the same name (called an event attribute). For example, here’s an update to the button that not only sets its Content property but also attaches an event handler to its Click event:

XAML:

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  Content="OK" Click="Button_Click"/>

C#:

System.Windows.Controls.Button b = new System.Windows.Controls.Button();
b.Click += Button_Click;
b.Content = "OK";

This requires a method called Button_Click to be defined with the appropriate signature. Note that XAML, like C#, is a case-sensitive language.

Namespaces

The most mysterious part about comparing the previous XAML examples with the equivalent C# examples is how the XML namespace http://schemas.microsoft.com/winfx/2006/xaml/presentation maps to the .NET namespace System.Windows.Controls. It turns out that the mapping to this and many other .NET namespaces is hard-coded. (In case you’re wondering, no web page exists at the schemas.microsoft.com URL—it’s just an arbitrary string like any namespace.)

The root object element in a XAML file must specify at least one XML namespace that is used to qualify itself and any child elements. You can declare additional XML namespaces (on the root or on children), but each one must be given a distinct prefix to be used on any identifiers from that namespace. For example, XAML files typically use a second namespace with the prefix x (denoted by using xmlns:x instead of just xmlns):

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

This is the XAML language namespace, which maps to types in the System.Windows.Markup namespace but also defines some special directives for the XAML parser. These directives often appear as attributes to XML elements, so they look like properties of the host element but actually are not. For a list of XAML keywords, see the “XAML Keywords” section later in this appendix.

Besides the default XML namespace used for core Silverlight elements such as Button and the XAML language namespace, the other XML namespaces used in Windows Phone apps do not have the same URL form. They leverage a clr-namespace directive that enables you to place a .NET namespace in a specific assembly directly inside XAML. For example:

xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"

The assembly specification at the end is necessary only if the desired types don’t reside in the same assembly that includes the XAML file. Such types are typically used with a local prefix, for example:

xmlns:local="clr-namespace:WindowsPhoneApp"

Using http://schemas.microsoft.com/winfx/2006/xaml/presentation as a default namespace and the XAML language namespace (http://schemas.microsoft.com/winfx/2006/xaml) as a secondary namespace with the prefix x is just a convention, just like it’s a convention to begin a C# file with a using System; directive. You could instead write the original XAML file as follows, and it would mean the same thing:

<ns:Button
  xmlns:ns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  Content="OK"/>

Of course, for readability it makes sense for your most commonly used namespace (also known as the primary XML namespace) to be prefix free and to use short prefixes for any additional namespaces.

Property Elements

Rich composition of elements is one of the highlights of Silverlight. This can be demonstrated with a button because you can put arbitrary content inside it; you’re not limited to just text! To demonstrate this, the following code embeds a simple square to make a Stop button like what might be found in a media player:

System.Windows.Controls.Button b = new System.Windows.Controls.Button();
System.Windows.Shapes.Rectangle r = new System.Windows.Shapes.Rectangle();
r.Width = 40;
r.Height = 40;
r.Fill = System.Windows.Media.Brushes.Red;
b.Content = r; // Make the square the content of the Button

A button’s Content property is of type System.Object, so it can easily be set to the 40×40 Rectangle object.

That’s pretty neat, but how can you do the same thing in XAML with property attribute syntax? What kind of string could you possibly set Content to that is equivalent to the preceding Rectangle declared in C#? There is no such string, but XAML fortunately provides an alternative (and more verbose) syntax for setting complex property values: property elements. It looks like the following:

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Button.Content>
  <Rectangle Height="40" Width="40" Fill="Red"/>
</Button.Content>
</Button>

The Content property is now set with an XML element instead of an XML attribute, making it equivalent to the previous C# code. The period in Button.Content is what distinguishes property elements from object elements. Property elements always take the form TypeName.PropertyName, they are always contained inside a TypeName object element, and they can never have attributes of their own (with one exception—the x:Uid attribute used for localization).

Many classes designate a property (via a custom attribute) that should be set to whatever content is inside the XML element. This property is called the content property, and it is really just a convenient shortcut to make the XAML representation more compact.

Button’s Content property is (appropriately) given this special designation, so the preceding button could be rewritten as follows:

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  <Rectangle Height="40" Width="40" Fill="Red"/>
</Button>

There is no requirement that the content property must actually be called Content; a list box uses its Items property as the content property.

Type Converters

Let’s look at the C# code equivalent to the following button:

XAML:

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  Content="OK" Background="Green"/>

C#:

System.Windows.Controls.Button b = new System.Windows.Controls.Button();
b.Content = "OK";
b.Background = System.Windows.Media.Brushes.Green;

Wait a minute. How can "Green" in XAML be equivalent to the static System.Windows.Media.Brushes.Green field (of type System.Windows.Media.SolidColorBrush) in C#? Indeed, this example exposes a subtlety with using strings to set properties in XAML that are a different data type than System.String or System.Object. In such cases, the XAML parser must look for a type converter that knows how to convert the string representation to the desired data type.

Silverlight provides type converters for many common data types: Brush, Color, FontWeight, Point, and so on. Unlike the XAML language, type converters generally support case-insensitive strings.

Without a type converter for Brush, you would have to use property element syntax to set the background in XAML, as follows:

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  Content="OK">
<Button.Background>
  <SolidColorBrush Color="Green"/>
</Button.Background>
</Button>

And even that is only possible because of a type converter for Color that can make sense of the "Green" string. If there were no Color type converter, you could still write the following:

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  Content="OK">
<Button.Background>
  <SolidColorBrush>
  <SolidColorBrush.Color>
    <Color A="255" R="0" G="128" B="0"/>
  </SolidColorBrush.Color>
  </SolidColorBrush>
</Button.Background>
</Button>

But this is only possible because of a type converter that can convert each numeric string into a Byte value expected by the A, R, G, and B properties of the Color type. Without this type converter, you would basically be stuck. Type converters don’t just enhance the readability of XAML, they also enable values to be expressed that couldn’t otherwise be expressed.

Children of Object Elements

A XAML file, like all XML files, must have a single root object element. Therefore, it should come as no surprise that object elements can support child object elements—not just property elements, which aren’t children as far as XAML is concerned. An object element can have three types of children: a value for a content property, collection items, or a value that can be type-converted to the object element.

The Content Property

The content property, introduced in the “Property Elements” section, is leveraged in every XAML file in this book. PhoneApplicationPage and UserControl both have a content property called Content. (PhoneApplicationPage actually inherits its Content property from UserControl.) Therefore, the following page:

<phone:PhoneApplicationPage
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
  ...
>
  <Grid>
    ...
  </Grid>
</phone:PhoneApplicationPage>

is equivalent to this more verbose version:

<phone:PhoneApplicationPage
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
  ...
>
  <phone:PhoneApplicationPage.Content>
    <Grid>
      ...
    </Grid>
  </phone:PhoneApplicationPage.Content>
</phone:PhoneApplicationPage>

Visual Studio actually shows an error when you use the more verbose form, but it works fine at run-time.

Collection Items

XAML enables you to add items to the two main types of collections that support indexing: lists and dictionaries.

Lists

A list is any collection that implements System.Collections.IList, such as List<T> or numerous other collection classes. For example, the following XAML adds two items to a list box whose Items property is an ItemCollection that implements IList:

<ListBox xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<ListBox.Items>
  <ListBoxItem Content="Item 1"/>
  <ListBoxItem Content="Item 2"/>
</ListBox.Items>
</ListBox>

This is equivalent to the following C# code:

System.Windows.Controls.ListBox listbox = new System.Windows.Controls.ListBox();
System.Windows.Controls.ListBoxItem item1 =
  new System.Windows.Controls.ListBoxItem();
System.Windows.Controls.ListBoxItem item2 =
  new System.Windows.Controls.ListBoxItem();
item1.Content = "Item 1";
item2.Content = "Item 2";
listbox.Items.Add(item1);
listbox.Items.Add(item2);

Furthermore, because Items is the content property for ListBox, you can shorten the XAML even further, as follows:

<ListBox xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  <ListBoxItem Content="Item 1"/>
  <ListBoxItem Content="Item 2"/>
</ListBox>

In all these cases, the code works because ListBox’s Items property is automatically initialized to any empty collection object. If a collection property is initially null instead (and is read/write, unlike ListBox’s read-only Items property), you would need to wrap the items in an explicit element that instantiates the collection. Typical elements do not act in this fashion.

Dictionaries

System.Windows.ResourceDictionary, a collection type used wherever resources are defined, implements System.Collections.IDictionary, so it supports adding, removing, and enumerating key/value pairs in procedural code, as you would do with a typical hash table. In XAML, you can add key/value pairs to any resource dictionary. For example, the following XAML adds two Colors:

<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Color x:Key="1" A="255" R="255" G="255" B="255"/>
  <Color x:Key="2" A="0" R="0" G="0" B="0"/>
</ResourceDictionary>

This leverages the XAML Key keyword (defined in the secondary XML namespace), which is processed specially and enables us to attach a key to each Color value. (The Color type does not define a Key property.) Therefore, the XAML is equivalent to the following C# code:

System.Windows.ResourceDictionary d = new System.Windows.ResourceDictionary();
System.Windows.Media.Color color1 = new System.Windows.Media.Color();
System.Windows.Media.Color color2 = new System.Windows.Media.Color();
color1.A = 255; color1.R = 255; color1.G = 255; color1.B = 255;
color2.A = 0;   color2.R = 0;   color2.G = 0;   color2.B = 0;
d.Add("1", color1);
d.Add("2", color2);

The value specified in XAML with x:Key is treated as a string; no type conversion is attempted.

More Type Conversion

Plain text can often be used as the child of an object element, as in the following XAML declaration of SolidColorBrush:

<SolidColorBrush>Green</SolidColorBrush>

This is equivalent to the following:

<SolidColorBrush Color="Green"/>

even though Color has not been designated as a content property. In this case, the first XAML snippet works because a type converter exists that can convert strings such as "Green" (or "green" or "#008000") into a SolidColorBrush object.

Loading & Parsing XAML

XAML is typically paired with a code-behind file. When you add a page or user control to a Visual Studio project, the generated XAML file contains an x:Class attribute on the root element that references the class defined in the code-behind file. When you reference any event handlers in XAML (via event attributes such as Click on Button), they must be defined in the class referenced by x:Class. The referenced class must derive from the type of the root element (such as PhoneApplicationPage in the case of pages).

When you compile your project, a C# source file gets generated and compiled into your assembly for each XAML file with a code-behind file. These files have a .g.cs suffix, where the g stands for generated. Each generated source file contains a partial class definition for the code-behind class. This partial class contains a field (internal by default) for every element named with x:Name in the XAML file, using the element name as the field name. It also defines the InitializeComponent method that does the work of loading the XAML, assigning the fields to the appropriate instances originally declared in XAML, and hooking up any event handlers (if any event handlers were specified in the XAML file).

If you want to dynamically load XAML yourself, you can use a very simple class called System.Windows.Markup.XamlReader. XamlReader has a single static method—Load—that accepts a XAML string, parses it, creates and initializes the appropriate .NET objects, then returns an instance of the root element. This method is the key to Chapter 11, “XAML Editor.”

If XAML content in a string has a grid as its root element, the following code could be used to create live objects representing all of its contents:

Grid grid = (Grid)XamlReader.Load(someString);

You can then treat this grid just like one that was statically defined on your current page. You could change its properties, add it to the current page (perhaps by setting it as a new value for the current page’s Content property), and so on.

You can retrieve child elements of this newly created grid by making use of the appropriate content properties or collection properties. The following code assumes that the grid’s fifth child (a zero-based index of 4) is a button:

Grid grid = (Grid)XamlReader.Load(someString);
// Grab the OK button, using hard-coded knowledge
Button okButton = (Button)grid.Children[4];

With a reference to the button, you can again do whatever you want: Set additional properties, attach event handlers, or perform additional actions that you can’t do from XAML, such as calling its methods.

Of course, code that uses a hard-coded index and other assumptions about the user interface structure isn’t very satisfying, as simple changes to the XAML can break it. Fortunately, XAML supports naming of elements so they can be found and used reliably from procedural code.

Naming XAML Elements

The XAML language namespace has a Name keyword that enables you to give any element a name. For the simple OK button that we’re imagining is embedded somewhere inside a Grid, the Name keyword can be used as follows:

<Button x:Name="okButton" Content="OK"/>

With this in place, you can update the preceding C# code to use the grid’s FindName method that searches its children (recursively) and returns the desired instance:

Grid grid = (Grid)XamlReader.Load(someString);
// Grab the OK button, knowing only its name
Button okButton = (Button)grid.FindName("okButton");

FindName is not unique to a grid; it is defined on FrameworkElement, the base class for every visual element in Silverlight.

XAML Keywords

The XAML language namespace (http://schemas.microsoft.com/winfx/2006/xaml) defines a handful of keywords that must be treated specially by any XAML parser. They mostly control aspects of how elements get exposed to procedural code. You’ve already seen some of them (such as Key, Name, and Class), but Table B.1 lists all of the ones supported by Windows Phone 7. They are listed with the conventional x prefix because that is how they usually appear in XAML and in documentation.

Table B.1 Keywords in the XAML Language Namespace Supported By Windows Phone 7, Assuming the Conventional x Namespace Prefix

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.214.32