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

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

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

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

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

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

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

Ответов (7)

Решение

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

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

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

Это решение прослушивает только щелчки элементов управления, производных от 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 и создал отдельный регистратор с именем «Взаимодействие» для регистрации взаимодействия с пользователем. Класс 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);
    }
}

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

13.04.08 08: 38: 37.069 ИНФОРМАЦИЯ Iact ToggleButton.Click by AnalysisButton в MyMainWindow
13.04.08 08: 38: 38.493 ИНФОРМАЦИЯ Iact ListBox.SelectionChanged by ListView в MyMainWindow. Выбрано: Андреас Ларсен
13.04.08 08: 38: 44.587 INFO Iact Button.Щелкните кнопкой EditEntryButton в MyMainWindow
13.04.08 08: 38: 46.068 INFO Iact Button.Щелкните OkButton в EditEntryDialog
13.04.08 08: 38: 47.395 ИНФОРМАЦИЯ Iact ToggleButton.Click by ExitButton в MyMainWindow

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

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

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

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

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

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

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

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

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

Недавно на форуме «Бизнес программного обеспечения» было обсуждение этой темы, которое я также разместил здесь: http://discuss.joelonsoftware.com/default.asp?biz.5.680205.26 .

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

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