Geeks With Blogs

The Life and Times of a Dev Yes, we're really that weird

Today someone asked me, "How do you cause the double click event to fire in a tree view control using UI Automation"?  Good question, and no, I don't have a good answer.  Apparently, there isn't a good way to simulate this.  In our case, we have a custom user control that is a popup that contains a WPF TreeView control.  The user browses through the control until they find the node that they want and then they double click on the control to select the node, which is then raised as an event through the CAB layer to anyone who cares.

So, to simulate the DoubleClick event we did the following:

1.  Using the article by Dima Zaharov, which honestly wasn't written all that well, I was able to hook into the automation stuff and add an invoke event to the control that contained the treeview.  I could have created a subclass of TreeView and overridden it's AutomationPeer, but that would have been more pain than it's worth.

2.  Create a DefaultAction method that is invoked when double click is clicked or Invoke is Invoked.  This ensures that the default code happens for all cases.

3.  Set automation properties to make it easier to get to the controls and use them.

We tried raising the TreeView.MouseDoubleClickEvent using the RaiseEvent syntax as described by Dima, but it didn't work, so we resorted to calling the method directly.  I didn't want to, but you do what works.  This successfully simulates what happens when the user double clicks.

So, here's the code.  Enjoy!  Please note that I've trimmed out a lot of the extra stuff to make it easier to follow, so it may not look right.

XAML - All we're doing here is creating a place for our treeview control to reside.  Note that you'd need to set up binding and such for this control to do much of anything.  Note the AutomationProperties values that are set.  There are many more properties that you can set and I'd recommend that you do so.

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    AutomationProperties.AutomationId="TreeViewControl"
    >
    <Grid>
    <Border>
        <StackPanel>
            <Border>
                <ScrollViewer>
                    <TreeView
                                      MouseDoubleClick="TreeView_DoubleClick"                              
                                      AutomationProperties.AutomationId="TreeView"
                              />
                </ScrollViewer>
            </Border>
        </StackPanel>
    </Border>
        
    </Grid>
</UserControl>

XAML Tree View Code Behind - As you can see, all we're doing is handling the double click event.  The important part is raising the event telling the world that the Invoke action was called.  That's the code begining with checking to see if a listener exists for this pattern.

    public partial class TreeView    {
        
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new TreeViewAutomationPeer(this);
        }
        private void TreeView_DoubleClick(object sender, RoutedEventArgs e)
        {
            DoInvoke();
        }

        public void DoInvoke()
        {
            //raising UI Automation InvokeEvent
            if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked))
            {
                AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(this);

                if (peer != null)
                {
                    peer.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked);
                }
            }

//Do Work here
        }
    
    }

Automation Peer Class - Here's the piece that lets us expose the pattern on our custom control.  You can also see where we're connecting to the DoInvoke method on TreeView class.  Note that this class needs to implement the interfaces for the patterns that it supports as well as return itself in the GetPattern call if it supports a specific pattern.

    public class TreeViewAutomationPeer : FrameworkElementAutomationPeer, IInvokeProvider
    {
        public TreeViewAutomationPeer(TreeView control)
            : base(control)
        {
        }

        protected override string GetClassNameCore()
        {
            return "TreeView";
        }

        protected override string GetLocalizedControlTypeCore()
        {
            return "custom";
        }

        protected override AutomationControlType GetAutomationControlTypeCore()
        {
            return AutomationControlType.Custom;
        }

        public override object GetPattern(PatternInterface patternInterface)
        {
            if (patternInterface == PatternInterface.Invoke)
            {
                return this;
            }

            return base.GetPattern(patternInterface);
        }

        public void Invoke()
        {
            TreeView tree = (TreeView) base.Owner;
            tree.DoInvoke();
        }

    }
Technorati Tags: ,,
Posted on Friday, January 11, 2008 3:21 PM | Back to top


Comments on this post: Custom Controls, UI Automation, Double Click and Invoke

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Robert May | Powered by: GeeksWithBlogs.net