Démarrage rapide avec WPF. Partie 1. Liaison, INotifyPropertyChanged et MVVM

Bonjour à tous!


Pour diverses raisons, la plupart d'entre nous utilisons des applications de bureau, au moins un navigateur :) Et certains d'entre nous doivent écrire les nôtres. Dans cet article, je veux passer en revue le processus de développement d'une simple application de bureau utilisant la technologie Windows Presentation Foundation (WPF) et appliquant le modèle MVVM. Ceux qui souhaitent continuer à lire, s'il vous plaît, sous cat.


Je pense qu’il n’est pas nécessaire de dire que WPF est le développement de Microsoft :) Cette technologie est conçue pour développer des applications de bureau pour Windows, à commencer par Windows XP. Pourquoi Cela est dû au fait que WPF s'exécute au-dessus de la plate-forme .NET, dont la configuration minimale requise est Windows XP et versions ultérieures. Malheureusement, WPF ne fonctionne pas sur d'autres plates-formes, bien qu'il y ait des chances que cela change dans un proche avenir: le cadre Avalonia basé sur WPF est en cours de développement .


Quelle est la particularité de WPF?


Deux différences principales entre WPF et d'autres outils de création de bureau:


  • Le langage de balisage XAML pour marquer l'interface de fenêtre elle-même.
  • Rendu via DirectX, accélération graphique matérielle.

Je n'entrerai pas dans les détails, car ce n'est pas tout à fait le sujet de l'article. Si vous êtes intéressé, alors google XAML, rendu WPF, milcore.dll et DirectX :)


De quoi parle cet article?


Cet article contient un exemple d'application basé sur la technologie WPF:



Je vais essayer d'orienter le matériau de l'article dans une direction pratique dans le style de «répéter après moi» avec des explications.


De quoi avons-nous besoin pour répéter l'article?


Petite expérience de développement en C # :) Au minimum, vous devez bien comprendre la syntaxe du langage. Vous aurez également besoin d'une machine Windows (Win 10 dans les exemples) sur laquelle Visual Studio est installé (dans les exemples, ce sera 2017, il existe une version communautaire gratuite). Lors de l'installation de VS, vous devrez activer la prise en charge du développement de bureau pour la plate-forme .NET


image


Dans cette section, je décrirai également la création d'un projet.


Nous lançons VS, créons un nouveau projet, sélectionnons le type d'application WPF App (.NET Framework) (vous pouvez le saisir dans la barre de recherche en haut à droite), nommez-le.


image


Après avoir créé un nouveau projet, la fenêtre de l'éditeur d'interface s'ouvre, ça ressemble à ça pour moi


image


En bas, il y a un éditeur de mise en page, en haut, il y a un aperçu de l'interface de la fenêtre, mais vous pouvez changer l'emplacement relatif de l'éditeur de code et de l'aperçu de l'interface afin qu'ils soient situés dans l'ordre horizontal à l'aide de ces boutons (à droite de la bordure des deux zones):


image


Avant de commencer


Les éléments de fenêtre (ils sont également appelés contrôles du mot Contrôle ) doivent être placés à l'intérieur du conteneur ou à l'intérieur d'un autre élément du type ContentControl. Un conteneur est un contrôle spécial qui vous permet de placer plusieurs contrôles enfants à l'intérieur et d'organiser leur disposition mutuelle. Exemples de conteneurs:


  • Grille - vous permet d'organiser les éléments par colonnes et lignes, la largeur de chaque colonne ou ligne est configurée individuellement.
  • StackPanel - vous permet d'organiser les enfants sur une seule ligne ou colonne.

Il y a d'autres conteneurs. Étant donné que le conteneur est également un contrôle, à l'intérieur du conteneur, il peut y avoir des conteneurs imbriqués contenant des conteneurs imbriqués, etc. Cela vous permet d'organiser de manière flexible les contrôles les uns par rapport aux autres. De plus, à l'aide de conteneurs, nous pouvons tout aussi facilement contrôler le comportement des contrôles imbriqués lors du redimensionnement d'une fenêtre.


Interface MVVM et INotifyPropertyChanged. Copie du texte.


Le résultat de cet exemple sera une application avec deux contrôles, dans l'un desquels vous pouvez éditer le texte, et dans l'autre juste afficher. Les modifications de l'une à l'autre seront transférées de manière synchrone sans copie explicite du texte à l'aide de la liaison .


