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


Using XamlReader.Load

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

The ability to create managed objects at runtime from XAML input is available in the managed Silverlight API, by using the static method XamlReader.Load. This topic explains how to use the Load method. Specifically, this topic explains the requirements for the input XAML, describes how to connect the output to the object tree, and discusses the XAML namescope issues that can affect using the FindName method against objects and parts that originated from a call to Load.

This topic contains the following sections.

  • Using XamlReader.Load
  • XamlReader.Load and Parsing Behavior
  • Constructors and XAML
  • XAML Namescope Considerations
  • XAML, Type Conversion, and Creating Objects
  • JavaScript API and CreateFromXAML
  • Related Topics

Using XamlReader.Load

XamlReader is a largely stateless static class with methods that create objects based on an input of XAML markup. XamlReader provides object construction behavior that parallels how XAML is parsed by the Silverlight runtime and the Silverlight application model. Parsing the XAML generates run-time object trees of managed objects. The object tree provides a way to program against those objects at runtime using either named references (identified by Name or x:Name in the parsed XAML) or by walking through parts of the complete tree .

There are several general concepts that are important to understand, when you create objects from XAML with the Load method:

  • The XAML content string must define a single root element.

  • The XAML content string must be well-formed XML, as well as being valid XAML.

  • The root element must declare the necessary XAML namespaces for any entities referenced in the XAML. This is true for the default Silverlight XAML namespace also; most strings for Load should specify xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" in the root element, so that the Silverlight core XAML namespace is the default XAML namespace.

  • Any custom assemblies referenced in a XAML namespace mapping must already be available to the application, typically through AssemblyPart packaging.

  • XAML for Load should not attempt to specify x:Class, or include any XAML-defined attributes for event handlers. Load logic cannot integrate the loaded XAML with code-behind classes at runtime. If you want to add event handlers, you must do so in code by referencing objects obtained from within the object tree structure of the Load result, and using language-specific syntax for attaching handlers (such as +=). For more information on attaching events using code, see Events Overview for Silverlight.

Connecting a Created Object Tree with the Main Silverlight Object Tree

The following illustration shows the relationship between the Silverlight object hierarchy and the object tree that is newly created from the XAML fragment.

Disconnected Silverlight object trees

A disconnected tree fragment

All Silverlight content in a browse-hosted HTML page ultimately renders because it is connected in an object relationship with the object that is loaded as RootVisual in the Silverlight application model. The RootVisual is then composited by Silverlight, and the appropriate content is rendered and passed to the HTML host by using one of the plugin/control access layers that Silverlight implements in its native code. Initially, any object tree that is not connected to the primary object tree (the tree based on RootVisual) is not rendered.

