Constructing Objects at Run Time

Constructing Objects at Run Time in Silverlight

The CreateFromXaml method enables you to create Silverlight objects dynamically at run time by loading object declarations written in XAML. You can then add the created content to the existing Microsoft Silverlight object hierarchy . Typically, you call Add to make the new content a child object of an existing object in the hierarchy. You can create a single Silverlight object, such as a TextBlock, or an entire tree of Silverlight objects. For more information on XAML, see XAML Syntax Overview.

This topic contains the following sections:

  • Overview
  • Creating a Single XAML Object
  • Creating a Tree of XAML Objects

Overview

The Silverlight content on your Web page is arranged as a hierarchy of objects in a tree structure. The single, topmost object in the structure is the root object, and corresponds to the root of the XAML file that is loaded as the plug-in's Source. The root object is generally a Canvas object, because Canvas can contain other objects such as TextBlock or MediaElement.

To add XAML content dynamically to the tree structure, you first create the XAML fragment by using the CreateFromXaml method. At this point, the XAML fragment you create is disconnected from the Silverlight object hierarchy. This means that the fragment is not rendered yet. However, you can modify the properties of the objects in the XAML fragment. Object methods cannot be invoked.

The XAML that you specify as the parameter to CreateFromXaml must have a single root (it must be well-formed XML in that regard). If you attempt to call CreateFromXaml by using a string that equates to XML that has multiple roots or is otherwise not well-formed, the CreateFromXaml method call will fail and raise an error. The XML root does not need to specify the Silverlight xmlns, as long as it is eventually added to content that does specify the Silverlight xmlns at its root.

Note   To help protect your Silverlight-based application against security attacks, we strongly recommend that you do not pass in untrusted XAML to the createFromXAML method. For example, untrusted XAML could contain a mock-up interface that mirrors a legitimate site, leading to a spoofing type security threat. Additionally, XAML contains references to script event handlers. Adding untrusted XAML to the live tree could result in unintended execution of script. Always validate that the XAML passed to createFromXAML is from a source that your Silverlight-based application considers trusted.

The following illustration shows the relationship between the Silverlight object hierarchy and the XAML fragment.

Disconnected Silverlight object hierarchy and XAML fragment

Disconnected Silverlight object hierarchy and XAML fragment

When you have created a disconnected object set from an XAML fragment, you can add it to the active Silverlight object hierarchy. The fragment can either be added as a child object to a parent object that is capable of supporting child objects, or set as a property value of an object. When the fragment becomes part of the Silverlight object hierarchy, the objects in the XAML fragment are rendered. The following illustration shows the relationship between the Silverlight object hierarchy and the XAML fragment when they are connected.

Connected Silverlight object hierarchy and XAML fragment

Connected Silverlight object hierarchy and XAML fragment

There are several requirements for adding XAML content dynamically to the Silverlight object hierarchy:

  • There must be existing XAML content associated with the Silverlight plug-in; you cannot use CreateFromXaml to replace the entire tree of content -- you must at the very least preserve the original root element. If you want to replace the entire tree, you can set Source, although note that Source requires an actual file to exist at a URI, and cannot be initialized with just a string. However, you can work around this issue by using inline XAML as the Source, and using the HTML DOM to document.write the contents of the SCRIPT block that contains the intended source XAML. For details, see Using Inline XAML as the XAML Source.
  • The CreateFromXaml method can be invoked only by the Silverlight plug-in.
  • XAML content that is created by using the CreateFromXaml method can be assigned only to one object. If you want to add objects created from identical XAML to different areas of the application, you must invoke CreateFromXaml multiple times and use different variables for the return value. (If you do this, be careful of name collisions; see XAML Namescopes.)
  • The Silverlight object that will contain the new XAML content must be capable of encapsulating an object, either as a child element or as a property value.

Creating a Single XAML Object

The following JavaScript example shows how to add a TextBlock to an existing Canvas object.

JavaScript
// MouseLeftButtonUp event handler for the root Canvas object.
function onMouseLeftButtonUp(sender, eventArgs)
{
    // Retrieve a reference to the plug-in.
    var plugin = sender.getHost();
    
    // Define a XAML fragment and create it.
    var xamlFragment = '<TextBlock Canvas.Top="200" Text="Click for more info..." />';
    textBlock = plugin.content.createFromXaml(xamlFragment, false);
    // Add the XAML fragment as a child of the root Canvas object.
    sender.children.add(textBlock);
}

Note   XAML content that is created by using the CreateFromXaml method is not rendered until it is added to an object by using the Add method.

If you want to use the x:Name attribute value as part of your XAML fragment, you have to provide the XML namespace reference as part of the XAML content. For example, for the previous XAML fragment example to use the x:Name attribute value, it will have to be rewritten as shown in the following example.
 

JavaScript
    // Define a XAML fragment and create it.
    var xamlFragment = '<TextBlock xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" ';
       xamlFragment += 'x:Name="myCanvas" Canvas.Top="200" Text="Click for more info" />';
    textBlock = plugin.content.createFromXaml(xamlFragment, false);

Note   If you use an x:Name attribute value that already identifies an object in the Silverlight object hierarchy, an error is generated when you invoke the Add method for the XAML fragment. For a complete discussion of namescopes and using Name values in the content that you create with CreateFromXAML, see XAML Namescopes.

Creating a LinearGradient Brush Object

You can also create XAML content that can be used as a property value for an object. The following JavaScript example shows how to create a LinearGradientBrush and add it as the Fill property for an Ellipse object.

