是否有任何工具/库(.Net/WPF)来测量和存储用于分析的UI导航数据?

cod*_*zen 18 .net wpf usage-tracking

我想在UI中测量和分析用户移动和手势,以优化应用程序用户体验.我曾想过功能跟踪库(如EQATEC或Preemptive的运行时智能)会允许这样做.但是,情况似乎并非如此.

理想情况下,我希望能够设置UI,然后通过热图捕获鼠标和键盘导航手势.

我的搜索空洞了.这里有OSS或商业广告吗?

San*_*ath 6

  1. 代码项目的这篇文章下载代码版本2
  2. 打开解决方案或仅打开Gma.UserActivityMonitor项目并盲目地将其转换为.NET 4.0
  3. HookManager.Callbacks.cs文件中进行以下更改.

    1. using System.Diagnostics;
    2. 更换

      s_MouseHookHandle = SetWindowsHookEx(
          WH_MOUSE_LL,
          s_MouseDelegate,
          Marshal.GetHINSTANCE(
              Assembly.GetExecutingAssembly().GetModules()[0]),
          0);
      
      Run Code Online (Sandbox Code Playgroud)

      using (Process curProcess = Process.GetCurrentProcess())
      using (ProcessModule curModule = curProcess.MainModule)
      {
          s_MouseHookHandle = SetWindowsHookEx(
              WH_MOUSE_LL,
              s_MouseDelegate,
             GetModuleHandle(curModule.ModuleName), 0);
      }
      
      Run Code Online (Sandbox Code Playgroud)
    3. 更换

      s_KeyboardHookHandle = SetWindowsHookEx(
          WH_KEYBOARD_LL,
          s_KeyboardDelegate,
          Marshal.GetHINSTANCE(
              Assembly.GetExecutingAssembly().GetModules()[0]),
          0);
      
      Run Code Online (Sandbox Code Playgroud)

      using (Process curProcess = Process.GetCurrentProcess())
      using (ProcessModule curModule = curProcess.MainModule)
      {
          s_KeyboardHookHandle = SetWindowsHookEx(
          WH_KEYBOARD_LL,
          s_KeyboardDelegate, 
          GetModuleHandle(curModule.ModuleName), 0);
      }
      
      Run Code Online (Sandbox Code Playgroud)
  4. 在类的定义中的HookManager.Windows.cs任何位置添加以下两行HookManager.

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr GetModuleHandle(string lpModuleName);
    
    Run Code Online (Sandbox Code Playgroud)
  5. 现在你应该能够构建它并将它放在一边.现在启动WPF部分.

  6. 创建名为WpfApplication1的新WPF项目.添加对在步骤1-5中构建项目/程序集的引用,添加对System.Windows.Forms的引用.
  7. 现在替换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>
    
    Run Code Online (Sandbox Code Playgroud)
  8. 现在将以下代码添加到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
    
    Run Code Online (Sandbox Code Playgroud)

9.构建并运行WPF应用程序,您可能需要using Gma.UserActivityMonitor;在MainWindow中添加指令.

10. 所有的功劳都归功于最初构建它的George Mamaladze,您可能想向他发送一封感谢信,如果您想从中获取资金,还可以查看原始作者的代码分发许可.

11.我刚做了一些小改动,以便与WPF一起使用.感谢您关注这篇长篇文章,我不知道如何分享这些代码,这就是提供这样的说明的原因.

12. 我非常擅长在SO上格式化代码,有人可以对如何格式化代码,XML做好评论.也有人帮我格式化这篇文章中的片段.谢谢.


cod*_*zen 2

在尝试了多种方法(包括此处的方法以及使用UIAutomation EventsETW for 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();
}
Run Code Online (Sandbox Code Playgroud)

虽然这里没有显示,但一旦我得到了,UIElement我就会执行日志记录,甚至可以使用它UIElement.DataContext来确定驱动视图的 ViewModel 的状态,这样我们就可以找到某些工作流程和数据状态以及视觉上的使用模式。状态。然后,我们可以获得相关报告,并通过工作流程和数据值的路径区分和比较我们的热图。