In Fortsetzung unserer WPF-Themenreihe lade ich Sie ein, diese Sammlung von sieben Best Practices durchzugehen.
Ganz gleich, ob Sie mit einer neuen oder einer bestehenden Anwendung beginnen, es kann nicht schaden, sich der Leistung Ihres Codes bewusst zu sein und zu wissen, was in dieser Richtung getan werden kann.
All dies sind Erkenntnisse, die ich im Laufe der Zeit gewonnen habe, als ich sah, wie bestimmte von mir geschriebene Codeteile funktionierten. Einige von ihnen konnte ich vom Standpunkt der Leistung aus verbessern. Andere wiederum vom Standpunkt des Codes oder sogar der Logik aus gesehen.
Hier sind sie:
#1 IsVisible Boolesche Eigenschaft
Wenn Sie die Sichtbarkeit eines UIElements aktualisieren müssen, ist die Standardempfehlung, die Sie (z. B. auf StackOverflow) finden, die Verwendung der booleschen Eigenschaft im Ansichtsmodell „IsVisible“ und eines Konverters, in dem Sie das boolesche IsVisible in Visibility.Visible oder Visibilty.Collapsed umwandeln.
Falscher Ansatz
Die Verwendung eines Konverters bedeutet zusätzlichen Code. Um eine Eigenschaft sichtbar oder unsichtbar zu machen, müssen Sie einen Konverter aufrufen, den Sie instanziieren müssen. Das ist zeit- und energieaufwendig.
Optimaler Ansatz
Die optimale und logischere Lösung wäre es, genau die Daten zu verwenden, die Sie benötigen, d. h. direkt den Sichtbarkeitstyp für die Eigenschaft IsVisible im Ansichtsmodell zu verwenden. Sie müssen das Rad nicht neu erfinden.
#2 Aktualisieren Sie nur, wenn es nötig ist
Manchmal fängt ein Benutzer an, einen Wert in einem Feld zu ändern, nur um ihn dann wieder zu ändern. In diesem Fall besteht keine Notwendigkeit, eine Aktualisierung an die Datenbank zu senden, da sich nichts geändert hat. Ich habe jedoch festgestellt, dass die am häufigsten verwendete Vorgehensweise die folgende ist:
Falscher Ansatz
Dies ist nicht gerade der optimale Ansatz, da die Aktualisierung unabhängig von der Änderung der Eigenschaft gesendet wird.
Optimaler Ansatz
Die bessere Methode wäre, wenn der neu eingegebene Wert gleich dem alten ist, dass keine Aktualisierung gesendet wird:
Optimaler Ansatz II
Als zweite gute Möglichkeit bietet Microsoft die Klasse BindableBase an, die den gesamten Code in einer Zeile behandelt.
Sie können also BindableBase von Prism verwenden und das Ereignis über die Methode SetProperty auslösen. Wenn BindableBase für Ihr aktuelles Projekt nicht verfügbar ist, implementieren Sie einfach in einer Basisklasse die SetProperty-Methode.
#3 Allgemeine Stile
Mittlerweile ist es vernünftig geworden, alle Stile an einem Ort zu haben. Wenn Sie sie über alle Dateien Ihrer Anwendung verstreut haben, müssen Sie, wenn Sie zum Beispiel alle Schaltflächen grün statt grau darstellen wollen, Änderungen an 20, 50 oder 100 verschiedenen Stellen vornehmen. Wenn Sie die Stile in einer einzigen Datei haben und dieser Stil von dort aus von jedem Element aufgerufen wird, das ihn benötigt, können Sie die Änderung an einer Stelle vornehmen, und dann wird sie überall, wo der Stil verwendet wird, übernommen.
Das spart nicht nur Arbeit, sondern verhindert auch, dass Sie doppelten Code verwenden. Verschieben Sie allgemeine Stile, Farben oder Daten in Ressourcendictionaries und Dictionaries, die in der App.xaml hinzugefügt werden, und verwenden Sie von dort aus StaticResource, um darauf zu verweisen. Dies ist auch nützlich, wenn Sie Themes haben.
#4 Konverter
Bei Konvertern sehe ich nicht unbedingt einen schlechten oder optimalen Ansatz. Sie können sie entweder von MarkupExtension ableiten – einer Klasse von Microsoft – und sie direkt dort verwenden, wo Sie sie brauchen; oder Sie können eine Instanz in der Ressource-Region des Steuerelements erstellen und sie dann mit Resource referenzieren – Sie erhalten das gleiche Ergebnis, aber der XAML-Code wird schwerer zu lesen sein.
Erster Ansatz
Der Konverter ist nicht von MarkupExtension abgeleitet. Sie müssen diese Instanziierung von zusätzlichem Code in XAML durchführen, und dies „verschmutzt“ die Datei mit nutzlosem Code, wie im folgenden Beispiel:
Zweiter Ansatz
Wenn Sie die MarkupExtension verwenden, können Sie sie direkt in XAML verwenden, ohne dass Sie weitere 2-3 Zeilen Code benötigen. Auf diese Weise wird jeder Konverter, der mit dieser Methode durchgeführt wird, direkt dort verwendet, wo Sie ihn benötigen. Dies würde sich in leichterem, besser lesbarem und weniger unübersichtlichem Code niederschlagen, wie im nächsten Beispiel:
Keine der beiden Methoden ist ein Fehler, es liegt an den Vorlieben des Programmierers. Aber diese Auszeichnungserweiterung ist auf Stack Overflow nicht sehr beliebt.
Dritter Ansatz
Wenn Sie noch effizienter sein wollen, können Sie einen Konverter mit MarkupExtension zu einem Ressourcenwörterbuch hinzufügen – auf diese Weise wird er nur einmal instanziert. Wie das folgende:
#5 Verzögerungen bei der Bindung
Dies ist nützlich für Textfelder. Standardmäßig sendet die App bei jedem eingegebenen Buchstaben die Information an die Datenbank, dass ein weiterer Buchstabe zu dem eingegebenen Wort hinzugefügt wurde. Wenn Sie jedoch eine Bindungsverzögerung von 1 bis 2 Sekunden einstellen, wird je nach Bedarf nur alle 2 Sekunden eine Aktualisierung an die Datenbank gesendet. Und in zwei Sekunden hat der Benutzer Zeit, sechs bis sieben Zeichen zu tippen. Dies bedeutet eine Aktualisierung anstelle von bis zu 6 Aktualisierungen alle 2 Sekunden bei Verwendung von Bindungsverzögerungen.
Dies führt zu einer besseren Leistung, weniger Energieverbrauch usw.
#6 Auslöser vs. VisualStates
Auch hier geht es nicht um die Frage, ob ein Ansatz gut oder schlecht ist. In den meisten Fällen sind Trigger der einfachste Weg, um einen visuellen Effekt zu erzielen, aber in einigen Fällen führt dies zu einer schlechten Leistung (z. B. führt der Mauszeiger über eine Liste von Elementen via Trigger zu einer „Geister-/Verzögerungsmarkierung“, während der VisualStates-Mechanismus die Markierung verbessert). Bedenken Sie also, dass VisualStates in diesem Fall der bessere Ansatz sind.
Hier sind einige Beispiele für einen einfachen Vergleich:
Verwendung von Triggern
Trigger sind viel einfacher zu verwenden (3 Zeilen Code). Das wäre einfacher zu schreiben:
Verwendung von VisualStates
VisualStates sind nativer in WPF als Triggers. Sie erfordern mehr Code, aber sie sind schneller und leistungsfähiger. Der Vorteil ist, dass ein Teil dieses Codes Standard ist:
Ich hatte z. B. eine Situation, in der ich einen Geisterschatten verwenden musste, und ich konnte den Unterschied deutlich sehen, da die Hervorhebung weit hinter der Maus zurückblieb, was man nicht möchte.
Manchmal kommt es auf den Test an: Wenn die Verzögerung für den Benutzer nicht spürbar ist, kann es in Ordnung sein, Auslöser zu verwenden, die weniger Code erfordern. Aber in Fällen, in denen die Leistung wichtig ist, ist es besser, visuelle Zustände zu verwenden.
#7 TemplateBinding
Meiner Meinung nach ist es am besten, TemplateBinding zu verwenden, wenn man innerhalb eines Templates arbeitet. Andernfalls können einige UI-Bugs erzeugt werden (UI-Inkonsistenz).
TemplateBinding ermöglicht es Ihnen, eine Schaltfläche an einige Eigenschaften innerhalb der Schaltfläche zu binden.
Innerhalb der Schaltfläche besteht das Styling aus mehreren Elementen: einem Rahmen, innerhalb des Rahmens einem Panel, innerhalb des Panels einem Symbol und innerhalb des Symbols einem Textfeld. Wenn wir einen roten Hintergrund für die Schaltfläche festlegen, müssen wir bei der Definition des Rahmens und des Feldes die Farben für jedes Element festlegen.
Hier sind zwei Beispiele, um dies zu veranschaulichen:
Schlechter Ansatz
Richtiger Ansatz
#… Die Liste ist offen
Dies sind natürlich nur einige der besseren Alternativen, die Sie beim Schreiben Ihres Codes verwenden können. Wenn Sie auf andere Beispiele für leistungsorientierte WPF-Code-Schreibtipps gestoßen sind oder eine andere Sichtweise als die in der aktuellen Liste teilen möchten, tun Sie dies bitte im Kommentarbereich unten.