8

你是如何检测你的 UI 的?在过去,我读到人们已经对他们的用户界面进行了检测,但我没有找到关于如何检测 UI 的示例或提示。

通过检测,我的意思是收集有关系统使用和性能的数据。有关 Instrumentation 的 MSDN 文章是http://msdn.microsoft.com/en-us/library/x5952w0c.aspx。我想捕捉用户点击了哪些按钮、他们使用了哪些键盘快捷键、他们使用哪些术语进行搜索等。

  • 你是如何检测你的用户界面的?
  • 您存储仪器的格式是什么?
  • 您如何处理检测数据?
  • 您如何使用此检测逻辑保持 UI 代码干净?

具体来说,我正在 WPF 中实现我的 UI,因此与检测基于 Web 的应用程序相比,这将带来额外的挑战。(即需要将检测数据传输回中心位置等)。也就是说,我觉得该技术可以通过附加属性等概念提供更容易的检测实现。

  • 您是否检测过 WPF 应用程序?您对如何实现这一点有任何提示吗?

编辑:以下博客文章提供了一个有趣的解决方案:Pixel-In-Gene 博客:WPF 应用程序的 UI 审核技术

4

7 回答 7

4

这是我如何使用简单的事件管理器连接到 UI 事件并提取事件的关键信息的示例,例如 UI 元素的名称和类型、事件名称和父窗口的类型名称。对于列表,我还提取所选项目。

此解决方案仅侦听从 ButtonBase(Button、ToggleButton、...)派生的控件的单击以及从 Selector(ListBox、TabControl、...)派生的控件中的选择更改。它应该很容易扩展到其他类型的 UI 元素或实现更细粒度的解决方案。该解决方案的灵感来自Brad Leach 的回答

public class UserInteractionEventsManager
{
    public delegate void ButtonClickedHandler(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowName);
    public delegate void SelectorSelectedHandler(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowName, object selectedObject);

    public event ButtonClickedHandler ButtonClicked;
    public event SelectorSelectedHandler SelectorSelected;

    public UserInteractionEventsManager()
    {
        EventManager.RegisterClassHandler(typeof(ButtonBase), ButtonBase.ClickEvent, new RoutedEventHandler(HandleButtonClicked));
        EventManager.RegisterClassHandler(typeof(Selector), Selector.SelectionChangedEvent, new RoutedEventHandler(HandleSelectorSelected));
    }

    #region Handling events

    private void HandleSelectorSelected(object sender, RoutedEventArgs e)
    {
        // Avoid multiple events due to bubbling. Example: A ListBox inside a TabControl will cause both to send the SelectionChangedEvent.
        if (sender != e.OriginalSource) return;

        var args = e as SelectionChangedEventArgs;
        if (args == null || args.AddedItems.Count == 0) return;

        var element = sender as FrameworkElement;
        if (element == null) return;

        string senderName = GetSenderName(element);
        string parentWindowName = GetParentWindowTypeName(sender);
        DateTime time = DateTime.Now;
        string eventName = e.RoutedEvent.Name;
        string senderTypeName = sender.GetType().Name;
        string selectedItemText = args.AddedItems.Count > 0 ? args.AddedItems[0].ToString() : "<no selected items>";

        if (SelectorSelected != null)
            SelectorSelected(time, eventName, senderName, senderTypeName, parentWindowName, selectedItemText);
    }

    private void HandleButtonClicked(object sender, RoutedEventArgs e)
    {
        var element = sender as FrameworkElement;
        if (element == null) return;

        string parentWindowName = GetParentWindowTypeName(sender);
        DateTime time = DateTime.Now;
        string eventName = e.RoutedEvent.Name;
        string senderTypeName = sender.GetType().Name;
        string senderName = GetSenderName(element);

        if (ButtonClicked != null) 
            ButtonClicked(time, eventName, senderName, senderTypeName, parentWindowName);
    }

    #endregion

    #region Private helpers

    private static string GetSenderName(FrameworkElement element)
    {
        return !String.IsNullOrEmpty(element.Name) ? element.Name : "<no item name>";
    }


    private static string GetParentWindowTypeName(object sender)
    {
        var parent = FindParent<Window>(sender as DependencyObject);
        return parent != null ? parent.GetType().Name : "<no parent>";
    }

    private static T FindParent<T>(DependencyObject item) where T : class
    {
        if (item == null) 
            return default(T);

        if (item is T)
            return item as T;

        DependencyObject parent = VisualTreeHelper.GetParent(item);
        if (parent == null)
            return default(T);

        return FindParent<T>(parent);
    }

    #endregion
}

为了进行实际的日志记录,我使用 log4net 并创建了一个名为“Interaction”的单独记录器来记录用户交互。这里的“Log”类只是我自己的 log4net 静态包装器。

