Подтвердить что ты не робот

Существуют ли какие-либо инструменты/библиотеки (.Net/WPF) для измерения и хранения данных навигационного интерфейса для анализа?

Я хочу измерить и проанализировать движения пользователя и жесты в пользовательском интерфейсе, чтобы улучшить работу пользователей приложения. Я предположил, что библиотеки отслеживания функций (например, EQATEC или Preemptive Runtime Intelligence) позволят это. Однако, похоже, это не так.

В идеале я хотел бы использовать инструмент пользовательского интерфейса, а затем записывать жесты навигации с помощью мыши и клавиатуры для отображения через тепловую карту.

Мои поиски вышли пустым. Здесь есть что-нибудь OSS или коммерческое?

4b9b3361

Ответ 1

Попробовав несколько подходов, в том числе и здесь, а также используя События UIAutomation и 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, которое управляет представлением, чтобы мы могли найти шаблоны использования во время определенных рабочих процессов и состояний данных, а также визуальных состояний. Затем мы можем получать отчеты об этом, а также различать и сравнивать наши карты тепла по путям с помощью рабочих потоков и значений данных.

Ответ 2

  • Загрузите Источники версии 2 из этой статьи статьи о проекте кода
  • Откройте решение или только проект 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, указанный в файле 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.Build и запустите приложение WPF, вам может потребоваться добавить директиву using Gma.UserActivityMonitor; в MainWindow.

10. Все, что было сделано в George Mamaladze, кто изначально построил его, вы можете отправить Благодарим вас за внимание, также проверьте лицензию на распространение этого кода от оригинального автора, если вы хотите сделать из этого деньги.

11. Я сделал несколько незначительных изменений, чтобы заставить его работать с WPF. Спасибо за следующий длинный пост, я не уверен, как поделиться этим кодом, поэтому предоставляя такие инструкции.

12. Я серьезно засасываю форматирование кода на SO, может кто-то оставить комментарий о том, как форматировать код, хорошо XML. Также мне помогают отформатировать фрагменты в этом сообщении. Спасибо.

Ответ 4

Попробуйте http://iographica.com он создает линии, где перемещается курсор мыши, и круги, где остановлен указатель мыши, чем больше круг, тем дольше он был остановлен там.

Ответ 5

Я знаю Snoop - это больше инструмент для проверки визуального дерева приложения WPF, но он также захватывает события (особенно это захватывает их информацией о том, какие элементы они произошли, как они путешествовали в визуальном дереве и где они обрабатываются). Поскольку это открытый исходный код, вы можете захотеть извлечь эту часть для отслеживания событий и зарегистрировать эту информацию в соответствии с вашими потребностями.