Поделиться через


XAML Namescopes

Microsoft Silverlight will reach end of support after October 2021. Learn more.

In Silverlight, a XAML namescope stores relationships between the XAML-defined names of objects and their instance equivalents. This is similar to the wider meaning of the term "namescope" in other programming languages and technologies.

This topic contains the following sections.

  • How XAML Namescopes Are Defined
  • Using XAML Namescopes at Run Time
  • Creating Objects at Run Time with XamlReader.Load
  • Creating Namescopes with XamlReader.Load
  • XAML Namescopes in Templates
  • XAML Namescopes for UserControls, ContentControls, and ItemControls
  • Setting Name at Run Time
  • Names and XAML Namescopes When Using the JavaScript API
  • Related Topics

How XAML Namescopes Are Defined

XAML namescopes are created by a XAML processor when XAML content is parsed. The most common XAML namescope scenario is when the initial source XAML content for a Silverlight-based application is parsed. Typically, the source XAML of the element tree for any given page of an application is defined within a single XAML file, which is completely in XAML format. Generally, each name specified within the XAML is added to the default XAML namescope, which is associated with the root element in the XAML markup provided. For this reason, this XAML namescope is typically referred to in this documentation as the root XAML namescope. As part of a markup compile operation that is implemented as a Silverlight-specific build action, names declared in XAML generate field references for later use as run time references. The markup compile also checks XAML namescope uniqueness at this point. Development tools might also provide a design-time check for name uniqueness as you author your XAML. However, XAML namescopes are truly defined and populated only at the point in time when the Silverlight XAML parser loads a XAML source into Silverlight and creates the object tree representation for the run time.

A name is specified in XAML markup by setting a value for one of two nearly equivalent attributes, Name or x:Name. (For details on the distinction between Name and x:Name, see Remarks in Name.) If you attempt to define the same name twice within any XAML that parses into a common XAML namescope, an exception is thrown during parsing.

Attributes in XAML can be specified only on object elements. Property elements (used to provide values for certain Silverlight properties in XAML) cannot have a Name or x:Name or any other attribute in the Silverlight implementation of XAML.

Using XAML Namescopes at Run Time

Names in XAML namescopes enable user code to reference the objects that were initially declared in XAML. The internal result of parsing XAML is that Silverlight creates a set of objects that retain some or all of the relationships these objects had in the XAML declarations. These relationships are maintained as specific object properties of the created objects, or are exposed to utility methods in the Silverlight programming model.

The most typical use of a name in a XAML namescope for Silverlight is as a direct reference to an object instance, which is enabled by the markup compile pass as a Silverlight build action, combined with a generated InitializeComponent method in the partial class templates.

You can also use the utility method FindName yourself at run time to return a reference to objects that were defined with a name in the XAML markup.

How Direct References to Named XAML Elements Work in a Silverlight Managed Project

What happens technically is that the XAML itself undergoes a markup compiler pass at the same time that the XAML and the partial class it defines for code-behind are compiled together. Each object element with a Name or x:Name attribute defined in the markup generates an internal field with a CLR name that matches the XAML name. This field is initially empty. Then the class generates an InitializeComponent method that is called only after all the XAML is loaded. Within the InitializeComponent logic, each internal field is then populated with the FindName return value for the equivalent name string.

You can observe this infrastructure for yourself by looking at the ".g" (generated) files that are created for each XAML page in the /obj subfolder of a Silverlight managed project after compilation. You can also see the fields and InitializeComponent method as members of your resulting assemblies if you reflect over them or otherwise examine their MSIL contents.

Creating Objects at Run Time with XamlReader.Load

XAML can be also be used as the string input for the XamlReader.Load method, which acts analogously to the initial XAML source parse operation. Load creates a new disconnected tree of Silverlight objects at run time. The disconnected tree can then be attached to some point on the main object tree. You must explicitly connect your created object tree, either by adding it to a content property collection such as Children, or by setting some other property that takes an object value (such as specifying a new ImageBrush for a Fill property value).

Creating Namescopes with XamlReader.Load

The preliminary XAML namescope defined by the new object tree created by Load evaluates any defined names in the provided XAML for uniqueness. If names in the provided XAML are not internally unique at this point, Load throws an exception. The disconnected object tree does not attempt to merge its XAML namescope with the main application XAML namescope, if or when it is connected to the main application object tree. After you connect the trees, your application has a unified object tree, but that tree has discrete XAML namescopes within it. The divisions occur at the connection points between objects, where you set some property to be the value returned from a Load call.

The complication of having discrete and disconnected XAML namescopes is that calls to the FindName method as well as direct managed object references no longer operate against a unified XAML namescope. Instead, the particular object that FindName is called on will imply the scope, with the scope being the XAML namescope that the calling object is within. In the direct managed object reference case, the scope is implied by the class where the code exists. Typically, the code-behind for run-time interaction of a "page" of Silverlight content exists in the partial class that backs the root "page", and therefore the XAML namescope is the root XAML namescope, not any discrete or disconnected XAML namescope.

