• Aucun résultat trouvé

Première mise en place de MVVM

Nous avons commencé à pratiquer un peu MVVM au chapitre précédent, en fournissant un contexte dans une classe séparée.

Ce contexte est le view-model, il prépare les données afin qu’elles soient affichables par la vue. Si on veut être un peu plus précis et utiliser un langage plus proche des patrons de conception, on pourrait dire que le view-model « adapte » le modèle pour la

vue.

Commençons par créer un nouveau projet pour mettre en place une version simplifiée de MVVM. Comme l’idée est de séparer les responsabilités, nous allons en profiter pour créer des répertoires pour notre modèle, nos vues et nos view-models.

Créons donc les répertoires et les fichiers suivants : Model

Client.cs

ServiceClient.cs View

VoirClientView.xaml ViewModel

VoirClientViewModel.cs

Et profitons-en pour supprimer le fichier MainPage.xaml, de manière à avoir la même architecture que sur la figure suivante.

Architecture de la solution MVVM

Par convention, vous aurez compris que le modèle se place dans le répertoire Model, que les vues se placent dans le répertoire View et que les view-models se placent dans le répertoire ViewModel. De même, on suffixera les vues par « View » et les view-models par « ViewModel ».

En l’état, notre application ne pourra pas démarrer ainsi, car notre application va essayer de démarrer en naviguant sur le fichier MainPage.xaml, que nous avons supprimé. Nous devons donc lui indiquer un nouveau point d’entrée. Cela se fait depuis le fichier WMAppManifest.xml qui est sous le répertoire Properties. Ouvrez-le et modifiez la Page de navigation, comme nous l’avons déjà fait, pour y mettre : View/VoirClientView.xaml.

Cela nous permet de dire que l’application doit démarrer en affichant la page VoirClientView.xaml.

Commençons par créer un modèle ultra simple, qui consiste en une classe client qui contient un prénom, un âge et un booléen indiquant s’il est un bon client :

Code : C#

public class Client

{ public string Prenom { get; set; } public int Age { get; set; }

public bool EstBonClient { get; set; } }

Ainsi qu’un service qui va nous simuler le chargement d’un client : Code : C#

public class ServiceClient

{ public Client Charger()

Maintenant, réalisons la vue. Nous allons simplement afficher le prénom et l’âge du client. Ceux-ci seront sur un fond vert si le client est un bon client et en rouge si c’est un mauvais client. Quelque chose comme ça :

Code : XML

<Grid x:Name="LayoutRoot" Background="Transparent">

<Grid.RowDefinitions>

<RowDefinition Height="Auto"/>

<RowDefinition Height="auto"/>

</Grid.RowDefinitions>

<StackPanel x:Name="TitlePanel" Grid.Row="0"

Margin="12,17,0,28">

<TextBlock x:Name="PageTitle" Text="Fiche client"

Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>

</StackPanel>

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"

Background="{Binding BonClient}">

<TextBlock Grid.Column="1" Text="{Binding Prenom}" />

<TextBlock Grid.Row="1" Text="Age : " />

<TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding Age}"

/>

</Grid>

</Grid>

On utilise les expressions de balisage pour indiquer les valeurs grâce au binding. Sauf qu’il est difficile de se rendre compte ainsi si la vue est bien construite, car il nous manque les valeurs de design. Qu’à cela ne tienne, nous savons désormais comment créer un contexte de design. Créons un nouveau répertoire sous le répertoire ViewModel que nous appelons Design et une nouvelle classe s’appelant DesignVoirClientViewModel.cs qui contiendra les valeurs de design suivantes :

Code : C#

public SolidColorBrush BonClient

{

get { return new SolidColorBrush(Color.FromArgb(100, 0, 255, 0)); }

} }

Pour rappel, SolidColorBrush se trouve dans l’espace de nom System.Windows.Media.

Il faut ensuite lier le contexte de design au view-model de design : Code : XML

<d:DesignProperties.DataContext>

<design:DesignVoirClientViewModel />

</d:DesignProperties.DataContext>

sans oublier d’importer l’espace de nom correspondant : Code : XML

xmlns:design="clr-namespace:DemoMvvm.ViewModel.Design"

Ainsi, nous pourrons avoir en mode design le résultat affiché à la figure suivante.

Affichage des données en mode design grâce à MVVM

Ce qui est le résultat attendu. Chouette !

Passons enfin au view-model. Nous avons vu qu’il devait implémenter l’interface INotifyPropertyChanged : Code : C#

