MVVM - Communication View / ViewModel
Dans ce chapitre, nous allons apprendre comment ajouter de l'interactivité aux applications MVVM et comment appeler proprement la logique. Vous verrez également que tout cela se fait en conservant le couplage lâche et une bonne structuration qui est au cœur du pattern MVVM. Pour comprendre tout cela, apprenons d'abord les commandes.
Affichage / Affichage de la communication du modèle via des commandes
Le modèle de commande a été bien documenté et utilise fréquemment le modèle de conception depuis quelques décennies. Dans ce modèle, il y a deux acteurs principaux, l'invocateur et le receveur.
Invocateur
L'invocateur est un morceau de code qui peut exécuter une logique impérative.
En règle générale, il s'agit d'un élément d'interface utilisateur avec lequel l'utilisateur interagit, dans le contexte d'une infrastructure d'interface utilisateur.
Il peut s'agir simplement d'un autre morceau de code logique ailleurs dans l'application.
Destinataire
Le récepteur est la logique destinée à être exécutée lorsque l'invocateur se déclenche.
Dans le contexte de MVVM, le récepteur est généralement une méthode de votre ViewModel qui doit être appelée.
Entre ces deux, vous avez une couche d'obstruction, ce qui implique que l'invocateur et le receveur n'ont pas à se connaître explicitement. Ceci est généralement représenté comme une abstraction d'interface exposée à l'invocateur et une implémentation concrète de cette interface est capable d'appeler le récepteur.
Jetons un coup d'œil à un exemple simple dans lequel vous apprendrez les commandes et comment les utiliser pour communiquer entre View et ViewModel. Dans ce chapitre, nous continuerons avec le même exemple du dernier chapitre.
Dans le fichier StudentView.xaml, nous avons un ListBox qui relie les données des étudiants à partir d'un ViewModel. Ajoutons maintenant un bouton pour supprimer un étudiant de la ListBox.
La chose importante est que travailler avec des commandes sur un bouton est très facile car elles ont une propriété de commande à connecter à une ICommand.
Ainsi, nous pouvons exposer une propriété sur notre ViewModel qui a un ICommand et s'y lie à partir de la propriété de commande du bouton, comme indiqué dans le code suivant.
<Button Content = "Delete"
Command = "{Binding DeleteCommand}"
HorizontalAlignment = "Left"
VerticalAlignment = "Top"
Width = "75" />
Ajoutons une nouvelle classe dans votre projet, qui implémentera l'interface ICommand. Voici l'implémentation de l'interface ICommand.
using System;
using System.Windows.Input;
namespace MVVMDemo {
public class MyICommand : ICommand {
Action _TargetExecuteMethod;
Func<bool> _TargetCanExecuteMethod;
public MyICommand(Action executeMethod) {
_TargetExecuteMethod = executeMethod;
}
public MyICommand(Action executeMethod, Func<bool> canExecuteMethod){
_TargetExecuteMethod = executeMethod;
_TargetCanExecuteMethod = canExecuteMethod;
}
public void RaiseCanExecuteChanged() {
CanExecuteChanged(this, EventArgs.Empty);
}
bool ICommand.CanExecute(object parameter) {
if (_TargetCanExecuteMethod != null) {
return _TargetCanExecuteMethod();
}
if (_TargetExecuteMethod != null) {
return true;
}
return false;
}
// Beware - should use weak references if command instance lifetime
is longer than lifetime of UI objects that get hooked up to command
// Prism commands solve this in their implementation
public event EventHandler CanExecuteChanged = delegate { };
void ICommand.Execute(object parameter) {
if (_TargetExecuteMethod != null) {
_TargetExecuteMethod();
}
}
}
}
Comme vous pouvez le voir, il s'agit d'une implémentation de délégation simple d'ICommand où nous avons deux délégués, un pour executeMethod et un pour canExecuteMethod qui peut être transmis lors de la construction.
Dans l'implémentation ci-dessus, il y a deux constructeurs surchargés, un pour seulement executeMethod et un pour les deux executeMethod et je peux canExecuteMethod.
Ajoutons une propriété de type MyICommand dans la classe StudentView Model. Nous devons maintenant construire une instance dans StudentViewModel. Nous utiliserons le constructeur surchargé de MyICommand qui prend deux paramètres.
public MyICommand DeleteCommand { get; set;}
public StudentViewModel() {
LoadStudents();
DeleteCommand = new MyICommand(OnDelete, CanDelete);
}
Ajoutez maintenant l'implémentation des méthodes OnDelete et CanDelete.
private void OnDelete() {
Students.Remove(SelectedStudent);
}
private bool CanDelete() {
return SelectedStudent != null;
}
Nous devons également ajouter un nouveau SelectedStudent afin que l'utilisateur puisse supprimer l'élément sélectionné de ListBox.
private Student _selectedStudent;
public Student SelectedStudent {
get {
return _selectedStudent;
}
set {
_selectedStudent = value;
DeleteCommand.RaiseCanExecuteChanged();
}
}
Voici l'implémentation complète de la classe ViewModel.
using MVVMDemo.Model;
using System.Collections.ObjectModel;
using System.Windows.Input;
using System;
namespace MVVMDemo.ViewModel {
public class StudentViewModel {
public MyICommand DeleteCommand { get; set;}
public StudentViewModel() {
LoadStudents();
DeleteCommand = new MyICommand(OnDelete, CanDelete);
}
public ObservableCollection<Student> Students {
get;
set;
}
public void LoadStudents() {
ObservableCollection<Student> students = new ObservableCollection<Student>();
students.Add(new Student { FirstName = "Mark", LastName = "Allain" });
students.Add(new Student { FirstName = "Allen", LastName = "Brown" });
students.Add(new Student { FirstName = "Linda", LastName = "Hamerski" });
Students = students;
}
private Student _selectedStudent;
public Student SelectedStudent {
get {
return _selectedStudent;
}
set {
_selectedStudent = value;
DeleteCommand.RaiseCanExecuteChanged();
}
}
private void OnDelete() {
Students.Remove(SelectedStudent);
}
private bool CanDelete() {
return SelectedStudent != null;
}
}
}
Dans StudentView.xaml, nous devons ajouter la propriété SelectedItem dans un ListBox qui se liera à la propriété SelectStudent.
<ListBox ItemsSource = "{Binding Students}" SelectedItem = "{Binding SelectedStudent}"/>
Voici le fichier xaml complet.
<UserControl x:Class = "MVVMDemo.Views.StudentView"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:local = "clr-namespace:MVVMDemo.Views"
xmlns:viewModel = "clr-namespace:MVVMDemo.ViewModel"
xmlns:data = "clr-namespace:MVVMDemo.Model"
xmlns:vml = "clr-namespace:MVVMDemo.VML"
vml:ViewModelLocator.AutoHookedUpViewModel = "True"
mc:Ignorable = "d"
d:DesignHeight = "300" d:DesignWidth = "300">
<UserControl.Resources>
<DataTemplate DataType = "{x:Type data:Student}">
<StackPanel Orientation = "Horizontal">
<TextBox Text = "{Binding Path = FirstName, Mode = TwoWay}"
Width = "100" Margin = "3 5 3 5"/>
<TextBox Text = "{Binding Path = LastName, Mode = TwoWay}"
Width = "100" Margin = "0 5 3 5"/>
<TextBlock Text = "{Binding Path = FullName, Mode = OneWay}"
Margin = "0 5 3 5"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid>
<StackPanel Orientation = "Horizontal">
<ListBox ItemsSource = "{Binding Students}"
SelectedItem = "{Binding SelectedStudent}"/>
<Button Content = "Delete"
Command = "{Binding DeleteCommand}"
HorizontalAlignment = "Left"
VerticalAlignment = "Top"
Width = "75" />
</StackPanel>
</Grid>
</UserControl>
Lorsque le code ci-dessus est compilé et exécuté, vous verrez la fenêtre suivante.
Vous pouvez voir que le bouton de suppression est désactivé. Il sera activé lorsque vous sélectionnez un élément.
Lorsque vous sélectionnez un élément et appuyez sur Supprimer. Vous verrez que la liste des éléments sélectionnés est supprimée et le bouton de suppression est à nouveau désactivé.
Nous vous recommandons d'exécuter l'exemple ci-dessus étape par étape pour une meilleure compréhension.