When you have created a disconnected object tree from a XAML fragment, you can add it to the primary Silverlight object tree by calling APIs of objects that exist in the primary object tree. The disconnected object tree can either be set to be a property value of an existing object, or added as a child object to a collection of other peer objects. (The latter case is really property setting too, but you are setting a parent object's collection property.) As soon as the fragment becomes part of the primary Silverlight object tree, Silverlight detects the change in composition of the primary object tree, and the objects in the XAML fragment are rendered. The following illustration shows the new relationship between the Silverlight object tree and the object tree result from parsing the XAML fragment, after they are connected.

Connected Silverlight object trees

Connected object tree

There are several requirements for adding XAML-originated content dynamically to the main Silverlight object tree:

  • There must be existing XAML content associated with the Silverlight plug-in; you cannot replace the entire tree of content. You must at the very least preserve the original root element.

  • The object that is created from XAML parsing API can be assigned to only one location in the primary object tree. (This is analogous to the initial XAML processing behavior — except for the special case of templates, there is a 1:1 relationship between the XAML and the objects it creates.) If you want to add objects created from identical XAML to different areas of the application's primary object tree, you must parse the XAML multiple times using the same input string, using different destinations for the return value.

  • The existing Silverlight object from the primary object tree that you want to use as the attachment point must support an appropriate property to set. In other words, the attachment point object must have some kind of settable property value. Some examples are as follows:

    • The attachment point object supports a generalized child element property such as Child or Content that takes a base type such as UIElement. The root object of the new object tree must be assignable to that property.

    • The attachment point object supports a collection property, and the collection type of that property supports an Add method. The new object tree can then be added as an item.

    • The attachment point object supports a specific-typed property value that matches the root object of the new tree. For example, you might create a new Brush with Load input, and then use it to set a Background value.

Security Concerns with Creating Objects from XAML

To help protect your Silverlight-based application against security attacks, we strongly recommend that you do not pass in untrusted XAML strings to Load (or to the JavaScript equivalent CreateFromXaml). Untrusted XAML could contain a mock-up interface that mirrors a legitimate site, leading to a spoofing security threat. Additionally, XAML potentially contains references to script event handlers. This is particularly of concern if you are not already declaring the managed API in your XAML by specifying x:Class from the currently loaded XAML: the default programming model mode in absence of x:Class is JavaScript. The result is that adding untrusted XAML to the main tree could result in unintended execution of script against your Silverlight application's object tree. Always confirm that the XAML content being used is from a trusted source.

XamlReader.Load and Parsing Behavior

Using the supplied XAML namespace, and possibly additional non-default XAML namespace information declared in the markup, object elements are evaluated and object names are used to construct objects. Attributes are used to find and set properties. If you provide markup that does not resolve within the Silverlight XAML vocabulary or any extended vocabulary, object creation and/or property setting will fail, a parsing exception is thrown, and the return value for Load will be null.

For more information on the XAML language and its language rules and terminology, see XAML Overview. If you are loading XAML that is backed by your own custom classes, see XAML and Custom Classes.

Constructors and XAML

Using the managed API for Silverlight, true constructors are available on the classes or structures that support elements that exist in the Silverlight element tree. In most cases where you want to construct an object at run time, you can simply call the constructor of the relevant class.

For managed code that backs a XAML implementation, the managed-code constructor serves a XAML-specific purpose. When a XAML processor encounters an object element, the processor searches for the backing managed class. The processor then calls the default (parameterless) constructor of that class immediately. Only after the default instance is created are any of the XAML attributes processed in order to set properties on the starting default instance. Therefore, any class that is intended to provide object element support in XAML must expose a public default constructor. This and other general concepts about XAML are explained in XAML Overview and XAML and Custom Classes.

If a given object could be defined in XAML with an object element, then you can also create it in code by following the same logic as a XAML parser: create the object with a default constructor, then set properties.

However, there are some exceptions to this general rule. In particular, Silverlight does not support defining a ControlTemplate in code.

XAML Namescope Considerations

Any x:Name or Name attribute values assigned in the XAML content for Load input are written into a new XAML namescope that corresponds to the created object tree. This XAML namescope will remain distinct even if the object tree that is created is subsequently connected to the primary XAML namescope of the currently loaded Silverlight content root. This means that if you subsequently attempt to call FindName to find the named elements that came from the Load output, you will not find them if you call FindName from an object in the primary XAML namescope. A recommendation is to always retain a variable for the object that is the root of the object tree that is added. If you call FindName from that object, then you are working against the created XAML namescope and will be able to return objects in that tree by name, and bridge the gap between XAML namescopes. For details on XAML namescope concepts, see XAML Namescopes.

XAML, Type Conversion, and Creating Objects

In some cases, properties that take a reference type as opposed to a value type can be set with an attribute syntax. Such cases rely on a type converter, which takes a string as input and constructs a new object on the basis of the string.

An example of this type-conversion behavior is the Color class. You can specify a color as a string. The string is processed by one of the following behaviors: creating a color by using values within a scheme such as ARGB, mapping to a static property of Colors, or resolving against a table of other named colors. In each case, a new Color is created with the appropriate ARGB values.

Because the conversion behavior for creating the named colors is native to the XAML processor and is not available to classes such as Color, Colors, or Brush, a useful technique can be to use Load to create a simple object tree, perhaps even just a single element. Set a property of type Brush, such as Control.Background, within that tree using XAML attribute syntax and specifying one of the named colors that do not have a static Colors value. Then, read the property value from the created object tree. You can use this SolidColorBrush value to set other Brush values at run time.

JavaScript API and CreateFromXAML

In the original JavaScript API for Silverlight version 1.0, the equivalent of Load is the JavaScript function CreateFromXaml. CreateFromXaml takes a XAML input string, and returns an object that is an object tree of Silverlight objects that can be accessed through runtime JavaScript.

JavaScript API use is increasingly uncommon by current Silverlight developers. However, if you do use the JavaScript API for Silverlight, CreateFromXaml is an important function, because the JavaScript API for Silverlight has no constructor logic. In order to instantiate any object for a Silverlight object graph at run time when using JavaScript API for Silverlight, you must provide a string for CreateFromXaml input and then use the return value to get the object. CreateFromXaml is basically an object construction engine for JavaScript API for Silverlight.

There are several notable differences between using XamlReader for managed code, and using the CreateFromXaml method for JavaScript. These differences modify some of the statements regarding XAML loading API in previous sections of this topic. The following is a list of differences that apply to JavaScript and CreateFromXaml:

  • For XamlReader, the required root element must specify a default XAML namespace. This is typically the Silverlight namespace, https://schemas.microsoft.com/winfx/2006/xaml/presentation. For CreateFromXaml, you can omit a default XAML namespace, in which case the namespace is implicitly assumed to be https://schemas.microsoft.com/winfx/2006/xaml/presentation.

  • For Load, the newly created object tree maintains a discrete XAML namescope when it is connected to the main Silverlight object tree. CreateFromXaml can also have this behavior if you specify an optional parameter, but the default behavior for object trees created from CreateFromXaml is that the XAML namescopes are merged if and when the object trees are combined. This introduces the possibility of name collisions at the time the merge is attempted.

  • JavaScript is loosely typed. The object returned from CreateFromXaml is within the JavaScript API and is interpreted implicitly in the context of the property it is being used to set. Explicitly casting the object return value is not necessary. By contrast, the managed API has the same strong typing requirements as managed code in general. Load returns a generic object. Before being added to the Silverlight object tree, the object must be of the type that is required by the property or collection where it is added, and this might require a cast.