JavaScript
// MouseEnter event handler for the Ellipse object.
function onMouseEnter(sender, eventArgs)
{
    // Set the Fill property of the Ellipse to the dynamically generated LinearGradientBrush.
    try
    {
    sender.fill = createLinearGradientBrush(sender.getHost());
    }
    catch(error)
    {
        alert(error.message);
    }
}
function createLinearGradientBrush(plugin)
{
    // Define a XAML fragment.
    var xamlFragment = '<LinearGradientBrush>';
       xamlFragment +=   '<GradientStop Color="Yellow" Offset="0.0" />';
       xamlFragment +=   '<GradientStop Color="Orange" Offset="0.5" />';
       xamlFragment +=   '<GradientStop Color="Red" Offset="1.0" />';
       xamlFragment += '</LinearGradientBrush>';
    // Create the XAML fragment and return it.
    return plugin.content.createFromXaml(xamlFragment, false);
}

In the previous example, if you want to assign the dynamically created LinearGradientBrush to more than one object, you have to invoke the createLinearGradientBrush function for each object, as shown in the following example.

JavaScript
function initFill(objectArray)
{
    // Set the Fill property for each item in the object array.
    for (i = 0; i < objectArray.length; i++)
    {
        objectArray[i].fill = createLinearGradientBrush();
    }
}

Defining Events in XAML Fragments

XAML fragments can define events as part of the content. The following JavaScript example shows how to define MouseEnter and MouseLeave events for a Rectangle object. In this case, the stroke of the Rectangle increases when the mouse enters the object boundary, and decreases when the mouse leaves the object boundary.

JavaScript
function onLoaded(sender, eventArgs)
{
    // Retrieve a reference to the plug-in.
    var plugin = sender.getHost();
    // Define a XAML fragment.
    var xamlFragment = '<Rectangle Width="100" Height="40" Fill="OldLace" ';
       xamlFragment += 'StrokeThickness="1" Stroke="Black" ';
       xamlFragment += 'MouseEnter="onMouseEnter" MouseLeave="onMouseLeave" />';
    // Create the XAML fragment.
    var rectangle = plugin.content.createFromXaml(xamlFragment, false);
    // Add the XAML fragment as a child of the root Canvas object.
    sender.children.add(rectangle);
}
function onMouseEnter(sender, eventArgs)
{
    // Increase the stroke on entering.
    sender.StrokeThickness = 2;
}
function onMouseLeave(sender, eventArgs)
{
    // Decrease the stroke on leaving.
    sender.StrokeThickness = 1;
}

Creating a Tree of XAML Objects

The CreateFromXaml method also enables you to create an entire tree of XAML objects. In addition, you can remove the tree as needed, without de-allocating the objects. The ability to add and remove XAML content dynamically lets you create application features such as tooltips that appear only when the mouse is over a certain part of the Web page.

The following JavaScript example shows how to create a custom tooltip that consists of a hierarchy of Canvas, Rectangle, and TextBlock objects. The tooltip is then added as a child to the main Canvas object when the mouse enters a specific object boundary. The tooltip is removed when the mouse leaves the specific object boundary.

JavaScript
// Define global variables.
var plugin, toolTip, mainCanvas;
// Set global variables for the plug-in and main Canvas objects.
function onLoaded(sender, eventArgs)
{
    plugin = sender.getHost();
    mainCanvas = sender;
}
function onMouseEnter(sender, args) {
    // Determine whether the tooltip is created.
    if (toolTip == null) {
        // Define the XAML fragment for the tooltip.
        var xamlFragment = '<Canvas Width="150" Height="30" Background="#FFFFE1">';
           xamlFragment +=   '<Rectangle Width="150" Height="30" Stroke="Black" />';
           xamlFragment +=   '<TextBlock Text="Hello, World" Canvas.Left="10" Canvas.Top="5" />';
           xamlFragment += '</Canvas>';
        // Create the XAML fragment for the tooltip.
        toolTip = plugin.content.createFromXaml(xamlFragment, false);
        // Position the tooltip at a relative x/y coordinate value.
        var cursorPosition = args.getPosition(sender);
        toolTip["Canvas.Left"] = cursorPosition.x + 30;
        toolTip["Canvas.Top"] = cursorPosition.y + 40;
    }
    // Add the tooltip to the Canvas object.
    mainCanvas.children.add(toolTip);
}
function onMouseLeave(sender, args)
{
    // Determine whether the tooltip is created.
    if (toolTip != null)
    {
        // Remove the tooltip from the Canvas object.
        mainCanvas.children.remove(toolTip);
    }
}

This JavaScript example also illustrates that you can modify the properties of a XAML fragment before it is attached to the Silverlight object hierarchy. However, you cannot invoke method calls on any object in the XAML fragment before it is attached to the Silverlight object hierarchy. Notice that the properties that are modified are Canvas.Left and Canvas.Top. These properties represent the position of the object relative to its parent. In this case, the parent is the main Canvas object, and the child is the tooltip Canvas object.

JavaScript
// Position the tooltip at a relative x/y coordinate value.
var cursorPosition = args.getPosition(sender);
toolTip["Canvas.Left"] = cursorPosition.x + 30;
toolTip["Canvas.Top"] = cursorPosition.y + 40;

Note   You can also modify a Silverlight object hierarchy by deleting objects. For example,the Remove method on the Canvas object's collection of children enables you to specify an object to delete. For more information, see Referencing and Modifying Silverlight Objects.

See Also

Silverlight Object Models and Scripting to the Silverlight Plug-In
Referencing and Modifying Silverlight Objects
XAML Namescopes
XAML Syntax Overview
Overviews and How-to Topics