Hallo allerseits!
Aus verschiedenen Gründen verwenden die meisten von uns Desktop-Anwendungen, zumindest einen Browser :) Und einige von uns müssen ihre eigenen schreiben. In diesem Artikel möchte ich den Entwicklungsprozess einer einfachen Desktop-Anwendung mit der Windows Presentation Foundation (WPF) -Technologie und der Anwendung des MVVM-Musters erläutern. Diejenigen, die weiterlesen möchten, bitte unter Katze.
Ich denke, es ist nicht notwendig zu sagen, dass WPF die Entwicklung von Microsoft ist :) Diese Technologie wurde entwickelt, um Desktop-Anwendungen für Windows zu entwickeln, beginnend mit Windows XP. Warum so? Dies liegt an der Tatsache, dass WPF auf der .NET-Plattform ausgeführt wird, deren Mindestanforderungen Windows XP und höher sind. Leider funktioniert WPF nicht auf anderen Plattformen, obwohl es Chancen gibt, dass sich dies in naher Zukunft ändern wird: Das WPF-basierte Avalonia- Framework befindet sich in der Entwicklung .
Was ist das Besondere an WPF?
Zwei Hauptunterschiede zwischen WPF und anderen Desktop-Erstellungstools:
- Die XAML-Markup-Sprache zum Markieren der Fensterschnittstelle selbst.
- Rendern über DirectX, Beschleunigung der Hardwaregrafik.
Ich werde nicht auf Details eingehen, weil Dies ist nicht ganz das Thema des Artikels. Bei Interesse dann Google XAML, WPF Rendering, milcore.dll und DirectX :)
Worum geht es in diesem Artikel?
Dieser Artikel enthält eine Beispielanwendung, die auf der WPF-Technologie basiert:
Ich werde versuchen, das Material des Artikels in einer praktischen Richtung im Stil von "Wiederholung nach mir" mit Erklärungen zu orientieren.
Was brauchen wir, um den Artikel zu wiederholen?
Wenig Entwicklungserfahrung in C # :) Zumindest müssen Sie die Sprachsyntax gut verstehen. Sie benötigen außerdem einen Windows-Computer (in den Beispielen Win 10), auf dem Visual Studio installiert ist (in den Beispielen wird es 2017 eine kostenlose Community- Version geben). Bei der Installation von VS müssen Sie die Unterstützung für die Desktop-Entwicklung für die .NET-Plattform aktivieren

Auch in diesem Abschnitt werde ich die Erstellung eines Projekts beschreiben.
Wir starten VS, erstellen ein neues Projekt, wählen den Anwendungstyp WPF App (.NET Framework) aus (Sie können ihn in der Suchleiste oben rechts eingeben) und nennen ihn wie Sie möchten.

Nach dem Erstellen eines neuen Projekts wird das Fenster des Schnittstelleneditors geöffnet. Für mich sieht es so aus

Unten befindet sich ein Layout-Editor, oben befindet sich eine Vorschau der Fensteroberfläche. Sie können jedoch die relative Position des Code-Editors und der Schnittstellenvorschau so ändern, dass sie mit diesen Schaltflächen (rechts am Rand der beiden Bereiche) in horizontaler Reihenfolge angezeigt werden:

Bevor Sie anfangen
Fensterelemente (sie werden vom Wort Control auch als Steuerelemente bezeichnet) sollten im Container oder in einem anderen Element des ContentControl-Typs platziert werden. Ein Container ist ein spezielles Steuerelement, mit dem Sie mehrere untergeordnete Steuerelemente darin platzieren und deren gegenseitige Anordnung organisieren können. Beispiele für Behälter:
- Raster - Ermöglicht das Organisieren von Elementen nach Spalten und Zeilen. Die Breite jeder Spalte oder Zeile wird einzeln konfiguriert.
- StackPanel - Ermöglicht das Anordnen von untergeordneten Elementen in einer einzelnen Zeile oder Spalte.
Es gibt andere Container. Da der Container auch ein Steuerelement ist, können sich im Container verschachtelte Container befinden, die verschachtelte Container usw. enthalten. Auf diese Weise können Sie Steuerelemente flexibel relativ zueinander anordnen. Mithilfe von Containern können wir das Verhalten verschachtelter Steuerelemente beim Ändern der Fenstergröße nicht weniger flexibel steuern.
MVVM- und INotifyPropertyChanged-Schnittstelle. Kopie des Textes.
Das Ergebnis dieses Beispiels ist eine Anwendung mit zwei Steuerelementen, in denen Sie den Text bearbeiten können und in der anderen nur anzeigen. Änderungen von einem zum anderen werden synchron übertragen, ohne dass der Text explizit per Bindung kopiert wird.
Wir haben also ein frisch erstelltes Projekt (ich habe es Ex1 genannt ), gehen zum Layout-Editor und ersetzen zunächst den Standardcontainer ( <Grid> </ Grid> ) durch <StackPanel> </ StackPanel> . Dieser Container wird ausreichen, weil Wir müssen nur zwei Steuerelemente übereinander platzieren. Wir geben explizit an, wie die Komponenten angeordnet werden, indem wir die Eigenschaft Orientation = "Vertical" hinzufügen. Fügen Sie dem Bedienfeldstapel einige Elemente hinzu: ein Feld zur Texteingabe und ein Feld zur Anzeige von Text. Da diese Steuerelemente keinen eingebetteten Code enthalten, können Sie sie mit einem selbstschließenden Tag beschreiben (siehe Code unten). Nach allen oben genannten Prozeduren sollten der Containerbeschreibungscode und die verschachtelten Steuerelemente die folgende Form haben:
<StackPanel Orientation="Vertical"> <TextBox /> <TextBlock /> </StackPanel>
Konzentrieren wir uns nun auf den Zweck dieses Beispiels. Wir möchten, dass beim Eingeben in das Textfeld derselbe Text synchron im Textblock angezeigt wird, ohne dass explizite Kopiervorgänge erforderlich sind. Wir brauchen eine Art verbindende Einheit, und hier kommen wir zu einer Bindung , die oben erwähnt wurde. Das Binden in der WPF-Terminologie ist ein Mechanismus, mit dem Sie einige Eigenschaften von Steuerelementen mit einigen Eigenschaften eines Objekts der C # -Klasse verknüpfen und diese Eigenschaften gegenseitig aktualisieren können, wenn sich einer der Teile des Bundles ändert (dies kann in die eine, die andere oder beide Richtungen gleichzeitig funktionieren). Für diejenigen, die mit Qt vertraut sind, können Sie eine Analogie von Slots und Signalen zeichnen. Um die Zeit nicht zu verlängern, fahren wir mit dem Code fort.
Um die Bindung zu organisieren, benötigen Sie die Eigenschaften der Steuerelemente und einige Eigenschaften einer bestimmten C # -Klasse. Lassen Sie uns zunächst den XAML-Code herausfinden. Der Text beider Steuerelemente wird in der Text-Eigenschaft gespeichert. Fügen Sie daher eine Bindung für diese Eigenschaften hinzu. Es wird so gemacht:
<TextBox Text="{Binding}"/> <TextBlock Text="{Binding}"/>
Wir haben eine Bindung hergestellt, aber im Moment ist nicht klar, warum :) Wir benötigen ein Objekt einer Klasse und eine Eigenschaft in diesem Objekt, an die die Bindung hergestellt wird (wie sie sagen, an die Sie binden müssen).
Was ist diese Klasse? Diese Klasse wird als Ansichtsmodell bezeichnet und dient als Verknüpfung zwischen der Ansicht (der Schnittstelle oder ihren Teilen) und dem Modell (Modell, d. H. Den Teilen des Codes, die für die Anwendungslogik verantwortlich sind. Auf diese Weise können Sie (in gewissem Umfang) trennen ) Die Anwendungslogik von der Schnittstelle (Ansicht, Ansicht) wird als Model-View-ViewModel (MVVM) -Muster bezeichnet . In WPF wird diese Klasse auch als DataContext bezeichnet .
Es reicht jedoch nicht aus, nur eine Ansichtsmodellklasse zu schreiben. Es ist notwendig, den Bindungsmechanismus irgendwie darüber zu informieren, dass sich die Ansichtsmodelleigenschaft oder die Ansichtseigenschaft geändert hat. Zu diesem Zweck gibt es eine spezielle Schnittstelle INotifyPropertyChanged , die das PropertyChanged- Ereignis enthält. Wir implementieren diese Schnittstelle im Framework der Basisklasse BaseViewModel . In Zukunft werden wir alle unsere Ansichtsmodelle von dieser Basisklasse erben, um die Implementierung der Schnittstelle nicht zu duplizieren. Fügen Sie dem Projekt das Verzeichnis ViewModels hinzu , und fügen Sie diesem Verzeichnis die Datei BaseViewModel.cs hinzu . Wir erhalten folgende Projektstruktur:

