Question

Comment instrumentez-vous votre interface utilisateur ?Dans le passé, j'ai lu que des gens instrumentaient leurs interfaces utilisateur, mais ce que je n'ai pas trouvé, ce sont des exemples ou des conseils sur la façon de procéder. comment pour instrumenter une interface utilisateur.

Par instrumentation, j'entends collecter des données concernant l'utilisation et les performances du système.Un article MSDN sur l'instrumentation est http://msdn.microsoft.com/en-us/library/x5952w0c.aspx.Je voudrais capturer les boutons sur lesquels les utilisateurs cliquent, les raccourcis clavier qu'ils utilisent, les termes qu'ils utilisent pour rechercher, etc.

  • Comment instrumentez-vous votre interface utilisateur ?
  • Sous quel format stockez-vous les instruments ?
  • Comment traitez-vous les données instrumentées ?
  • Comment gardez-vous votre code d'interface utilisateur propre avec cette logique d'instrumentation ?

Plus précisément, j'implémente mon interface utilisateur dans WPF, ce qui entraînera des défis supplémentaires par rapport à l'instrumentation d'une application Web.(c'est à dire.besoin de transférer les données instrumentées vers un emplacement central, etc.).Cela dit, je pense que la technologie peut faciliter la mise en œuvre de l'instrumentation via des concepts tels que les propriétés attachées.

  • Avez-vous instrumenté une application WPF ?Avez-vous des conseils sur la manière d'y parvenir ?

Modifier:Le billet de blog suivant présente une solution intéressante : Blog Pixel-In-Gene :Techniques d'audit de l'interface utilisateur sur les applications WPF

Était-ce utile?

La solution 4

Le billet de blog suivant donne quelques bonnes idées pour instrumenter une application WPF :Techniques d'audit de l'interface utilisateur sur les applications WPF.

Autres conseils

Voici un exemple de la façon dont j'utilise un gestionnaire d'événements simple pour me connecter aux événements de l'interface utilisateur et extraire les informations clés des événements, telles que le nom et le type d'élément d'interface utilisateur, le nom de l'événement et le nom du type de la fenêtre parent.Pour les listes, j'extrais également l'élément sélectionné.

Cette solution n'écoute que les clics des contrôles dérivés de ButtonBase (Button, ToggleButton, ...) et les modifications de sélection dans les contrôles dérivés de Selector (ListBox, TabControl, ...).Il devrait être facile de l'étendre à d'autres types d'éléments d'interface utilisateur ou d'obtenir une solution plus fine.La solution s'inspire de La réponse de 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
}

Et pour effectuer la journalisation proprement dite, j'utilise log4net et j'ai créé un enregistreur distinct nommé « Interaction » pour enregistrer l'interaction de l'utilisateur.La classe 'Log' ici est simplement mon propre wrapper statique pour 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);
    }
}

La sortie ressemblerait alors à ceci, en omettant les entrées de journal non pertinentes.

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

Vous pourriez envisager log4net.Il s'agit d'un cadre de journalisation robuste qui existe dans une seule DLL.Cela se fait également dans un mode de type "non exigeant", de sorte que si un processus critique est en cours, il ne sera pas enregistré tant que les ressources ne seront pas libérées un peu plus.

Vous pouvez facilement configurer un ensemble d'enregistreurs de niveau INFO et suivre toutes les interactions utilisateur dont vous avez besoin, et il ne faudrait pas un crash de bug pour vous envoyer le fichier.Vous pouvez également enregistrer tous vos codes ERREUR et FATAL dans un fichier séparé qui pourrait facilement vous être envoyé par courrier pour traitement.

Si vous utilisez des commandes WPF, chaque commande personnalisée peut alors enregistrer l'action entreprise.Vous pouvez également enregistrer la manière dont la commande a été lancée.

Peut-être le Automatisation de l'interface utilisateur Microsoft pour WPF peut-il aider ?C'est un cadre pour automatiser votre interface utilisateur, il peut peut-être être utilisé pour enregistrer des éléments pour vous...

Nous utilisons Automation Framework pour tester automatiquement notre interface utilisateur dans WPF.

Clause de non-responsabilité:Je travaille pour l'entreprise qui vend ce produit, non seulement cela, mais je suis développeur sur ce produit particulier :) .

Si vous êtes intéressé par un produit commercial offrant cela, Runtime Intelligence (un complément fonctionnel à Dotfuscator) qui injecte une fonctionnalité de suivi de l'utilisation dans vos applications .NET est disponible.Nous fournissons non seulement la mise en œuvre réelle de la fonctionnalité de suivi, mais également la fonctionnalité de collecte, de traitement et de reporting des données.

Il y a eu récemment une discussion sur le forum Business of Software sur ce sujet que j'ai également posté ici : http://discuss.joelonsoftware.com/default.asp?biz.5.680205.26 .

Pour un aperçu de haut niveau de nos produits, voir ici : http://www.preemptive.com/runtime-intelligence-services.html .

De plus, je travaille actuellement sur la rédaction d'une documentation plus technique, car nous réalisons qu'il s'agit d'un domaine que nous pourrions certainement améliorer. Veuillez me faire savoir si quelqu'un souhaite être averti lorsque je l'aurai terminé.

Je n'ai pas encore développé avec WPF.Mais je suppose que c'est la même chose que la plupart des autres applications dans la mesure où vous souhaitez garder le code de l'interface utilisateur aussi léger que possible.Un certain nombre de modèles de conception peuvent être utilisés à cet effet, comme le modèle évident MVC et Façade.Personnellement, j'essaie toujours de garder les objets voyageant entre les couches UI et BL aussi légers que possible, en les gardant au niveau primitif si je le peux.

Cela m'aide ensuite à me concentrer sur l'amélioration de la couche d'interface utilisateur sans craindre que quoi que ce soit ne se passe une fois que j'ai renvoyé mes données (primitives).

J'espère avoir bien compris votre question et désolé, je ne peux pas offrir une aide plus contextuelle avec WPF.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top