WPF-Architektur
Dieses Thema bietet eine geführte Tour durch die Klassenhierarchie in Windows Presentation Foundation (WPF). Es behandelt die meisten der wichtigsten Subsysteme von WPF und beschreibt, wie sie interagieren. Es werden auch einige der durch die Architekten von WPF getroffenen Entscheidungen erläutert.
System.Object
Das primäre WPF-Programmiermodell wird mithilfe von verwaltetem Code verfügbar gemacht. In der frühen Entwurfsphase von WPF gab es einige Diskussionen darüber, wo die Linie zwischen den nicht verwalteten und den verwalteten Komponenten des Systems gezogen werden soll. Die CLR bietet eine Reihe von Funktionen, die die Entwicklung produktiver und stabiler machen (einschließlich Speicherverwaltung, Fehlerbehandlung, Allgemeines Typsystem usw.), aber sie haben Ihren Preis.
Die Hauptkomponenten von WPF sind in der folgenden Abbildung dargestellt. Die roten Abschnitte des Diagramms (PresentationFramework, PresentationCore und Milcore) sind die wichtigsten Teile von WPF. Von diesen ist nur eine Komponente nicht verwaltet – Milcore. Milcore ist in nicht verwaltetem Code geschrieben, um eine enge Integration mit DirectX zu ermöglichen. In WPF wird alles mithilfe der DirectX-Engine angezeigt, was effizientes Hardware- und Software-Rendering gestattet. Ebenso erforderte WPF genaue Kontrolle über den Speicher und die Ausführung. Die Kompositions-Engine in Milcore ist extrem sensibel in Bezug auf die Performance und erforderte, dass viele Vorteile der CLR zugunsten der Leistung geopfert werden mussten.
Die Kommunikation zwischen den verwalteten und nicht verwalteten Teilen von WPF wird weiter unten in diesem Thema behandelt. Das restliche verwaltete Programmiermodell wird nachfolgend beschrieben.
System.Threading.DispatcherObject
Die meisten Objekte in WPF leiten von DispatcherObject ab, welches die grundlegenden Konstrukte zur Handhabung von Parallelität und Threading bereitstellt. WPF basiert auf einem vom Dispatcher implementierten Nachrichtensystem. Dies funktioniert sehr ähnlich wie das vertraute Win32-Nachrichtensystem und tatsächlich verwendet der WPF-Dispatcher Win32-Nachrichten für threadübergreifende Aufrufe.
Im Grunde gibt es zwei Kernkonzepte, die man bei der Diskussion über Parallelität in WPF verstehen muss: den Dispatcher und Thread-Affinität.
Während der Entwurfsphase von WPF war es das Ziel, den Schritt zu einem einzigen Ausführungsthread in einem Nicht-Thread „affinierten“ Modell zu schaffen. Thread-Affinität findet dann statt, wenn eine Komponente die Identität des ausgeführten Threads verwendet, um einen irgendwie gearteten Zustand zu speichern. Die häufigste Form dieser Vorgehensweise ist, den threadlokalen Speicher (TLS) zu verwenden, um einen Zustand zu speichern. Thread-Affinität erfordert, dass jeder logische Ausführungsthread im Besitz von nur einem physischen Thread im Betriebssystem ist, was speicherintensiv werden kann. Letzten Endes wurde das WPF-Threadingmodell synchron mit dem bestehenden User32-Threadingmodell einzelner Ausführungsthread mit Threadaffinität gehalten. Der Hauptgrund hierfür war Interoperabilität – Systeme wie OLE 2.0, die Zwischenablage und Internet Explorer erfordern alle die Ausführung in Singlethread-Affinität (STA).
Angesichts der Tatsache, dass Sie Objekte mit STA-Threading haben, benötigen Sie Möglichkeiten der Kommunikation zwischen Threads und der Überprüfung, dass Sie sich im richtigen Thread befinden. Genau dies ist die Aufgabe des Verteilers. Der Verteiler ist ein Basis-Nachrichtensystem mit mehreren priorisierten Warteschlangen. Zu diesen Nachrichten gehören z.B. Rohdateneingabe-Benachrichtigungen (Maus bewegt), Framework-Funktionen (Layout) oder Benutzerbefehle (diese Methode ausführen). Durch Ableiten vom DispatcherObject erstellen Sie ein CLR-Objekt mit STA-Verhalten, und Sie erhalten zum Erstellungszeitpunkt einen Zeiger auf den Dispatcher.
System.Windows.DependencyObject
Eine der primären Architekturstrategien bei der Konzeption von WPF war die Bevorzugung von Eigenschaften über Methoden oder Ereignissen. Eigenschaften sind deklarativ und machen es einfacher, eine Absicht statt einer Aktion anzugeben. Dies bot auch Unterstützung für ein modell- oder datengesteuertes System zur Anzeige von Inhalten der Benutzeroberfläche. Diese Strategie hatte den beabsichtigten Effekt, mehr Eigenschaften zu schaffen, an die Sie anbinden können, um das Verhalten einer Anwendung besser zu steuern.
Damit größere Teile des Systems durch Eigenschaften gesteuert werden konnten, war ein vielfältigeres Eigenschaftensystem vonnöten, als das, was die CLR bot. Ein einfaches Beispiel für diese Vielfalt sind die Änderungsbenachrichtigungen. Um eine zwei-Wege-Bindung zu ermöglichen, müssen beide Seiten der Bindung Änderungsbenachrichtigungen unterstützen. Um an Eigenschaftswerte ein bestimmtes Verhalten zu binden, müssen Sie benachrichtigt werden, wenn sich der Eigenschaftswert ändert. Das Microsoft .NET Framework verfügt zwar über eine Schnittstelle namens INotifyPropertyChange, die es Objekten erlaubt, Benachrichtigungen zu veröffentlichen; dies ist jedoch nur optional.
WPF bietet ein umfangreicheres Eigenschaftssystem, das vom Typ DependencyObject abgeleitet ist. Dieses Eigenschaftensystem ist in der Tat ein Eigenschaftensystem der „Abhängigkeiten” in dem Sinne, dass es Abhängigkeiten zwischen Eigenschaftsausdrücken nachverfolgt und Eigenschaftswerte automatisch neu überprüft, wenn sich die Abhängigkeiten ändern. Wenn Sie z. B. eine Eigenschaft haben, die erbt (z. B. von FontSize), dann wird das System automatisch aktualisiert, wenn sich diese Eigenschaft in einem übergeordneten Element eines Elements ändert, das diesen den Wert erbt.
Das WPF-Eigenschaftensystem fußt auf dem Konzept eines Eigenschaftsausdrucks. In dieser ersten Version von WPF ist das Eigenschaftensystem geschlossen, und die Ausdrücke werden alle als Teil des Frameworks bereitgestellt. Ausdrücke sind der Grund, warum im Eigenschaftensystem keine Datenbindungen, Stile oder Vererbungen hartcodiert sind, sondern stattdessen von später gebildeten Ebenen innerhalb des Frameworks bereitgestellt werden.
Das Eigenschaftensystem ermöglicht auch verstreutes Speichern von Eigenschaftswerten. Da Objekte Dutzende (wenn nicht gar Hunderte) von Eigenschaften haben können, und die meisten Werte im Standardzustand sind (geerbt, von Stilen festgelegt usw.), benötigt nicht jede Instanz eines Objekts die vollständige Last aller seiner definierten Eigenschaften.
Das abschließende neue Feature des Eigenschaftensystems ist das Konzept der angefügten Eigenschaften. WPF-Elemente basieren auf dem Prinzip der Komposition und der Wiederverwendung von Komponenten. Es kommt häufig vor, dass ein Element (z. B. ein Grid-Layoutelement) zusätzliche Daten zu untergeordneten Elementen benötigt, um sein Verhaltens zu steuern (z. B. die Zeilen-/Spalteninformationen). Statt alle diese Eigenschaften jedem Element zuzuordnen, darf jedes beliebige Objekt Eigenschaftsdefinitionen für andere Objekte bereitstellen. Dies ähnelt den „Expando”-Funktionen von JavaScript.
System.Windows.Media.Visual
Ist ein System einmal definiert, besteht der nächste Schritt darin, Pixel auf den Bildschirm zu zeichnen. Die Visual-Klasse stellt Funktionen für das Erstellen einer Baumstruktur visueller Objekte bereit, die jeweils optional Zeichnungsanweisungen und Metadaten enthalten, wie diese Anweisungen (Clipping, Transformation usw.) zu rendern sind. Visual wurde extrem schlank und flexibel entworfen, sodass die meisten Features keine öffentliche API-Exposition haben und stark auf geschützte Rückruffunktionen bauen.
Visual ist tatsächlich der Einstiegspunkt in das WPF-Kompositionssystem. Visual ist der Verbindungpunkt zwischen diesen beiden Subsystemen: der verwalteten API und dem nicht verwalteten Milcore.
WPF zeigt Daten an, indem es die von Milcore verwalteten nicht verwalteten Datenstrukturen durchläuft. Diese Strukturen, die man Kompositionsknoten nennt, stellen eine hierarchische Anzeige-Struktur mit Rendering-Anweisungen in jedem Knoten dar. Auf diese Struktur, die auf der rechten Seite der folgenden Abbildung dargestellt ist, kann nur über ein Nachrichtenprotokoll zugegriffen werden.
Wenn Sie in WPF programmieren, erstellen Sie Visual-Elemente und abgeleitete Typen, die intern über dieses Nachrichtenprotokoll mit der Kompositions-Baumstruktur kommunizieren. Jedes Visual in WPF kann einen, keinen oder mehrere Kompositionsknoten erzeugen.
Es gibt hier ein sehr wichtiges architektonisches Detail zu beachten: die gesamte Struktur von visuellen Objekten und Zeichenanweisungen wird zwischengespeichert. Im Grafik-Vokabular heißt dies, WPF verwendet ein Retained-Rendering-System, was so viel wie „zurückbehaltenes Rendering” bedeutet. Dies ermöglicht es dem System, mit hohen Aktualisierungsraten neu zu zeichnen, ohne dass das Kompositionssystem dies aufgrund von Rückrufen auf Benutzercode blockiert. Dies verhindert eine scheinbar nicht reagierende Anwendung.
Ein weiteres wichtiges Detail, das man dem Diagramm eher nicht entnehmen kann, ist wie das System die Komposition tatsächlich durchführt.
In User32 und GDI arbeitet das System mit einem Clippingsystem mit Direktmodus. Wenn eine Komponente gerendert werden muss, erstellt das System eine Clippinggrenze, außerhalb derer die Komponente keine Pixel berühren darf. Danach wird die Komponente aufgefordert, Pixel in dieses Feld zu zeichnen. Dieses System funktioniert sehr gut auf Systemen mit beschränktem Arbeitsspeicher, weil man bei jeder Änderung immer nur die betroffene Komponente anfassen muss – es tragen nie zwei Komponenten zur Farbe eines einzelnen Pixels bei.
WPF verwendet als Zeichenmodell einen „Maleralgorithmus”. Dies bedeutet, dass, statt jede Komponente zu beschneiden, diese stattdessen aufgefordert wird, von hinten nach vorne in die Anzeige zu rendern. Dadurch kann jede Komponente die Anzeige der vorherigen Komponente übermalen. Der Vorteil dieses Modells ist, dass Sie komplexe, teilweise transparente Formen schaffen können. Mit der heutigen modernen Grafikhardware ist dieses Modell relativ schnell (was noch nicht der Fall war, als User32/GDI erstellt wurden).
Wie bereits erwähnt, ist eine zentrale Strategie von WPF der Schritt zu einem deklarativen, „eigenschaftenzentrierten” Programmiermodell. Im visuellen System zeigt sich dies an einigen interessanten Stellen.
Wenn man sich zunächst das Grafiksystem mit seinem zurückbehaltenden Modus vor Augen führt, ist dies wirklich ein großer Schritt weg von einem imperativen Modell vom Typ „DrawLine/DrawLine”, hin zu einem datenorientierten Modell: „new Line() / new Line()/”. Dieser Schritt hin zu datengesteuertem Rendering ermöglicht es, komplexe Operationen in den Zeichenanweisungen mithilfe von Eigenschaften auszudrücken. Die aus Drawing abgeleiteten Typen sind gewissermaßen das Rendering-Objektmodell.
Wenn Sie sich zum Zweiten das Animationssystem anschauen, sehen Sie, dass es fast vollständig deklarativ ist. Statt einen Entwickler zu benötigen, der die nächste Position oder Farbe berechnet, können Sie Animationen als eine Reihe von Eigenschaften für ein Animationsobjekt ausdrücken. Diese Animationen können dann die Absicht des Entwicklers oder Designers ausdrücken (bewege diese Schaltfläche innerhalb von 5 Sekunden von hier nach dort), und das System ermittelt die effizienteste Methode dieses zu bewerkstelligen.
System.Windows.UIElement
UIElement definiert Kernsubsysteme einschließlich Layout, Eingaben und Ereignisse.
Layout ist in WPF ein Kernkonzept. In vielen Systemen gibt es entweder einen festen Satz von Layout-Modellen (HTML unterstützt drei Modelle für das Layout: fließend, absolut und Tabellen) oder keine (User32 unterstützt wirklich nur die absolute Positionierung). WPF begann mit der Annahme, dass Entwickler und Designer ein flexibles, erweiterbares Layout-Modell möchten, das durch Eigenschaftswerte an Stelle von imperativer Logik gesteuert werden kann. Auf der UIElement-Ebene wird der Grundvertrag für das Layout eingeführt – ein Zweiphasenmodell mitMeasure- und Arrange-Durchgängen.
Mit Measure kann eine Komponente seine gewünschte Größe bestimmen. Diese Phase ist getrennt von Arrange, da es viele Situationen gibt, in denen ein übergeordnetes Element eines seiner untergeordneten Elemente auffordern wird, mehrere Messungen zur Ermittlung seiner optimalen Position und Größe durchzuführen. Die Tatsache, dass übergeordnete Elemente von untergeordneten Elementen Messungen anfordern, zeigt eine andere wichtige Strategie von WPF – die Größe dem Inhalt anzupassen. Alle Steuerelemente in WPF unterstützen die Fähigkeit, ihre Größe der natürlichen Größe ihres Inhalts anzupassen. Dies erleichtert die Lokalisierung ungemein und lässt ein dynamische Layout von Elementen zu, die Ihre Größe ändern. Die Arrange-Phase gibt übergeordneten Elementen die Möglichkeit, Position und endgültige Größe jedes untergeordneten Elements zu bestimmen.
Oft wird viel Zeit damit verbracht, über die Ausgabeseite von WPF zu sprechen – Visual und verwandte Objekte. Es gibt jedoch auch eine enorme Anzahl an Innovationen auf der Eingabeseite. Die wahrscheinlich wichtigste Änderung des Eingabemodells für WPF ist das konsistente Modell, Eingabeereignisse durch das System weiterzuleiten.
Eingabedaten starten als Signal in einem Kernelmodus-Gerätetreiber und werden durch einen komplexen Prozess, der den Windows-Kernel und User32 einbezieht, an den korrekten Prozess und Thread weitergeleitet. Sobald die zur Eingabe gehörende User32-Nachricht an WPF weitergeleitet ist, wird sie in eine WPF-Rohdaten-Eingabenachricht konvertiert und an den Dispatcher gesendet. In WPF können Rohdaten-Eingabeereignisse in mehrere tatsächliche Ereignisse konvertiert werden, was die Implementierung von Funktionen wie „MouseEnter“ auf einer niedrigen Systemebene mit garantierter Übermittlung ermöglicht.
Jedes Eingabeereignis wird in mindestens zwei Ereignisse konvertiert: ein „Vorschau”-Ereignis und das tatsächliche Ereignis. Allen Ereignissen in WPF ist gemein, dass sie durch die Elementbaumstruktur geleitet werden. Man spricht bei Ereignissen von „bubblen”, wenn sie die Struktur vom Ziel nach oben bis zum Stamm durchlaufen, und sie „tunneln”, wenn sie im Stamm starten und die Struktur abwärts bis zum Ziel durchlaufen. Eingabe-Vorschauereignisse tunneln, was jedem Element der Struktur die Möglichkeit gibt, etwas herauszufiltern oder eine Aktion auf das Ereignis durchzuführen. Das tatsächliche (Nicht-Vorschau-) Ereignis bubblet anschließend vom Ziel aufwärts zum Stamm.
Diese Auftrennung zwischen tunnelnder und bubblender Phase sorgt dafür, dass die Implementierung von Funktionen wie Tastenkombinationen auch in einer gemischten Umgebung konsistent funktioniert. In User32 würden Sie Zugriffstasten mithilfe einer einzelnen globalen Tabelle implementieren, die alle zu unterstützenden Zugriffstasten enthält (z.B. STRG + N für "Neu"). Sie würden im Verteiler Ihrer Anwendung TranslateAccelerator aufrufen, welches die eingehende Nachrichten in User32 untersuchen und bestimmen würde, welche davon registrierten Zugriffstaste entsprechen. In WPF würde dies nicht funktionieren, da das System vollständig „komponierbar“ ist – jedes Element darf jede beliebige Zugriffstaste behandeln und verwenden. Dieses zweiphasige Eingabemodell ermöglicht es Komponenten, ihre eigene „TranslateAccelerator“-Komponente zu implementieren.
Um einen Schritt weitergehen, führt UIElement zudem das Konzept der CommandBindings ein. Im WPF-Befehlssystem können Entwickler Funktionen im Hinblick auf einen Befehlsendpunkt definieren – eine Implementierung von ICommand. Befehlsbindungen ermöglichen es Elementen, eine Zuordnung zwischen einer Eingabegeste (STRG + N) und einem Befehl (Neu) zu definieren. Sowohl die Eingabegesten als auch die Befehlsdefinitionen sind erweiterbar und können zum Zeitpunkt der Verwendung miteinander verknüpft werden. Dies macht es für den Endbenutzer extrem einfach, z.B. die Zuordnung von Tastenkombinationen anzupassen, die innerhalb einer Anwendung verwendet werden soll.
Was dieses Thema betrifft, lag der Schwerpunkt auf den „Kern-”Features von WPF – Features, die in der PresentationCore-Assembly implementiert wurden. Das gewünschte Ergebnis beim Zusammensetzen von WPF war, eine klare Trennung zwischen grundlegenden Teilen(wie dem Layoutvertrag mit Measure und Arrange) und Teilen des Frameworks (wie der Implementierung eines spezifischen Layouts wie Grid) zu schaffen. Es war das Ziel, externen Entwicklern einen Erweiterungspunkt tief im Stapel anzubieten, der ihnen erlauben würde, bei Bedarf ihre eigenen Frameworks zu erstellen.
System.Windows.FrameworkElement
FrameworkElement kann auf zwei verschiedene Arten betrachtet werden. Zum Einen führt es eine Reihe von Richtlinien und Anpassungen auf Subsysteme ein, die auf den unteren Ebenen von WPF eingeführt wurden. Zum Anderen führt es auch eine Reihe neuer Subsysteme ein.
Die wichtigste Richtlinie, die mit FrameworkElement eingeführt wurde, betrifft das Anwendungslayout. FrameworkElement baut auf dem grundlegenden Layoutvertrag auf, der durch UIElement eingeführt wurde und erweitert ihn um das Konzept eines Layout-„Slots”, der es Layoutautoren erleichtert, eine konsistente Menge durch Eigenschaften gesteuerter layoutsemantischer Objekte zu schaffen. Eigenschaften wie HorizontalAlignment, VerticalAlignment, MinWidth und Margin (um nur einige zu nennen) verleihen allen von FrameworkElement abgeleiteten Komponenten ein einheitliches Verhalten innerhalb von Layoutcontainern.
FrameworkElement bietet außerdem einfachere API-Exposition vieler Funktionen aus den Kernebenen von WPF. Zum Beispiel bietet FrameworkElement über die BeginStoryboard-Methode direkten Zugang zur Animation. Ein Storyboard bietet eine Möglichkeit, mehrere Animationen für eine Reihe von Eigenschaften zu skripten.
Die zwei wichtigsten Dinge, die FrameworkElement einführt, sind Datenbindungen und Stile.
Das Datenbindungs-Subsystem in WPF sollte allen Benutzern, die bereits eine Anwendungs-Benutzeroberfläche mit Windows Forms oder ASP.NET erstellt haben, relativ vertraut sein. In jedem dieser Systeme besteht eine einfache Möglichkeit, um auszudrücken, dass Sie eine oder mehrere Eigenschaften aus einem angegebenen Element an Daten binden möchten. WPF bietet vollständige Unterstützung für Eigenschaftenbindung, Transformation und Listenbindung.
Eines der interessantesten Features der Datenbindung in WPF ist die Einführung von Datenvorlagen. Mit Datenvorlagen können Sie deklarativ angeben, wie Daten visualisiert werden sollen. Statt eine benutzerdefinierte Benutzeroberfläche zu erzeugen, die an Daten gebunden werden kann, können Sie stattdessen das Problem umzukehren, und die Daten selbst die Anzeige bestimmen lassen, die erstellt werden soll.
Formatierung ist im Grunde eine schlanke Form der Datenbindung. Mithilfe von Formatierungen kann eine Reihe von Eigenschaften aus einer freigegebenen Definition an eine oder mehrere Instanzen eines Elements gebunden werden. Stile werden auf ein Element entweder durch einen expliziten Verweis (durch Festlegen der Style-Eigenschaft) angewendet, oder implizit durch die Verknüpfung eines Stils mit dem CLR-Typ des Elements.
System.Windows.Controls.Control
Das wichtigste Feature von Control ist das Vorlagensystem. Da das Kompositionssystem von WPF ein zurückbehaltendes Renderingsystem ist, erlaubt das Vorlagensystem eine Renderingbeschreibung in einer parametrisierten, deklarativen Art und Weise. Ein ControlTemplate ist im Grunde nichts weiter als ein Skript zur Erstellung einer Reihe untergeordneter Elemente, die eine Bindung an Eigenschaften haben, die das Steuerelement anbietet.
Control bietet eine Reihe von Bestandseigenschaften, Foreground, Background, Padding, um nur einige zu nennen, die Vorlagenautoren dann verwenden können, um die Anzeige eines Steuerelements anzupassen. Die Implementierung eines Steuerelements stellt ein Datenmodell und ein Interaktionsmodell bereit. Das Interaktionsmodell definiert einen Satz von Befehlen (z.B. „Schließen” für Fenster) und Bindungen an Eingabegesten (z.B. ein Klick auf das rote X in der oberen Ecke des Fensters). Das Datenmodell bietet eine Reihe von Eigenschaften, um entweder das Interaktionsmodell oder die Anzeige anzupassen (durch die Vorlage festgelegt).
Diese Auftrennung zwischen Datenmodell (Eigenschaften), Interaktionsmodell (Befehle und Ereignisse) und Anzeigemodell (Vorlagen) ermöglicht die vollständige Anpassung von Aussehen und Verhalten eines Steuerelements.
Ein allgemeiner Aspekt des Datenmodells von Steuerelementen ist das Inhaltsmodell. Wenn Sie sich ein Steuerelement wie Button ansehen, werden Sie feststellen, dass es eine Eigenschaft namens „Inhalt“ vom Typ Object hat. In Windows Forms und ASP.NET wäre diese Eigenschaft gewöhnlich eine Zeichenfolge – was allerdings den Typ des Inhalts einschränkt, den Sie in eine Schaltfläche packen können. Der Inhalt einer Schaltfläche kann entweder eine einfache Zeichenfolge, ein komplexes Datenobjekt oder eine komplette Elementstruktur sein. Bei einem Datenobjekt wird die Anzeige mithilfe einer Datenvorlage erstellt.
Zusammenfassung
WPF wurde entworfen, um Ihnen die Erstellung dynamischer, datengesteuerter Präsentationssysteme zu ermöglichen. Jeder Teil des Systems dient zum Erstellen von Objekten über Eigenschaftensätze, die das Verhalten steuern. Datenbindungen sind ein grundlegender Teil des Systems und auf jeder Ebene integriert.
Herkömmliche Anwendungen erzeugen eine Anzeige und binden anschließend Daten an. In WPF ist die Steuerung, ist jeder Aspekt der Anzeige, durch irgendeine Art von Datenbindung generiert. Der Text in einer Schaltfläche wird angezeigt, indem ein zusammengesetztes Steuerelement in der Schaltfläche erstellt und seine Anzeige an die Content-Eigenschaft der Schaltfläche gebunden wird.
Wenn Sie beginnen, WPF-basierte Anwendungen zu entwickeln, sollte Ihnen alles sehr vertraut vorkommen. Sie können Eigenschaften festlegen, Objekte verwenden und Daten im Wesentlichen in der gleichen Art und Weise anbinden, wie Sie dies in Windows Forms oder ASP.NET täten. Wenn Sie dann tiefer in die Architektur von WPF eintauchen, werden Sie entdecken, dass es Ihnen die Möglichkeit bietet, wesentlich vielfältigere Anwendungen zu erstellen, deren grundlegende Steuerungsmechanismen über Daten stattfinden.
Siehe auch
.NET Desktop feedback