Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Wenn Sie das systeminterne Verhalten von WPF-Objekten verstehen, können Sie die richtigen Kompromisse zwischen Funktionalität und Leistung erzielen.
Wenn Ereignishandler nicht aus Objekten entfernt werden, bleiben diese möglicherweise aktiv
Der Delegat, den ein Objekt an sein Ereignis übergibt, ist gewissermaßen ein Verweis auf dieses Objekt. Daher können Ereignishandler Objekte länger lebendig halten als erwartet. Wenn ein Objekt bereinigt wird, das registriert wurde, um auf ein Ereignis eines Objekts zu lauschen, ist es erforderlich, dass Sie diesen Delegaten entfernen, bevor Sie das Objekt freigeben. Das Beibehalten nicht benötigter Objekte erhöht die Speicherauslastung der Anwendung. Dies gilt insbesondere, wenn das Objekt der Stamm einer logischen Struktur oder einer visuellen Struktur ist.
WPF führt ein schwaches Ereignislistenermuster für Ereignisse ein, das dann nützlich sein kann, wenn die Beziehungen der Objektlebensdauer zwischen Quelle und Listener schwierig zu überwachen sind. Einige vorhandene WPF-Ereignisse verwenden dieses Muster. Wenn Sie Objekte mit benutzerdefinierten Ereignissen implementieren, könnte dieses Muster für Sie nützlich sein. Weitere Informationen finden Sie unter Schwache Ereignismuster.
Es gibt mehrere Tools, z. B. clR Profiler und den Working Set Viewer, die Informationen zur Speicherauslastung eines bestimmten Prozesses liefern können. Der CLR-Profiler umfasst eine Reihe sehr nützlicher Ansichten des Belegungsprofils, darunter z. B. ein Histogramm der zugewiesenen Typen, Zuordnungs- und Aufrufdiagramme, eine Zeitachse mit den Garbage Collections verschiedener Generationen und dem daraus entstehenden Status des verwalteten Heaps nach diesen Bereinigungen sowie eine Aufrufstruktur, die Zuweisungen und Laden von Assemblys für jede Methode veranschaulicht. Weitere Informationen finden Sie unter Performance.
Anhängigkeitseigenschaften und Objekte
Im Allgemeinen ist der Zugriff auf eine Abhängigkeitseigenschaft eines DependencyObject nicht langsamer als der Zugriff auf eine CLR-Eigenschaft. Während beim Festlegen eines Eigenschaftswerts ein geringer Leistungsaufwand besteht, ist das Abrufen eines Werts so schnell wie das Abrufen des Werts aus einer CLR-Eigenschaft. Der geringe Leistungsaufwand wird dadurch ausgeglichen, dass Abhängigkeitseigenschaften robuste Funktionen wie Datenbindung, Animation, Vererbung und Formatierung unterstützen. Weitere Informationen finden Sie unter Übersicht über Abhängigkeitseigenschaften.
DependencyProperty-Optimierungen
Sie sollten Abhängigkeitseigenschaften in Ihrer Anwendung sehr sorgfältig definieren. Wenn Ihre DependencyProperty nur Rendertyp-Optionen für Metadaten statt anderer Metadatenoptionen wie z. B. AffectsMeasure betrifft, sollten Sie sie entsprechend kennzeichnen, indem Sie ihre Metadaten überschreiben. Weitere Informationen über das Überschreiben oder Abrufen von Eigenschaftenmetadaten finden Sie unter Metadaten für Abhängigkeitseigenschaften.
Möglicherweise ist es effizienter, mit einem Handler für Eigenschaftenänderungen die Messungs-, Anordnungs- und Rendering-Durchläufe manuell für ungültig zu erklären, wenn nicht alle Eigenschaftenänderungen „Messung“, „Anordnung“ und „Rendering“ betreffen. So können Sie beispielsweise festlegen, dass ein Hintergrund nur dann erneut gerendert wird, wenn ein Wert größer als ein festgelegter Grenzwert ist. In diesem Fall würde Ihr Handler für Eigenschaftenänderungen nur „render“ für ungültig erklären, wenn der festgelegte Grenzwert überschritten wird.
Sie können eine Abhängigkeitseigenschaft nicht frei vererben
Standardmäßig sind registrierte Abhängigkeitseigenschaften nicht vererbbar. Sie können jedoch jede Eigenschaft explizit vererbbar machen. Obwohl dies nützlich ist, beeinträchtigt das Konvertieren einer Eigenschaft in eine vererbbare Eigenschaft die Leistung, da die Zeit für das Aufheben der Validierung erhöht wird.
RegisterClassHandler sorgfältig verwenden
Wenn Sie RegisterClassHandler aufrufen, können Sie den Status Ihrer Instanz speichern. Es ist jedoch wichtig zu beachten, dass der Handler für jede Instanz aufgerufen wird, was zu Leistungsproblemen führen kann. Verwenden Sie RegisterClassHandler nur, wenn Ihre Anwendung erfordert, dass Sie den Instanzstatus speichern.
Legen Sie den Standardwert für eine Abhängigkeitseigenschaft während der Registrierung fest
Wenn Sie eine DependencyProperty erstellen, die einen Standardwert erfordert, legen Sie diesen mithilfe der als Parameter an die Methode Register der DependencyPropertyübergebenen Standardmetadaten fest. Nutzen Sie diese Vorgehensweise, statt den Eigenschaftswert in einem Konstruktor oder in jeder Instanz des Elements festzulegen.
Den PropertyMetadata-Wert mithilfe von 'Register' festlegen
Bei der Erstellung von DependencyProperty haben Sie die Möglichkeit, PropertyMetadata entweder mit der Methode Register oder OverrideMetadata festzulegen. Obwohl Ihr Objekt einen statischen Konstruktor zum Aufrufen von OverrideMetadatahaben könnte, ist dies nicht die optimale Lösung und beeinträchtigt die Leistung. Für eine optimale Leistung legen Sie PropertyMetadata während des Aufrufs auf Register fest.
Gefrierbare Objekte
Ein Freezable ist eine besondere Art von Objekt, das zwei Zustände hat: nicht fixiert und fixiert. Wenn Sie Objekte immer dann, wenn es möglich ist, fixieren, wird dadurch die Leistung Ihrer Anwendung verbessert und ihr Arbeitssatz reduziert. Weitere Informationen finden Sie unter der Übersicht über Freezable-Objekte.
Jedes Freezable hat ein Changed-Ereignis, das ausgelöst wird, wenn es sich ändert. Änderungsbenachrichtigungen sind jedoch in Bezug auf die Anwendungsleistung kostspielig.
Betrachten Sie das folgende Beispiel, in dem jedes Rectangle dasselbe Brush-Objekt verwendet:
rectangle_1.Fill = myBrush;
rectangle_2.Fill = myBrush;
rectangle_3.Fill = myBrush;
// ...
rectangle_10.Fill = myBrush;
rectangle_1.Fill = myBrush
rectangle_2.Fill = myBrush
rectangle_3.Fill = myBrush
' ...
rectangle_10.Fill = myBrush
WPF stellt standardmäßig einen Ereignishandler für das Changed-Ereignis des SolidColorBrush Objekts bereit, um die Fill-Eigenschaft des Rectangle Objekts ungültig zu machen. In diesem Fall muss die SolidColorBrush die Rückruffunktion für jedes Changed jedes Mal abrufen, wenn sie ihr Rectangle-Ereignis auslöst. Die Anhäufung dieser Aufrufe der Rückruffunktion führt zu deutlichen Leistungseinbußen. Darüber hinaus ist es sehr leistungsintensiv, an diesem Punkt Handler hinzuzufügen und zu entfernen, da die Anwendung die gesamte Liste durchlaufen müsste, um dies zu tun. Wenn in Ihrem Anwendungsszenario die SolidColorBrush-Klasse nie geändert wird, müssen Sie die Konsequenzen für die unnötige Wartung von Changed-Ereignishandlern tragen.
Das Fixieren eines Freezable-Objekts kann dessen Leistung verbessern, da es keine Ressourcen für die Verwaltung Änderungsbenachrichtigungen mehr aufwenden muss. Die folgende Tabelle zeigt die Größe einer einfachen SolidColorBrush, wenn deren IsFrozen-Eigenschaft auf true
festgelegt ist, und wenn deren Eigenschaft nicht festgelegt ist. Dies setzt voraus, dass Sie einen Pinsel auf die Fill-Eigenschaft von zehn Rectangle-Objekten anwenden.
Zustand | Größe |
---|---|
SolidColorBrush fixiert | 212 Bytes |
SolidColorBrush nicht fixiert | 972 Bytes |
Im folgenden Codebeispiel wird dieses Konzept veranschaulicht:
Brush frozenBrush = new SolidColorBrush(Colors.Blue);
frozenBrush.Freeze();
Brush nonFrozenBrush = new SolidColorBrush(Colors.Blue);
for (int i = 0; i < 10; i++)
{
// Create a Rectangle using a non-frozed Brush.
Rectangle rectangleNonFrozen = new Rectangle();
rectangleNonFrozen.Fill = nonFrozenBrush;
// Create a Rectangle using a frozed Brush.
Rectangle rectangleFrozen = new Rectangle();
rectangleFrozen.Fill = frozenBrush;
}
Dim frozenBrush As Brush = New SolidColorBrush(Colors.Blue)
frozenBrush.Freeze()
Dim nonFrozenBrush As Brush = New SolidColorBrush(Colors.Blue)
For i As Integer = 0 To 9
' Create a Rectangle using a non-frozed Brush.
Dim rectangleNonFrozen As New Rectangle()
rectangleNonFrozen.Fill = nonFrozenBrush
' Create a Rectangle using a frozed Brush.
Dim rectangleFrozen As New Rectangle()
rectangleFrozen.Fill = frozenBrush
Next i
Durch geänderte Handler auf nicht fixierten Freezable-Objekte bleiben Objekte möglicherweise aktiv
Der Delegat, den ein Objekt an das Changed-Ereignis eines Freezable-Objekts übergibt, ist effektiv ein Verweis auf dieses Objekt. Aufgrund von Changed-Ereignishandlern können Objekte länger als erwartet aktiv sein. Wenn ein Objekt bereinigt wird, das registriert wurde, um auf ein Changed-Ereignis eines Freezable-Objekts zu lauschen, ist es erforderlich, dass Sie diesen Delegaten entfernen, bevor Sie das Objekt freigeben.
WPF bindet Changed-Ereignisse auch intern ein. Beispielsweise lauschen alle Abhängigkeitseigenschaften, die Freezable als Wert akzeptieren, automatisch auf Changed-Ereignisse. Die Fill-Eigenschaft, die Brush verwendet, veranschaulicht dieses Konzept.
Brush myBrush = new SolidColorBrush(Colors.Red);
Rectangle myRectangle = new Rectangle();
myRectangle.Fill = myBrush;
Dim myBrush As Brush = New SolidColorBrush(Colors.Red)
Dim myRectangle As New Rectangle()
myRectangle.Fill = myBrush
Bei der Zuweisung von myBrush
zu myRectangle.Fill
wird dem Changed-Ereignis des SolidColorBrush-Objekts ein Delegat hinzugefügt, der auf das Rectangle-Objekt zurückverweist. Dies bedeutet, dass folgender Code myRect
nicht zur automatischen Garbage Collection berechtigt:
myRectangle = null;
myRectangle = Nothing
In diesem Fall bleibt myRectangle
durch myBrush
aktiv und führt einen Rückruf aus, wenn es sein Changed-Ereignis auslöst. Beachten Sie, dass, wenn Sie myBrush
der Fill-Eigenschaft eines neuen Rectangle zuweisen, myBrush
einfach ein weiterer Ereignishandler hinzugefügt wird.
Es wird empfohlen, diese Arten von Objekten zu bereinigen, indem Sie Brush aus der Fill-Eigenschaft entfernen, wodurch wiederum der Changed-Ereignishandler entfernt wird.
myRectangle.Fill = null;
myRectangle = null;
myRectangle.Fill = Nothing
myRectangle = Nothing
Virtualisierung der Benutzeroberfläche
WPF stellt außerdem eine Variante des StackPanel-Elements zur Verfügung, das automatisch datengebundenen untergeordneten Inhalt „virtualisiert“. In diesem Kontext bezieht sich das Wort virtualisieren auf eine Technik, mit der eine Teilmenge von Objekten aus einer größeren Anzahl von Datenelementen generiert wird, basierend darauf, welche Elemente auf dem Bildschirm sichtbar sind. Es ist sowohl speicher- als auch prozessorintensiv, eine große Anzahl von UI-Elementen zu generieren, wenn zu einem bestimmten Zeitpunkt nur wenige auf dem Bildschirm sind. VirtualizingStackPanel (durch von VirtualizingPanel bereitgestellte Funktionalität) berechnet sichtbare Elemente und funktioniert mit ItemContainerGenerator von ItemsControl (z. B ListBox oder ListView), um nur Elemente für sichtbare Objekte zu erstellen.
Als Leistungsoptimierung werden visuelle Objekte für diese Elemente nur generiert oder lebendig gehalten, wenn sie auf dem Bildschirm sichtbar sind. Wenn sie sich nicht mehr im sichtbaren Bereich des Steuerelements befinden, werden die visuellen Objekte möglicherweise entfernt. Dies ist nicht mit der Datenvirtualisierung zu verwechseln, bei der Datenobjekte nicht alle in der lokalen Auflistung vorhanden sind, sondern bei Bedarf gestreamt werden.
Die folgende Tabelle zeigt die verstrichene Zeit beim Hinzufügen und Rendern von 5.000 TextBlock-Elementen in ein StackPanel und ein VirtualizingStackPanel. In diesem Szenario stellen die Messungen die Zeitspanne dar, die vergeht, zwischen dem Anfügen einer Textzeichenfolge an die ItemsSource-Eigenschaft eines ItemsControl-Objekts und dem Zeitpunkt, zu dem die Paneelelemente die Textzeichenfolge anzeigen.
Hostpanel | Renderzeit (ms) |
---|---|
StackPanel | 3210 |
VirtualizingStackPanel | 46 |
Siehe auch
.NET Desktop feedback