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
- Related Topics
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="http://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.
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.
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
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.
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.
For XamlReader, the required root element must specify a default XAML namespace. This is typically the Silverlight namespace, http://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 http://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.