/// <summary>
/// The user interaction logger uses <see cref="UserInteractionEventsManager"/> to listen for events on GUI elements, such as buttons, list boxes, tab controls etc.
/// The events are then logged in a readable format using Log.Interaction.Info().
/// </summary>
public class UserInteractionLogger
{
    private readonly UserInteractionEventsManager _events;
    private bool _started;

    /// <summary>
    /// Create a user interaction logger. Remember to Start() it.
    /// </summary>
    public UserInteractionLogger()
    {
        _events = new UserInteractionEventsManager();

    }

    /// <summary>
    /// Start logging user interaction events.
    /// </summary>
    public void Start()
    {
        if (_started) return;

        _events.ButtonClicked += ButtonClicked;
        _events.SelectorSelected += SelectorSelected;

        _started = true;
    }

    /// <summary>
    /// Stop logging user interaction events.
    /// </summary>
    public void Stop()
    {
        if (!_started) return;

        _events.ButtonClicked -= ButtonClicked;
        _events.SelectorSelected -= SelectorSelected;

        _started = false;
    }

    private static void SelectorSelected(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowTypeName, object selectedObject)
    {
        Log.Interaction.Info("{0}.{1} by {2} in {3}. Selected: {4}", senderTypeName, eventName, senderName, parentWindowTypeName, selectedObject);
    }

    private static void ButtonClicked(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowTypeName)
    {
        Log.Interaction.Info("{0}.{1} by {2} in {3}", senderTypeName, eventName, senderName, parentWindowTypeName);
    }
}

然后输出看起来像这样,省略不相关的日志条目。

04/13 08:38:37.069 INFO Iact ToggleButton.Click by MyMainWindow 中的 AnalysisButton
04/13 08:38:38.493 信息 Iact ListBox.SelectionChanged 由 MyMainWindow 中的 ListView。已选择:安德烈亚斯·拉森
04/13 08:38:44.587 INFO Iact Button.Click 由 MyMainWindow 中的 EditEntryButton
04/13 08:38:46.068 INFO Iact Button.Click by OkButton 在 EditEntryDialog
04/13 08:38:47.395 INFO Iact ToggleButton.Click by ExitButton in MyMainWindow
于 2011-04-13T06:51:41.190 回答
3

以下博客文章为检测 WPF 应用程序提供了很多好主意:WPF 应用程序 上的 UI 审核技术

于 2008-08-28T02:08:26.253 回答
2

你可以考虑log4net。它是一个健壮的日志框架,存在于单个 DLL 中。它也是在“非要求”类型模式下完成的,因此如果一个关键进程正在进行,它不会记录,直到资源被释放更多一点。

您可以轻松设置一堆 INFO 级别的记录器并跟踪您需要的所有用户交互,并且将文件发送给您自己不会发生错误崩溃。然后,您还可以将所有 ERROR 和 FATAL 代码记录到单独的文件中,这些文件可以很容易地邮寄给您进行处理。

于 2008-08-11T23:02:48.060 回答
2

如果您使用 WPF 命令,则每个自定义命令都可以记录所采取的操作。您还可以记录启动命令的方式。

于 2008-08-12T04:38:19.957 回答
0

也许 WPF 的Microsoft UI 自动化可以提供帮助?它是一个自动化你的 UI 的框架,也许它可以用来为你记录东西......

我们使用自动化框架在 WPF 中自动测试我们的 UI。

于 2008-08-12T11:31:13.367 回答
-1

我还没有使用 WPF 进行开发。但我认为它与大多数其他应用程序相同,因为您希望保持 UI 代码尽可能轻量。可以使用许多设计模式,例如显而易见的MVC外观。我个人总是尽量保持在 UI 和 BL 层之间移动的对象尽可能轻,如果可以的话,将它们保持为基元。

然后,这可以帮助我专注于改进 UI 层,而无需担心一旦我将(原始)数据扔回去就会发生任何事情。

我希望我正确理解了你的问题,很抱歉我不能提供更多关于 WPF 的上下文帮助。

于 2008-08-11T21:21:31.977 回答
-1

免责声明:我为销售该产品的公司工作,不仅如此,我还是该特定产品的开发人员:)。

如果您对提供此功能的商业产品感兴趣,则可以使用 Runtime Intelligence(Dotfuscator 的一个功能性插件)将使用跟踪功能注入您的 .NET 应用程序。我们不仅提供跟踪功能的实际实施,还提供数据收集、处理和报告功能。

最近在软件业务论坛上讨论了这个主题,我也在此处发布了该主题:http ://discuss.joelonsoftware.com/default.asp?biz.5.680205.26 。

有关我们产品的高级概述,请参见此处: http: //www.preemptive.com/runtime-intelligence-services.html

此外,我目前正在编写一些更面向技术的文档,因为我们意识到这是一个我们肯定可以改进的领域,如果有人有兴趣在我完成时收到通知,请告诉我。

于 2008-10-02T21:17:23.260 回答