/// <summary>
        /// Get Glimpses of UIA Elements
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        private static string GetGlimpsesOfUIAElements(IUIAutomationElementArray arr)
        {
            if (arr.Length != 0)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append('[');

                for (int i = 0; i < arr.Length; i++)
                {
                    var e = arr.GetElement(i);

                    if (i != 0)
                    {
                        sb.Append(", ");
                    }

                    sb.Append($"{GetGlimpseOfUIAElement(e)}");
                    Marshal.ReleaseComObject(e);
                }

                sb.Append(']');
                return(sb.ToString());
            }

            return(null);
        }
Пример #2
0
        /// <summary>
        /// Get DesktopElements from UIAElements.
        /// </summary>
        /// <param name="uia"></param>
        /// <returns>An IEnumerable of <see cref="DesktopElement"/></returns>
        private static IEnumerable <DesktopElement> ElementsFromUIAElements(IUIAutomationElementArray elementArray)
        {
            if (elementArray == null)
            {
                throw new ArgumentNullException(nameof(elementArray));
            }

            // Return an empty IEnumerable<DesktopElement> instead of null from ElementsFromUIAElements so that downstream calls to Linq extensions on the IEnumerable don't throw null reference exceptions.
            var count = elementArray.Length;

            if (count <= 0)
            {
                return(Enumerable.Empty <DesktopElement>());
            }

            // This function was originally an iterator
            // Meaning it used the yield keyword to yield return each element
            // But that caused a com exception IRL
            // So now we use a list
            List <DesktopElement> elements = new List <DesktopElement>();

            for (int i = 0; i < count; ++i)
            {
                var uiaElement = elementArray.GetElement(i);
                var e          = ElementFromUIAElement(uiaElement);
                if (e == null)
                {
                    continue;
                }

                elements.Add(e);
            } // for each element

            return(elements);
        }
        private void EnumarateChildren(IUIAutomationElement element)
        {
            Console.WriteLine("{0}", element.CurrentName.Trim());


            IUIAutomationCacheRequest cacheRequest = _automation.CreateCacheRequest();

            cacheRequest.AddProperty(System.Windows.Automation.AutomationElement.NameProperty.Id);
            cacheRequest.AddProperty(System.Windows.Automation.AutomationElement.ControlTypeProperty.Id);
            cacheRequest.TreeScope = TreeScope.TreeScope_Element | TreeScope.TreeScope_Children | TreeScope.TreeScope_Subtree;

            IUIAutomationCondition cond;

            cond = _automation.CreatePropertyConditionEx(
                System.Windows.Automation.AutomationElement.ControlTypeProperty.Id,
                System.Windows.Automation.ControlType.Window.Id,
                PropertyConditionFlags.PropertyConditionFlags_IgnoreCase);

            IUIAutomationElementArray elementList = element.FindAllBuildCache(TreeScope.TreeScope_Children, cond, cacheRequest);

            if (elementList == null)
            {
                return;
            }

            for (int i = 0; i < elementList.Length; i++)
            {
                EnumarateChildren(elementList.GetElement(i));
            }
        }
 internal AutomationElementCollection(IUIAutomationElementArray elementArray)
 {
     this._elements = new AutomationElement[elementArray.Length];
     for (var index = 0; index < elementArray.Length; ++index)
     {
         this._elements[index] = new AutomationElement(autoElement: elementArray.GetElement(index: index));
     }
 }
        /// <summary>
        /// Change IUIAutomationElementArray To a list of DesktopElement
        /// </summary>
        /// <param name="array"></param>
        /// <returns></returns>
        public static List <DesktopElement> ToListOfDesktopElements(this IUIAutomationElementArray array)
        {
            List <DesktopElement> list = new List <DesktopElement>();

            for (int i = 0; i < array.Length; i++)
            {
                list.Add(new DesktopElement(array.GetElement(i)));
            }

            return(list);
        }
 private static void DeselectAll(IUIAutomationElementArray elementArray)
 {
     for (var i = 0; i < elementArray.Length; i++)
     {
         var selectedItem = elementArray.GetElement(i);
         if (selectedItem.GetCurrentPattern(UIA_PatternIds.UIA_SelectionItemPatternId) is
             IUIAutomationSelectionItemPattern selectedItemPattern)
         {
             selectedItemPattern.RemoveFromSelection();
         }
     }
 }
Пример #7
0
        public Element[] GetAllElements(TreeScope Scope)
        {
            IUIAutomationElementArray elementsToFind = null;

            elementsToFind = IUIElement.FindAll(Scope, new CUIAutomationClass().CreateTrueCondition());
            int size = elementsToFind.Length;

            Element[] arr = new Element[size];
            for (int i = 0; i < size; i++)
            {
                arr[i] = new Element(elementsToFind.GetElement(i));
            }
            return(arr);
        }
