Sunday, October 16, 2011

WPF Commands Part 1

I'm working on deeper understanding on how commands work in WPF. How do RoutedCommands work? First, I add a class to my solution and call it Commands.

public static class Commands
{
public static readonly RoutedCommand MyClick = new RoutedCommand();
}

Next, I declare the command and add a binding in my User Control.

<Button 
Command="{x:Static my:Commands.MyClick}"
Grid.Column="2" Height="23" HorizontalAlignment="Left" Margin="0"
Name="button1" VerticalAlignment="Top" Width="75" Content="Button"/>

<UserControl.CommandBindings>
<CommandBinding
Command="{x:Static my:Commands.MyClick}"
CanExecute="CommandBinding_CanExecute"
Executed="CommandBinding_Executed">
</CommandBinding>
</UserControl.CommandBindings>

Next, I add the handlers for the CommandBinding_CanExecute and CommandBinding_Executed.

private void CommandBinding_CanExecute(object sender, System.Windows.Input.CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}

private void CommandBinding_Executed(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
{
System.Windows.Forms.MessageBox.Show("Executed in DimensionsView");
}

So far, so good - the button is clicked, the message appears. However, that's too simple - next, I need to make sure my other user control will be notified when the button is clicked. This is the bit I was not able to do quickly on my first attempt. It appears that I need to implement a MVVM pattern to achieve this.

Fist I will implement the pattern in the simplest possible way. I'll create a ViewModel for my User Control. The ViewModel provides the mechanics of creating a command. The two functions serve the following purpose: CanClick() checks if the button click can happen. Some logic can be placed there and, if it returns false, the button will in fact be disabled. DoClick() actually runs the code that should happen on button click.

public class DimensionsViewModel
{
private DelegateCommand _clickCommand;

public ICommand ClickCommand
{
get
{
if (_clickCommand == null)
{
_clickCommand = new DelegateCommand(new Action(DoClick), new Func(CanClick));
}
return _clickCommand;
}
}

private bool CanClick()
{
/* code to check if the button can be clicked */
return true;
}

private void DoClick()
{
System.Windows.Forms.MessageBox.Show("Click in DimensionsView");
}
}

The Button in the xaml file needs to know which command to run.

<Button 
Command="{Binding Path=ClickCommand}"
Grid.Column="2" Height="23" HorizontalAlignment="Left" Margin="0"
Name="button1" VerticalAlignment="Top" Width="75" Content="Button"/></pre><p>And finally, the User Control needs to know which ViewModel to use:</p><pre class="brush:xaml"><UserControl x:Class="DynamicGrid.DimensionsView"

...
xmlns:my="clr-namespace:DynamicGrid">
<UserControl.DataContext>
<my:DimensionsViewModel/>
</UserControl.DataContext>
...
</UserControl>

And this is it! What I have achieved so far: there is absolutely no code in the xaml.cs file. Here's proof, the whole contents of the DimensionsView.xaml.cs:

public partial class DimensionsView : UserControl
{
public DimensionsView()
{
InitializeComponent();
}
}

Now someone can work on the UI and edit the xaml file, and someone can work on the business logic and work with the ViewModel, and they would not even need to check out the same file and then merge their changes or bump into one another in any way.

by . Also posted on my website

No comments: