2008-08-11 80 views
7

如何檢測用戶界面?在過去我已經讀過人們已經裝備了他們的用戶界面,但是我沒有找到的是如何用來測試用戶界面的示例或提示。檢測用戶界面

通過檢測,我的意思是收集有關係統使用和性能的數據。有關Instrumentation的MSDN文章是http://msdn.microsoft.com/en-us/library/x5952w0c.aspx。我想捕捉用戶點擊哪些按鈕,他們使用哪些鍵盤短接,他們使用什麼條件進行搜索等。

  • 您是如何檢測用戶界面的?
  • 你存儲儀器的格式是什麼?
  • 你如何處理儀器化數據?
  • 你如何使用這個工具邏輯來保持你的UI代碼清潔?

具體來說,我在WPF中實現我的用戶界面,所以與提供基於Web的應用程序相比,這將提供額外的挑戰。 (即需要將儀表數據傳回中心位置等)。這就是說,我覺得這項技術可以通過像附加屬性這樣的概念提供一個更簡單的儀器實現。

  • 你裝了WPF應用程序嗎?你有什麼技巧可以實現這個目標嗎?

編輯:下面的博客文章提出了一個有趣的解決方案:Pixel-In-Gene Blog: Techniques for UI Auditing on WPF apps

回答

2

以下博客文章爲WPF應用程序提供了不少好主意: Techniques for UI Auditing on WPF apps

+0

EventManager.RegisterClassHandler真的奏效了,當我需要做一些互動日誌記錄點擊按鈕,列表選擇等非常需要一些代碼來鉤在整個GUI必要的事件。 – angularsen 2011-04-11 07:08:29

-1

我還沒有使用WPF開發的。但我會假設它一樣,你最想要的其他應用程序以保持UI代碼儘可能輕。在此可以使用許多設計模式,例如明顯的MVCFaçade。我個人總是儘量讓UI和BL層之間的物體保持儘可能的輕巧,如果可能的話,讓它們保持原始狀態。

然後,這可以幫助我集中在無任何事情,一旦我把我的(原始)數據傳回的關注提高了UI層..

我希望我理解正確的問題,抱歉,我不能提供更多的WPF的上下文幫助。

2

你可以考慮log4net。它是一個健壯的日誌框架,存在於單個DLL中。它也是在「非苛刻」類型模式下完成的,因此如果一個關鍵進程正在進行,它將不會記錄,直到資源釋放更多一點。

您可以輕鬆設置一堆INFO級別的記錄器,並跟蹤所有您需要的用戶交互,並且不會因爲將文件發送給自己而導致錯誤崩潰。您也可以將所有ERROR和FATAL代碼記錄到可以輕鬆郵寄給您處理的單獨文件中。

2

如果您使用WPF命令,則每個自定義命令都可以記錄所採取的操作。您還可以記錄命令啓動的方式。

0

也許WPF的Microsoft UI Automation可以幫忙嗎?它是一個用於自動化UI的框架,也許它可以用來爲你記錄東西......

我們使用自動化框架自動測試WPF中的UI。

0

聲明:我爲銷售該產品的公司工作,不僅如此,而且我是這款產品的開發人員:)。

如果您對提供此商業產品感興趣,那麼可以在您的.NET應用程序中注入使用情況跟蹤功能的Runtime Intelligence(Dotfuscator的一種功能性附加功能)。我們不僅提供跟蹤功能的實際實施,還提供數據收集,處理和報告功能。

最近有一個關於這個話題的軟件商業論壇的討論,我也發佈在這裏:http://discuss.joelonsoftware.com/default.asp?biz.5.680205.26

對於我們的東西的高層次概述看到這裏:http://www.preemptive.com/runtime-intelligence-services.html

此外,我目前正在編寫一些更技術化的文檔,因爲我們認識到這是一個我們可以改進的領域,請讓我知道是否有人有興趣在我完成它時收到通知。

3

下面是我如何使用簡單的事件管理器鉤住UI事件並提取事件的關鍵信息(如UI元素的名稱和類型,事件名稱以及父窗口的類型名稱)的示例。對於列表我也提取選定的項目。

此解決方案只偵聽從ButtonBase(Button,ToggleButton,...)派生的控件的點擊和從Selector(ListBox,TabControl,...)派生的控件中的選擇更改。應該很容易擴展到其他類型的UI元素或實現更細粒度的解決方案。該解決方案受到Brad Leach's answer的啓發。

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 AnalysisButton in MyMainWindow 
04/13 08:38:38.493 INFO  Iact ListBox.SelectionChanged by ListView in MyMainWindow. Selected: Andreas Larsen 
04/13 08:38:44.587 INFO  Iact Button.Click by EditEntryButton in MyMainWindow 
04/13 08:38:46.068 INFO  Iact Button.Click by OkButton in EditEntryDialog 
04/13 08:38:47.395 INFO  Iact ToggleButton.Click by ExitButton in MyMainWindow