Пример #8
0
        /// <summary>
        /// Get all elements from the current element that match the specified control type.
        /// </summary>
        /// <param name="Scope"></param>
        /// <param name="ControlType"></param>
        /// <returns></returns>
        public ControlElement[] GetControlsByControlType(TreeScope Scope, LocalizedControlType ControlType)
        {
            IUIAutomationElementArray elementsToFind = null;
            IUIAutomationCondition    cond           = new CUIAutomationClass().CreatePropertyCondition(By.ControlType.GetId(), ControlType.GetControlTypeValue());

            elementsToFind = this.IUIElement.FindAll(Scope, cond);
            int size = elementsToFind.Length;

            ControlElement[] arr = new ControlElement[size];
            for (int i = 0; i < size; i++)
            {
                arr[i] = new ControlElement(elementsToFind.GetElement(i));
            }
            return(arr);
        }
Пример #9
0
        //public void RefreshScrollingContainer(IntPtr hwnd)
        //{

        //}

        public IUIAutomationElement[] GetAppsArray(IUIAutomationElement homeElement)
        {
            IUIAutomationCondition condition = _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId,
                                                                                   UIA_ControlTypeIds.UIA_ListItemControlTypeId);
            IUIAutomationElementArray appsList = homeElement.FindAll(TreeScope.TreeScope_Children, condition);

            Console.WriteLine(appsList.Length);
            IUIAutomationElement[] appsArray = new IUIAutomationElement[appsList.Length];
            for (int i = 0; i < appsList.Length; i++)
            {
                appsArray[i] = appsList.GetElement(i);
                Console.WriteLine(appsArray[i].CurrentAutomationId);
            }
            return(appsArray);
        }
Пример #10
0
        /// <summary>
        /// Change IUIAutomationElementArray To a list of DesktopElement
        /// </summary>
        /// <param name="array"></param>
        /// <returns></returns>
        public static List <DesktopElement> ToListOfDesktopElements(this IUIAutomationElementArray array)
        {
            if (array == null)
            {
                throw new ArgumentNullException(nameof(array));
            }

            List <DesktopElement> list = new List <DesktopElement>();

            for (int i = 0; i < array.Length; i++)
            {
                list.Add(new DesktopElement(array.GetElement(i)));
            }

            return(list);
        }
Пример #11
0
        public IUIAutomationElement[] GetTopFreeHomeArray(IUIAutomationElement element)
        {
            IUIAutomationCondition[] conditionArray = new IUIAutomationCondition[2];
            conditionArray[0] = _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_NamePropertyId, "Top free");
            conditionArray[1] = _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_ListItemControlTypeId);

            IUIAutomationCondition    conditions  = _automation.CreateAndConditionFromArray(conditionArray);
            IUIAutomationElementArray topFreeList = element.FindAll(TreeScope.TreeScope_Children, conditions);

            IUIAutomationElement[] topFreeArray = new IUIAutomationElement[topFreeList.Length];
            for (int i = 0; i < topFreeList.Length; i++)
            {
                topFreeArray[i] = topFreeList.GetElement(i);
            }

            return(topFreeArray);
        }
Пример #12
0
 internal static AutomationElement[] ConvertToElementArray(IUIAutomationElementArray array)
 {
     AutomationElement[] elementArray;
     if (array != null)
     {
         elementArray = new AutomationElement[array.Length];
         for (int i = 0; i < array.Length; i++)
         {
             elementArray[i] = AutomationElement.Wrap(array.GetElement(i));
         }
     }
     else
     {
         elementArray = null;
     }
     return(elementArray);
 }
        private void AutomationTree_TargetUpdated(object sender, DataTransferEventArgs e)
        {
            //IUIAutomationCondition uIAutomationCondition = new IUIAutomationCondition();
            CUIAutomation8            cUIAutomation8 = new CUIAutomation8();
            IUIAutomationElementArray rootChildrens  = cUIAutomation8.GetRootElement().FindAll(TreeScope.TreeScope_Children, cUIAutomation8.CreateTrueCondition());
            TreeViewItem tree = new TreeViewItem();

            tree.Header = cUIAutomation8.GetRootElement().GetCurrentPropertyValue(30005);
            //  IUIAutomationTreeWalker uIAutomationTreeWalker;
            // uIAutomationTreeWalker.
            for (int i = 0; i < rootChildrens.Length; i++)
            {
                IUIAutomationElement children = rootChildrens.GetElement(i);
                tree.Items.Add(String.Format("{0}  {1}", children.GetCurrentPropertyValue(30005), children.CurrentLocalizedControlType));
            }
            automationTree.Items.Add(tree);
            //MessageBox.Show(rootElement.GetCurrentPropertyValue(30012));
        }
