Referencing and Modifying Silverlight Objects

Referencing and Modifying Silverlight Objects

You can access and modify objects in your Microsoft Silverlight-based application at run time, using the Silverlight object model. For objects that are part of a collection or child objects, you can use the methods of the Collection object to reference and modify object values.

This topic contains the following sections:

  • Referencing Objects
  • Working with Collections
  • Walking the Object Tree
  • Referencing Object Properties
  • Attached Properties
  • Changing the Z-Order of Objects

Referencing Objects

You can reference objects in the Silverlight object hierarchy by using the Silverlight object model. There is also another object model available for scripting to HTML content: the document object model (DOM). However, the DOM does not identify the content that is loaded by a Silverlight plug-in as being part of the DOM. For more information about the DOM and the distinction between the DOM and the Silverlight object model, see Silverlight Object Models and Scripting to the Silverlight Plug-In.

Referencing the Silverlight Plug-In

You can retrieve the Silverlight plug-in value by using the GetHost method from any object in the Silverlight object model. This method is useful in event-handler functions in which the sender parameter is passed. As long as the event handled was a Silverlight object model event, the sender is guaranteed to be a Silverlight object. The following JavaScript example shows how to retrieve the Silverlight plug-in instance by using the GetHost method.

JavaScript
function onKeyUp(sender, keyEventArgs)
{
    // Determine whether the keystroke combination CTRL+V was detected.
    if ((keyEventArgs.key == 51) && (keyEventArgs.ctrl == true))
    {
        // Retrieve a reference to the plug-in.
        var plugin = sender.getHost();
        // Determine whether the 1.0 version of Silverlight is available.
        alert("Silverlight 1.0: " + plugin.isVersionSupported("1.0"));
    }
}

A Silverlight plug-in has its own properties, methods, and events that are available to the Silverlight object model. For more information, see Silverlight Object Models and Scripting to the Silverlight Plug-In.

Finding a Silverlight Object Created from XAML

You can find any object in the Silverlight object hierarchy by using the FindName method and referencing the object's Name or x:Name attribute value. The object you are searching for does not have to be a direct child element of the object that invokes the FindName method. If the method is successful and finds an object by that name, a reference to the object is returned; otherwise, null is returned. The following JavaScript example shows how to find a named object defined in the initial XAML source when the root Canvas is loaded.

JavaScript
// Loaded event handler for the root Canvas object.
function onLoaded(sender, eventArgs)
{
    // Retrieve the object corresponding to the x:Name attribute value.
    var found = sender.findName("RedRect");
    // Determine whether the object was found.
    if (found != null)
    {
        alert(found.toString());
    }
    else
    {
        alert("Object not found");
    }
}

For more information about how to use FindName, particularly when combined with searching for elements that were created through CreateFromXaml at run time, see XAML Namescopes.

Finding the XAML Root

Although it does not have a name, you can retrieve the Canvas element that is the root element of a Silverlight plug-in's content by checking the Root property.

Working with Collections

All collections in the Silverlight object model support basic collection operations, as defined by the Collection abstract class. Many Silverlight object model operations involve working with these collections. In particular, to add any content at run time, you must add it as a new item in an existing collection. This might be the Children collection of the required Canvas root that is referenced by Source and defined in the initial XAML, or it might be a collection that is contained at a deeper level in the object tree.

Referencing an Object in a Collection

You can retrieve a specific child of a parent object by referencing the index of the collection of the parent object. The following JavaScript example shows how to retrieve a child of a parent Canvas object by using the GetItem method.

JavaScript
function getObject(parent, index)
{
    // Determine if the index is valid.
    if (index < parent.children.count)
    {
        // Retrieve the child object at the specified index in the collection.
        var object = parent.children.getItem(index);
    }
    return object;
}

Enumerating Child Objects

You can enumerate the child objects of a parent object by accessing the children collection of the parent object, and looping through each index in the collection while the index is lower than the count. Many (but not all) such enumerable collections are named Children in the Silverlight object model. The following JavaScript example shows how to enumerate the children of a parent Canvas object, which has a child element collection named Children.

