Understanding XAML Syntax, Document Structure, and Features

XAML files are easily spotted by their .xaml extension and often have a corresponding code-behind file (.xaml.cs). In this section, we'll take a look at the structure of these XAML files, the XAML syntax, and the most important features of XAML.

images Note An easy way to understand XAML is to inspect existing XAML files and compare the XAML to the output in the designer. Enabling split mode (that is, XAML and designer in the same view), a feature available in both Expression Blend and Visual Studio 2010, is the ideal means for doing so, because this allows you to make a change in one and immediately see the result in the other.

Core XAML Syntax!

XAML is based on XML, and thus inherits numerous concepts from it—the two most important being elements and attributes. Therefore, if you're familiar with XML, you'll already be familiar with the basic syntax of XAML.

Each element in a XAML file maps to a .NET class, which will often be a control. In most cases, an element will consist of both an opening and a closing tag. An opening tag consists of a less-than angle bracket (<), followed by the class name, and closed with a greater-than angle bracket (>). The closing tag is similar; however, the < is followed by a forward slash (/). For example, to insert a button into your XAML, you can use the following syntax:

<Button></Button>

Because the button in this example does not have any content (that is, nothing between the opening and closing tags), you may choose to use the shorter form, where the closing tag is omitted, but the > is preceded by a /, as demonstrated here:

<Button />

When you reference a control in XAML, you no doubt will want to assign values to its properties. There are a number of ways to do this in XAML, as will be described later in the “Assigning Property Values to Controls” section, but here, we'll specifically look at the attribute syntax for doing so. Each attribute on the XML element maps to a property or event on the referenced control.

