views:

85

answers:

2

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

A: 

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();
    }
}
Samuel Jack
@Samuel, I really like the extension methods you created. However, I'm getting null when using browser.FindDesByClassPath(new[]{"Frame Tab"}); Any ideas on why that might happen?
Kevin
Can you show us what the tree in UISpy looks like? Can you see a child of the IE window that has class "Frame Tab"?
Samuel Jack
@Samuel, I added a link to a screen shot. I also realized that the object is inside of an IFrame. Thanks for the help.
Kevin
@Kevin: it looks like Frames don't play nicely with UI Automation. Try an experiment. Open UISpy, and make sure the Ctrl-Click select mode is on. Then Ctrl-Click an element within the frame. When I do this, UISpy is unable to find the element, and it reports an error in the Output pane.If that's the case, the only way open to you, I think, is using the AutomationElement.FromPoint method to find the frame, then navigate to the Silverlight control from there.
Samuel Jack