Preface
As this article is not intended to be a beginner's article, I presume you know what WPF commands are and what they are used for. But if you don't, I think this is the right time to learn about them. There are many good articles and books describing the nature and usage of WPF commands. One great article is by Richard Griffin, which you can read on his blog here: WPF Commands, a scenic tour: Part I. I also recommend reading Smart Routed Commands in WPF by Josh Smith for learning advanced concepts in WPF commands.
Introduction
Commands in WPF can be defined and maintained in a static class. The commands need to be bound with the window whose controls use them. To bind the commands, the CanExecute
and Executed
events need to defined in the window itself. But most of the time, people want all their commands and related CanExecute
and Executed
events in one single static class which makes it easier to maintain the program logic from one central place. The purpose of this article is to tell you how to enable this facility.
Defining the commands
Let's define some commands which we will be using for this example.
public class MyAppCommands
{
private static RoutedUICommand _AddContact;
private static RoutedUICommand _EditContact;
private static RoutedUICommand _DeleteContact;
static MyAppCommands()
{
_AddContact = new RoutedUICommand("Add a new contact",
"AddContact", typeof(MyAppCommands));
_EditContact = new RoutedUICommand("Edit an existing contact",
"EditContact", typeof(MyAppCommands));
_DeleteContact = new RoutedUICommand("Delete an existing contact",
"DeleteContact", typeof(MyAppCommands));
}
public static RoutedUICommand AddContact
{
get { return _AddContact; }
}
public static RoutedUICommand EditContact
{
get { return _EditContact; }
}
public static RoutedUICommand DeleteContact
{
get { return _DeleteContact; }
}
}
Now, let us assume that we are using these three commands from a window which uses three buttons to fire the three commands, respectively.
<Window x:Class="CentralCommands.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Commands="clr-namespace:CentralCommands"
Title="MainWindow" Height="300" Width="300"
x:Name="mainWindow">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button HorizontalAlignment="Stretch" Margin="20"
Name="btnAdd" VerticalAlignment="Stretch" Grid.Row="0"
Command="Commands:MyAppCommands.AddContact">Add Contact</Button>
<Button HorizontalAlignment="Stretch" Margin="20"
Name="btnEdit" VerticalAlignment="Stretch" Grid.Row="1"
Command="Commands:MyAppCommands.EditContact">Edit Contact</Button>
<Button HorizontalAlignment="Stretch" Margin="20"
Name="btnDelete" VerticalAlignment="Stretch" Grid.Row="2"
Command="Commands:MyAppCommands.DeleteContact">Save Contact</Button>
</Grid>
</Window>
As of now, if you execute this code, all the buttons will be disabled. The reason is that we have not bound the CanExecute
and/or Executed
events for the commands. If only CanExecute
is defined, the button will only be enabled when the CanExecute
event sets the CanExecute
property of the CanExecuteRoutedEventArgs
parameter passed to the method to true
, but nothing will happen if you click the button. If Executed
is defined for a command, the Executed
method will be called when you click the button. If you only define Executed
, the button using the command will be enabled, no matter what.
Let's define the CanExecute
and Executed
events for the commands in our MyAppCommands
static class. The commands do nothing other than display a message box about what command was executed. AddContact
and DeleteContact
are enabled and EditContact
is disabled for testing purposes.
public static void AddContact_Executed(object sender,
ExecutedRoutedEventArgs e)
{
MessageBox.Show("Add contact command executed");
}
public static void AddContact_CanExecute(object sender,
CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
public static void EditContact_Executed(object sender,
ExecutedRoutedEventArgs e)
{
MessageBox.Show("Edit contact command executed");
}
public static void EditContact_CanExecute(object sender,
CanExecuteRoutedEventArgs e)
{
e.CanExecute = false;
}
public static void DeleteContact_Executed(object sender,
ExecutedRoutedEventArgs e)
{
MessageBox.Show("Delete contact command executed");
}
public static void DeleteContact_CanExecute(object sender,
CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
The problem
Now, we need to bind these events with the commands in the command bindings of the window. Let's try to define the events using the x:Static
markup extension which binds any static by-value entity to the source (we only bind the AddContact
command for the time being):
<Window.CommandBindings>
<CommandBinding Command="Commands:MyAppCommands.AddContact"
CanExecute="{x:Static Commands:MyAppCommands.AddContact_CanExecute}"
Executed="{x:Static Commands:MyAppCommands.AddContact_Executed}"/>
</Window.CommandBindings>
If you try to compile this code, you will get the following error:
CanExecute="{x:Static Commands:MyAppCommands.AddContact_CanExecute}"
is not valid. '{x:Static Commands:MyAppCommands.AddContact_CanExecute}'
is not a valid event handler method name. Only instance methods
on the generated or code-behind class are valid.
Why are we getting the error? The event is defined as static in the MyAppCommand
class, but we still cannot bind the event to the command. The reason is, the current WPF version's XAML does not allow us to bind event handlers in this way. The event handlers must be defined in the code-behind file inside the MainWindow
class. I don't know if this is a bug, an accidently left out feature, or if we are not even supposed to use this functionality, but this stops us from defining a centralized location for handling all commands' Executed
and CanExecute
events.
The solution
The solution to this problem is to use code instead of XAML to define the bindings. Let us make a static function which will bind the commands and their respective events to the MainWindow
window.
public static void BindCommandsToWindow(Window window)
{
window.CommandBindings.Add(
new CommandBinding(AddContact, AddContact_Executed, AddContact_CanExecute));
window.CommandBindings.Add(
new CommandBinding(EditContact, EditContact_Executed, EditContact_CanExecute));
window.CommandBindings.Add(
new CommandBinding(DeleteContact, DeleteContact_Executed, DeleteContact_CanExecute));
}
We need to call this method from the MainWindow.Loaded
event, passing the MainWindow
instance:
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
MyAppCommands.BindCommandsToWindow(this);
}
Now, try to run and execute the code.
Voila! It works.
A small tip
You can send the command a parameter by using the CommandParameter
dependency property of the button. As the data is sent as an object, you can send almost anything you want. The parameter is sent to both the CanExecute
and Executed
events. Let us suppose we want to set the status of Edit
's CanExecute
using code so that when Add is clicked, Edit is available, and when Delete is clicked, it's not. For clarity, I am making a separate class named CommandParameter
which defines a bool
property CanEditBeExecuted
, although it can be done without defining a separate class at all. We also define a parameter property of a CommandParameter
type and set CanEditBeExecuted
to false
in the constructor of MainWindow
.
public class CommandParameter
{
public bool CanEditBeExecuted { get; set; }
}
public partial class MainWindow : Window
{
public CommandParameter parameter { get; set; }
public MainWindow()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
parameter = new CommandParameter();
parameter.CanEditBeExecuted = false;
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
MyAppCommands.BindCommandsToWindow(this);
}
}
Now, define the CommandParameter
for the buttons:
<Button HorizontalAlignment="Stretch"
Margin="20" Name="btnAdd"
VerticalAlignment="Stretch" Grid.Row="0"
Command="Commands:MyAppCommands.AddContact"
CommandParameter="{Binding ElementName=mainWindow, Path=parameter}">Add Contact
</Button>
<Button HorizontalAlignment="Stretch" Margin="20"
Name="btnEdit" VerticalAlignment="Stretch" Grid.Row="1"
Command="Commands:MyAppCommands.EditContact"
CommandParameter="{Binding ElementName=mainWindow, Path=parameter}">Edit Contact
</Button>
<Button HorizontalAlignment="Stretch" Margin="20"
Name="btnDelete" VerticalAlignment="Stretch" Grid.Row="2"
Command="Commands:MyAppCommands.DeleteContact"
CommandParameter="{Binding ElementName=mainWindow, Path=parameter}">Save Contact
</Button>
We also change the command's Execute
and CanExecute
handlers to reflect our strategy.
public static void AddContact_Executed(object sender, ExecutedRoutedEventArgs e)
{
CommandParameter parameter = e.Parameter as CommandParameter;
if (parameter != null)
parameter.CanEditBeExecuted = true;
MessageBox.Show("Add contact command executed");
}
public static void AddContact_CanExecute(object sender,
CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
public static void EditContact_Executed(object sender,
ExecutedRoutedEventArgs e)
{
MessageBox.Show("Edit contact command executed");
}
public static void EditContact_CanExecute(object sender,
CanExecuteRoutedEventArgs e)
{
CommandParameter parameter = e.Parameter as CommandParameter;
if (parameter != null)
e.CanExecute = parameter.CanEditBeExecuted;
else
e.CanExecute = false;
}
public static void DeleteContact_Executed(object sender,
ExecutedRoutedEventArgs e)
{
CommandParameter parameter = e.Parameter as CommandParameter;
if (parameter != null)
parameter.CanEditBeExecuted = false;
MessageBox.Show("Delete contact command executed");
}
public static void DeleteContact_CanExecute(object sender,
CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
If you will execute this code now, you will notice that the edit button does not reflect the changes made to the CanEditBeExecuted
property. If you will search the net, the normal answer you will find is that this happens because the property cannot notify the button when it is changed. The solution is to derive CommandParameter
from INotifyPropertyChanged
and raise the property changed event when CanEditBeExecuted
is changed. The answer is no. All you need to do is this: you need to instantiate the parameter object before InitializeComponent
and the edit button will reflect the changes. So, if you change the constructor the following way, the edit button will reflect the changes made to CanEditBeExecuted
.
public MainWindow()
{
parameter = new CommandParameter();
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
parameter.CanEditBeExecuted = false;
}
I just mentioned this tip because I think many people make this same mistake, and the solutions available on the net regarding the matter are mostly misleading. Although implementing INotifyPropertyChanged
is a good idea, implementing it just because of the above mentioned reasons, is not.
Conclusion
I hope people who want a centralized static class to handle all the command functionality at one single place will find this article useful. Please feel free to comment or point out mistakes.