Donc, nous avons un projet fraîchement créé (je l'ai nommé Ex1 ), allez dans l'éditeur de mise en page et remplacez tout d'abord le conteneur par défaut ( <Grid> </Grid> ) par <StackPanel> </StackPanel> . Ce conteneur sera suffisant, car nous devrons placer seulement deux contrôles l'un au-dessus de l'autre. Nous spécifions explicitement la disposition des composants en ajoutant la propriété Orientation = "Vertical" . Ajoutez quelques éléments à l'intérieur de la pile de panneaux: un champ pour saisir du texte et un champ pour afficher du texte. Étant donné que ces contrôles ne contiendront pas de code incorporé, vous pouvez les décrire avec une balise à fermeture automatique (voir le code ci-dessous). Après toutes les procédures ci-dessus, le code de description du conteneur et les contrôles imbriqués doivent prendre la forme suivante:


<StackPanel Orientation="Vertical"> <TextBox /> <TextBlock /> </StackPanel> 

Concentrons-nous maintenant sur le but de cet exemple. Nous voulons que lors de la saisie dans la zone de texte, le même texte s'affiche de manière synchrone dans le bloc de texte, tout en évitant l'opération de copie explicite. Nous avons besoin d'une sorte d'entité de connexion, et nous arrivons ici à une chose comme la liaison , qui a été mentionnée ci-dessus. La liaison dans la terminologie WPF est un mécanisme qui vous permet d'associer certaines propriétés de contrôles à certaines propriétés d'un objet de classe C # et de mettre à jour mutuellement ces propriétés lorsque l'une des parties du bundle change (cela peut fonctionner dans l'une, l'autre ou les deux directions à la fois). Pour ceux qui connaissent Qt, vous pouvez faire une analogie entre les slots et les signaux. Afin de ne pas étirer le temps, passons au code.


Ainsi, pour organiser la liaison, vous avez besoin des propriétés des contrôles et de certaines propriétés d'une certaine classe C #. Tout d'abord, découvrons le code XAML. Le texte des deux contrôles est stocké dans la propriété Text, ajoutez donc une liaison pour ces propriétés. C'est fait comme ceci:


 <TextBox Text="{Binding}"/> <TextBlock Text="{Binding}"/> 

Nous avons fait une liaison, mais pour l'instant il n'est pas clair pourquoi :) Nous avons besoin d'un objet d'une certaine classe et d'une propriété dans cet objet auquel la liaison sera faite (comme on dit, à laquelle vous devez vous lier).


Alors, quelle est cette classe? Cette classe est appelée modèle de vue et sert de lien entre la vue (l'interface ou ses parties) et le modèle (modèle, c'est-à-dire les parties du code qui sont responsables de la logique d'application. Cela vous permet de séparer (dans une certaine mesure ) la logique d'application de l'interface (vue, vue) est appelée le modèle Model-View-ViewModel (MVVM) . Dans WPF, cette classe est également appelée DataContext .


Cependant, il ne suffit pas d'écrire une classe de modèle de vue. Il est nécessaire d'indiquer au mécanisme de liaison que la propriété du modèle de vue ou la propriété de vue a changé. Pour ce faire, il existe une interface spéciale INotifyPropertyChanged , qui contient l'événement PropertyChanged . Nous implémentons cette interface dans le cadre de la classe de base BaseViewModel . À l'avenir, nous hériterons de tous nos modèles de vue de cette classe de base afin de ne pas dupliquer l'implémentation de l'interface. Ajoutez donc le répertoire ViewModels au projet et ajoutez le fichier BaseViewModel.cs à ce répertoire. Nous obtenons la structure de projet suivante:


image


Code d'implémentation du modèle de vue de base:


 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)); } } } 

Créons notre viewmodel pour notre classe MainWindow , héritant de celui de base. Pour ce faire, dans le même répertoire ViewModels , créez le fichier MainWindowViewModel.cs , dans lequel il y aura un tel code:


 namespace Ex1.ViewModels { public class MainWindowViewModel : BaseViewModel { } } 

Super! Nous devons maintenant ajouter une propriété à ce modèle de vue, sur laquelle nous lierons le texte de nos contrôles. Comme il s'agit de texte, le type de cette propriété doit être une chaîne :


 public string SynchronizedText { get; set; } 

En conséquence, nous obtenons un tel code


 namespace Ex1.ViewModels { public class MainWindowViewModel : BaseViewModel { public string SynchronizedText { get; set; } } } 

