Инструментирование пользовательского интерфейса

Как вы инструментируете свой пользовательский интерфейс? В прошлом я читал, что люди инструментировали свои пользовательские интерфейсы, но я не нашел примеров или советов о том, как инструментировать пользовательский интерфейс.

Под инструментированием я подразумеваю сбор данных об использовании и производительности системы. Статья MSDN об инструментарии: http://msdn.microsoft.com/en-us/library/x5952w0c.aspx. Я хотел бы зафиксировать, на какие кнопки нажимают пользователи, какие сочетания клавиш они используют, какие термины используют для поиска и т. д.

  • Как вы инструментируете свой пользовательский интерфейс?
  • В каком формате вы храните инструментарий?
  • Как вы обрабатываете инструментированные данные?
  • Как вы поддерживаете чистоту своего кода пользовательского интерфейса с помощью этой инструментальной логики?

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

  • Вы инструментировали приложение WPF? Есть ли у вас какие-либо советы о том, как этого можно достичь?

Изменить: в следующем сообщении в блоге представлено интересное решение: Блог Pixel-In-Gene: методы аудита пользовательского интерфейса в приложениях WPF


person Brad Leach    schedule 11.08.2008    source источник


Ответы (7)


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

Это решение прослушивает только щелчки элементов управления, полученных из ButtonBase (Button, ToggleButton,...), и изменения выбора в элементах управления, полученных из Selector (ListBox, TabControl,...). Его должно быть легко расширить на другие типы элементов пользовательского интерфейса или получить более детальное решение. Решение основано на ответе Брэда Лича.

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 и создал отдельный регистратор с именем «Взаимодействие» для регистрации взаимодействия с пользователем. Класс «Журнал» здесь просто моя собственная статическая оболочка для 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
person angularsen    schedule 13.04.2011

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

person Brad Leach    schedule 28.08.2008
comment
EventManager.RegisterClassHandler действительно помог, когда мне нужно было вести журнал взаимодействия для нажатий кнопок, выбора списка и т. д. Требуется очень мало кода, чтобы подключиться к необходимым событиям для всего графического интерфейса. - person angularsen; 11.04.2011
comment
Я просто хотел предоставить ссылку, которая работала для меня: blog.pixelingene.com/2008/08/ - person Cyrus Downey; 20.10.2015

Вы можете использовать log4net. Это надежная структура ведения журналов, которая существует в одной библиотеке DLL. Это также делается в режиме «нетребовательного» типа, поэтому, если происходит критический процесс, он не будет записываться в журнал, пока ресурсы не будут освобождены немного больше.

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

person Dillie-O    schedule 11.08.2008

Если вы используете команды WPF, каждая пользовательская команда может регистрировать выполненное действие. Вы также можете зарегистрировать способ запуска команды.

person Brian Leahy    schedule 12.08.2008

Возможно, вам поможет Microsoft UI Automation для WPF? Это фреймворк для автоматизации вашего пользовательского интерфейса, возможно, его можно использовать для регистрации вещей для вас...

Мы используем Automation Framework для автоматического тестирования нашего пользовательского интерфейса в WPF.

person Arcturus    schedule 12.08.2008

Отказ от ответственности: я работаю в компании, которая продает этот продукт, не только это, но я разработчик этого конкретного продукта :).

Если вы заинтересованы в коммерческом продукте, обеспечивающем это, тогда доступен Runtime Intelligence (функциональное дополнение к Dotfuscator), которое внедряет функции отслеживания использования в ваши приложения .NET. Мы обеспечиваем не только фактическую реализацию функций отслеживания, но также функции сбора, обработки и отчетности.

Недавно на форуме Business of Software состоялась дискуссия по этой теме, которую я также разместил здесь: http://discuss.joelonsoftware.com/default.asp?biz.5.680205.26 .

Общий обзор наших материалов см. здесь: http://www.preemptive.com/runtime-intelligence-services.html .

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

person Joe Kuemerle    schedule 02.10.2008

Я еще не разрабатывал с использованием WPF. Но я предполагаю, что это то же самое, что и большинство других приложений, в том смысле, что вы хотите, чтобы код пользовательского интерфейса был как можно более легким. В этом может использоваться ряд шаблонов проектирования, таких как очевидный MVC и Фасад. Лично я всегда стараюсь, чтобы объекты, перемещающиеся между слоями UI и BL, были как можно более легкими, по возможности сохраняя их примитивами.

Затем это помогает мне сосредоточиться на улучшении уровня пользовательского интерфейса, не беспокоясь о том, что что-то произойдет, когда я верну свои (примитивные) данные обратно.

Надеюсь, я правильно понял ваш вопрос, и извините, я не могу предложить более контекстную помощь по WPF.

person Rob Cooper    schedule 11.08.2008