This post is part of a Prism 2 series:
Prism 2: WPF and Silverlight – Inversion of Control
Prism 2: WPF and Silverlight – Multi Platform Targeting
Prism 2: WPF and Silverlight – Modularity
Prism 2: WPF and Silverlight – Model-View-ViewModel
Prism 2: WPF and Silverlight – View Injection/View Discovery
Prism 2: WPF and Silverlight – Commands
Prism 2: WPF and Silverlight – Events
Prism 2: WPF and Silverlight – Services
Commands and MVVM
When working with MVVM pattern we quickly reach the point when using WPF commands is not a good option because Wpf commands are tied to the logical tree meaning that they cannot be handled outside of the view.
This has big impact on unit testing and components design. What we really want is to move command handling code from View to ViewModel. Unlike Wpf commands, commands in Prism can be handled outside of the logical tree in a loosely coupled way enabling better separation of concerns.
There are two command types in Prism (both implement ICommand interface): DelegateCommand and CompositeCommand.
DelegateCommand<T>
We use DelegateCommand to delegate handling logic to the ViewModel.
Let’s say that we want to create login screen for our application.
We may start from creating a ViewModel:
1: public class LoginViewModel
2: {
3: private DelegateCommand<string> loginCommand;
4:
5: public DelegateCommand<string> LoginCommand
6: {
7: get { return loginCommand; }
8: private set { loginCommand = value; }
9: }
10:
11: public LoginViewModel()
12: {
13: this.LoginCommand = new DelegateCommand<string>(Login, CanLogin);
14: }
15:
16: void Login(string userName)
17: {
18: Debug.WriteLine("working..");
19: }
20:
21: bool CanLogin(string userName)
22: {
23: return !string.IsNullOrEmpty(userName);
24: }
25: }
As we can see, property of type DelegateCommand<string> has been created.
This command expects a string parameter (user name).
When creating command, we pass two delegates to the constructor:
1: // DelegateCommand constructor:
2: public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod) { .. }
3:
4: // DelegateCommand initialization:
5: this.LoginCommand = new DelegateCommand<string>(Login, CanLogin);
First one (Login) will be called when command executes.
Second one (CanLogin) will be called to check whether command can execute.
CanLogin simply checks if userName is null or empty, and if it is, command won’t execute (and Login button will be disabled)
This is how our view may look like:
1: xmlns:commands="clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation"
2:
3: <StackPanel>
4: <TextBlock Text="UserName:"/>
5: <TextBox x:Name="UserNameInput" />
6: <Button Content="Login"
7: commands:Click.Command="{Binding LoginCommand}"
8: commands:Click.CommandParameter="{Binding Text, ElementName=UserNameInput}" />
9: </StackPanel>
Looking at the code above we see that Login Button has a binding to LoginCommand (commands:Click.Command="{Binding LoginCommand}" ) and we pass the content of user name text box as a parameter to the command (commands:Click.CommandParameter="{Binding Text, ElementName=UserNameInput}").
When we run the test solution (link at the bottom of this post), we’ll see that Login button is disabled (UserNameInput textbox is empty)
Login button will be enabled as soon as we type a user name and command will execute when Login button is clicked.
CompositeCommand
CompositeCommand is a command that contains multiple other commands.
CompositeCommand can execute only when all child commands can execute
IActiveAware
IActiveAware interface (implemented by DelegateCommand) is used in scenarios when we want command to be executed only if it’s in an active view.
Compositecommand takes monitorCommandActivity parameter in it’s constructor, when set to true, CompositeCommand will execute commands that implement IActiveAware and have IsActive property set to true. Commands that do not implenet IActiveAware will be executed as well.
This logic can be changed by overriding ShouldExecute() method.
It’s comes in handy when implementing commands such as SaveAll or CloseAll.
Silverlight Support
Prism Commands are supported by Silverlight (unlike Wpf commands).
By default this is enabled on controls that inherit from ButtonBase but we can write custom Attached Behaviors to support other controls and commands.
I’ll write about this technique in the nearest future.
Playground
Solution that shows Command support in Silverlight can be downloaded here.
– COMMENTS FROM PREVIOUS BLOG:
31/07/2009 02:48:07 #
Hi. Your example is useful but simplistic. In any login scenario, a password must be provided as well as the username. If we are to follow the same logic, then we would need to pass 2 parameters to the command. From what I can tell, there’s no way to do that with the current classes in Prism. Or am I wrong?
One solution that I can think of is to ignore the CommandParameter, and just bing to username and password UI elements values to the corresponding properties in the view model. Then, when the command is invoked, the values of the properties are verified, so no parameters are needed. I’m not sure if it’s a good practice, though. What do you think of this? Or please suggest a different approach.
blackjack2150
31/07/2009 03:04:04 #
![]()
Hi blackjack,
This is exactly what I do in situations you just described (and quite often when I have only one parameter too) – just skip the param and read all you need from the properties in viewmodel.
05/09/2009 02:56:30 #
Am I missing something? I thought in order to support binding a couple of things need to happen.
1) Your view model needs to inherit from DependencyObject or some object higher up in the food chain.
2) Your DelegateCommand<T> should be a dependency property (as well as any properties that you will use as parameters for the command outside of using the CommandParameter attached property)
Ryan Haney
05/09/2009 03:08:35 #
Also, every time the state changes for your "can execute command" function, you need to force a requery. In the case of dependency properties, you can do this as such:
public static readonly DependencyProperty UsernameProperty =
DependencyProperty.Register("Username", typeof(string), typeof(LoginViewModel), new PropertyMetadata(new PropertyChangedCallback(
(d, e) => {
LoginViewModel vm = d as LoginViewModel;
vm.LoginCommand.RaiseCanExecuteChanged();
})));
Ryan Haney
Buy me a coffee to sponsor more posts like this!