Il semble donc qu'ils l'ont fait. Il reste à lier sur cette propriété de la vue et est prêt. Faisons-le maintenant:


 <TextBox Text="{Binding Path=SynchronizedText}"/> <TextBlock Text="{Binding Path=SynchronizedText}"/> 

Nishtyak, nous commençons le projet, nous tapons dans la zone de texte iiiii ... rien ne se passe))) Eh bien, ça va, en fait nous allons dans le bon sens, nous n'avons tout simplement pas atteint le bon point.


Je propose de m'arrêter un instant et de réfléchir à ce qui nous manque. Nous avons une vue. Viewmodel aussi. Des propriétés comme zabindili. L'interface souhaitée est implémentée. Nous avons fait beaucoup de travail pour copier une ligne de texte pathétique, pourquoi avons-nous besoin de cela ???!?! 111


D'accord, blagues à part. Nous avons oublié de créer un objet de modèle de vue et quelque chose d'autre (plus à ce sujet plus tard). Nous avons décrit la classe elle-même, mais cela ne veut rien dire, car nous n'avons pas d'objets de cette classe. Ok, où avez-vous besoin de stocker un lien vers cet objet? Plus près du début de l'exemple, j'ai mentionné un certain DataContext utilisé dans WPF. Ainsi, toute vue possède une propriété DataContext à laquelle nous pouvons attribuer un lien à notre modèle de vue. Faisons-le. Pour ce faire, ouvrez le fichier MainWindow.xaml et appuyez sur F7 pour ouvrir le code de cette vue. Il est presque vide, il n'a qu'un constructeur de classe de fenêtre. Ajoutez-y la création de notre modèle de vue et placez-le dans le DataContext de la fenêtre (n'oubliez pas d'ajouter en utilisant l'espace de noms souhaité):


 public MainWindow() { InitializeComponent(); this.DataContext = new MainWindowViewModel(); } 

C'était simple, mais toujours pas suffisant. Pourtant, lorsque l'application démarre, aucune synchronisation de texte ne se produit. Que faut-il faire d'autre?


Vous devez déclencher l'événement PropertyChanged lorsque la propriété SynchronizedText change et informer la vue qu'elle doit surveiller cet événement. Donc, pour déclencher l'événement, modifiez le code du modèle de vue:


 public class MainWindowViewModel : BaseViewModel { private string _synchronizedText; public string SynchronizedText { get => _synchronizedText; set { _synchronizedText = value; OnPropertyChanged(nameof(SynchronizedText)); } } } 

Qu'avons-nous fait ici? Nous avons ajouté un champ masqué pour stocker du texte, l'avons enveloppé dans une propriété existante et lorsque nous modifions cette propriété, nous changeons non seulement le champ masqué, mais appelons également la méthode OnPropertyChanged définie dans le modèle de vue de base et déclenchons l'événement PropertyChanged déclaré dans l'interface INotifyPropertyChanged , également implémenté dans la base afficher les modèles. Il s'avère que chaque fois que le texte est modifié, l'événement PropertyChanged se produit, auquel le nom de la propriété du modèle de vue qui a été modifié est transmis.


Eh bien, presque tout, la ligne d'arrivée! Il reste à spécifier la vue qu'il doit écouter l'événement PropertyChanged :


 <TextBox Text="{Binding Path=SynchronizedText, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}"/> <TextBlock Text="{Binding Path=SynchronizedText, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/> 

Outre le fait que nous avons indiqué par quel déclencheur la mise à jour doit avoir lieu, nous avons également indiqué dans quelle direction cette mise à jour est suivie: d'une vue à l'autre du modèle ou vice versa. Puisque nous saisissons du texte dans la zone de texte, nous ne sommes intéressés que par les changements dans la vue, nous sélectionnons donc le mode OneWayToSource . Dans le cas du bloc de texte, tout est exactement le contraire: nous nous intéressons aux modifications du modèle de vue afin de les afficher dans la vue, nous sélectionnons donc le mode OneWay . Si nous voulions que les modifications soient suivies dans les deux sens, nous ne pouvions pas du tout spécifier le mode , ni spécifier explicitement TwoWay .


Alors, lancez le programme, tapez le texte et voi-la! Le texte change de façon synchrone, et nous n'avons rien copié nulle part!


image


Merci de votre attention, à suivre. Nous traiterons du DataTemplate et du modèle de commande.

Source: https://habr.com/ru/post/fr427325/


All Articles