User Interface auditing is a fundamental requirement when you want to do usability testing and analysis. The auditing process captures user data about how the mouse and keyboard are used. We will stick to Mouse and Keyboard since they are the predominant input devices. Of course you can also involve the Stylus and touch-based gestures, if your apps use it.
The techniques I delineate below are only a subset of the complete list. For proprietary reasons I cannot reveal everything but you should be able to extrapolate easily.
Our goal is really to get a better picture of how the user is using our application. Some of the interesting questions could be:
There are lots of questions one can ask, only limited by our imagination. The important thing to remember is that the data we capture, makes it easy to answer these questions.
Events
The next obvious question is “How do we capture this data?”.
Since we are interested in Mouse and Keyboard events, we need a way to intercept all Mouse and Keyboard activity happening in the application. WPF makes this easy with the Class handlers. This allows us to see all Mouse and Keyboard events, including the ones that are handled. Class handlers can be registered for all RoutedEvents that are exposed via the EventManager. To register a Class Handler you need to use the EventManager.RegisterClassHandler() method.
public static void RegisterClassHandler(
Type classType,
RoutedEvent routedEvent,
Delegate handler,
bool handledEventsToo)
If you want to intercept all MouseDown Events happening on the Window, you can do so using:
EventManager.RegisterClassHandler(typeof(Window),
Mouse.MouseDownEvent, OnMouseDownHandler, true);
As an extreme case, if you wanted to handle all RoutedEvents in the application, you can do it with the following snippet
foreach (RoutedEvent routedEvent in EventManager.GetRoutedEvents())
{
EventManager.RegisterClassHandler(typeof(Window),
routedEvent, OnRoutedEvent, true);
}
Event Context
But handling events is only a small part of the whole story. Events by themselves are meaningless unless you associate enough context with it. Context gives you more information about where the event occurred, what time, place, etc. All of this can be stored in a class like the EventContext. Here is a possible class to get you thinking:
public sealed class EventContext
{
public EventContext(string view, string business)
{
View = view;
Business = business;
}
public string View { get; internal set; }
public string Business { get; internal set; }
public string EventName { get; set; }
public string Name { get; internal set; }
public string AutomationName { get; internal set; }
public string Snapshot { get; internal set; }
public Rect Bounds { get; internal set; }
public DateTime TimeStamp { get; internal set; }
public Type PresenterType { get; set; }
public override string ToString()
{
return "View = " + View + ", Business = " + Business;
}
}
The most interesting properties are the View and Business, which we will tackle soon. The other more obvious ones include the RoutedEvent’s name (EventName), the Name of the Control, it’s AutomationName, a bitmap Snapshot file, its Bounds and the TimeStamp. We also have the PresenterType that points to the Type of the DataContext associated with the control at the time of the event. If you need more in-depth information about the DataContext, you can do some extra introspection.
I know you are really curious about the View and Business properties. These are high level properties that are established on the UI at different points in the Logical Tree, generally at a parent component that encapsulates a group of controls that all serve to achieve one task. So you can think of the View as really a task name. Similarly the Business property defines what business function is being satisfied with the View. Although I am calling it View and Business you can change it to suit your needs. The idea to get a high-level information about how the controls and events are related and how they tie into the ultimate task at hand.
Where do View and Business come from ?
View and Business are attached properties defined on a static class. It does not have any property changed handlers. Attached properties give us a non-intrusive way to associate extra information with a UI object. In my case I had as class like AuditProperties that defined these properties.
public static class AuditProperties
{
public static readonly DependencyProperty BusinessProperty =
DependencyProperty.Register(
"Business", typeof (string), typeof (AuditProperties));
public static readonly DependencyProperty ViewProperty =
DependencyProperty.Register(
"View", typeof (string), typeof (AuditProperties));
...
...
}
Later when you are ready to attach these properties, you go through your application’s Logical tree and add them. Coming up with the View and Business values requires a group effort involving the Design, Development teams and the Domain experts.
Capturing context
Now that we have a better way to associate EventContext with an event, we are now ready to log. At runtime when the class handler gets invoked, we capture the EventContext by introspecting event data and also walk up the Logical Tree until we find the View and Business properties. Once the complete context is found, we safely log it (or persist it).
The Bottom line
So UI Auditing is all about setting up Class Handlers for events, associating extra metadata with the application, capturing EventContext and then persisting the context at runtime. Frankly this is only the tip of the iceberg. The real ice is the event-context analysis after the audit. Can we do some extra magic over there? Sure we can, but I’ll leave that to the reader for now :)