Reputation: 23747
I'm attempting to use the MS UI Automation Framework in my application for some automated testing. My goal for now is to listen to GUI events and record them, similar to the sample provided (TestScriptGeneratorSample) in the Windows SDK.
Just using that demo application, I can launch a simple Windows Forms application with a single button and see that it records click events as "invoke" UIA events. However, for each click of the button, the demo application records 4 "invoke" events. Why is that? I don't see this issue when using a WPF button.
My guess is that since the System.Windows.Forms.Button
class supports MSAA instead of the UIA, the part of UIA that is bridging the MSAA interface is misbehaving, or at least behaving in a way that I don't understand and can't find any documentation about.
Maybe it's reporting an invoke event for mouse down, up, click, and then the actual button press?
Can anyone explain this behavior, and/or provide a workaround so that one button press results in one invoke event?
Edit: This is under WinXP SP3. I just installed the Windows Automation API 3.0 update and still see the same behavior.
Edit 2: I found this example where the author off-handedly mentions this behavior as a bug in the Win32 control, but cites no evidence...
Here's my sample application (form with a button on it), along with event listening baked into it. Add references to UIAutomationClient
and UIAutomationTypes
. Click the button and see invoke happen four times instead of one.
using System;
using System.Drawing;
using System.Windows.Automation;
using System.Windows.Forms;
namespace WindowsFormsApplication6
{
public partial class Form1 : Form
{
private TextBox m_Output;
public Form1()
{
InitializeComponent();
// set up the GUI with a single button...
Button b = new Button {Location = new Point(5, 5), Name = "Test", Text = "Click Me"};
// ... and a textbox to write events to.
m_Output = new TextBox { Location = new Point(5, 30), Name = "Output", Multiline = true, Size = new Size(250, 200) };
this.Controls.Add(b);
this.Controls.Add(m_Output);
// get the button as an UIA element
AutomationElement button = AutomationElement.FromHandle(b.Handle);
// attach a handler to the button, listening for the Invoke event
Automation.AddAutomationEventHandler(
InvokePattern.InvokedEvent,
button,
TreeScope.Element,
OnInvoked);
}
// Handler for button invoke events
private void OnInvoked(object Sender, AutomationEventArgs E)
{
AppendText("Invoked: " + ((AutomationElement)Sender).Current.AutomationId);
}
// Just dumps text to the textbox
private void AppendText(string Text)
{
if (m_Output.InvokeRequired)
{
m_Output.BeginInvoke((Action<string>)AppendText, Text);
}
else
{
m_Output.AppendText(DateTime.Now.ToString("hh:mm:ss.fff") + ": " + Text + Environment.NewLine);
}
}
}
}
Upvotes: 4
Views: 3948
Reputation: 23747
For what it's worth, I've worked around it by filtering the multiple events being issued into one. During testing I found the following:
Name
property) calc.exe
):
HasKeyboardFocus
property) HasKeyboardFocus
property) Using the FrameworkId
property on the AutomationElement
that is associated with the event, I can distinguish between the first and second two situations (it's "Winform"
for .NET buttons and "Win32"
for Win32 buttons). Then for the two Win32 scenarios I just ensure I get a HasKeyboardFocus
property change event before recording the invoked event.
I haven't seen this not work yet since I always seem to get the HasKeyboardFocus
property change event, even if the button already was focused (i.e. I press it twice).
I'd still like to see some more insight if anyone has some...
Upvotes: 0