.net wpf usage-tracking
Sources Version 2

¿Existen herramientas/bibliotecas(.Net/WPF) para medir y almacenar datos de navegación de UI para su análisis?



usage-tracking (5)

Quiero medir y analizar los movimientos y gestos del usuario en la interfaz de usuario para perfeccionar la experiencia del usuario de la aplicación. Me había imaginado que las bibliotecas de seguimiento de características (como EQATEC o Remperado Inteligencia de Preemptive) lo permitirían. Sin embargo, este no parece ser el caso.

Idealmente, me gustaría poder instrumentar una IU y luego capturar los gestos de navegación del mouse y el teclado para mostrarlos a través de un mapa de calor.

Mis búsquedas han quedado vacías. ¿Existe algo de OSS o comercial aquí?


  1. Descargar Sources Version 2 de este artículo en el proyecto de código
  2. Abra la solución o solo el proyecto Gma.UserActivityMonitor y Gma.UserActivityMonitor ciegas a .NET 4.0
  3. En el archivo HookManager.Callbacks.cs realice los siguientes cambios.

    1. Añadir using System.Diagnostics;
    2. Reemplazar

      s_MouseHookHandle = SetWindowsHookEx( WH_MOUSE_LL, s_MouseDelegate, Marshal.GetHINSTANCE( Assembly.GetExecutingAssembly().GetModules()[0]), 0);

      Con

      using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { s_MouseHookHandle = SetWindowsHookEx( WH_MOUSE_LL, s_MouseDelegate, GetModuleHandle(curModule.ModuleName), 0); }

    3. Reemplazar

      s_KeyboardHookHandle = SetWindowsHookEx( WH_KEYBOARD_LL, s_KeyboardDelegate, Marshal.GetHINSTANCE( Assembly.GetExecutingAssembly().GetModules()[0]), 0);

      Con

      using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { s_KeyboardHookHandle = SetWindowsHookEx( WH_KEYBOARD_LL, s_KeyboardDelegate, GetModuleHandle(curModule.ModuleName), 0); }

  4. En HookManager.Windows.cs agregue las siguientes dos líneas en cualquier lugar de la definición de la clase HookManager .

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern IntPtr GetModuleHandle(string lpModuleName);

  5. Ahora deberías poder construir esto y mantenerlo a un lado. Ahora comienza la parte WPF.

  6. Cree un nuevo proyecto WPF con el nombre WpfApplication1 preferiblemente. Agregue una referencia al proyecto / ensamblaje que construyó en el paso 1-5 , agregue una referencia a System.Windows.Forms .
  7. Ahora, MainWindow.xaml el MainWindow.xaml con el siguiente XAML, asegúrese de verificar el nombre de la clase y el nombre del proyecto, acabo de crear WpfApplication1 para la prueba.

    <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>

  8. Ahora agregue el siguiente código al archivo MainWindow.xaml.cs definido por la clase MainWindow.

    #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. Construya y ejecute su aplicación WPF, es posible que necesite agregar using Gma.UserActivityMonitor; Directiva en MainWindow.

10. Todo el crédito es para George Mamaladze, quien originalmente lo construyó, es posible que desee enviarle una nota de agradecimiento a él, también verifique la licencia de distribución para este código del autor original si desea ganar dinero con él.

11. Acabo de hacer algunos cambios menores para que funcione con WPF. Gracias por seguir este largo post, no estoy seguro de cómo compartir este código, es por eso que proporciono instrucciones como esta.

12. Me estoy chupando seriamente el formato del código en SO, ¿alguien puede dejar un comentario sobre cómo formatear el código, así XML? También alguien me ayude a formatear fragmentos en este post. Gracias.


Después de probar una serie de enfoques, incluidos los de aquí, así como el uso de UIAutomation Events y ETW para WPF , he decidido adjuntar un controlador a los eventos de WPF. Esto me permite no solo capturar los datos del evento, sino también la UIElement que tiene la atención de los usuarios, por lo que es mucho más fácil rastrear la acción y la intención del usuario. Sin esto, necesitaría capturar una imagen visual de la pantalla y hacer una determinación visual de lo que está sucediendo.

Aquí hay una muestra:

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(); }

Si bien no se muestra aquí, una vez que obtengo el UIElement , realizo el registro e incluso puedo usar el UIElement.DataContext para determinar el estado del ViewModel que está impulsando la vista para que podamos encontrar patrones de uso durante ciertos flujos de trabajo y estados de datos. así como los estados visuales. Luego podemos obtener informes sobre esto, así como diferenciar y comparar nuestros mapas de calor por rutas a través del flujo de trabajo y valores de datos.



Pruebe http://iographica.com; crea líneas donde se movió el cursor del mouse y círculos en los que el cursor del mouse se detuvo, mientras más grande sea el círculo, más tiempo se detuvo allí.


Sé que Snoop es más una herramienta para inspeccionar el árbol visual de una aplicación WPF, pero también captura eventos (especialmente los captura con información sobre qué elementos ocurrieron, cómo viajaron en el árbol visual y dónde se manejan). Como es de código abierto, es posible que desee extraer esa parte sobre el seguimiento de eventos y registrar esta información según sus necesidades.