Question

I have an object on my asp.net page hosting a Silverlight xap (in my particular case it is in an IFrame, but I'm curious about regular objects as well). I can find the element in UI Spy, but the name just says "Silverlight Control". Trying to find that AutomationElement in my automated test is unsuccessful (control is null every time). Is there a setting in the Silverlight code or in the html that would help? How can I distinguish it if there are multiple Silverlight controls on the same page?

<object id="silverlightClient" style="display:none;" data="data:application/x-silverlight-2," type="application/x-silverlight-2">
    <param name="source" value="../../ClientBin/SilverlightApplication.xap"/>
    <param name="onerror" value="onSilverlightError" />
    <param name="background" value="#00000000" /> 
    <param name="minRuntimeVersion" value="4.0.41019.0" />
    <param name="autoUpgrade" value="true" />
    <param name="windowless" value="false" />
</object>

   TreeWalker tw = new TreeWalker(new System.Windows.Automation.PropertyCondition(AutomationElement.NameProperty, "Silverlight Control));
   AutomationElement control = tw.GetFirstChild(ancestor);

UI Spy

Identification
    ClassName: "MicrosoftSilverlight"
    ControlType: "ControlType.Window"
    Culture: "(null)"
    AutomationId: "71857844"
    LocalizedControlType: "window"
    Name: "Silverlight Control"
    ProcessId: "7636 (iexplore)"
    RuntimeId: "42 2163886"
    IsPassword: "False"
    IsControlElement: "True"
    IsContentElement: "True"

EDIT: added image, I also realized that the object is inside of an IFrame. UISpyImage - title name removed

Was it helpful?

Solution

I've created some extension methods to make working with AutomationElement somewhat easier. I've pasted the relevant ones below, but you can read more about them here.

I'm assuming you've got a reference to the root IE window. If not, but you know it's Process Id you can find it like so:

var ieWindow = AutomationElement.RootElement.FindChildByCondition(new PropertyCondition(AutomationElement.ProcessIdProperty, ieProcessId));

Assuming there's only one Frame open in IE, and a single Silverlight control on it, you can then do:

var silverlightControl = ieWindow.FindDescendentByClassPath(
                         new[]{
                               "Frame Tab",
                                 "TabWindowClass",
                                   "Shell DocObject View",
                                     "Internet Explorer_Server",
                                       "MicrosoftSilverlight",
                               });

If you have more than one Silverlight control, I don't know of a way to distinguish them through UIAutomation. I would try dropping the "MicrosoftSilverlight" entry from the Class path above, so that you get a reference to the Explorer page. Then use

AutomationElement.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "MicrosoftSilverlight"))

to find all the SilverlightControls, then probe them each in turn locate some element within them that allows you to distinguish between them.

Here are the extension methods:

public static class AutomationExtensions
{
    public static AutomationElement FindDescendentByClassPath(this AutomationElement element, IEnumerable<string> classNames)
    {
        var conditionPath = CreateClassNameConditionPath(classNames);

        return element.FindDescendentByConditionPath(conditionPath);
    }

    public static AutomationElement FindDescendentByConditionPath(this AutomationElement element, IEnumerable<Condition> conditionPath)
    {
        if (!conditionPath.Any())
        {
            return element;
        }

        var result = conditionPath.Aggregate(
            element,
            (parentElement, nextCondition) => parentElement == null
                                                  ? null
                                                  : parentElement.FindChildByCondition(nextCondition));

        return result;
    }

    public static AutomationElement FindChildByCondition(this AutomationElement element, Condition condition)
    {
        var result = element.FindFirst(
            TreeScope.Children,
            condition);

        return result;
    }

    public static IEnumerable<Condition> CreateClassNameConditionPath(IEnumerable<string> classNames)
    {
        return classNames.Select(name => new PropertyCondition(AutomationElement.ClassNameProperty, name, PropertyConditionFlags.IgnoreCase)).ToArray();
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top