Пример #14
0
        private void loadChildren(TreeNode node)
        {
            IUIAutomationElementArray array = node.element.FindAll(TreeScope.TreeScope_Children, automation.CreateTrueCondition());

            if (0 == array.Length)
            {
                node.children = null;
                node.isLeaf   = true;
            }
            else
            {
                for (int i = 0; i < array.Length; i++)
                {
                    IUIAutomationElement e = array.GetElement(i);
                    TreeNode             n = new TreeNode(e);
                    node.children.Add(n);
                    this.loadChildren(n);
                }
            }
        }
Пример #15
0
        public ViewTree(IUIAutomationElement element, IUIAutomation automation)
        {
            this.root        = new TreeNode(element);
            this.root.parent = null;
            IUIAutomationElementArray array = element.FindAll(TreeScope.TreeScope_Children, automation.CreateTrueCondition());

            if (0 == array.Length)
            {
                this.root.children = null;
                this.root.isLeaf   = true;
            }
            else
            {
                for (int i = 0; i < array.Length; i++)
                {
                    IUIAutomationElement e = array.GetElement(i);
                    TreeNode             n = new TreeNode(e);
                    this.root.children.Add(n);
                }
            }
        }
Пример #16
0
        /// <summary>
        /// Get an element from the current element that matches the specified conditon.
        /// </summary>
        /// <param name="Scope"></param>
        /// <param name="Condition"></param>
        /// <returns></returns>
        public ControlElement[] GetControls(TreeScope Scope, SearchCondition Condition)
        {
            IUIAutomationElementArray elementsToFind = null;

            ControlElement[] arr = null;

            for (int i = 0; i < 100; i++)
            {
                try
                {
                    elementsToFind = this.IUIElement.FindAll(Scope, Condition.UIAutomationCondition);
                }
                catch (COMException e) { }
                catch (UnauthorizedAccessException ex) { }

                if (elementsToFind != null)
                {
                    int size = elementsToFind.Length;
                    arr = new ControlElement[size];
                    for (int j = 0; j < size; j++)
                    {
                        arr[j] = new ControlElement(elementsToFind.GetElement(j));
                    }
                    break;
                }
                else
                {
                    Thread.Sleep(100);
                }
            }

            if (elementsToFind == null)
            {
                return(null);
            }
            else
            {
                return(arr);
            }
        }
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //
        // 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);
        }
