Accueil Nos publications Blog MVVM Light, Commandes et CanExecute

MVVM Light, Commandes et CanExecute

Si vous aviez l’habitude de WPF et des RoutedCommand (implémentant l’interface ICommand), vous avez dû remarquer qu’en utilisant MvvmLight et les RelayCommand dans vos projets le comportement de l’action CanExecute est sensiblement différent.

Mise en place de la commande

Afin de comprendre le problème, nous allons mettre en place une commande simple d’authentification par exemple. (Pour rappel, il s’agit d’une application Silverlight).

[csharp]
public RelayCommand LoginCommand { get; private set; }

private bool Login_CanExecute()
{
return !string.IsNullOrEmpty(Login) && !string.IsNullOrEmpty(Password);
}

private void Login_Executed()
{
//Some stuff to authenticate user
}

private void InitializeCommands()
{
LoginCommand = new RelayCommand(Login_Executed, Login_CanExecute);
}

public SettingsViewModel()
{
if (IsInDesignMode) {
// Code runs in Blend –> create design time data.
}
else {
// Code runs "for real": Connect to service, etc…
InitializeCommands();
}
}

private string _Login;
public string Login
{
get { return _Login; }
set
{
if (_Login != value)
{
_Login = value;
RaisePropertyChanged("Login");
}
}
}

private string _Password;
public string Password
{
get { return _Password; }
set
{
if (_Password != value)
{
_Password = value;
RaisePropertyChanged("Password");
}
}
}
[/csharp]

et dans notre code xaml :

[xml]
<TextBlock Text=”Login” />
<TextBox Text=”{Binding Path=Login, Mode=TwoWay}” />

<TextBlock Text=”Password” />
<PasswordBox Password=”{Binding Path=Password, Mode=TwoWay}” />

<Button Command=”{Binding LoginCommand}” Content=”Log In” />
[/xml]

Petit rappel pour les RoutedCommand

Les RoutedCommand font parties d’un mécanisme propre à WPF (l’objectif n’est pas d’en parler dans cet article). Dans un long document qui en parle sur la MSDN, on peut y lire ceci dans un petit paragraphe :

Routed commands will automatically enable or disable all associated UI controls when the handler indicates the command is disabled.

Quel est le comportement attendu dans notre exemple ?

Tant que le login et le password ne sont pas renseignés, le bouton est censé être désactivé. Cependant, même s’ils sont renseignés, ce n’est pas le cas. Il faut quitter le contexte, puis, y revenir, et seulement si vos données sont persistées (Le Login et le Password), alors, la condition du CanExecute (Login_CanExecute) sera levée et du fait valide.

Quelle est la différence entre les RoutedCommand et les RelayCommand ?

La réponse a déjà été abordée ici. Le mécanisme d’évènements routés dans WPF permet aux commandes d’informer le contrôle sur lequel ils sont bindés du fait d’avoir un état activé ou pas. Concernant les RelayCommand, ce mécanisme de notification n’est pas automatique malheureusement et il faut notifier la commande que les conditions du CanExecute ont changé. Ce processus est indispensable.

Du coup, il faut nous modifier nos propriétés Login et Password puis notifier le changement de la condition du CanExecute sur la commande:

[csharp]
private string _Login;
public string Login
{
get { return _Login; }
set
{
if (_Login != value)
{
_Login = value;
RaisePropertyChanged(&amp;amp;amp;amp;amp;quot;Login&amp;amp;amp;amp;amp;quot;);
LoginCommand.RaiseCanExecuteChanged();
}
}
}

private string _Password;
public string Password
{
get { return _Password; }
set
{
if (_Password != value)
{
_Password = value;
RaisePropertyChanged(&amp;amp;amp;amp;amp;quot;Password&amp;amp;amp;amp;amp;quot;);
LoginCommand.RaiseCanExecuteChanged();
}
}
}
[/csharp]

Conclusion

Et voilà, nous avons pu ainsi résoudre notre “problème” et obtenir le même comportement que sous WPF. Il existe des solutions pour éviter d’avoir à notifier manuellement de la façon dont nous nous y sommes pris ici, mais, ce sera l’objet d’un autre article.

Bon Développement !