JavaScript
function getChildren(parent, index)
{
    // Enumerate the children of the Canvas object.
    for (i = 0; i < parent.children.count; i++)
    {
        var child = parent.children.getItem(i);
        // Display the index and type of the child object.
        alert(i + ": " + child.toString());
    }
}

Adding Objects

You can add XAML content to objects that support child objects, such as the Canvas object. The following JavaScript example shows how to create a TextBlock and add it to the root Canvas object by using the Add method on the object's collection of child objects.

JavaScript
var textBlock;
function onLoaded(sender, eventArgs)
{
    // Retrieve the ID of the plug-in.
    var plugin = sender.getHost();
    // Define and create a XAML fragment.
    var xamlFragment = '<TextBlock Canvas.Top="200" Opacity=".5" Text="Click for more info" />';
    textBlock = plugin.content.createFromXaml(xamlFragment);
    // Add the TextBlock to the root Canvas object.
    sender.children.add(textBlock);
}
// Toggle the Opacity property for the TextBlock.
function onToggle()
{
    if (textBlock.opacity)
    {
        textBlock.opacity = 0;
    }
    else
    {
        textBlock.opacity = 1;
    }
}

How Adding Objects Affects Z-Order

When you use the Add method, you add the object to the end of the parent object's collection. Because sibling objects are rendered starting from the first object to the last object, an object added to the end of a collection is displayed on top of all of its sibling objects. The following JavaScript example shows three objects added to a collection.

JavaScript
// Add three overlapping Rectangle objects.
function AddItems(rootCanvas)
{
    var plugin = rootCanvas.getHost();
    var xamlFragment = '<Rectangle Fill="Maroon" Canvas.Top="20" Canvas.Left="20" Height="100" Width="100" />';
    var rectangle_1 = plugin.content.createFromXaml(xamlFragment);
    rootCanvas.children.add(rectangle_1);
    var xamlFragment = '<Rectangle Fill="LightBlue" Canvas.Top="40" Canvas.Left="40" Height="100" Width="100" />';
    var rectangle_2 = plugin.content.createFromXaml(xamlFragment);
    rootCanvas.children.add(rectangle_2);
    var xamlFragment = '<Rectangle Fill="Teal" Canvas.Top="60" Canvas.Left="60" Height="100" Width="100" />';
    var rectangle_3 = plugin.content.createFromXaml(xamlFragment);
    rootCanvas.children.add(rectangle_3);
}

The following illustration shows the result of the previous JavaScript example.

Z-order of added objects

Z-order of added objects

Notice that when you add objects to a collection, the object that was added most recently is higher in the z-order. In this case, the third rectangle is displayed on top of the two other rectangles. Notice the index value of each new object as it is added to the collection.

Inserting an Object into a Collection

When you use the Insert method, you add the object to a specified index in the collection. Existing objects are shifted to make room for the new inserted object, and the index for other objects in the collection is potentially offset. The following JavaScript example shows an object inserted into the beginning of a collection.

JavaScript
// Insert an overlapping Rectangle object.
function InsertItem(rootCanvas)
{
    var plugin = rootCanvas.getHost();
    var xamlFragment = '<Rectangle Fill="Black" Canvas.Top="0" Canvas.Left="0" Height="100" Width="100" />';
    var rectangle_4 = plugin.content.createFromXaml(xamlFragment);
    rootCanvas.children.insert(0, rectangle_4);
}

The following illustration shows the result of the previous JavaScript example.

Z-order of inserted objects

Z-order of inserted objects

Notice the rendering of the inserted object. Because the inserted object is the first object in the collection, it is overlaid by all of its siblings that are rendered afterwards.

Removing Objects

You can remove objects from the Silverlight object hierarchy by using the Remove and RemoveAt methods. As soon as objects are removed from the Silverlight object hierarchy, they are no longer rendered. When you remove an object by using the Remove or RemoveAt methods, you are disconnecting the object from the Silverlight object hierarchy. The disconnected object is now a XAML fragment that can be connected back to the object hierarchy by using the Add method. The following two illustrations show the process of disconnecting objects from the Silverlight object hierarchy.

