Вот пример того, как я использую простой менеджер событий для подключения к событиям пользовательского интерфейса и извлечения ключевой информации о событиях, такой как имя и тип элемента пользовательского интерфейса, имя события и имя типа родительского окна. Для списков я также извлекаю выбранный пункт.
Это решение прослушивает только щелчки элементов управления, полученных из ButtonBase (Button, ToggleButton, ...), и изменения выбора в элементах управления, полученных из Selector (ListBox, TabControl, ...). Это должно быть легко распространено на другие типы элементов пользовательского интерфейса или для достижения более мелкозернистого решения. Решение основано на ответе Брэда Лича .
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
}
А для ведения реальной регистрации я использую log4net и создал отдельный регистратор с именем «Взаимодействие» для регистрации взаимодействия с пользователем. Класс 'Log' здесь - просто моя собственная статическая оболочка для log4net.
/// <summary>
/// The user interaction logger uses <see cref="UserInteractionEventsManager"/> to listen for events on GUI elements, such as buttons, list boxes, tab controls etc.
/// The events are then logged in a readable format using Log.Interaction.Info().
/// </summary>
public class UserInteractionLogger
{
private readonly UserInteractionEventsManager _events;
private bool _started;
/// <summary>
/// Create a user interaction logger. Remember to Start() it.
/// </summary>
public UserInteractionLogger()
{
_events = new UserInteractionEventsManager();
}
/// <summary>
/// Start logging user interaction events.
/// </summary>
public void Start()
{
if (_started) return;
_events.ButtonClicked += ButtonClicked;
_events.SelectorSelected += SelectorSelected;
_started = true;
}
/// <summary>
/// Stop logging user interaction events.
/// </summary>
public void Stop()
{
if (!_started) return;
_events.ButtonClicked -= ButtonClicked;
_events.SelectorSelected -= SelectorSelected;
_started = false;
}
private static void SelectorSelected(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowTypeName, object selectedObject)
{
Log.Interaction.Info("{0}.{1} by {2} in {3}. Selected: {4}", senderTypeName, eventName, senderName, parentWindowTypeName, selectedObject);
}
private static void ButtonClicked(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowTypeName)
{
Log.Interaction.Info("{0}.{1} by {2} in {3}", senderTypeName, eventName, senderName, parentWindowTypeName);
}
}
Вывод будет выглядеть примерно так, пропуская нерелевантные записи в журнале.
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