Hier ist ein Beispiel, wie ich einen einfachen Ereignis-Manager zum Anhängen an die UI-Ereignisse und Extrahieren von Schlüsselinformationen der Ereignisse, wie Name und Typ des UI-Elements, Name des Ereignisses und den Typnamen des übergeordneten Fensters verwenden. Für Listen extrahiere ich auch den ausgewählten Artikel.
Diese Lösung lauscht nur auf Klicks von Steuerelementen abgeleitet von ButtonBase (Button, ToggleButton, ...) und Auswahländerungen in Steuerelementen abgeleitet von Selector (ListBox, TabControl, ...). Es sollte einfach sein, sie auf andere Arten von UI-Elementen zu erweitern oder eine feinkörnigere Lösung zu erreichen. Die Lösung ist inspiriert von Brad Leach's answer.
public class UserInteractionEventsManager
{
public delegate void ButtonClickedHandler(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowName);
public delegate void SelectorSelectedHandler(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowName, object selectedObject);
public event ButtonClickedHandler ButtonClicked;
public event SelectorSelectedHandler SelectorSelected;
public UserInteractionEventsManager()
{
EventManager.RegisterClassHandler(typeof(ButtonBase), ButtonBase.ClickEvent, new RoutedEventHandler(HandleButtonClicked));
EventManager.RegisterClassHandler(typeof(Selector), Selector.SelectionChangedEvent, new RoutedEventHandler(HandleSelectorSelected));
}
#region Handling events
private void HandleSelectorSelected(object sender, RoutedEventArgs e)
{
// Avoid multiple events due to bubbling. Example: A ListBox inside a TabControl will cause both to send the SelectionChangedEvent.
if (sender != e.OriginalSource) return;
var args = e as SelectionChangedEventArgs;
if (args == null || args.AddedItems.Count == 0) return;
var element = sender as FrameworkElement;
if (element == null) return;
string senderName = GetSenderName(element);
string parentWindowName = GetParentWindowTypeName(sender);
DateTime time = DateTime.Now;
string eventName = e.RoutedEvent.Name;
string senderTypeName = sender.GetType().Name;
string selectedItemText = args.AddedItems.Count > 0 ? args.AddedItems[0].ToString() : "<no selected items>";
if (SelectorSelected != null)
SelectorSelected(time, eventName, senderName, senderTypeName, parentWindowName, selectedItemText);
}
private void HandleButtonClicked(object sender, RoutedEventArgs e)
{
var element = sender as FrameworkElement;
if (element == null) return;
string parentWindowName = GetParentWindowTypeName(sender);
DateTime time = DateTime.Now;
string eventName = e.RoutedEvent.Name;
string senderTypeName = sender.GetType().Name;
string senderName = GetSenderName(element);
if (ButtonClicked != null)
ButtonClicked(time, eventName, senderName, senderTypeName, parentWindowName);
}
#endregion
#region Private helpers
private static string GetSenderName(FrameworkElement element)
{
return !String.IsNullOrEmpty(element.Name) ? element.Name : "<no item name>";
}
private static string GetParentWindowTypeName(object sender)
{
var parent = FindParent<Window>(sender as DependencyObject);
return parent != null ? parent.GetType().Name : "<no parent>";
}
private static T FindParent<T>(DependencyObject item) where T : class
{
if (item == null)
return default(T);
if (item is T)
return item as T;
DependencyObject parent = VisualTreeHelper.GetParent(item);
if (parent == null)
return default(T);
return FindParent<T>(parent);
}
#endregion
}
Und die eigentliche Protokollierung zu tun, ich benutze log4net und erstellt einen separaten Logger namens ‚Interaktion‘ Interaktion mit dem Benutzer zu protokollieren. Die Klasse 'Log' hier ist einfach meine eigene statische Wrapper für log4net.
Die Ausgabe würde dann etwa so aussehen und nicht relevante Protokolleinträge auslassen.
04/13 08:38:37.069 INFO Iact ToggleButton.Click by AnalysisButton in MyMainWindow
04/13 08:38:38.493 INFO Iact ListBox.SelectionChanged by ListView in MyMainWindow. Selected: Andreas Larsen
04/13 08:38:44.587 INFO Iact Button.Click by EditEntryButton in MyMainWindow
04/13 08:38:46.068 INFO Iact Button.Click by OkButton in EditEntryDialog
04/13 08:38:47.395 INFO Iact ToggleButton.Click by ExitButton in MyMainWindow
EventManager.RegisterClassHandler hat wirklich den Trick, wenn ich für Tastenklicks, Listenauswahl usw. Sehr wenig Code benötigt wird, Protokollierung einige Interaktion zu tun, die für die gesamte GUI auf die notwendigen Ereignisse zum einhängen. – angularsen
Ich wollte nur einen Link zur Verfügung stellen, der für mich funktionierte: http://blog.pixelingene.com/2008/08/techniques-for-ui-auditing-on-wpf-apps/ –