VII. Les commandes▲
Jusqu'à présent, nous avons utilisé lors de clics sur des boutons, des gestionnaires d'évènements comme en Winform et ASP.Net. Des nouveautés ont été introduites sur Silverlight/WPF de ce côté. En effet, désormais il est préférable de ne plus utiliser les gestionnaires d'évènements pour les boutons, mais plutôt de « binder » une commande à la propriété Command de Button.
Mais qu'est-ce qu'une commande ? Une commande est une instance d'une classe implémentant ICommand. Bien souvent, dans les frameworks courants, on retrouve une implémentation assez similaire à celle que nous allons voir ici.
ICommand se compose de deux méthodes Execute et CanExecute, la première contient le traitement qui doit se produire lors du déclenchement et la seconde permet de faire un test pour savoir si le traitement peut se dérouler. Il y a également un évènement qui permet de notifier aux abonnés de la commande si le contexte a changé de telle manière qu'un appel à CanExecute ne retourne pas le même résultat qu'avant (exemple : suite à une manipulation utilisateur le test de sécurité qui était faux, devient vrai et l'utilisateur peut ainsi être autorisé à cliquer).
Afin d'avoir quelque chose d'assez générique, voici une proposition d'implémentation :
public
class
CommandImpl :
ICommand
{
private
readonly
Action<
object
>
_execute;
private
readonly
Func<
object
,
bool
>
_canExecute;
public
CommandImpl
(
Action<
object
>
execute)
:
this
(
execute,
null
)
{
}
public
CommandImpl
(
Action<
object
>
execute,
Func<
object
,
bool
>
canExecute)
{
_execute =
execute;
_canExecute =
canExecute;
}
public
void
Execute
(
object
parameter)
{
if
(
_execute !=
null
)
_execute
(
parameter);
}
public
bool
CanExecute
(
object
parameter)
{
if
(
_canExecute !=
null
)
return
_canExecute
(
parameter);
else
return
true
;
}
public
event
EventHandler CanExecuteChanged;
public
void
RequerySuggested
(
)
{
if
(
CanExecuteChanged !=
null
)
CanExecuteChanged
(
this
,
EventArgs.
Empty);
}
}
Cette proposition contient deux constructeurs, le premier précise uniquement l'action à mener (à utiliser quand il n'y a pas de restrictions sur l'utilisation du bouton) et le second précise également les conditions d'exécution.
L'utilisation est la suivante :
<Grid
x
:
Name
=
"LayoutRoot"
Margin
=
"5"
>
<Grid.RowDefinitions>
<RowDefinition
Height
=
"Auto"
/>
<RowDefinition
Height
=
"Auto"
/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition
Width
=
"100"
/>
<ColumnDefinition
Width
=
"100"
/>
<ColumnDefinition
Width
=
"Auto"
/>
</Grid.ColumnDefinitions>
<TextBox
Text
=
"{Binding Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
<Button Grid.
Column
=
"1"
Width
=
"50"
Content
=
"Submit"
Command
=
"{Binding Path=MyCommand}"
/>
</Grid>
public
partial
class
MainWindow :
INotifyPropertyChanged
{
private
String _value;
public
String Value
{
get
{
return
_value;
}
set
{
_value =
value
;
RaisePropertyChanged
(
"Value"
);
MyCommand.
RequerySuggested
(
);
}
}
public
CommandImpl MyCommand {
get
;
private
set
;
}
public
MainWindow
(
)
{
InitializeComponent
(
);
DataContext =
this
;
MyCommand =
new
CommandImpl
(
MyCommandExecute,
MyCommandCanExecute);
}
private
void
MyCommandExecute
(
object
o)
{
MessageBox.
Show
(
"Hello world!"
);
}
private
bool
MyCommandCanExecute
(
object
o)
{
return
!
String.
IsNullOrWhiteSpace
(
Value);
}
#region INotifyPropertyChanged
public
event
PropertyChangedEventHandler PropertyChanged;
private
void
RaisePropertyChanged
(
string
propertyName)
{
if
(
PropertyChanged !=
null
)
PropertyChanged
(
this
,
new
PropertyChangedEventArgs
(
propertyName));
}
#endregion
}
Ici, pour le traitement (Execute), on spécifie la méthode à appeler, mais cela pourrait aussi être une fonction anonyme (un délégué Action<object>). Ce traitement affiche une MessageBox. De la même manière on utilise une fonction qui retourne un booléen afin de vérifier que la propriété Value est bien remplie (on pourrait également se servir d'une fonction anonyme Func<object,bool>). Comme ma commande dépend de la propriété Value, la méthode RequerySuggested de la commande est appelée dans le setter de la propriété. Ainsi, dès que Value change, la condition est réévaluée. Lors du test de ce code, on s'aperçoit qu'on vient d'implémenter d'une façon élégante un contrôle de saisie.