-
Posts: 1524
Nickname: nitronic
Registered: Jul, 2006
|
Norbert Eder works as a software architect.
|
|
|
|
WPF: RoutedCommand, RoutedUICommand, ICommand und Commands im Allgemeinen - Eine Einführung
|
Posted: Jan 2, 2008 10:45 AM
|
|
Einige mögen bereits wissen, dass unter der Windows Presentation Foundation die Möglichkeit besteht, Commands zu verwenden. Für andere mag es neu sein. Wem ein Command gar nichts sagt, der möge sich meinen Beitrag Patterns: Command Pattern zuvor zu Gemüte führen.
Das Command-System wurde in die Windows Presentation Foundation integriert, um durchzuführende Aufgaben/Tasks besser vom User Interface (Oberfläche) zu trennen.
Warum UI von Aufgaben trennen
Ein oft geschilderter Vorteil der WPF ist, dass das grafische Design nicht zwingend vom Entwickler gemacht werden muss. So kann hier in der Tat ein Designer Hand anlegen (jedoch muss er etwas von XAML verstehen). Das ist jedoch nicht der Punkt, auf den ich hinaus möchte.
Ein Grundsatz der Softwareentwicklung ist es, stabile und robuste Software zu bauen. Dazu muss natürlich getestet werden. Manuelle Tests, die alles abdecken sind aber einer bestimmten Projektgröße kaum durchzuführen. Automatisierte Tests müssen her. Mit Hilfe von Unit Tests können nun Teile der Anwendung getestet werden. Dies funktioniert jedoch nur dann, wenn diese Teile vom grafischen Aufbau komplett getrennt sind. Sinnvoller Weise werden nun sämtliche Aufgaben von der UI getrennt und werden/bleiben so testbar.
Wie kann die WPF hier helfen?
Mit Hilfe der WPF Commands muss hier kein großer Aufwand betrieben werden, um obiges Ziel auch tatsächlich zu erreichen. Nun gibt es jedoch zwei abzudeckende Möglichkeiten:
- Commands müssen das UI aktualisieren
- Commands können komplett frei von allen UI-Einflüssen ausgeführt werden
Muss der Command mit der Oberfläche interagieren, sind RoutedUICommands genau der richtige Ansatz. Im zweiten Fall ist das Interface ICommand zu implementieren.
Bezüglich der RoutedUICommands ist es nun so, dass durch die WPF bereits einige fertige Implementierungen vorhanden sind:
Der Vorteil liegt darin, dass diese Commands sehr einfach an UIElemente gebunden und ausgeführt werden können. Hier ein kleines (unvollständiges) Beispiel zur Veranschaulichung:
<UserControl.CommandBindings>
<CommandBinding
Command="MediaCommands.Play"
CanExecute="OnQueryExecute"
Executed="Execute"/>
</UserControl.CommandBindings>
<StackPanel Grid.Row="2" Orientation="Horizontal">
<Button
Command="MediaCommands.Play"
Content="{Binding RelativeSource={RelativeSource Self},
Path=Command.Text}" />
</StackPanel>
Da diese Commands jedoch ein spezifisches Verhalten anbieten, welches nicht immer benötigt wird, ist es mitunter notwendig, eigene Commands zu implementieren, um die gewünschte Funktionalität zu erhalten.
Eigene Commands via ICommand implementieren
Müssen eigene Funktionalitäten als Command abgebildet werden, kann dies mit Hilfe einer Implementierung des ICommand-Interfaces passieren. Dieses Interface schreibt zwei Methoden vor und ein Event vor:
- CanExecute
- Execute
- CanExecuteChanged
Ein simpler Beispielcommand, welcher ein bestimmtes Objekt entgegeben nimmt und dieses manipuliert, könnte wie folgt aussehen:
public class MySaveCommand : ICommand
{
#region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (parameter is EasySum)
{
EasySum es = (EasySum)parameter;
es.Result = es.Summand1 + es.Summand2;
}
}
#endregion
}
Eine weitere - und vor allem übliche - Möglichkeit besteht darin, einen RoutedCommand zu erstellen.
RoutedCommand erstellen
Mit Hilfe von RoutedCommands ist es sehr einfach, einen Command für das User Interface zu erstellen. Dazu benötigt es nicht sehr viel. Das nachfolgende Beispiel zeigt, wie anhand eines RoutedCommands die Hintergrundfarbe eines Panels verändert werden kann.
Hierzu muss unser neuer Command definiert werden:
public static RoutedCommand ChangeBackgroundCommand = new RoutedCommand();
Des weiteren müssen zwei Eventhandler implementiert werden. Zum einen für das CanExecute-Event und zum anderen für das Executed-Event.
private void ChangeBackgroundCommandCanExecute(
object sender, CanExecuteRoutedEventArgs e)
{
if (sender is Panel)
e.CanExecute = true;
else
e.CanExecute = false;
}
private void ChangeBackgroundCommandExecuted(
object sender, ExecutedRoutedEventArgs e)
{
Panel target = e.Source as Panel;
if (target != null)
{
if (target.Background == Brushes.AliceBlue)
{
target.Background = Brushes.LemonChiffon;
}
else
{
target.Background = Brushes.AliceBlue;
}
}
}
Im Eventhandler für das CanExecute-Event überprüfen wir lediglich, ob das übergebene UIElement auch tatsächlich ein Panel ist. Ist dem so, lassen wir eine Manipulation der Hintergrundfarbe zu. Andernfalls wird eine Ausführung des Commands verwehrt.
Im Eventhandler für das Executed-Event wird dann die Hintergrundfarbe geändert bzw. wieder auf die Ursprungsfarbe zurück gestellt.
Damit der Command nun auch verwendet werden kann, muss ein CommandBinding erstellt werden:
<StackPanel Name="MyStackPanel"
Background="AliceBlue"
Focusable="True">
<StackPanel.CommandBindings>
<CommandBinding
Command="{x:Static custom:Window1.ChangeBackgroundCommand}"
Executed="ChangeBackgroundCommandExecuted"
CanExecute="ChangeBackgroundCommandCanExecute"/>
</StackPanel.CommandBindings>
<Label>A StackPanel</Label>
<Button Command="{x:Static custom:Window1.ChangeBackgroundCommand}"
CommandParameter="ButtonOne"
CommandTarget="{Binding ElementName=MyStackPanel}"
Content="Change Background" />
</StackPanel>
Wie zu sehen ist, wird hier der aufzurufende Command definiert, als auch die beiden Eventhandler, die bereits implementiert wurden. custom definiert hier lediglich einen eigenen Namespace.
Das CommandBinding ist an einem StackPanel definiert, welches ein Label und einen Button enthält, welcher dann schlussendlich den Command auslöst.
Fazit
Dieser Artikel hat das der WPF zugrunde liegende Konzept der Commands vorgestellt und eine kleine Übersicht inklusive einiger Beispiele gegeben. Ebenso wurden die Unterschiede erläutert. Damit sollte nun jeder angehende WPF-Entwickler das notwendigste Wissen mitbringen, um eigene Commands zu entwickeln und diese in seinen Anwendungen einzusetzen. Natürlich wird es durchaus noch notwendig sein, das eine oder andere nachzulesen. Der Start sollte damit allerdings gemacht sein.
Read: WPF: RoutedCommand, RoutedUICommand, ICommand und Commands im Allgemeinen - Eine Einführung
|
|