Implementierungscode für das Basisansichtsmodell:
using System.ComponentModel; namespace Ex1.ViewModels { public class BaseViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }
Lassen Sie uns unser Ansichtsmodell für unsere MainWindow- Klasse erstellen , das von der Basisklasse erbt. Erstellen Sie dazu im selben ViewModels- Verzeichnis die Datei MainWindowViewModel.cs , in der sich der folgende Code befindet:
namespace Ex1.ViewModels { public class MainWindowViewModel : BaseViewModel { } }
Großartig! Jetzt müssen wir diesem Ansichtsmodell eine Eigenschaft hinzufügen, an die wir den Text unserer Steuerelemente binden. Da dies Text ist, muss der Typ dieser Eigenschaft string sein :
public string SynchronizedText { get; set; }
Als Ergebnis erhalten wir einen solchen Code
namespace Ex1.ViewModels { public class MainWindowViewModel : BaseViewModel { public string SynchronizedText { get; set; } } }
Es scheint also, dass sie es getan haben. Es bleibt an dieser Eigenschaft aus der Ansicht zu binden und ist bereit. Lass es uns jetzt tun:
<TextBox Text="{Binding Path=SynchronizedText}"/> <TextBlock Text="{Binding Path=SynchronizedText}"/>
Nishtyak, wir starten das Projekt, wir geben das Textfeld ein iiiii ... nichts passiert))) Nun, es ist okay, tatsächlich gehen wir den richtigen Weg, wir haben einfach nicht den richtigen Punkt erreicht.
Ich schlage vor, einen Moment innezuhalten und darüber nachzudenken, was uns fehlt. Wir haben eine Aussicht. Viewmodel auch. Eigenschaften wie zabindili. Die gewünschte Schnittstelle ist implementiert. Wir haben viel Arbeit geleistet, um eine erbärmliche Textzeile zu kopieren. Warum brauchen wir das ???!?! 111
Okay, Witze beiseite. Wir haben vergessen, ein Ansichtsmodellobjekt und etwas anderes zu erstellen (dazu später mehr). Wir haben die Klasse selbst beschrieben, aber das hat nichts zu bedeuten, da wir keine Objekte dieser Klasse haben. Ok, wo müssen Sie einen Link zu diesem Objekt speichern? Näher am Anfang des Beispiels erwähnte ich einen bestimmten DataContext, der in WPF verwendet wird. Jede Ansicht verfügt also über eine DataContext- Eigenschaft, der wir unserem Ansichtsmodell einen Link zuweisen können. Lass es uns tun. Öffnen Sie dazu die Datei MainWindow.xaml und drücken Sie F7, um den Code für diese Ansicht zu öffnen. Es ist fast leer, es hat nur einen Fensterklassenkonstruktor. Fügen Sie die Erstellung unseres Ansichtsmodells hinzu und platzieren Sie es im DataContext des Fensters (vergessen Sie nicht, using mit dem gewünschten Namespace hinzuzufügen):
public MainWindow() { InitializeComponent(); this.DataContext = new MainWindowViewModel(); }
Es war einfach, aber immer noch nicht genug. Beim Starten der Anwendung findet jedoch keine Textsynchronisierung statt. Was muss noch getan werden?
Sie müssen das PropertyChanged- Ereignis auslösen , wenn sich die SynchronizedText- Eigenschaft ändert, und die Ansicht darüber informieren, dass dieses Ereignis überwacht werden soll. Um das Ereignis auszulösen, ändern Sie den Code des Ansichtsmodells:
public class MainWindowViewModel : BaseViewModel { private string _synchronizedText; public string SynchronizedText { get => _synchronizedText; set { _synchronizedText = value; OnPropertyChanged(nameof(SynchronizedText)); } } }
Was haben wir hier gemacht? Wir haben ein verstecktes Feld zum Speichern von Text hinzugefügt, es in eine vorhandene Eigenschaft eingeschlossen und beim Ändern dieser Eigenschaft nicht nur das versteckte Feld geändert, sondern auch die im Basisansichtsmodell definierte OnPropertyChanged- Methode aufgerufen und das in der ebenfalls in der Basis implementierten INotifyPropertyChanged- Schnittstelle deklarierte PropertyChanged- Ereignis ausgelöst Modelle anzeigen. Es stellt sich heraus, dass jedes Mal, wenn der Text geändert wird, das PropertyChanged- Ereignis auftritt, an das der Name der Eigenschaft des geänderten Ansichtsmodells übergeben wird.
Na ja, fast alles, die Ziellinie! Es bleibt die Ansicht anzugeben, dass das PropertyChanged- Ereignis abgehört werden soll :
<TextBox Text="{Binding Path=SynchronizedText, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}"/> <TextBlock Text="{Binding Path=SynchronizedText, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
Neben der Tatsache, dass wir angegeben haben, durch welchen Trigger das Update stattfinden soll, haben wir auch angegeben, in welche Richtung dieses Update verfolgt wird: von Ansicht zu Ansicht Modell oder umgekehrt. Da wir Text in das Textfeld eingeben, sind wir nur an den Änderungen in der Ansicht interessiert, daher wählen wir den OneWayToSource- Modus. Beim Textblock ist genau das Gegenteil der Fall: Wir sind an Änderungen im Ansichtsmodell interessiert, um sie in der Ansicht anzuzeigen, und wählen daher den OneWay- Modus. Wenn wir wollten, dass die Änderungen in beide Richtungen verfolgt werden, konnten wir den Modus überhaupt nicht oder TwoWay explizit angeben.
Führen Sie also das Programm aus, geben Sie den Text ein und voi-la! Der Text ändert sich synchron und wir haben nirgendwo etwas kopiert!

Vielen Dank für Ihre Aufmerksamkeit, um fortzufahren. Wir werden uns mit der DataTemplate und dem Befehlsmuster befassen.