2010-11-04 22 views
18

Quiero medir y analizar los movimientos y gestos del usuario en la interfaz de usuario a fin de refinar la experiencia del usuario de la aplicación. Había imaginado que las bibliotecas de seguimiento de características (como EQATEC o Preemptive's Runtime Intelligence) lo permitirían. Sin embargo, este no parece ser el caso.¿Existen herramientas/bibliotecas (.Net/WPF) para medir y almacenar datos de navegación UI para el análisis?

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

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

Respuesta

2

Después de probar varios enfoques, incluyendo las de aquí, así como el uso de UIAutomation Events y ETW for WPF, he decidido en una simple unión de un manejador de eventos de WPF. Esto me permite no solo capturar los datos del evento, sino también el UIElement que tiene la atención de los usuarios, por lo que es mucho más fácil rastrear la acción e 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.

He aquí 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 consiga el UIElement puedo realizar el registro e incluso puede entonces utilizar el UIElement.DataContext para determinar el estado del modelo de vista que está llevando a la vista de forma podemos encontrar patrones de uso durante ciertos flujos de trabajo y estados de datos, así como estados visuales. A continuación, 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.

1

Echa un vistazo a esta aplicación. Que no hace lo que quiere, pero que podría ser útil como punto de partida para la implementación de las funciones que necesita: Perceptor: An artificially intelligent guided navigation system for WPF

+0

Gracias Giorgi: es un proyecto interesante, pero realmente es más sesgado para usar la entrada exacta que quiero capturar. La forma en que captura la entrada es un poco intrusiva (un UIElement personalizado y un conjunto de propiedades adjuntas). Prefiero encontrar algo que no tenga que modificar código para usar. – codekaizen

1

Trate http://iographica.com crea líneas donde el cursor del ratón se movía, y los círculos donde el cursor del ratón se detuvo, el más grande el círculo, más tiempo fue detenido allí.

+0

Esto parece lo que quiero ver, sin embargo, para correlacionar la salida con los elementos del usuario y luego con las historias de los usuarios, tendría que capturar capturas de pantalla junto con la salida de IOGraphica, y luego usar algún tipo de análisis para determinar lo que el usuario realmente estaba haciendo con su entrada. Esto sería demasiado trabajo. – codekaizen

1

Snoop es más una herramienta para inspeccionar el árbol visual de una aplicación WPF, pero lo hace también capturar eventos (especialmente capta estos con información sobre cuáles son los elementos que pasó, cómo viajaban en el árbol visual y donde ser manejado). 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.

6
  1. Descargar Sources Version 2 de esta article on code project
  2. Abra la solución o solamente Gma.UserActivityMonitor proyecto y ciegamente convertirlo en .NET 4.0
  3. En HookManager.Callbacks.cs de ficheros hacen 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 complemento siguiendo dos líneas en cualquier parte de la definición de clase HookManager.

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 
    public static extern IntPtr GetModuleHandle(string lpModuleName); 
    
  5. Ahora usted debería ser capaz de construir esto y mantener a un lado. Ahora comienza la parte de WPF.

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

    <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 suma siguiente código al archivo MainWindow MainWindow.xaml.cs definido clase.

    #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.Build y ejecutar su aplicación WPF, puede ser necesario añadir using Gma.UserActivityMonitor; Directiva en MainWindow.

10. Todo el mérito es de George Mamaladze que originalmente construyó, es posible que desee enviar una nota de agradecimiento a él, también comprobar la licencia de distribución para este código de autor original si quieres hacer dinero de eso

11. Solo hice un par de cambios menores para que funcione con WPF. Gracias por seguir esta larga publicación, no estoy seguro de cómo compartir este código, es por eso que proporciona instrucciones como esta.

12. Estoy realmente chupando el formato del código en SO, alguien puede dejar un comentario sobre cómo formatear el código, XML también. También alguien me ayude a formatear fragmentos en esta publicación. Gracias.

+0

si mi edición es aceptada, mire el historial de revisión para ver cómo formatear –

+0

Gracias @Markus. –

+0

Esto funciona bien, pero es bastante intrusivo. He encontrado algo similar, pero no tan pesado. – codekaizen

Cuestiones relacionadas