How to recognize WPF visual drawings in UIAutomation?

Our application has a canvas to which we add visual drawings (e.g. lines, polygons, etc.)

// 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());

      

Our goal is to add these visuals to the canvas using automation and confirm that they are added correctly. We use an infrastructure based on Microsoft UIAutomation.

When using the Inspection tool to check the visual structure, I couldn't find the canvas. Do some research and figured out that you need to override the method OnCreateAutomationPeer

from UIElement

and return an applicable object AutomationPeer

in order to be able to see this in automation.

Made the change and I can now see the canvas, however I can still see any images added under the canvas.

Can anyone help me understand what is the problem?

Attempts / Alternatives:

  • Tried using OnCreateAutomationPeer method but DrawingVisual

    dont get from UIElement

    and i cant add UIElement

    s before Canvas.VisualCollection

    .
  • Image recognition is an option, but we are trying to avoid it for performance / maintenance reasons.
+3


source to share


1 answer


Only UIElement can be seen from UI Automation (as you saw, OnCreateAutomationPeer starts from this class, not from the Visual class).

So, you need to add a UIElement (or obtained as a FrameworkElement) to the canvas if you want it to be accessible by UIAutomation.

You can create your own class as described here: Using DrawingVisual Objects either with a custom UserControl or use an existing one that suits you, but that has to come from UIElement somehow.



Once you have a good class, you can use the default AutomationPeer automatically, or override the method and adapt it more closely.

If you want to keep the Visuals, one solution is to change the containing object (but it must still come from the UIElement). For example, here, if I follow the article in the link, I can write a custom containing object (instead of a canvas of your sample code, so you might be able to adjust a bit):

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
    }
}

      

+4


source







All Articles