Ejemplo n.º 1
0
        private static void ThrowUnableToFindChildException(string path, IUIAutomationElement item)
        {
            // if not found, build a list of available children for debugging purposes
            var validChildren = new List <string>();

            try
            {
                var children = item.GetCachedChildren();
                for (var i = 0; i < children.Length; i++)
                {
                    validChildren.Add(SimpleControlTypeName(children.GetElement(i)));
                }
            }
            catch (InvalidOperationException)
            {
                // if the cached children can't be enumerated, don't blow up trying to display debug info
            }

            throw new InvalidOperationException(
                      string.Format(
                          "Unable to find a child named {0}.  Possible values: ({1}).",
                          path,
                          string.Join(", ", validChildren)
                          )
                      );
        }
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //
        // This function is not used by the sample, but it shows an alternative approach to building up
        // the cache of hyperlinks. Depending on the action taken by the target app when providing data
        // to be stored in the cache of results, different approaches taken by the UIA client can have
        // different performance benefits.
        //
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////
        private void BuildListOfHyperlinksFromWindowInternalAlternateApproach(bool fSearchLinkChildren)
        {
            // If we're already building up a list of links, ignore this request to refresh the list.
            // (A shipping app might consider queueing this request in order to refresh the list again
            // once the in-progress refreshing action is complete.)
            if (_fRefreshInProgress)
            {
                return;
            }

            _fRefreshInProgress = true;

            // First build a cache request in the same way as done elsewhere in the sample.
            // This means that for each element returned following the search, they will
            // have their name, bounding rect and Invoke pattern cached.

            IUIAutomationCacheRequest cacheRequest = _automation.CreateCacheRequest();

            cacheRequest.AddProperty(_propertyIdName);
            cacheRequest.AddProperty(_propertyIdBoundingRectangle);

            cacheRequest.AddPattern(_patternIdInvoke);

            // At this point elsewhere in the sample, we specified that we only wanted data
            // for the hyperlink elements (and optionally their children) cached. In this
            // approach here, we will say that we want data for ALL descendants of the
            // found elements cached.

            cacheRequest.TreeScope = TreeScope.TreeScope_Descendants;

            // Now create a property condition as we've done elsewhere, to say that we're only
            // interested in elements which are in the Control view and are hyperlinks.

            IUIAutomationCondition conditionControlView = _automation.ControlViewCondition;
            IUIAutomationCondition conditionHyperlink   = _automation.CreatePropertyCondition(_propertyIdControlType, _controlTypeIdHyperlink);

            IUIAutomationCondition condition = _automation.CreateAndCondition(conditionControlView, conditionHyperlink);

            // Now unlike steps we took elsewhere, specify that the cache request should
            // have an additional filter of the property condition we just created.
            cacheRequest.TreeFilter = condition;

            // Elsewhere in the sample, we called FindAllBuildCache(). This returned an array of hyperlink
            // elements with their data cached, (and optinally their children with cache data too.) This
            // CacheLinksFromWindow() function takes a different approach. The element that is returned
            // from the call to BuildUpdatedCache() below is the browser element with some data cached.
            // But the returned element will have an array of cached child elements, and each of those
            // elements will by the hyperlinks we need. So the cache request here has specified through
            // its tree filter that we're only interest in hyperlinks, whereas elsewhere in this sample,
            // we supplied that condition in the search call we made. How much difference this makes to
            // the performance of calls depends on the action taken by the target application.

            // *** Note, using this appoach, we can't also cache data for the direct children of the
            // hyperlinks as we did elsewhere in the sample. So whether this approach is practical
            // depends on the needs of the client application.

            // Note that with a property conditions, it's not possible to request that the set of elements
            // returned from a search are all elements which have a control type of hyperlink OR whose parent
            // has a control type of hyperlink.

            // Get a handle to the window of interest.
            IntPtr hwnd = Win32.FindWindow(strBrowserWindowClass, null);

            if (hwnd != IntPtr.Zero)
            {
                IUIAutomationElement elementBrowser = _automation.ElementFromHandleBuildCache(hwnd, cacheRequest);
                if (elementBrowser != null)
                {
                    _linkItems.Clear();

                    IUIAutomationElementArray arrayChildren = elementBrowser.GetCachedChildren();
                    if (arrayChildren != null)
                    {
                        int cElements = arrayChildren.Length;

                        // Process each returned hyperlink element.
                        for (int idxElement = 0; idxElement < cElements; idxElement++)
                        {
                            IUIAutomationElement elementChild = arrayChildren.GetElement(idxElement);

                            // Take the same action elsewhere in the sample to present the hyperlink
                            // in the sample app UI.
                            string strLinkName = GetCachedDataFromElement(elementChild, fSearchLinkChildren);
                            if (strLinkName != null)
                            {
                                strLinkName = strLinkName.Trim();

                                LinkItem item = new LinkItem();
                                item.linkName = strLinkName;
                                item.element  = elementChild;

                                _linkItems.Add(item);
                            }
                        }

                        // Notify the main UI thread that a list of links is ready for processing. Do not block in this call.
                        _listViewLinks.BeginInvoke(_UIUpdateDelegate, _linkItems);
                    }
                }
            }

            // Allow another refresh to be performed now.
            _fRefreshInProgress = false;
        }
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //
        // GetCachedDataFromElement()
        //
        // Get the cached name from a UIA element. If the element doesn't have a name,
        // optionally try to find a name from the cached children of the element.
        //
        // Runs on the background thread.
        //
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////
        private string GetCachedDataFromElement(IUIAutomationElement element, bool fSearchLinkChildren)
        {
            // (A shipping app would do parameter checking here.)

            string strName = null;

            // Get the bounding rectangle for the hyperlink. By retrieving this from the
            // cache, we avoid the time-consuming cross-process call to get the data.
            tagRECT rectBounds = element.CachedBoundingRectangle;

            // If the hyperlink has a zero-size bounding rect, ignore the element. This
            // might happen if the hyperlink has scrolled out of view. (We could also
            // investigate whether using the IsOffscreen property tells us that the link
            // can be ignored. In fact, if the IsOffscreen property is reliable, we could
            // have included a property condition of IsOffcreen is false in the original
            // search, and not check whether the link's visible here.)
            if ((rectBounds.right > rectBounds.left) && (rectBounds.bottom > rectBounds.top))
            {
                // Get the name of the element. This will often be the text shown on the screen.
                // Note that the set of get_Cached* functions (or get_Current*), are convenient
                // ways of retrieving the same data that could be retrieved through the functions
                // GetCachedPropertyValue() (or GetCurrentPropertValue().) In the case of get_CachedName(),
                // the alternative would be to call GetCachedPropertyValue() with UIA_NamePropertyId.
                string strNameFound = element.CachedName;
                if (strNameFound.Length > 0)
                {
                    // A shipping app would check for more than an empty string. (A link might
                    // just have " " for a name.)
                    strName = strNameFound;
                }
                else
                {
                    // The hyperlink has no usable name. Consider using the name of a child element of the hyperlink.
                    if (fSearchLinkChildren)
                    {
                        // Given that hyperlink element has no name, use the name of first child
                        // element that does have a name. (In some cases the hyperlink element might
                        // contain an image or text element that does have a useful name.) We can take
                        // this action here because we supplied TreeScope_Children as the scope of the
                        // cache request that we passed in the call to FindAllBuildCache().

                        IUIAutomationElementArray elementArrayChildren = element.GetCachedChildren();
                        if (elementArrayChildren != null)
                        {
                            int cChildren = elementArrayChildren.Length;

                            // For each child element found...
                            for (int idxChild = 0; idxChild < cChildren; ++idxChild)
                            {
                                IUIAutomationElement elementChild = elementArrayChildren.GetElement(idxChild);
                                if (elementChild != null)
                                {
                                    string strNameChild = elementChild.CachedName;

                                    // Check the name of the child elements here. We don't
                                    // care what type of element it is in this sample app.
                                    if (strNameChild.Length > 0)
                                    {
                                        // We have a usable name.
                                        strName = strNameChild;
                                        break;
                                    }

                                    // Try the next child element of the hyperlink...
                                }
                            }
                        }
                    }
                }
            }

            return(strName);
        }