If you call FindName to get a named object in the root XAML namescope, the method will not find the objects from a discrete XAML namescope created by Load(String). Conversely, if you call FindName from an object obtained from out of the discrete XAML namescope, the method will not find named objects in the root XAML namescope.

This discrete XAML namescope issue only affects finding objects by name in XAML namescopes by using the FindName call.

To get references to objects that are defined in a different XAML namescope, you can use several techniques:

  • Walk the entire tree in discrete steps with Parent and/or collection properties that are known to exist in your object tree structure (such as the collection returned by Panel.Children)..

    If you are calling from a discrete XAML namescope and want the root XAML namescope, it is always easy to get a reference to the current Application. You can get the visual root (the root XAML element, also known as the content source) from the current application in one line of code with the call Application.Current.RootVisual. You can then cast to FrameworkElement and call FindName from this scope.

  • If you are calling from the root XAML namescope and want an object within a discrete XAML namescope, the best thing to do is to plan ahead in your code and retain a reference to the object that was returned by XamlReader.Load(String) and then added to the main object tree. This object is now a valid object for calling FindName within the discrete XAML namescope. You could keep this object available as a global variable or otherwise pass it by using method parameters.

  • You can avoid names and XAML namescope considerations entirely by examining the visual tree. The VisualTreeHelper API enables you to traverse the visual tree in terms of parent objects and child collections, based purely on position and index.

XAML Namescopes in Templates

Templates in Silverlight XAML provide the ability to reuse and reapply content in a straightforward way, but templates might also include elements with names defined at the template level. That same template might be used multiple times in a page. For this reason, templates define their own XAML namescopes, independent of the containing page where the style or template is applied. 

Consider the following example:

<UserControl
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"  >
  <UserControl.Resources>
    <ControlTemplate x:Key="MyTemplate">
      ....
      <TextBlock x:Name="MyTextBlock" />
    </ControlTemplate>
  </UserControl.Resources>
  <StackPanel>
    <SomeControl Template="{StaticResource MyTemplate}" />
    <SomeControl Template="{StaticResource MyTemplate}" />
  </StackPanel>
</UserControl>

Here, the same template is applied to two different controls. If templates did not have discrete XAML namescopes, the MyTextBlock name used in the template would cause a name collision. Each instantiation of the template has its own XAML namescope, so in this example each instantiated template's XAML namescope would contain exactly one name. However, the root XAML namescope does not contain the name from either template.

Because of the separate XAML namescopes, finding named elements within a template from the scope of the page where the template is applied requires a different technique. Rather than calling FindName on some object in the object tree, you first obtain the object that has the template applied, and then call GetTemplateChild.

If you are a control author and you are generating a convention where a particular named element in an applied template is the target for a behavior that is defined by the control itself, you can use the GetTemplateChild method from your control implementation code. The GetTemplateChild method is protected, so only the control author has access to it. Also, there are conventions that control authors should follow in order to name parts and template parts and report these as CLR attribute values applied to the control class. This technique makes the names of important parts discoverable to control users who might wish to apply a different template, which would need to replace the named parts in order to maintain control functionality. For details, see topics under Control Customization.

XAML Namescopes for UserControls, ContentControls, and ItemControls

As part of the definitions for any type of control, you generally define part of the control in XAML. When you do this, you create a XAML namescope that applies specifically to the definition, and not to the usages. For this reason, you generally should not rely on Name or x:Name attributes you define in your definitions to be accessible from user code that instantiates your control class.

For controls that are designed to be templateable (ContentControl or ItemsControl), there is a TemplatePartAttribute attributing convention whereby parts of a template that have a name in the original template definition are attributed in the class definition. This convention is part of the control contract for a given custom control. There is already a disconnected namescope for these cases because the template defines its own namescope. Working within that namescope requires a OnApplyTemplate override that calls GetTemplateChild.

For the case of a UserControl, there is no equivalent template part attribute convention for parts of the UserControl in the definition XAML, nor is there a template applied at all. Nevertheless, the namescopes between definition and usage remain disconnected, because the definition namescope is defined and then effectively sealed when you package your UserControl into an assembly for reuse. A best practice here is to define your UserControl such that any value that needs to be set to modify the definition XAML is also exposed as a public property of the UserControl.

Setting Name at Run Time

At run time, a Silverlight XAML namescope is extensible. Whenever you add a FrameworkElement to the object tree, if that object has a value for Name, the name and the object it refers to is added to the hashtable that represents the current XAML namescope. The name must be unique. If the name is not unique in the XAML namescope, an exception occurs, with the exception coming from the operation that attempted to create an invalid tree structure in terms of the name uniqueness. For example, the exception might come from a call to Add, or an attempt to set Content. For more information on setting Name at run time, see Remarks in Name.

Names and XAML Namescopes When Using the JavaScript API

Using the JavaScript API, there are some technical differences in how XAML namescopes are created and used. For more information, see XAML Namescopes and JavaScript API.

See Also

Reference

Concepts