Removing an object from the Silverlight object hierarchy

Removing an object from the Silverlight object hierarchy

Disconnected XAML fragment and Silverlight object hierarchy

Disconnecting an object

The following JavaScript example shows how to remove a TextBlock from its parent Canvas object by using the Remove method on the object's collection of children. To use Remove, you must reference a particular object by name, which means that the object to remove must have had its name established through either the x:Name or Name attribute as part of its original XAML definition.

JavaScript
function removeCaption(rootCanvas)
{
    // Retrieve the TextBlock object.
    var captionTextBlock = rootCanvas.findName("myCaption");
    if (captionTextBlock != null)
    {
        rootCanvas.children.remove(captionTextBlock);
    }
}

The RemoveAt method removes a child object at a specified index value in the collection. This means that the child object does not require an x:Name / Name attribute value. The following JavaScript example shows how to remove the first object from a parent Canvas object by using the RemoveAt method.

JavaScript
// Remove the first child object from the parent collection.
myCanvas.children.removeAt(0);

Removing All Objects from a Collection

You can remove all objects in a collection by using the Clear method, which is equivalent to using the RemoveAt method for each item in the collection. The following JavaScript example shows how to remove all items from a collection by using the Clear method.

JavaScript
// Remove all child objects from the parent collection.
myCanvas.children.clear();

Walking the Object Tree

Walking the object tree is a common technique in object models. "Walking the tree" means that you use properties that either reference child objects (typically these are collections) or parent relationships to a containing object (usually this is done from within a collection, and returns the collection itself). You will call a succession of child properties and parent properties to navigate the axes of the object tree until you have retrieved the property that contains the object that you were looking for.

As a general rule, you should construct your content in the Silverlight object model in such a way that does not require walking the tree. All that is necessary to support this is to give any object that you intend to retrieve later a value for the x:Name / Name attribute in the XAML that creates it. Because objects are often defined either in the initial Source XAML or in subsequent CreateFromXaml calls that add XAML fragments into the tree, you generally always have the opportunity to give an object a name. Then you can find any named object with FindName. FindName will return an object in a single code step and is a much less error-prone technique for getting objects from the object tree.

However, there are cases where it is not possible or practical to give an object a name and thus enable retrieving it with FindName. One such scenario is if you are adding dynamic content that is in some way supplied by the user, and cannot predict the number of items that are added. Typically accessing the created objects will require that you enumerate the collection that you add objects to. In this case, you should not have to walk the object tree particularly deeply, and the enumeration techniques described earlier in the Enumerating Child Objects section should suffice.

Walking the object tree downwards (away from the root) multiple levels is generally possible as long as you know the points at which the contained objects will have collections. You may have to use try/catch techniques to detect this, by checking whether Children exists and Count is nonzero. However, remember that some collections (for example, Inlines) in the object model are contained in properties that are not named Children. Walking the object tree upwards (towards the root) is more cumbersome, because not all objects support GetParent; only UIElement objects support this method. Therefore, you can generally walk upwards only through successive Children collections, but not through the object tree that might define storyboards or geometries.

Referencing Object Properties

Properties in the Silverlight 1.0 object model are directly exposed to JavaScript through an object.property notation, in the same way that most other programming models that support JavaScript expose their properties. Many properties can also have a value set in XAML, by specifying the property through a variety of possible XAML syntaxes (see XAML Syntax Overview). The Silverlight object model also provides two accessor methods, GetValue and SetValue, which get and set properties based on passing the relevent property's name as a string. You generally do not need to use the GetValue/SetValue methods. The object.property notation for getting and setting properties is entirely equivalent and is generally more intuitive.

Using the GetValue Method

The following JavaScript example shows how to get a property value by using both the GetValue method and the conventional object.property notation.

