Frage

Wie instrumentieren Sie Ihre Benutzeroberflächen?In der Vergangenheit habe ich gelesen, dass Leute ihre Benutzeroberflächen instrumentiert haben, aber Beispiele oder Tipps dazu habe ich nicht gefunden Wie um eine Benutzeroberfläche zu instrumentieren.

Mit Instrumentierung meine ich das Sammeln von Daten zur Nutzung und Leistung des Systems.Ein MSDN-Artikel zur Instrumentierung ist http://msdn.microsoft.com/en-us/library/x5952w0c.aspx.Ich möchte erfassen, auf welche Schaltflächen Benutzer klicken, welche Tastaturkürzel sie verwenden, welche Begriffe sie zum Suchen verwenden usw.

  • Wie instrumentieren Sie Ihre Benutzeroberfläche?
  • In welchem ​​Format speichern Sie die Instrumentierung?
  • Wie verarbeiten Sie die instrumentierten Daten?
  • Wie halten Sie Ihren UI-Code mit dieser Instrumentierungslogik sauber?

Konkret implementiere ich meine Benutzeroberfläche in WPF, sodass dies im Vergleich zur Instrumentierung einer webbasierten Anwendung zusätzliche Herausforderungen mit sich bringt.(d. h.die instrumentierten Daten zurück an einen zentralen Ort übertragen werden müssen usw.).Dennoch bin ich der Meinung, dass die Technologie über Konzepte wie angehängte Eigenschaften eine einfachere Implementierung der Instrumentierung ermöglichen könnte.

  • Haben Sie eine WPF-Anwendung instrumentiert?Habt ihr Tipps, wie das gelingen kann?

Bearbeiten:Der folgende Blogbeitrag stellt eine interessante Lösung vor: Pixel-In-Gene-Blog:Techniken für die UI-Überwachung von WPF-Apps

War es hilfreich?

Lösung 4

Der folgende Blogbeitrag gibt einige gute Ideen für die Instrumentierung einer WPF-Anwendung:Techniken für die UI-Überwachung von WPF-Apps.

Andere Tipps

Hier ist ein Beispiel dafür, wie ich einen einfachen Ereignismanager verwende, um eine Verbindung zu den UI-Ereignissen herzustellen und Schlüsselinformationen der Ereignisse zu extrahieren, wie z. B. Name und Typ des UI-Elements, Name des Ereignisses und Typname des übergeordneten Fensters.Bei Listen extrahiere ich auch das ausgewählte Element.

Diese Lösung lauscht nur auf Klicks von von ButtonBase abgeleiteten Steuerelementen (Button, ToggleButton, ...) und auf Auswahländerungen in von Selector abgeleiteten Steuerelementen (ListBox, TabControl, ...).Es sollte einfach sein, es auf andere Arten von UI-Elementen zu erweitern oder eine detailliertere Lösung zu erreichen.Die Lösung ist inspiriert von Brad Leachs Antwort.

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
}

Und um die eigentliche Protokollierung durchzuführen, verwende ich log4net und habe einen separaten Logger namens „Interaction“ erstellt, um die Benutzerinteraktion zu protokollieren.Die Klasse „Log“ hier ist einfach mein eigener statischer Wrapper für 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);
    }
}

Die Ausgabe würde dann etwa so aussehen, wobei nicht relevante Protokolleinträge weggelassen würden.

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

Sie könnten darüber nachdenken log4net.Es handelt sich um ein robustes Protokollierungsframework, das in einer einzigen DLL vorhanden ist.Dies erfolgt auch in einem „nicht fordernden“ Modus, sodass bei einem kritischen Prozess keine Protokollierung erfolgt, bis die Ressourcen etwas mehr freigegeben sind.

Sie könnten problemlos eine Reihe von Loggern auf INFO-Ebene einrichten und alle benötigten Benutzerinteraktionen verfolgen, und es wäre kein Fehlerabsturz erforderlich, um die Datei an sich selbst zu senden.Sie könnten dann auch alle Ihre ERROR- und FATAL-Codes in einer separaten Datei protokollieren, die Ihnen zur Bearbeitung einfach per Post zugesandt werden könnte.

Wenn Sie WPF-Befehle verwenden, könnte jeder benutzerdefinierte Befehl dann die durchgeführte Aktion protokollieren.Sie können auch protokollieren, wie der Befehl initiiert wurde.

Vielleicht der Microsoft-UI-Automatisierung denn WPF kann helfen?Es ist ein Framework zur Automatisierung Ihrer Benutzeroberfläche. Vielleicht kann es zum Protokollieren von Dingen für Sie verwendet werden ...

Wir verwenden das Automation Framework zum automatischen Testen unserer Benutzeroberfläche in WPF.

Haftungsausschluss:Ich arbeite für das Unternehmen, das dieses Produkt verkauft, und nicht nur das, sondern ich bin auch Entwickler dieses speziellen Produkts :) .

Wenn Sie an einem kommerziellen Produkt interessiert sind, das dies bietet, steht Ihnen Runtime Intelligence (ein funktionales Add-on zu Dotfuscator) zur Verfügung, das Nutzungsverfolgungsfunktionen in Ihre .NET-Anwendungen einfügt.Wir bieten nicht nur die eigentliche Implementierung der Tracking-Funktionalität, sondern auch die Datenerfassungs-, Verarbeitungs- und Berichtsfunktionen.

Kürzlich gab es im Business of Software-Forum eine Diskussion zu diesem Thema, die ich auch hier gepostet habe: http://discuss.joelonsoftware.com/default.asp?biz.5.680205.26 .

Einen allgemeinen Überblick über unsere Produkte finden Sie hier: http://www.preemptive.com/runtime-intelligence-services.html .

Darüber hinaus arbeite ich derzeit an der Erstellung einer eher technisch orientierten Dokumentation, da wir erkennen, dass dies ein Bereich ist, den wir definitiv verbessern können. Bitte teilen Sie mir mit, ob jemand daran interessiert ist, benachrichtigt zu werden, wenn ich die Dokumentation fertiggestellt habe.

Ich habe noch nicht mit WPF entwickelt.Aber ich gehe davon aus, dass es dasselbe ist wie bei den meisten anderen Anwendungen, da Sie den UI-Code so einfach wie möglich halten möchten.Hierbei können eine Reihe von Entwurfsmustern verwendet werden, beispielsweise das Offensichtliche MVC Und Fassade.Ich persönlich versuche immer, die Objekte, die sich zwischen der UI- und der BL-Ebene bewegen, so leicht wie möglich zu halten und sie, wenn möglich, auf Grundelemente zu beschränken.

Dies hilft mir dann, mich auf die Verbesserung der UI-Ebene zu konzentrieren, ohne mir Sorgen machen zu müssen, dass irgendetwas passiert, wenn ich meine (primitiven) Daten zurückwerfe.

Ich hoffe, ich habe Ihre Frage richtig verstanden und kann leider keine weitere kontextbezogene Hilfe zu WPF anbieten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top