Pregunta

¿Cómo estás instrumentando tu interfaz de usuario?En el pasado he leído que la gente ha instrumentado sus interfaces de usuario, pero lo que no he encontrado son ejemplos o consejos sobre cómo para instrumentar una interfaz de usuario.

Por instrumentación me refiero a recopilar datos sobre el uso y el rendimiento del sistema.Un artículo de MSDN sobre instrumentación es http://msdn.microsoft.com/en-us/library/x5952w0c.aspx.Me gustaría capturar en qué botones hacen clic los usuarios, qué atajos de teclado usan, qué términos usan para buscar, etc.

  • ¿Cómo estás instrumentando tu interfaz de usuario?
  • ¿En qué formato estás almacenando la instrumentación?
  • ¿Cómo está procesando los datos instrumentados?
  • ¿Cómo mantiene limpio su código de interfaz de usuario con esta lógica de instrumentación?

Específicamente, estoy implementando mi interfaz de usuario en WPF, por lo que esto supondrá desafíos adicionales en comparación con la instrumentación de una aplicación basada en web.(es decir.necesidad de transferir los datos instrumentados a una ubicación central, etc.).Dicho esto, creo que la tecnología puede proporcionar una implementación más sencilla de la instrumentación a través de conceptos como propiedades adjuntas.

  • ¿Ha instrumentado una aplicación WPF?¿Tiene algún consejo sobre cómo se puede lograr esto?

Editar:La siguiente publicación de blog presenta una solución interesante: Blog de Pixel-In-Gene:Técnicas para la auditoría de UI en aplicaciones WPF

¿Fue útil?

Solución 4

La siguiente publicación de blog ofrece algunas buenas ideas para instrumentar una aplicación WPF:Técnicas para la auditoría de UI en aplicaciones WPF.

Otros consejos

A continuación se muestra un ejemplo de cómo uso un administrador de eventos simple para conectarme a los eventos de la interfaz de usuario y extraer información clave de los eventos, como el nombre y el tipo de elemento de la interfaz de usuario, el nombre del evento y el nombre del tipo de la ventana principal.Para las listas también extraigo el elemento seleccionado.

Esta solución solo escucha clics de controles derivados de ButtonBase (Button, ToggleButton,...) y cambios de selección en controles derivados de Selector (ListBox, TabControl,...).Debería ser fácil ampliarlo a otros tipos de elementos de la interfaz de usuario o lograr una solución más detallada.La solución está inspirada en La respuesta 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
}

Y para realizar el registro real, utilizo log4net y creé un registrador separado llamado 'Interacción' para registrar la interacción del usuario.La clase 'Registro' aquí es simplemente mi propio contenedor estático para 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 salida entonces se vería así, omitiendo las entradas de registro no relevantes.

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

Podrías considerar log4net.Es un marco de registro sólido que existe en una única DLL.También se realiza en un modo de tipo "no exigente", de modo que si hay un proceso crítico en marcha, no se registrará hasta que se liberen un poco más los recursos.

Podrías configurar fácilmente un montón de registradores de nivel de INFORMACIÓN y realizar un seguimiento de toda la interacción del usuario que necesitas, y no haría falta un error para enviarte el archivo a ti mismo.Luego, también puede registrar todos sus códigos de ERROR y FATAL en un archivo separado que podría enviársele fácilmente por correo para su procesamiento.

Si utiliza comandos de WPF, cada comando personalizado podría registrar la acción realizada.También puede registrar la forma en que se inició el comando.

Quizás el Automatización de la interfaz de usuario de Microsoft ¿Para WPF puede ayudar?Es un marco para automatizar su interfaz de usuario, tal vez pueda usarse para registrar cosas por usted...

Usamos Automation Framework para probar automáticamente nuestra interfaz de usuario en WPF.

Descargo de responsabilidad:Trabajo para la empresa que vende este producto, no solo eso, sino que soy desarrollador de este producto en particular :).

Si está interesado en un producto comercial que proporcione esto, entonces está disponible Runtime Intelligence (un complemento funcional de Dotfuscator) que inyecta la funcionalidad de seguimiento de uso en sus aplicaciones .NET.Proporcionamos no solo la implementación real de la funcionalidad de seguimiento, sino también la funcionalidad de recopilación, procesamiento y generación de informes de datos.

Recientemente hubo una discusión en el foro Business of Software sobre este tema que también publiqué aquí: http://discuss.joelonsoftware.com/default.asp?biz.5.680205.26 .

Para obtener una descripción general de alto nivel de nuestro material, consulte aquí: http://www.preemptive.com/runtime-intelligence-services.html .

Además, actualmente estoy trabajando en la redacción de documentación más técnica, ya que nos damos cuenta de que es un área que definitivamente podríamos mejorar. Si alguien está interesado en recibir una notificación cuando la haya completado, avíseme.

Todavía no he desarrollado usando WPF.Pero supongo que es lo mismo que la mayoría de las otras aplicaciones en el sentido de que desea mantener el código de la interfaz de usuario lo más ligero posible.Se pueden utilizar varios patrones de diseño en esto, como el obvio mvc y Fachada.Personalmente, siempre trato de mantener los objetos que viajan entre las capas UI y BL lo más livianos posible, manteniéndolos en primitivos si puedo.

Esto luego me ayuda a concentrarme en mejorar la capa de la interfaz de usuario sin preocuparme de que suceda nada una vez que devuelva mis datos (primitivos).

Espero haber entendido tu pregunta correctamente y lamento no poder ofrecer más ayuda contextual con WPF.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top