bool IEnumerator.MoveNext()
            {
                if (_cancellationToken.IsCancellationRequested)
                {
                    return(false);
                }

                if (_elementQueue.Count == 0)
                {
                    return(false);
                }

                _current = _elementQueue.Dequeue();

                AutomationElement childAutomationElement = TreeWalker.ControlViewWalker.GetFirstChild(_current, _cancellationToken);

                while (childAutomationElement != null)
                {
                    if (_cancellationToken.IsCancellationRequested)
                    {
                        return(false);
                    }

                    if (_controlType.CanBeNestedUnder(childAutomationElement))
                    {
                        _elementQueue.Enqueue(childAutomationElement);
                    }

                    childAutomationElement = TreeWalker.ControlViewWalker.GetNextSibling(childAutomationElement);
                }

                return(true);
            }
        public ChildIterator(string name, AutomationElement automationElement, CancellationToken cancellationToken)
        {
            _automationElement = automationElement;
            _cancellationToken = cancellationToken;
            // tree walker is used instead of FindAll, FindAll cannot be "interrupted"
            _treeWalker = TreeWalker.ControlViewWalker;

            /* using "plain" ControlViewWalker because the advanced one with conditions is taking too long to complete
             * and sometimes it returns elements that are coming from a different subtree - which can be avoided by comparing parent
             * of the element with automationElement, but that will reduce speed
             * overall the reason is unknown
             * new TreeWalker(new AndCondition(
             * Automation.ControlViewCondition,
             * // treewalker was jumping around to another windows
             * new PropertyCondition(AutomationElement.ProcessIdProperty, automationElement.Current.ProcessId),
             * // return only the elements that are visible
             * //new PropertyCondition(AutomationElement.IsOffscreenProperty, false),
             * condition));*
             */
            var automationId = automationElement.Current.AutomationId;
            var condition    = Condition.TrueCondition;

            if (name != null)
            {
                controlType = name.AsControlType();
                condition   = new PropertyCondition(AutomationElement.ControlTypeProperty, controlType);

                if (!controlType.CanBeNestedUnder(automationElement.Current.ControlType))
                {
                    condition = Condition.FalseCondition;
                    _empty    = true;
                }
            }
        }
            bool IEnumerator.MoveNext()
            {
                if (_cancellationToken.IsCancellationRequested)
                {
                    return(false);
                }

                /*if (_matching.Any())
                 * {
                 *  Current = _matching.Dequeue();
                 *  return true;
                 * }*/

                if (_elementQueue.Count == 0)
                {
                    return(false);
                }

                _current = _elementQueue.Dequeue();

                AutomationElement childAutomationElement = TreeWalker.ControlViewWalker.GetFirstChild(_current);

                /*var children = _current.FindAll(TreeScope.Children, Condition.TrueCondition).Cast<AutomationElement>()
                 *  .OrderBy(c => c.Current.BoundingRectangle.TopLeft, new PointComparer())
                 *  .ToList();*/

                //System.Diagnostics.Debug.WriteLine($"FindElements by type {_controlType.ProgrammaticName}, root {_current.ToDiagString()} ... ({children.Count()})");
                while (childAutomationElement != null)
                {
                    if (_cancellationToken.IsCancellationRequested)
                    {
                        return(false);
                    }

                    if (_controlType.CanBeNestedUnder(childAutomationElement))
                    {
                        _elementQueue.Enqueue(childAutomationElement);
                    }

                    childAutomationElement = TreeWalker.ControlViewWalker.GetNextSibling(childAutomationElement);
                }

                /*
                 * foreach (AutomationElement automationElement in children)
                 * {
                 *  if (_cancellationToken.IsCancellationRequested)
                 *  {
                 *      return false;
                 *  }
                 *
                 *  if (!_controlType.CanBeNestedUnder(automationElement))
                 *  {
                 *      continue;
                 *  }
                 *
                 *  _elementQueue.Enqueue(automationElement);
                 * }*/

                return(true);
            }
            bool IEnumerator.MoveNext()
            {
                if (_cancellationToken.IsCancellationRequested)
                {
                    return(false);
                }

                if (_elementQueue.Count == 0)
                {
                    return(false);
                }

                _current = _elementQueue.Dequeue();

                AutomationElement childAutomationElement = TreeWalker.ControlViewWalker.GetFirstChild(_current, _cancellationToken);

                while (childAutomationElement != null)
                {
                    if (_cancellationToken.IsCancellationRequested)
                    {
                        return(false);
                    }

                    if (_controlType.CanBeNestedUnder(childAutomationElement))
                    {
                        _elementQueue.Enqueue(childAutomationElement);
                    }

                    // sometimes the app can stop responding when performing long operation thanks to poor designed
                    // try to wait for the response in a loop
                    while (true)
                    {
                        if (_cancellationToken.IsCancellationRequested)
                        {
                            return(false);
                        }

                        try
                        {
                            childAutomationElement = TreeWalker.ControlViewWalker.GetNextSibling(childAutomationElement);
                            break;
                        }
                        catch (System.Runtime.InteropServices.COMException comEx) when(comEx.IsTimeout())
                        {
                            // retry after a short sleep
                            Thread.Sleep(TimeSpan.FromMilliseconds(250));
                            // childAutomationElement = TreeWalker.ControlViewWalker.GetNextSibling(childAutomationElement);
                        }
                    }
                }

                return(true);
            }