Attributes are specified within the opening tag of an element. The control name should be followed by a whitespace character (space or new line), after which you can start assigning values to attributes. The syntax for an attribute is the name of the property you wish to assign a value to, followed by an equal sign (=), followed by the value surrounded by double quotes ("). For example, you can assign some text (Hello) to the Content property of the Button control in the previous example using attribute syntax like so:

<Button Content="Hello" />

While the standard character to surround the value of the attribute is the double quote ("), you can exchange it with a single quote (') if necessary. You might want to do this, for example, if you wanted to actually include a double quote as a part of the value, in which case any double quote character between the single quotes will be considered a part of the value and ignored as being a XAML markup token character.

images Note Element and attribute names are case sensitive.

Creating an Object Hierarchy

When you create a XAML file, you are essentially defining a hierarchy of objects—also known as an object graph or object tree. A XAML file can have only a single root-level node, and elements are nested within each other to define the layout and content of the user interface. Most of the objects in the object tree in Silverlight will be controls.

images Note The topmost object in the application's object hierarchy is called the root visual, and a reference to this object can be obtained in code using the RootVisual property of the Application object.

Let's inspect the structure of a simple XAML file. Say we've added a new file named SimpleXAMLFile.xaml to the AdventureWorks project (created in Chapter 1), using the Silverlight User Control item template. This will generate the following XAML file:

<UserControl x:Class="AdventureWorks.SimpleXAMLFile"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    
    <Grid x:Name="LayoutRoot" Background="White">

    </Grid>
</UserControl>

Unfortunately, despite being a simple XAML file, it still incorporates a range of XAML concepts that we aren't quite ready to cover yet, including some namespace prefix declarations (the attributes starting with xmlns:) and some attached properties, such as the d:DesignHeight and d:DesignWidth properties, which differ from standard properties such as the Background property on the Grid element. Let's ignore these for the time being, strip this XAML right back to just its structure, and focus on that:

<UserControl>
    <Grid>
    </Grid>
</UserControl>

Here, you have UserControl as your root element, with a Grid control element as its content. You could read this as, “The user control contains a grid control.” You cannot add any more elements to the UserControl element, because the UserControl control accepts only a single child element. However, the Grid control does accept multiple child elements.

images Note A nice way of visualizing and navigating the object hierarchy (especially when your XAML file is complex) is using the Document Outline tool window in Visual Studio (View images Other Windows images Document Outline).

This is a very simple object hierarchy, and a long way from defining anything useful; however, we'll keep adding to this basic structure throughout this chapter to define a very simple user interface.

images Note The object hierarchy is often referred to as the visual tree. You can traverse the objects in the visual tree in code using the VisualTreeHelper class, found in the System.Windows.Media namespace. It exposes methods to get the children of a XAML object (GetChild and GetChildrenCount), the parent of a XAML object (GetParent), and all the objects within a given area on the screen around an object (FindElementsInHostCoordinates).

Defining Namespaces

Before we stripped the XAML file introduced earlier back to its core structure, you may have noticed that the opening tag of the root element contained a number of attributes that start with xmlns:. Let's take another look at that element:

<UserControl x:Class="AdventureWorks.SimpleXAMLFile"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"     mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

A number of namespace prefixes are defined on this root node (in bold), each declared using xmlns, the XML attribute used for declaring namespace prefixes. XAML namespace prefix declarations are conceptually similar to adding using statements at the top of a class in C#, but with a few notable differences. To use controls in your XAML file, the CLR namespace that they are defined in must be declared in the root element and assigned a prefix that is unique within the XAML file. This prefix will be used when referencing a control in order to remove any ambiguity as to which control you are referring to.

images Note Although namespace prefixes are generally declared in the root element in the XAML file, they can actually be declared at any level (that is, in any element) in a XAML file and be referenced anywhere below that element in the hierarchy. However, it is generally standard practice to define them all on the root element of the XAML file.

Prefixes are a type of alias, reducing the verbosity of XAML by letting you qualify a control with the prefix you have defined, rather than with its whole namespace. The prefix is defined immediately following the colon after xmlns.

Note that the first namespace declared on the root element in the example isn't actually assigned a prefix:

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

This is because it is defining the default namespace (the Silverlight namespace) for the file, enabling Silverlight controls to be used throughout the file without the need to qualify their names with a namespace prefix. However, the second namespace reference does define a prefix (x—used to reference the XAML namespace):

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

images Note Unfortunately, these default namespace declarations aren't the best examples to use, as none of them directly reference CLR namespaces and assemblies, as will the namespaces you generally declare. Instead, they map to URIs—known as consolidated namespaces. Consolidated namespaces combine multiple namespace declarations into one declaration, enabling you to declare a single URI namespace instead of needing to reference multiple namespaces. However, you don't need to worry about this concept—leave these declarations as they are, and simply add additional declarations as required following them. If you do want to consolidate a number of namespaces, you can use the XmlnsDefinition attribute, which will be described in Chapter 10. Another consolidated namespace that isn't referenced by default, but that you may find useful, is the SDK consolidated namespace—it brings all the namespaces in the Silverlight SDK together, so that instead of defining prefixes for all these namespaces in your XAML file, you can simply define just the one namespace prefix (sdk) instead to reference all the controls in the SDK:

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

When you declare a namespace prefix, it will almost always begin with clr-namespace:; referencing a CLR namespace and the assembly that contains it. Prefixes can be anything of your choosing, but it is best to make them short, yet meaningful. For example, in the following declaration we are declaring a prefix for the System.Windows.Controls namespace in the System.Windows.Controls.Data assembly, which we'll name data:

xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

Once you've declared a namespace prefix, you can use controls within that namespace like so:

<data:DataGrid></data:DataGrid>

images Note IntelliSense in Visual Studio 2010 is very helpful when both declaring namespaces and referencing controls in those namespaces. After you enter a namespace prefix and press = (e.g., xmlns:data=), IntelliSense will display a list of all the namespaces and their corresponding assemblies referenced in the project. You can then select a namespace from the list and press Tab to insert the namespace and assembly name into the declaration. When using the designer, dropping a control from the toolbox onto the design surface, from a namespace that is not currently declared, will automatically declare its namespace and assign a default namespace prefix. When using a control, starting the open tag of the element with (<) will display a list of the namespace prefixes defined in the file, along with the controls from the default namespace. After you select or type the namespace prefix, IntelliSense will display a list of the controls in the mapped namespace for you to choose from. The XAML syntax for declaring namespaces and using controls from those namespaces may initially look intimidating, but IntelliSense makes writing this XAML completely effortless.

Assigning Property Values to Controls

After inserting a control element into a XAML file, you will in most cases want to set some property values on that control (e.g., the background color of a text box). You can of course use the Properties window in both Visual Studio and Expression Blend to do so (in most cases), but let's look at the different methods of assigning property values to controls in XAML. The method you use will depend on the nature of the property and the value you are assigning to it. The reasons you might use each method will be explained, along with its corresponding syntax.

Attribute Syntax

The simplest way to assign values to properties in XAML is to set them inline, using attribute syntax. This syntax is used when the property has a primitive type, such as string, integer, Boolean, and so on, or when the property's type has an associated type converter that can accept a string and convert it to that type. Regardless of the type of the property, all values should be enclosed within single or, preferably, double quotes. The following example demonstrates assigning the Content property of a Button control using attribute syntax:

<Button Content="Hello" />

Content Element Syntax

Some controls define a property whose content (that is, the XAML between its beginning and end tags) will be assigned to the property's value. This is known as content element syntax, and the control defines this content property by decorating its class with the ContentProperty attribute. A further discussion of how this behavior is achieved will be covered in Chapter 12. There's no clear way of determining which property, if any, is assigned the content of the control, but you will find it in the MSDN documentation for the control. As a general rule, however, most properties defined as content properties are named Content because this property is provided by the ContentControl base class that most controls accepting custom content derive from. The following example has exactly the same output as the previous example. However, it uses content element syntax to assign the value to the Content property, because the Content property is designated as a content property on the Button control:

<Button>Hello</Button>

images Note No quotes are required around the value when using content element syntax.

You may wonder at this point why there are two methods to achieve exactly the same outcome. The reason is that rather than just simple values, many content properties accept complex values that can't be assigned using attribute syntax. (The value would be treated as plain text if you were to try.) Instead, using content element syntax, the Content property of the Button control can accept any XAML object hierarchy that will be displayed as the content of the button. You may have noticed that unlike the Button control in Windows Forms, there doesn't appear to be an Image property on the Silverlight Button control. This may initially make you think the Button control in Silverlight is very limited in its capabilities, but you would be mistaken, because this is where the Content property demonstrates the flexibility of XAML. Because the Content property will accept XAML elements as values, you can specify some XAML to display both an image and some text as the content of the button, as the following example demonstrates:

<Button Width="80" Height="35">
    <StackPanel Orientation="Horizontal">
        <Image Source="accept.png" Width="16" Height="16" />
        <TextBlock Margin="7,0,0,0" Text="OK" />
    </StackPanel>
</Button>

Here, we are using a StackPanel (discussed later in this chapter) containing both an Image control and a TextBlock control to achieve the result shown in Figure 2-1. Although using the StackPanel may initially appear to require more work than achieving the same outcome in Windows Forms, it does enable you to easily lay out the content of the button in whatever form you choose, rather than how the control chooses to implement the layout, and demonstrates the incredible flexibility of Silverlight and XAML. Using this method you could have any Silverlight control in the content of the Button, such as a ComboBox or even a DataGrid—practicality issues aside!

images

Figure 2-1. A Button control with custom content

images Note Unless the content property is a collection, the content you assign to it can consist only of a single control XAML element, although that element can have child elements as required. Otherwise, you will get the error, “The property XXX is set more than once.” If the property is a collection, you can assign multiple elements, as detailed shortly, using collection syntax.

Property Element Syntax

What if you want to assign a complex value to a property that isn't defined as a content property on a control? This is still possible using property element syntax. Property element syntax requires you to create a new element between the control element's opening and closing tags, of the form <control.property>. The dot in the element's name indicates to the XAML parser that this is a property element rather than an object element.

In this example, we'll assign the same value to the Content property of the button as we did in the previous example, but this time, we'll assign the property value using property element syntax, with the property element highlighted in bold:

<Button Width="80" Height="35">
    <Button.Content>
        <StackPanel Orientation="Horizontal">
            <Image Source="accept.png" Width="16" Height="16" />
            <TextBlock Margin="7,0,0,0" Text="OK" />
        </StackPanel>
    </Button.Content>
</Button>

You will find yourself often using property element syntax to assign resources to a control's Resources property (as will be discussed later in this chapter in the “Working with Resources and Resource Dictionaries” section), or basically any control property that accepts a complex value that can be defined in XAML. However, if that property is designated as a content property, like the Content property in the preceding example, it's standard to simply use the content element syntax instead and omit the property element's tags.

Collection Syntax

As stated earlier, properties that can accept XAML content can be assigned only a single control (although that element can have its own child elements), unless the property is a collection type. If the property is a collection type, you can use collection syntax to assign multiple items (generally, controls) to that collection, with each top-level element defined in the value being added as an item to the collection. The Items property on the ComboBox control is an example of where collection syntax can be used, as it is a collection that maintains all the items in the combo box. Because the Items property is designated as the content property for the ComboBox control, we can simply use content element syntax to assign the controls to the Items property. This method is equally applicable using property element syntax.

<ComboBox>
    <ComboBoxItem Content="First item" />
    <ComboBoxItem Content="Second item" />
    <ComboBoxItem Content="Third item" />
    <ComboBoxItem Content="Fourth item" />
</ComboBox>

images Note You can also assign values to multiple properties on a control, maintain these in a central location as a resource, and assign them to multiple controls using styles. These will be discussed later in this chapter in the “Styles” section.

Attached Properties

Attached properties are special types of properties that are assigned a value on a control, but the properties actually belong to another control, usually higher up in the hierarchy. In essence, controls can register particular properties as attached properties, which can then be used by any control. This may seem a rather confusing concept initially; therefore, the best way to demonstrate this concept is by an example.

Say you have a control whose position in the user interface is being controlled by a parent control—for example, a Button control that is contained within a Canvas control. No positioning properties are defined on the Button control, as the type of layout control the button is contained within determines how it is positioned and what positioning properties it requires (the “Layout Controls” section later in this chapter discusses this topic further). The Canvas control needs to know where to position the Button control on its surface, so it needs to expose two properties, Left and Top, as attached properties, which can be used by the Button control to allow the Canvas to position the button accordingly. You may like to consider it as the Button control inheriting the Left and Top properties from the Canvas control.

To use these Canvas properties on the Button control, you need to include the name of the control that the properties belong to, using property element syntax. The following example demonstrates the Button control assigning values to the Left and Top properties that it inherited from the parent Canvas control:

<Canvas>
    <Button Canvas.Left="30" Canvas.Top="15" />
</Canvas>

images Note The values of attached properties actually belong to the control that you are setting them on. In this case, despite the Left and Top properties belonging to the Canvas control, the values themselves belong to the Button control.

The ToolTipService control is used to provide tooltip functionality to controls and uses attached properties to do so. However, unlike the previous example, where the control defining the attached properties exists higher up in the control hierarchy from the control that the attached property values are being applied to, ToolTipService demonstrates that this does not necessarily have to be the case. As previously mentioned, attached properties can be used by any control, and not necessarily via inheritance.

In this example we are using the ToolTipService.ToolTip attached property to assign a tooltip to a Button control:

<Button ToolTipService.ToolTip="This is an example tooltip" />

images Note Attached properties require a special technique to be used if you want to set their values in your code-behind. This will be covered in Chapter 11.

XAML Namespace Properties

The namespace assigned to the x: prefix, found in almost every XAML file, maps to the XAML namespace. This namespace contains a number of important attached properties that you will make extensive use of in your XAML files. Let's take a look at the most important properties in this namespace, and the scenarios in which you might use them.

x:Class

Looking at the first line of the root element in this XAML file, you will notice that it assigns a value to the x:Class attribute:

<UserControl x:Class="AdventureWorks.SimpleXAMLFile" ... />

This property is used to associate this XAML file with its corresponding code-behind class. As this property is automatically configured when you create a new XAML file in Visual Studio, you will rarely need to worry about it—just be aware of its importance.

x:Name

You will need to give controls names to interact with them in the code-behind or bind to them in XAML using ElementName binding, which will be discussed in Chapter 11. Most controls will have a Name property that you can use to assign the control a name:

<TextBox Name="UserNameTextBox" />

For those that don't have this property, you can use the x:Name attached property from the XAML namespace instead, like so:

<TextBox x:Name="UserNameTextBox" />

It doesn't matter which method you use, because they both serve exactly the same purpose. However, good standard to stick to is to use the Name property where it has been implemented and use x:Name only where it hasn't. Alternatively, you can choose to standardize on x:Name, which is applicable to every control.

Regardless of which you choose, a member variable will be automatically created for the control once you have given it a name, in the hidden code-generated designer class, which you can use to refer to that control in code.

images Note At times, the member variable will not be created in the hidden code-generated designer class until you compile the application.

x:Key

Instead of a name, resources are assigned a unique key using this property, which controls can use to reference that resource. See the “Working with Resources and Resource Dictionaries” section later in this chapter for more information.

x:ClassModifier and x:FieldModifier

By default, the backing class to your XAML file will be have a scope of public, and surprisingly, the corresponding backing fields for any controls that you give a name to will have their scope set to internal. The x:ClassModifier and x:FieldModifier properties enable you to explicitly set these access levels in the XAML: the x:ClassModifier property is used to specify the class's scope, and the x:FieldModifier property to set the scope for controls. For example, the following XAML uses this property to set the button's scope to private, preventing it from being accessed from outside the XAML file's backing class:

<Button x:Name="OKButton" x:FieldModifier="private" Content="OK" />

images Note If you change the access level of the class using the x:ClassModifier property, you will need to manually update the scope for your code-behind class accordingly; otherwise, you will receive a compilation error stating that partial declarations of the class have conflicting accessibility modifiers (that is, “the scope of the automatically generated partial designer class for the XAML file does not match that of the code-behind partial class”).

Design-Time Properties

The Expression Blend 2008 namespace contains some attached properties that can be used to improve the design-time experience when developing Silverlight applications. The values for these attached properties will then be ignored at runtime.

Take another look at the root element of the simple XAML file that we have been using:

<UserControl x:Class="AdventureWorks.SimpleXAMLFile"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

In bold are two attached properties, DesignHeight and DesignWidth, from the Expression Blend 2008 namespace (assigned a prefix of d), and another attached property, Ignorable, from the Markup Compatibility namespace (assigned a prefix of mc).

Try removing the DesignHeight and DesignWidth properties from the element, including their values. You will find that the design surface in the designer has shrunk to the size of its contents—which means shrinking away to nothing because there's currently no content in the user control. You could set the Height and Width properties of the UserControl instead, which will work but will also affect how the user control will be rendered at runtime, where you may want it to expand to fill the browser window area. Therefore, the DesignHeight and DesignWidth properties can be very useful for sizing your design surface at design time, while still allowing the user control to behave as required at runtime.

If you were to look in the Expression Blend 2008 namespace, you would also find that there are other design-time data-related properties, including DataContext, DesignData, and DesignSource, in addition to the DesignInstance and DesignData markup extensions. It can often be useful to specify the structure of the data that you will be binding to and populate your design surface with sample data in order to see how it might look at runtime. This is a concept often referred to as blendability. This concept will be explained in detail in Chapter 10.

In order for these design-time properties to be ignored at runtime, the mc:Ignorable attached property is used, from the Markup Compatibility namespace, to tell the XAML parser to ignore the namespace declaration d and any references to it. If you remove this property and attempt to compile your Silverlight application, you will find that the compilation fails, stating the following:

XAML Namespace http://schemas.microsoft.com/expression/blend/2008 is not resolved

Markup Extensions

Often, you have scenarios where you want to set the value of a control's property, but this value is not known at design time or should be determined at runtime. A typical example of this would be when you want to display data that has been retrieved from the server—a problem that the concept of data binding was designed to solve.

To support these types of scenarios and specify them in XAML, including but not limited to enabling data binding, is the concept of markup extensions. A markup extension is essentially a class containing some logic that is evaluated at runtime. The resulting value is then assigned to the control's property that the markup extension was applied to. When used in XAML, markup extensions are denoted by the curly brackets surrounding them. The brackets indicate to the XAML parser that what is inside them is a markup extension, and therefore the value to be assigned to the control's property needs to be evaluated by the markup extension. Common markup extensions include

  • StaticResource: Used to assign a resource as the value of the property (discussed later in this chapter in the “Working with Resources and Resource Dictionaries” section).
  • Binding: Used to specify a data binding expression (discussed later in this chapter in the “Data Binding” section).
  • TemplateBinding: Used in custom control templates to assign a value from a property exposed by the control (discussed further in Chapter 12).
  • RelativeSource: Used to bind the value of a property to that of another control relative to it in the object hierarchy (discussed further in Chapter 11).
  • x:Null: Used to assign a null value to a property.

The simplest use of a markup extension is to use x:Null to assign a null value to a property, as demonstrated here:

<TextBox Background="{x:Null}" />

Markup extensions can have properties whose values can be assigned following the markup extension name (within curly brackets). For example, here, we are using the Binding markup extension and assigning ListPrice as the value of its Path property:

<TextBox Text="{Binding Path=ListPrice}" />

images Note Notice the lack of double quotes around the property value. Since the entire value of the property (the markup extension) is surrounded by quotes, to use quotes within this value would result in invalid markup, and your application would fail to compile. Therefore, you must omit the double quotes from around the value, or use single quotes (') instead, which would be required if the value contains a comma.

Some markup extensions have specified a default property, enabling you to specify a value for that property without explicitly identifying the property. For example, since the Path property of the Binding markup extension is its default property, we can instead write the same expression like so:

<TextBox Text="{Binding ListPrice}" />

Note that the properties of a markup extension, in turn, can be assigned a markup extension value. Here, we are assigning a StaticResource markup extension to the Converter property of the Binding markup extension:

<TextBox Text="{Binding ListPrice, Converter={StaticResource CurrencyConverter}}" />

images Note To support markup extensions, a new concept had to be introduced on the code side of things, with special types of properties called dependency properties. These will be discussed later in this chapter, but it's important to note that all properties to which you want to assign a markup extension must be dependency properties. This should not be too much of an issue, however, because most of the important properties on the standard Silverlight controls are already registered as such.

A new feature in Silverlight 5 is the ability to write your own markup extensions. Writing custom markup extensions will be covered in Chapter 10.

images Note On occasion, you may have a scenario where you want to assign a literal value to a property in XAML whose first character is an open curly bracket. However, the XAML parser will attempt to process this as a markup extension, and a runtime error will occur. This error is especially likely when specifying tokens in a URI mapping with the navigation framework (covered in the next chapter), as these also use curly brackets as identifiers. To work around this issue, you can use an escape sequence, which is an open bracket immediately followed by a closing bracket ({}), after which the rest of the value will be treated as a literal. For example, to assign a value of {page} to the Uri property of a UriMapping element in XAML, you would need to preface the value with {}, like so:

<navigation:UriMapping Uri="{}{page}" MappedUri="/Views/{page}.xaml" />

Namescopes

XAML namescopes are not a concept you will likely need to worry about much, as they are handled automatically by the XAML parser behind the scenes. However, they are worth mentioning, as they important in how named controls are managed in XAML.

If you were to create two controls in a view, assign them the same name (for example, ItemText), and then attempt to compile your application, you would get the following compile error:

The type 'XXX' already contains a definition for 'ItemText'

The designer would also state the following:

Name 'ItemText' alread exists in the current name scope.

This makes perfect sense, because when you name a control, the compiler creates a variable in the code-behind that you can use to reference that control in your code. If you had multiple controls with the same name in your view, Silverlight wouldn't know which of these controls you were actually referring to.

However, ensuring that all control names added to the object hierarchy are unique is simply impossible. For example, let's say you have a control that is made up of a number of elements. If one of the elements making up the control had a name, using the control multiple times in a single view would result in the named element being added to the object hierarchy multiple times. Thus, multiple controls with the same name would exist in the object hierarchy, colliding with one another. To permit this scenario without creating collisions, Silverlight segments the object hierarchy into namescopes at given intervals. Control names within each namescope still need to be unique, but controls in different namescopes can have the same name.

Your application will have a default namescope, created with the root visual, and other namescopes will be automatically created by the XAML parser where required. Examples of instances where a new namescope may be created include when you load in XAML from an external source, such as when you use XAMLReader.Load, when you use a UserControl, and when you use a template (DataTemplate, ControlTemplate, and so on).

Templates will be discussed later in this chapter, but let's look at a simple example of where new namescopes are created due to the use of a template. Say you have a ListBox control with a number of items, and you want to customize the appearance of each item by assigning a template to them. This template may have various controls within it. For example, it may have an Image control and a TextBlock control, like so:

<ListBox Name="FolderList">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Image Source="folder.png" />
                <TextBlock Name="ItemText" Text="{Binding}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

In the preceding XAML, the TextBlock in the template is assigned a name of ItemText. However, once the template is applied to multiple list items, there will be multiple text blocks having the name ItemText in the object hierarchy. Therefore, the XAML parser will create a new namescope each time the template is applied to a list item, and the controls created from that template will live in that, preventing the control names from colliding with one another in the same namescope used by the view.

There are two instances in which you are likely to find the effects of namescopes. When you use the FindName method on a control to find an element with a given name below that control in the object hierarchy, it will search for that name only within the current namescope. Therefore, searching in the view containing the ListBox in the previous example for an element named ItemText, like so

object namedElement = this.FindName("ItemText");

will not find the TextBlock control, because any instances of it will be in a different namescope.

In addition, it's possible to bind the property of a control to that of another named control, using ElementName binding, but you can bind only to other controls that exist in the same namescope.

images Note Any named control that forms a part of a template, such as the ItemText TextBox in the recent ListBox item template example, will not have a variable created for it in the code-behind. This is because the control may be created multiple times in the view, and the Silverlight runtime wouldn't know which instance you are referring to.

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

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