public class VoirClientViewModel : INotifyPropertyChanged { private string prenom;

private SolidColorBrush bonClient;

public SolidColorBrush BonClient {

get { return bonClient; }

set { NotifyPropertyChanged(ref bonClient, value); } }

public event PropertyChangedEventHandler PropertyChanged;

public void NotifyPropertyChanged(string nomPropriete) {

if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(nomPropriete));

}

private bool NotifyPropertyChanged<T>(ref T variable, T valeur, [CallerMemberName] string nomPropriete = null)

{

if (object.Equals(variable, valeur)) return false;

variable = valeur;

NotifyPropertyChanged(nomPropriete);

return true;

} }

Rien de sorcier, nous définissons également les propriétés Prenom, Age et BonClient. Reste à charger notre modèle depuis notre view-model et à affecter les propriétés du view-model à partir des valeurs du modèle :

Code : C#

public void ChargeClient()

{ ServiceClient service = new ServiceClient();

Client client = service.Charger();

Prenom = client.Prenom;

Age = client.Age;

if (client.EstBonClient)

BonClient = new SolidColorBrush(Color.FromArgb(100, 0, 255, 0));

else

BonClient = new SolidColorBrush(Color.FromArgb(100, 255, 0, 0));}

Il nous manque une dernière chose, hautement indispensable, qui est de lier la vue au view-model. Pour l’instant, nous avons vu que nous pouvions le faire depuis le code-behind de la vue, avec :

Code : C#

public partial class VoirClientView : PhoneApplicationPage { public VoirClientView()

Il y a une autre solution qui évite de passer par le code, en utilisant le XAML : Code : XML

<phone:PhoneApplicationPage.DataContext>

<viewmodel:VoirClientViewModel />

</phone:PhoneApplicationPage.DataContext>

Ce qui revient au même, vu qu’on positionne la propriété DataContext de la page à une instance du view-model. C’est cette solution que nous allons privilégier ici.

Vous n’aurez bien sûr pas oublié d’inclure l’espace de nom qui va bien : Code : XML

xmlns:viewmodel="clr-namespace:DemoMvvm.ViewModel"

Revenons à présent un peu sur ce que nous avons fait. Nous avons créé une vue, la page XAML, liée à l’exécution au view-model VoirClientViewModel et liée en design au pseudo view-view-model DesignVoirClientViewModel. Le pseudo model de design expose des données en dur, pour nous permettre d’avoir des données dans le designer alors que le view-model utilise et transforme le view-model pour exposer les mêmes données.

Une bonne pratique ici serait de définir une interface avec les données à exposer et que nos deux view-models l’implémentent.

Créons donc un répertoire Interface dans le répertoire ViewModel et créons l’interface IVoirClientViewModel : Code : C#

public interface IVoirClientViewModel { string Prenom { get; set; }

int Age { get; set; }

SolidColorBrush BonClient { get; set; } }

Nos deux view-models doivent implémenter cette interface :

Code : C#

public class VoirClientViewModel : INotifyPropertyChanged, IVoirClientViewModel

{ … }

Et :

Code : C#

public class DesignVoirClientViewModel : IVoirClientViewModel { public string Prenom

public SolidColorBrush BonClient {

Vous aurez remarqué que nous avons rajouté le mutateur set dans le view-model de design, et qu’elle n’a besoin de rien faire.

Nous pouvons encore faire une petite amélioration. Ici, elle est mineure car nous n’avons qu’un seul view-model, mais elle sera intéressante dès que nous en aurons plusieurs. En effet, chaque view-model doit implémenter l’interface

INotifyPropertyChanged et avoir le code suivant : Code : C#

private bool NotifyPropertyChanged<T>(ref T variable, T valeur, [CallerMemberName] string nomPropriete = null)

{ if (object.Equals(variable, valeur)) return false;

variable = valeur;

NotifyPropertyChanged(nomPropriete);

return true;

}

Nous pouvons factoriser ce code dans une classe de base dont vont dériver tous les view-models. Créons pour cela un répertoire FrameworkMvvm et dedans, mettons-y la classe ViewModelBase :

Code : C#

public class ViewModelBase : INotifyPropertyChanged {

public event PropertyChangedEventHandler PropertyChanged;

public void NotifyPropertyChanged(string nomPropriete) {

if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(nomPropriete));

}

public bool NotifyPropertyChanged<T>(ref T variable, T valeur, [CallerMemberName] string nomPropriete = null)

{

if (object.Equals(variable, valeur)) return false;

variable = valeur;

NotifyPropertyChanged(nomPropriete);

return true;

} }

N’oubliez pas de passer la méthode NotifyPropertyChanged en public.

Il ne reste plus qu’à supprimer ce code du view-model et de le faire hériter de cette classe de base : Code : C#

public class VoirClientViewModel : ViewModelBase, IVoirClientViewModel

{ … }