2008-08-11 11 views
7

¿Cómo está instrumentando las IU? En el pasado he leído que las personas han instrumentado sus interfaces de usuario, pero lo que no he encontrado son ejemplos o consejos sobre cómo para instrumentar una interfaz de usuario.Instrumentación de una IU

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

  • ¿Cómo está instrumentando su UI?
  • ¿Qué formato está almacenando la instrumentación?
  • ¿Cómo está procesando los datos instrumentados?
  • ¿Cómo se mantiene el código de UI limpio con esta lógica de instrumentación?

Específicamente, estoy implementando mi UI en WPF, por lo que esto proporcionará desafíos adicionales en comparación con la instrumentación de una aplicación basada en la web. (es decir, es necesario 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 entrada del blog presenta una solución interesante: Pixel-In-Gene Blog: Techniques for UI Auditing on WPF apps

Respuesta

2

La siguiente entrada de blog ofrece bastantes buenas ideas para instrumentar una aplicación WPF: Techniques for UI Auditing on WPF apps.

+0

EventManager.RegisterClassHandler realmente hizo el truco cuando tenía que hacer algún tipo de interacción registro para clics de los botones, selecciones de la lista etc. Se necesita muy poco código para enganchar a los eventos necesarios para toda la interfaz gráfica de usuario. – angularsen

+0

Solo quería proporcionar un enlace que funcionó para mí: http://blog.pixelingene.com/2008/08/techniques-for-ui-auditing-on-wpf-apps/ –

-1

aún no he desarrollado utilizando WPF .. Pero yo asumiría que su lo mismo que la mayoría de las otras aplicaciones en que se desea para mantener el código de la interfaz de usuario lo más ligero posible. Se pueden utilizar varios patrones de diseño en este caso, como el obvio MVC y el Façade. Personalmente siempre trato de mantener los objetos que viajan entre las capas UI y BL lo más ligeros posible, manteniéndolos a primitivos si puedo.

Esto ayuda a concentrarme en mejorar la capa de interfaz de usuario, sin la preocupación de que algo salga de una vez que lanzo mis datos (primitivos) de vuelta ..

espero que entendí bien su pregunta, y lo siento, no puede ofrecer más ayuda contextual con WPF.

2

Puede considerar log4net. Es un marco de registro robusto que existe en una sola DLL. También se realiza en un modo de tipo "no exigente", de modo que si se está llevando a cabo un proceso crítico, no se registrará hasta que los recursos se liberen un poco más.

Puede configurar fácilmente un grupo de registradores de nivel INFO y rastrear toda la interacción del usuario que necesita, y no se necesitaría un error para enviar el archivo a usted mismo. También puede registrar todo su código ERROR y FATAL para separar el archivo que podría enviarse fácilmente para su procesamiento.

2

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

0

¿Quizás el Microsoft UI Automation para WPF puede ayudar? Es un marco para automatizar su interfaz de usuario, quizás se puede usar para registrar cosas para usted ...

Usamos el Marco de automatización para autoevaluar nuestra interfaz de usuario en WPF.

0

Descargo de responsabilidad: Yo trabajo para la compañía que vende este producto, no solo eso, sino que soy un desarrollador de este producto en particular :).

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

Hubo recientemente una discusión sobre 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 nuestras cosas, consulte aquí: http://www.preemptive.com/runtime-intelligence-services.html.

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

3

Aquí hay un ejemplo de cómo uso un gestor de eventos simple para enlazar a los eventos de UI y extraer información clave de los eventos, como nombre y tipo de elemento de UI, nombre del evento y nombre de tipo de ventana principal. Para las listas, también extraigo el elemento seleccionado.

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

Y para hacer el registro real, yo uso log4net y creé un registrador por separado llamada 'interacción' para registrar la interacción del usuario. La clase 'Log' aquí es simplemente mi propia envoltura estática 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 se vería así, omitiendo 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 
Cuestiones relacionadas