Как распознать визуальные эффекты рисования WPF в UIAutomation?

В нашем приложении есть холст, на который мы добавляем визуальные элементы для рисования (например, линии, многоугольники и т. д.).

// sample code

var canvas = new Canvas(); // create canvas
var visuals = new VisualCollection(canvas); // link the canvas to the visual collection
visuals.Add(new DrawingVisual()); // add the visuals to the canvas
visuals.Add(new DrawingVisual());

Наша цель — добавить эти визуальные элементы на холст с помощью автоматизации и проверить, правильно ли они добавлены. Мы используем фреймворк, основанный на Microsoft UIAutomation.

При использовании такого инструмента, как «Проверка», для проверки визуальной структуры я не мог найти холст. Провел некоторое исследование и выяснил, что вам нужно переопределить метод OnCreateAutomationPeer из UIElement и вернуть соответствующий объект AutomationPeer, чтобы увидеть это в автоматизации.

Внес изменения, и теперь я вижу холст, однако я все еще не вижу визуальных эффектов, добавленных под холст.

Кто-нибудь может помочь мне понять, в чем проблема?

Что пробовали/альтернативы:

  1. Пытался использовать метод OnCreateAutomationPeer, но DrawingVisual не являются производными от UIElement, и я не могу добавить UIElement к Canvas.VisualCollection.
  2. Распознавание изображений — это вариант, но мы пытаемся избежать его из соображений производительности/обслуживания.

person Schu    schedule 01.10.2014    source источник


Ответы (1)


Только UIElement можно увидеть из UI Automation (как вы видели, OnCreateAutomationPeer начинается с этого класса, а не с класса Visual).

Поэтому вам нужно добавить UIElement (или производный от FrameworkElement) на холст, если вы хотите, чтобы его можно было использовать с помощью UIAutomation.

Вы можете создать свой собственный класс, как описано здесь: Использование объектов DrawingVisual или с пользовательский элемент управления UserControl или используйте существующий, который соответствует вашим потребностям, но он должен каким-то образом быть производным от UIElement.

Если у вас есть хороший класс, вы можете использовать AutomationPeer по умолчанию или переопределить метод и адаптировать его более точно.

Если вы хотите сохранить визуальные объекты, одним из решений является изменение содержащего их объекта (но он все равно должен быть производным от UIElement). Например, здесь, если я следую статье по ссылке, я могу написать собственный содержащий объект (вместо холста вашего примера кода, поэтому вам, возможно, придется немного адаптироваться) следующим образом:

public class MyVisualHost  : UIElement
{
    public MyVisualHost()
    {
        Children = new VisualCollection(this);
    }

    public VisualCollection Children { get; private set; }


    public void AddChild(Visual visual)
    {
        Children.Add(visual);
    }

    protected override int VisualChildrenCount
    {
        get { return Children.Count; }
    }

    protected override Visual GetVisualChild(int index)
    {
        return Children[index];
    }

    protected override AutomationPeer OnCreateAutomationPeer()
    {
        return new MyVisualHostPeer(this);
    }

    // create a custom AutomationPeer for the container
    private class MyVisualHostPeer : UIElementAutomationPeer
    {
        public MyVisualHostPeer(MyVisualHost owner)
            : base(owner)
        {
        }

        public new MyVisualHost Owner
        {
            get
            {
                return (MyVisualHost)base.Owner;
            }
        }

        // a listening client (like UISpy is requesting a list of children)
        protected override List<AutomationPeer> GetChildrenCore()
        {
            List<AutomationPeer> list = new List<AutomationPeer>();
            foreach (Visual visual in Owner.Children)
            {
                list.Add(new MyVisualPeer(visual));
            }
            return list;
        }
    }

    // create a custom AutomationPeer for the visuals
    private class MyVisualPeer : AutomationPeer
    {
        public MyVisualPeer(Visual visual)
        {
        }

        // here you'll need to implement the abstrat class the way you want
    }
}
person Simon Mourier    schedule 02.10.2014
comment
Спасибо за ответ Саймон. Использование DrawingVisuals, но не UIElements на холсте, было сознательным решением для повышения производительности. В нашем приложении на холст добавлено множество фигур. Мне интересно, есть ли другой выход из этого. - person Schu; 02.10.2014
comment
Еще раз спасибо Саймон. Это должно работать. У меня было что-то похожее, я передавал визуальные элементы внутри OnCreateAutomationPeer и пытался не отставать от изменений в визуальной коллекции и как бы застрял там. Вы решили эту проблему с помощью свойства Children, добавленного под владельцем. - person Schu; 02.10.2014
comment
@SimonMourier: мне любопытно ... в вашем предыдущем посте говорится, что из UI Automation можно увидеть только UIElement. Canvas (и такие дети, как: Path) наследуются от UIElement, так почему же они не видны UIA Microsoft? - person Pressacco; 20.12.2019
comment
@Pressacco - Может не значит Будет. Если класс является производным от UIElement, он может быть виден из UIA, но только в том случае, если он (или его иерархия) переопределяет OnCreateAutomationPeer и возвращает что-то отличное от null. Canvas нет, Panel нет, FrameworkElement нет. В общем, большинство элементов управления и документов подходят. Холст не является элементом управления. Но вы можете получить и переопределить себя - person Simon Mourier; 20.12.2019
comment
@SimonMourier: Спасибо за разъяснение: может или будет. Очень признателен. - person Pressacco; 20.12.2019