Search code examples
winformsmicrosoft-ui-automation

Exposing custom properties using UI Automation Framework


Given a very basic WinForms custom/user control, using System.Windows.Automation it is possible to manipulate built in properties for the custom control.

This is done like this:

public object GetPropertyValue(int propertyId)
      {
         if (propertyId == AutomationElementIdentifiers.NameProperty.Id)
           {
              return "Hello World!";
           }
      }

What I would like to do is expose custom properties to ui automation such as ReadyState, LastAccessed, Etc.

Is this possible?


Solution

  • No, you can't extend the list of properties, and this is complicated by the fact you use Winforms that has a poor UI Automation support (it uses IAccessible with bridges etc.).

    What you can do though is add some fake objects to the automation tree, for example, here is a sample Winforms UserControl that does it:

    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
    
            Button button = new Button();
            button.Location = new Point(32, 28);
            button.Size = new Size(75, 23);
            button.Text = "MyButton";
            Controls.Add(button);
    
            Label label = new Label();
            label.Location = new Point(49, 80);
            label.Size = new Size(35, 13);
            label.Text = "MyLabel";
            Controls.Add(label);
    
            MyCustomProp = "MyCustomValue";
        }
    
        public string MyCustomProp { get; set; }
    
        protected override AccessibleObject CreateAccessibilityInstance()
        {
            return new UserControl1AccessibleObject(this);
        }
    
        protected class UserControl1AccessibleObject : ControlAccessibleObject
        {
            public UserControl1AccessibleObject(UserControl1 ownerControl)
                : base(ownerControl)
            {
            }
    
            public new UserControl1 Owner
            {
                get
                {
                    return (UserControl1)base.Owner;
                }
            }
    
            public override int GetChildCount()
            {
                return 1;
            }
    
            public override AccessibleObject GetChild(int index)
            {
                if (index == 0)
                    return new ValueAccessibleObject("MyCustomProp", Owner.MyCustomProp);
    
                return base.GetChild(index);
            }
        }
    }
    
    public class ValueAccessibleObject : AccessibleObject
    {
        private string _name;
        private string _value;
    
        public ValueAccessibleObject(string name, string value)
        {
            _name = name;
            _value = value;
        }
    
        public override AccessibleRole Role
        {
            get
            {
                return AccessibleRole.Text; // activate Value pattern
            }
        }
    
        // note you need to override with member values, base value cannot always store something
        public override string Value { get { return _value; } set { _value = value; } }
        public override string Name { get { return _name; } }
    }
    

    And this is how it appears in the automation tree (using the inspect.exe tool):

    enter image description here

    Note this technique also supports writing back to the property because it's based on the ValuePattern.