Custom UI Automation Providers in Depth: Part 4

In part 3, we learned how to analyze our control to decide which properties it would be appropriate to support and how to add them. This time, we’ll get into patternsThe sample code for this section is here.

A UIA control pattern is very similar to an interface in object-oriented programming. A control exposes a pattern when it has a particular ability that it wants to make available to UIA clients. Some simple examples will illustrate the idea:

  • The Invoke Pattern means that a control can be ‘pushed’, or activated (e.g. button, list view item)
  • The Expand/Collapse Pattern means that a control can be expanded or collapsed (e.g. a combo box or a tree node)
  • The Value Pattern means that a control has a value in addition to its name (e.g. an edit box, a hyperlink)
  • The Transform Pattern means that a control can be moved, resized, or rotated (e.g. a window)

Although they are much like interfaces, they are called patterns because they represent patterns of behavior that span a variety of different controls. There are two other key differences from interfaces:

  1. A pattern can be added or removed by a control dynamically. For instance, a tree node might expose Expand/Collapse at one time, but if its children are removed, it will not expose it anymore. This isn’t (usually) legitimate behavior for interfaces.
  2. A pattern may not be implemented by the same object that implements IRawElementProviderSimple. This gives the developer flexibility in structuring his/her code.

Well, then. How do you know which patterns your provider should implement? I think there are two broad ways to answer this. You might analyze this a priori, looking at the list of patterns to see which ones make sense for your provider. That’s probably my favorite way. However, you could also look at the Control Type you have chosen (the whatness of your control) to see which patterns it implies. MSDN provides guidance for each control type as to whether it requires certain patterns. For example, the Hyperlink Control Type usually exposes Value Pattern whenever the hyperlink has some kind of target which is distinct from its visible name. This guidance is a good backup check: if you choose a Control Type that implies certain patterns and then choose not to implement them, your clients will be confused.

For our sample TriColor provider, we picked the Custom control type, so MSDN is less than usually helpful. We’ll have to analyze from the top down.  Looking over the list of patterns, there are really two that apply: Value and Selection. Our control clearly has a value: red, yellow or green. It also has a selection: the currently selected item. The Selection pattern requires the idea of sub-elements, though, and we haven’t learned that yet, so let’s set that one aside and do Value instead.

At this point, the implementation is almost anti-climactic. First, implement the appropriate interface for your pattern. I said that you could put it on a different object, but I’m not going to do so in the sample. The Value pattern has three methods. IsReadOnly is just a property – false, in our case. The Value getter should return a string, and our value is an enum, so we just use ToString(). SetValue() is more interesting: the incoming value is a string, so we’ll have to parse it. Enum.Parse() does the job nicely for us. This gives us:

     /// <summary>
    /// Provider for the TriColor control itself.
    /// </summary>
    public class TriColorProvider : BaseSimpleProvider, IValueProvider
    {
        #region IValueProvider Members

        public bool IsReadOnly
        {
            get { return false; }
        }

        // Getting the value is easy - it's just the control's value turning into a string.
        public string Value
        {
            get { return this.control.Value.ToString(); }
        }

        // Setting the value requires turning a string back into the value
        public void SetValue(string value)
        {
            // This will throw an ArgumentException if it doesn't work
            this.control.Value = (TriColorValue)Enum.Parse(typeof(TriColorValue), value, true /* ignoreCase */);
        }

        #endregion

There’s one last step to hook this up: each time a client asks for a pattern, the provider gets to decide whether to return anything and which object to return.  This is the GetPatternProvider() method, which we skipped in part 2.  We support exactly one pattern at this point, which makes the implementation pretty easy:

 public override object GetPatternProvider(int patternId)
{
    // We just respond with ourself for the patterns we support.
    if (patternId == ValuePatternIdentifiers.Pattern.Id)
    {
        return this;
    }
    return base.GetPatternProvider(patternId);
 }

And that’s it – we’ve implemented value pattern. Most patterns are like this – pretty easy, in the end. There was one wrinkle I hit while debugging: my sample control wasn’t expecting to have its value changed from the outside. I had to call Invalidate() when the value changed to force a repaint; otherwise, it looked like nothing had happened.

To test this out, fire up the sample and look at it with Inspect. You can see now that there are two new properties in the right pane: Value.IsReadOnly (false) and Value.Value (“Red”, unless you’ve changed it). Better yet, go into the Action menu in Inspect and choose Value.SetValue … You’ll get a dialog where you can try setting various values to the control. Setting the value to “Yellow” or “Green” does indeed change the value, as you would hope:

Inspect_SetValue

And there we are. Now, I really want to do Selection Pattern also, but I need to be able to represent sub-elements within my control, which is the domain of Fragments. So we’ll do those next.

Next time: IRawElementProviderFragment