JavaScript
function onMouseLeftButtonUp(sender, index)
{
    // Get the property value using the GetValue method.
    var opacity = sender.getValue("opacity");
    // Get the property value using the equivalent "." notation.
    var opacity = sender.opacity;
    alert("Opacity = " + opacity);
}

Using the SetValue Method

The following JavaScript example shows how to set a property value by using both the SetValue method and the conventional object.property notation.

JavaScript
function onMouseEnter(sender, index)
{
    // Set the property value using the SetValue method.
    sender.setValue("opacity", 0.5);
    // Set the property value using the equivalent "." notation.
    sender.opacity = 0.5;
}

Attached Properties

Attached properties are a concept that is part of the XAML language. You can set an attached property on objects or XAML elements even if that property is not conventionally defined in the setting object's list of properties. For attached properties such as Canvas.Top, the object.property notation does not work, because the two "dots" in the syntax create ambiguity. Instead, you set attached properties in Silverlight by using a syntax that resembles an indexer syntax: object["attachedPropertyName"], where attachedPropertyName is a string that defines the full dotted name of the attached property (for example "Canvas.Top"). In both the GetValue/SetValue notations and the abridged notation, the "." (dot) is considered part of the property name/identifier.

JavaScript
// Set the property value using the SetValue method.
sender.setValue("Canvas.Top", 40);
// Set the property value using the equivalent "." notation.
sender["Canvas.Top"] = 40;

Changing the Z-Order of Objects

The Canvas.ZIndex property value represents the z-order rendering behavior of objects in a collection. The following XAML example shows how to define two sets of Rectangle objects. In the second set of rectangles, the Canvas.ZIndex property is set to invert the z-order.

XAML
<!-- Render rectangles using default z-order (position within the Canvas). -->
<Canvas>
  <Rectangle
    Fill="Maroon" Canvas.Top="20" Canvas.Left="20" Height="100" Width="100" />
  <Rectangle
    Fill="LightBlue" Canvas.Top="40" Canvas.Left="40" Height="100" Width="100" />
  <Rectangle
    Fill="Teal" Canvas.Top="60" Canvas.Left="60" Height="100" Width="100" />
</Canvas>
<!-- Render rectangles using explicit z-order by using Canvas.ZIndex. -->
<Canvas Canvas.Left="200">
  <Rectangle
    Canvas.ZIndex="2"
    Fill="Maroon" Canvas.Top="20" Canvas.Left="20" Height="100" Width="100" />
  <Rectangle
    Canvas.ZIndex="1"
    Fill="LightBlue" Canvas.Top="40" Canvas.Left="40" Height="100" Width="100" />
  <Rectangle
    Canvas.ZIndex="0"
    Fill="Teal" Canvas.Top="60" Canvas.Left="60" Height="100" Width="100" />
</Canvas>

The following illustration displays the result of the previous XAML content example.

Changing the z-order of objects within a collection

Changing the z-order of objects within a collection

The first set of Rectangle objects uses the default z-order rendering of objects, which is based on the position of the child object in the Canvas collection. The first object in the collection is rendered first, followed by the second object, and so on. The second set of Rectangle objects uses the Canvas.ZIndex property to override the default z-ordering of objects in the collection.

In the second set of Rectangle objects, the first object (the maroon-colored one) is the highest in the z-order, or closest to the foreground. Its Canvas.ZIndex value is "2". This is a purely arbitrary value, but it must be greater than any other child object Canvas.ZIndex value in the Canvas collection. If you do not set the Canvas.ZIndex value explicitly, it defaults to 0.

You can also set the Canvas.ZIndex value to a negative value such as "-99", which places the object even farther from the foreground. Canvas.ZIndex values are aligned along the z-axis. Higher values are closer to the foreground; lower values are farther from the foreground. The following illustration shows the relationship of z-order to z-axis.

Relationship of z-order to z-axis

Relationship of z-order to z-axis

For more information about z-order and other object positioning techniques, see Object Positioning in Silverlight.

See Also

Silverlight Object Models and Scripting to the Silverlight Plug-in
Silverlight Events
Overviews and How-to Topics