Существуют ли какие-либо инструменты / библиотеки (.Net/WPF) для измерения и хранения навигационных данных пользовательского интерфейса для анализа?
Я хочу измерять и анализировать пользовательские движения и жесты в пользовательском интерфейсе, чтобы улучшить взаимодействие с пользователем приложения. Я предполагал, что библиотеки отслеживания функций (такие как EQATEC или Preemptive Runtime Intelligence) позволят это. Тем не менее, это не так.
В идеале я хотел бы иметь возможность настраивать пользовательский интерфейс, а затем захватывать жесты навигации мыши и клавиатуры для отображения через тепловую карту.
Мои поиски оказались пустыми. Есть ли здесь что-нибудь OSS или реклама?
5 ответов
Попробовав несколько подходов, в том числе и здесь, а также использование UIAutomation Events и ETW для WPF, я решил просто присоединить обработчик к событиям WPF. Это позволяет мне захватывать не только данные о событиях, но и UIElement, который привлекает внимание пользователей, поэтому намного легче отслеживать действия и намерения пользователя. Без этого мне нужно было бы сделать снимок экрана и определить, что происходит.
Вот пример:
private Int32 _eventCount;
public MainWindow()
{
InitializeComponent();
EventManager.RegisterClassHandler(typeof(UIElement), MouseEnterEvent, (RoutedEventHandler)handleEvent, true);
EventManager.RegisterClassHandler(typeof(UIElement), MouseLeaveEvent, (RoutedEventHandler)handleEvent, true);
EventManager.RegisterClassHandler(typeof(UIElement), MouseMoveEvent, (RoutedEventHandler)handleEvent, true);
EventManager.RegisterClassHandler(typeof(UIElement), MouseUpEvent, (RoutedEventHandler)handleEvent, true);
EventManager.RegisterClassHandler(typeof(UIElement), MouseDownEvent, (RoutedEventHandler)handleEvent, true);
EventManager.RegisterClassHandler(typeof(UIElement), KeyUpEvent, (RoutedEventHandler)handleEvent, true);
EventManager.RegisterClassHandler(typeof(UIElement), KeyDownEvent, (RoutedEventHandler)handleEvent, true);
}
private void handleEvent(object sender, RoutedEventArgs e)
{
var uiElement = e.Source as UIElement;
if (uiElement == null)
{
return;
}
EventStatusDisplay.Text = e.Source + " " + e.RoutedEvent.Name;
EventCountDisplay.Text = (++_eventCount).ToString();
var over = Mouse.DirectlyOver as UIElement;
MouseIsOverDisplay.Text = over == null ? "" : over.ToString();
}
Хотя это не показано здесь, как только я получаю UIElement
Я веду логирование и могу даже тогда использовать UIElement.DataContext
определить состояние ViewModel, которая управляет представлением, чтобы мы могли найти шаблоны использования во время определенных рабочих процессов и состояний данных, а также визуальных состояний. Затем мы можем получать отчеты по этому вопросу, а также дифференцировать и сравнивать наши тепловые карты по путям через рабочий процесс и значения данных.
- Загрузите Sources Version 2 из этой статьи о проекте code
- Откройте решение или только
Gma.UserActivityMonitor
проект и вслепую конвертировать его в.NET 4.0 В
HookManager.Callbacks.cs
файл внесите следующие изменения.- добавлять
using System.Diagnostics;
замещать
s_MouseHookHandle = SetWindowsHookEx( WH_MOUSE_LL, s_MouseDelegate, Marshal.GetHINSTANCE( Assembly.GetExecutingAssembly().GetModules()[0]), 0);
С
using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { s_MouseHookHandle = SetWindowsHookEx( WH_MOUSE_LL, s_MouseDelegate, GetModuleHandle(curModule.ModuleName), 0); }
замещать
s_KeyboardHookHandle = SetWindowsHookEx( WH_KEYBOARD_LL, s_KeyboardDelegate, Marshal.GetHINSTANCE( Assembly.GetExecutingAssembly().GetModules()[0]), 0);
С
using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { s_KeyboardHookHandle = SetWindowsHookEx( WH_KEYBOARD_LL, s_KeyboardDelegate, GetModuleHandle(curModule.ModuleName), 0); }
- добавлять
В
HookManager.Windows.cs
добавить следующие две строки в любом местеHookManager
определение класса.[DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern IntPtr GetModuleHandle(string lpModuleName);
Теперь вы должны быть в состоянии построить это и держать в стороне. Теперь начинается часть WPF.
- Желательно создать новый проект WPF с именем WpfApplication1. Добавьте ссылку на проект / сборку, созданную на шаге 1-5, добавьте ссылку на System.Windows.Forms.
Теперь замените
MainWindow.xaml
следуя XAML, убедитесь, что проверили имя класса и имя проекта, я только что создал WpfApplication1 для тестирования.<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="0.30*"/> <RowDefinition Height="0.70*"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Orientation="Horizontal"> <StackPanel Orientation="Vertical"> <CheckBox Name="MouseMove" Padding="5" Content="Mouse Move" Width="120" Height="30" Click="checkBoxOnMouseMove_CheckedChanged"/> <CheckBox Name="MouseClick" Padding="5" Content="Mouse Click" Width="120" Height="30" Click="checkBoxOnMouseClick_CheckedChanged"/> <CheckBox Name="MouseDown" Padding="5" Content="Mouse Down" Width="120" Height="30" Click="checkBoxOnMouseDown_CheckedChanged"/> </StackPanel> <StackPanel Orientation="Vertical"> <CheckBox Name="MouseUp" Padding="5" Content="Mouse Up" Width="120" Height="30" Click="checkBoxOnMouseUp_CheckedChanged"/> <CheckBox Name="MouseDouble" Padding="5" Content="Mouse Double" Width="120" Height="30" Click="checkBoxMouseDoubleClick_CheckedChanged"/> <CheckBox Name="MouseWheel" Padding="5" Content="Mouse Wheel" Width="120" Height="30" Click="checkBoxMouseWheel_CheckedChanged"/> </StackPanel> <StackPanel Orientation="Vertical"> <CheckBox Name="KeyDown" Padding="5" Content="Key Down" Width="120" Height="30" Click="checkBoxKeyDown_CheckedChanged"/> <CheckBox Name="KeyPress" Padding="5" Content="Key Press" Width="120" Height="30" Click="checkBoxKeyPress_CheckedChanged"/> <CheckBox Name="KeyUp" Padding="5" Content="Key Up" Width="120" Height="30" Click="checkBoxKeyUp_CheckedChanged"/> </StackPanel> <StackPanel Orientation="Vertical"> <TextBlock Name="labelMousePosition" Text="x={0:####}; y={1:####}"/> <TextBlock Name="labelWheel" Text="Wheel={0:####}"/> </StackPanel> </StackPanel> <TextBlock Name="textBoxLog" Text="START" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Visible"/> </Grid> </Window>
Теперь добавьте следующий код в класс MainWindow, определенный в файле MainWindow.xaml.cs.
#region Check boxes to set or remove particular event handlers. private void checkBoxOnMouseMove_CheckedChanged(object sender, EventArgs e) { if ((bool)MouseMove.IsChecked) { HookManager.MouseMove += HookManager_MouseMove; } else { HookManager.MouseMove -= HookManager_MouseMove; } } private void checkBoxOnMouseClick_CheckedChanged(object sender, EventArgs e) { if ((bool)MouseClick.IsChecked) { HookManager.MouseClick += HookManager_MouseClick; } else { HookManager.MouseClick -= HookManager_MouseClick; } } private void checkBoxOnMouseUp_CheckedChanged(object sender, EventArgs e) { if ((bool)MouseUp.IsChecked) { HookManager.MouseUp += HookManager_MouseUp; } else { HookManager.MouseUp -= HookManager_MouseUp; } } private void checkBoxOnMouseDown_CheckedChanged(object sender, EventArgs e) { if ((bool)MouseDown.IsChecked) { HookManager.MouseDown += HookManager_MouseDown; } else { HookManager.MouseDown -= HookManager_MouseDown; } } private void checkBoxMouseDoubleClick_CheckedChanged(object sender, EventArgs e) { if ((bool)this.MouseDouble.IsChecked) { HookManager.MouseDoubleClick += HookManager_MouseDoubleClick; } else { HookManager.MouseDoubleClick -= HookManager_MouseDoubleClick; } } private void checkBoxMouseWheel_CheckedChanged(object sender, EventArgs e) { if ((bool)MouseWheel.IsChecked) { HookManager.MouseWheel += HookManager_MouseWheel; } else { HookManager.MouseWheel -= HookManager_MouseWheel; } } private void checkBoxKeyDown_CheckedChanged(object sender, EventArgs e) { if ((bool)KeyDown.IsChecked) { HookManager.KeyDown += HookManager_KeyDown; } else { HookManager.KeyDown -= HookManager_KeyDown; } } private void checkBoxKeyUp_CheckedChanged(object sender, EventArgs e) { if ((bool)KeyUp.IsChecked) { HookManager.KeyUp += HookManager_KeyUp; } else { HookManager.KeyUp -= HookManager_KeyUp; } } private void checkBoxKeyPress_CheckedChanged(object sender, EventArgs e) { if ((bool)KeyPress.IsChecked) { HookManager.KeyPress += HookManager_KeyPress; } else { HookManager.KeyPress -= HookManager_KeyPress; } } #endregion //################################################################## #region Event handlers of particular events. They will be activated when an appropriate check box is checked. private void HookManager_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) { textBoxLog.Text += (string.Format("KeyDown - {0}\n", e.KeyCode)); } private void HookManager_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e) { textBoxLog.Text += (string.Format("KeyUp - {0}\n", e.KeyCode)); } private void HookManager_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { textBoxLog.Text += (string.Format("KeyPress - {0}\n", e.KeyChar)); } private void HookManager_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) { labelMousePosition.Text = string.Format("x={0:0000}; y={1:0000}", e.X, e.Y); } private void HookManager_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) { textBoxLog.Text += (string.Format("MouseClick - {0}\n", e.Button)); } private void HookManager_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { textBoxLog.Text += (string.Format("MouseUp - {0}\n", e.Button)); } private void HookManager_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { textBoxLog.Text += (string.Format("MouseDown - {0}\n", e.Button)); } private void HookManager_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e) { textBoxLog.Text += (string.Format("MouseDoubleClick - {0}\n", e.Button)); } private void HookManager_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e) { labelWheel.Text = string.Format("Wheel={0:000}", e.Delta); } #endregion
9. Создайте и запустите приложение WPF, возможно, вам потребуется добавить using Gma.UserActivityMonitor;
директива в MainWindow.
10. Вся заслуга принадлежит Джорджу Мамаладзе, который изначально его построил, вы можете отправить ему письмо с благодарностью, а также проверить лицензию на распространение этого кода у оригинального автора, если хотите заработать на этом деньги.
11. Я только что сделал несколько небольших изменений, чтобы он работал с WPF. Спасибо за то, что вы прочитали этот длинный пост, я не уверен, как поделиться этим кодом, поэтому предоставил такие инструкции.
12. Я серьезно отстой в хорошем форматировании кода на SO, может кто-то оставить комментарий о том, как правильно отформатировать код, XML. Также кто-то поможет мне отформатировать фрагменты в этом посте. Спасибо.
Попробуйте http://iographica.com/ он создает линии, где курсор мыши перемещался, и круги, где курсор мыши остановился, чем больше круг, тем дольше он был остановлен там.
Посмотрите на это приложение. Он не делает то, что вы хотите, но он может быть полезен в качестве отправной точки для реализации необходимых вам функций: Perceptor: искусственно управляемая навигационная система для WPF
Я знаю, что Snoop - это больше инструмент для проверки визуального дерева приложения WPF, но он также фиксирует события (особенно он фиксирует их с информацией о том, с какими элементами они произошли, как они перемещались в визуальном дереве и где они обрабатывались). Поскольку это открытый исходный код, вы можете извлечь эту часть об отслеживании событий и записать эту информацию в соответствии с вашими потребностями.