Note – these posts are put together after a short time with Silverlight 4 as a way of providing pointers to some of the new features that Silverlight 4 has to offer. I’m posting these from the PDC as Silverlight 4 is announced for the first time so please bear that in mind when working through these posts.
WPF has tended to have more features than Silverlight around Commanding.
The basic idea of commanding is just that a piece of UI fires some logical command rather than invoking some event handler directly.
That’s nice as it means that you can easily have multiple pieces of UI firing the same command and it also lets you take the handling of the command away from the piece of UI that invokved it. A quick example;
The user interface displays a button “Save” and a menu option “File->Save”. These both need to be disabled when the document state is not dirty and they both need to be handled by a common routine on the Document class called Save.
Commanding makes that possible. In WPF you can do this kind of thing as in;
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Window.CommandBindings> <CommandBinding Command="ApplicationCommands.Save" CanExecute="CanSaveExecute" Executed="ExecuteSave" /> </Window.CommandBindings> <DockPanel> <Menu DockPanel.Dock="Top"> <MenuItem Header="File"> <MenuItem Command="ApplicationCommands.Save" /> </MenuItem> </Menu> <Grid> <Button Content="{Binding Source={x:Static ApplicationCommands.Save},Path=Name}" Command="ApplicationCommands.Save" /> </Grid> </DockPanel> </Window>
and partner that up with a little code behind for the ExecuteSave and CanSaveExecute methods.
In Silverlight 3 there’s a little bit of this kind of functionality in that it does have the ICommand interface;
// Summary: // Defines the contract for commanding, using the same contract as used in WPF. public interface ICommand { // Summary: // Occurs when changes occur that affect whether the command should execute. event EventHandler CanExecuteChanged; // Summary: // Defines the method that determines whether the command can execute in its // current state. // // Parameters: // parameter: // Data used by the command. If the command does not require data to be passed, // this object can be set to null. // // Returns: // true if this command can be executed; otherwise, false. bool CanExecute(object parameter); // // Summary: // Defines the method to be called when the command is invoked. // // Parameters: // parameter: // Data used by the command. If the command does not require data to be passed, // this object can be set to null. void Execute(object parameter); }
but there’s no controls that have Command properties ( or the accompanying CommandParameter properties ) and nor are there and implementations of ICommand or fancier ideas like the CommandBindings and routed commands of WPF.
This has caused various frameworks like Prism to add their own implementations of ICommand and also to add particular attached command properties – e.g. Prism adds its own Click property with a Click.Command and Click.CommandParameter property attached to it.
Silverlight 4 does a little more around commands in that both ButtonBase ( which forms the baseclass for Button, HyperlinkButton ) now has both a Command and a CommandParameter property on it as does Hyperlink. As far as I know, there aren’t any implementations of ICommand in the framework so you’d still have to do something around that like writing something of your own. I quickly put one together;
public class MyDelegateCommand<T> : ICommand { public MyDelegateCommand(Action<T> executeAction, Func<T, bool> canExecuteAction) { this.executeAction = executeAction; this.canExecuteAction = canExecuteAction; } public MyDelegateCommand(Action<T> executeAction) : this(executeAction, null) { } public bool CanExecute(object parameter) { return (canExecuteAction((T)parameter)); } public event EventHandler CanExecuteChanged; public void Execute(object parameter) { executeAction((T)parameter); } Action<T> executeAction; Func<T,bool> canExecuteAction; }
and then I can use that as part of “a UI” as below where I’m data-binding the value of the Command and CommandParameter on the Button to a property called SomeCommand on an instance of a type I’ve called ViewModel;
<UserControl x:Class="SilverlightApplication2.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:SilverlightApplication2" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <UserControl.Resources> <local:ViewModel x:Key="viewModel" /> </UserControl.Resources> <StackPanel x:Name="LayoutRoot" Background="White" DataContext="{StaticResource viewModel}"> <TextBox x:Name="txtValue"/> <Button Content="Click Me" Command="{Binding SomeCommand}" CommandParameter="{Binding ElementName=txtValue,Path=Text}"/> </StackPanel> </UserControl>
where the ViewModel class behind this just looks like;
public class ViewModel { public ICommand SomeCommand { get { if (someCommand == null) { someCommand = new MyDelegateCommand<string>( p => { /* Do something here */ }, p => { return ( p == "SomeText" ); }); } return (someCommand); } } ICommand someCommand; }
and so we’ve built a button which will only enable when the TextBox has the Text value “SomeText” and at that point the button will enable and clicking the button will fire the first lambda that we provided into the constructor of the MyDelegateCommand<T> – here it doesn’t do anything but you get the idea.
As in many areas, with commands here Silverlight 4 is adding some of the additional fatures from WPF and adding this kind of support for commanding is one of those steps forward and mirrors some of the work that’s already there in Prism for Silverlight 3 but it’s great that this kind of functionality is moving into the core framework.