Пример #18
0
        // Step 2: Find all the links of interest on the loaded page.
        private void buttonFindLinks_Click(object sender, EventArgs e)
        {
            checkedListBoxLinks.Items.Clear();

            if (webBrowserPage.Url == null)
            {
                MessageBox.Show(this,
                                "Please load the web page of interest.",
                                "Link Getter",
                                MessageBoxButtons.OK);

                labelLinkCount.Text = "No links found.";

                return;
            }

            // Get the UIA element for the webBrowser with the page of interest loaded.
            IUIAutomationElement elementBrowser =
                automation.ElementFromHandle(webBrowserPage.Handle);

            // Build up a cache request for all the data we need, to reduce the time it
            // takes to access the link data once with have the collection of links.
            IUIAutomationCacheRequest cacheRequest = automation.CreateCacheRequest();

            cacheRequest.AddProperty(propertyIdName);

            cacheRequest.AddPattern(patternIdValue);
            cacheRequest.AddProperty(propertyIdValueValue);

            // Assume the links have names as expected, and we don't need to
            // search children of the links for names.
            cacheRequest.TreeScope = TreeScope.TreeScope_Element;

            // We want the collection of all links on the page.
            IUIAutomationCondition conditionControlView = automation.ControlViewCondition;
            IUIAutomationCondition conditionHyperlink   =
                automation.CreatePropertyCondition(
                    propertyIdControlType, controlTypeIdHyperlink);

            IUIAutomationCondition finalCondition = automation.CreateAndCondition(
                conditionControlView, conditionHyperlink);

            // TODO: Call FindAllBuildCache() in a background thread in case it takes
            // a while. As it is, the app UI's going to freeze.

            // Now get the collection of links.
            IUIAutomationElementArray elementArray = elementBrowser.FindAllBuildCache(
                TreeScope.TreeScope_Descendants, finalCondition, cacheRequest);

            if (elementArray != null)
            {
                // Process each returned hyperlink element.
                for (int idxLink = 0; idxLink < elementArray.Length; ++idxLink)
                {
                    IUIAutomationElement elementLink = elementArray.GetElement(idxLink);

                    // Despite the fact that we've got the names of the UIA links, don't
                    // use that information here. Perhaps we will use it in the future.

                    IUIAutomationValuePattern valuePattern =
                        (IUIAutomationValuePattern)elementLink.GetCurrentPattern(
                            patternIdValue);
                    if (valuePattern != null)
                    {
                        // We're only interested in references the page makes to itself.
                        string strValueLink = valuePattern.CachedValue;
                        var    index        = strValueLink.IndexOf('#');
                        if ((index > 0) && strValueLink.StartsWith(textBoxURL.Text))
                        {
                            checkedListBoxLinks.Items.Add(new LinkItem()
                            {
                                linkName = elementLink.CachedName,
                                linkURL  = strValueLink
                            });
                        }
                    }
                }
            }

            // Let's assume we'll want most of the links found.
            SetLinkCheckedState(true);

            labelLinkCount.Text = "Count of links found: " +
                                  checkedListBoxLinks.Items.Count;
        }
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //
        // 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;
        }
        private List <TextAttributeViewModel> GetTextRangeAttributeKeyValuePair(TextRange tr, KeyValuePair <int, string> kv, bool collapse)
        {
            List <TextAttributeViewModel> list = new List <TextAttributeViewModel>();

            dynamic value = tr.GetAttributeValue(kv.Key);

            switch (kv.Key)
            {
            case TextAttributeType.UIA_AnimationStyleAttributeId:
                if (value is int)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, AnimationStyle.GetInstance().GetNameById(value)));
                }
                break;

            case TextAttributeType.UIA_BackgroundColorAttributeId:
            case TextAttributeType.UIA_ForegroundColorAttributeId:
            case TextAttributeType.UIA_OverlineColorAttributeId:
            case TextAttributeType.UIA_StrikethroughColorAttributeId:
            case TextAttributeType.UIA_UnderlineColorAttributeId:
                if (value is int)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, string.Format("#{0:X8}", value)));
                }
                break;

            case TextAttributeType.UIA_BulletStyleAttributeId:
                if (value is int)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, BulletStyle.GetInstance().GetNameById(value)));
                }
                break;

            case TextAttributeType.UIA_CapStyleAttributeId:
                if (value is int)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, CapStyle.GetInstance().GetNameById(value)));
                }
                break;

            case TextAttributeType.UIA_CultureAttributeId:
                if (value is int culture)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, Invariant($"{CultureInfo.GetCultureInfo(culture).EnglishName} ({culture})")));
                }
                break;

            case TextAttributeType.UIA_StyleIdAttributeId:
            case TextAttributeType.UIA_SayAsInterpretAsAttributeId:
                // VT_I4
                if (value is int)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, SayAsInterpretAs.GetInstance().GetNameById(value)));
                }
                break;

            case TextAttributeType.UIA_FontNameAttributeId:
            case TextAttributeType.UIA_StyleNameAttributeId:
            case TextAttributeType.UIA_LineSpacingAttributeId:
                // VT_BSTR
                if (value is string)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, value));
                }
                break;

            case TextAttributeType.UIA_FontSizeAttributeId:
            case TextAttributeType.UIA_IndentationFirstLineAttributeId:
            case TextAttributeType.UIA_IndentationLeadingAttributeId:
            case TextAttributeType.UIA_IndentationTrailingAttributeId:
            case TextAttributeType.UIA_MarginBottomAttributeId:
            case TextAttributeType.UIA_MarginLeadingAttributeId:
            case TextAttributeType.UIA_MarginTopAttributeId:
            case TextAttributeType.UIA_MarginTrailingAttributeId:
            case TextAttributeType.UIA_BeforeParagraphSpacingAttributeId:
            case TextAttributeType.UIA_AfterParagraphSpacingAttributeId:
                // VT_R8
                if (value is double || value is long)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, value.ToString()));
                }
                break;

            case TextAttributeType.UIA_FontWeightAttributeId:
                if (value is int)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, Axe.Windows.Desktop.Styles.FontWeight.GetInstance().GetNameById(value)));
                }
                break;

            case TextAttributeType.UIA_HorizontalTextAlignmentAttributeId:
                if (value is int)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, Axe.Windows.Desktop.Styles.FontWeight.GetInstance().GetNameById(value)));
                }
                break;

            case TextAttributeType.UIA_IsHiddenAttributeId:
            case TextAttributeType.UIA_IsItalicAttributeId:
            case TextAttributeType.UIA_IsReadOnlyAttributeId:
            case TextAttributeType.UIA_IsSubscriptAttributeId:
            case TextAttributeType.UIA_IsSuperscriptAttributeId:
            case TextAttributeType.UIA_IsActiveAttributeId:
                if (value is bool)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, value.ToString()));
                }
                break;

            case TextAttributeType.UIA_OutlineStylesAttributeId:
                if (value is int)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, OutlineStyle.GetInstance().GetNameById(value)));
                }
                break;

            case TextAttributeType.UIA_OverlineStyleAttributeId:
            case TextAttributeType.UIA_StrikethroughStyleAttributeId:
            case TextAttributeType.UIA_UnderlineStyleAttributeId:
                if (value is int)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, TextDecorationLineStyle.GetInstance().GetNameById(value)));
                }
                break;

            case TextAttributeType.UIA_TabsAttributeId:
                var txt = ConvertArrayToString(value);

                if (txt != null)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, txt));
                }
                break;

            case TextAttributeType.UIA_TextFlowDirectionsAttributeId:
                if (value is int)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, Axe.Windows.Desktop.Styles.FlowDirection.GetInstance().GetNameById(value)));
                }
                break;

            case TextAttributeType.UIA_AnnotationTypesAttributeId:
                StringBuilder sb = new StringBuilder();
                if (value is double)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, AnnotationType.GetInstance().GetNameById((int)value)));
                }
                else if (value is Array arr)
                {
                    if (collapse && arr.Length > 0)     // collapse the array into a single row
                    {
                        var count = new Dictionary <string, int>();
                        foreach (var val in arr)
                        {
                            var key = AnnotationType.GetInstance().GetNameById((int)val);
                            if (count.ContainsKey(key))
                            {
                                count[key]++;
                            }
                            else
                            {
                                count[key] = 1;
                            }
                        }

                        StringBuilder strBuild = new StringBuilder();
                        foreach (var item in count)
                        {
                            strBuild.Append(Invariant($"{item.Key}: {item.Value}, "));
                        }
                        strBuild.Length -= 2;   //remove final , and <space>
                        list.Add(new TextAttributeViewModel(kv.Key, kv.Value, strBuild.ToString()));
                    }
                    else     // create a row for each array value
                    {
                        if (arr.Length > 0)
                        {
                            for (int i = 0; i < arr.Length; i++)
                            {
                                list.Add(new TextAttributeViewModel(kv.Key, string.Format(CultureInfo.InvariantCulture, "{0}[{1}]", kv.Value, i), AnnotationType.GetInstance().GetNameById((int)arr.GetValue(i))));
                            }
                        }
                    }
                }
                break;

            case TextAttributeType.UIA_SelectionActiveEndAttributeId:
                if (value is int)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, ActiveEnd.GetInstance().GetNameById(value)));
                }
                break;

            case TextAttributeType.UIA_CaretPositionAttributeId:
                if (value is int)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, CaretPosition.GetInstance().GetNameById(value)));
                }
                break;

            case TextAttributeType.UIA_CaretBidiModeAttributeId:
                if (value is int)
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, CaretBidiMode.GetInstance().GetNameById(value)));
                }
                break;

            case TextAttributeType.UIA_AnnotationObjectsAttributeId:
                if (value is IUIAutomationElementArray)
                {
                    IUIAutomationElementArray arr = value;
                    if (arr.Length > 0)
                    {
                        for (int i = 0; i < arr.Length; i++)
                        {
                            list.Add(new TextAttributeViewModel(kv.Key, string.Format(CultureInfo.InvariantCulture, Resources.TextRangeViewModel_GetTextRangeAttributeKeyValuePair_AnnotationObjects_0, i), new DesktopElement((IUIAutomationElement)arr.GetElement(i))));
                        }
                    }
                }
                break;

            case TextAttributeType.UIA_LinkAttributeId:
                // do nothing for now until it is shown as necessary information.
                //try
                //{
                //    IUIAutomationTextRange lnk = Marshal.GetObjectForIUnknown(value) as IUIAutomationTextRange;
                //    list.Add(new TextAttributeViewModel(kv.Value, new TextRangeViewModel(new TextRange(lnk))));
                //}
                //catch (Exception e)
                //{
                //    e.ReportException();
                //}
                break;

            default:
                // need to make a decision for these Attributes since it return Object.
                if (value.GetType().Name != "__ComObject")
                {
                    list.Add(new TextAttributeViewModel(kv.Key, kv.Value, value));
                }
                break;
            }

            return(list);
        }