Command Changes in Prism 4

One small change that was made in Prism 4 was to update the DelegateCommand class to deal with command parameters a little more cleanly, and also to update the weak reference handling inside the DelegateCommand to work correctly based on the way Silverlight 4 implemented command hookup as opposed to WPF. Here is the scoop.

Command Parameters with DelegateCommand Need to Be Nullable

The ICommand.Execute and ICommand.CanExecute methods take a single parameter of type object. That parameter can be populated with a parameter by setting the CommandParameter property of a command source (such as a Button) to some object. However, because it is defined in terms of object and may never be set, the pattern establishes that the argument should be nullable.

DelegateCommand<T> in past releases of Prism allowed you to put a strongly typed wrapper around the parameter so that the target methods could just take in the argument type they expected to be provided when the method was invoked. So for example, if I have a SaveCommand property in my view model bound to a Button in the view, and I expected that the view would also pass the current customer by binding the CommandParameter property on the Button to an instance of a Customer (perhaps by binding it to the SelectedItem property in a data bound grid of customers), then I could just have a handling method like so:

   1: private void OnSaveCustomer(Customer cust)

   2: {

   3:     // Do what's needed to save

   4: }

That worked fine as long as the type you were passing was a reference type that could handle the fact that:

a) The view might not actually set the CommandParameter at all

b) Even if the CommandParameter is set to a Binding, at parse time the Binding might not resolve to a value yet and could pass a null when CanExecute is initially called.

But the strong typing on the DelegateCommand also made it look like it should be fine to do something like hook up to a DelegateCommand<int>. If you did though, you would likely run into a problem that down in the Prism code, there was an explicit cast to the target type. And if a null came through in the parse process or the CommandParameter was not set, you can’t cast null to int, so kaboom, deep down in the Prism code.

This is now fixed in Prism 4, although maybe not in the way you would expect. The Prism team initially looked into finding a way to just make it work with value types. But there was not really a way to do that without violating the principal of least surprise in some cases. The command method signatures imply nullability by being defined in terms of type object. Putting a strongly typed wrapper on there to avoid having to cast the parameters yourself is a nice convenience, but shouldn’t change the semantics of the underlying interface.

So the fix in Prism 4 is that the constructor of DelegateCommand<T> checks to make sure the type argument T is either a reference type or a Nullable<T>. The reason it checks in the constructor instead of using a generic type constraint (such as the class constraint) is that Nullable<T> is actually a struct itself.

So the bad news is that if you had code that worked OK before passing value types for T with DelegateCommand<T>, you are going to have to change that code to use Nullable<T> instead to work in Prism 4. But really that is being done because even though it may have worked in some cases before, it was really violating the semantics of the command interface that the DelegateCommand represented and would not work consistently across the board with value types as a result.

DelegateCommand (Not of T)

Another common request in Prism was to have a non-generic DelegateCommand class. If you never intend to pass an argument, it was a pain to always have to declare your commands in terms of DelegateCommand<object> and then have a signature in your Execute and CanExecute handlers that took an object parameter that you always ignored. Now if you never expect a parameter to be passed to your command handlers, you can just define your command in terms of DelegateCommand, and the Execute and CanExecute handlers can just have no parameters (Action and Func<bool> respectively) so your code looks a little cleaner.

Because of this, you will also see that the common functionality is factored out to a DelegateCommandBase class that both DelegateCommand and DelegateCommand<T> derive from.

WeakReference Handling

One of the things the DelegateCommand does for you in Prism is that it tries to help protect you from leaking memory if the lifetimes of the command source (i.e. Button in a view) and the command handler (i.e. target method in a view model) are different. For example, in some scenarios, you might choose to keep an instance of a view model around in memory, and create new instances of a view and bind each instance of the view to the view model only when it is being shown.

Until Silverlight 4, there were no built in command handlers, so this was really only relevant to WPF anyway. And WPF does things a little differently under the covers than the implementation that ended up in Silverlight 4. In WPF, in addition to holding a reference to the command object itself ( so that Execute and CanExecute can be called), the command source (i.e. Button) holds a reference to the delegate instance that exposed the CanExecuteChanged event (which would be something like your view model, as opposed to the command object itself). Silverlight 4 does not hold that additional reference.

So that required a small change that you might run across if you are looking at the code, where the DelegateCommand uses a weak reference wrapper to command source when you are in WPF and not in Silverlight. This is to accommodate this difference in the platforms.

Bottom line, the weak reference is used in WPF to avoid leaking memory if the command source comes and goes and the command target sticks around, but not used in Silverlight because it is not needed.

Summary

That is about it as far as changes to commands. Bottom line, any code you had before should work as it was unless you were using value types as parameters. In that case you will need to change them to take Nullable<yourvaluetype> to work with Prism 4. The weak reference stuff will continue to work as it always has, but be correct for the Silverlight reference handling which is different under the covers than the WPF hookup. And having a parameterless option for commands is a handy addition I know I will use a lot.

Comments are closed.