Download source code
Prism ships with a commanding framework that allows developers to extend current Silverlight controls, whether the ones ship with Silverlight or 3rd party controls, to support commanding. Commands are major when developing Silverlight applications based on a presentational design patterns such as Model View ViewModel (MVVM).
One of the Telerik controls that is used in most applications is the RadContextMenu. This control exposes the ItemClick event that fires when a menu item is clicked. However, the only way to handle this event is in the code-behind.
When talking about MVVM, there is always the question of what to put in the View and what to place in the ViewModel? For instance UI events could be handled in the code-behind, but definitely any functionality implemented by the event handler that might need to call a Web Service method to decide on a business rule or so, should be placed inside the ViewModel. Therefore, what can be done is expose few decision-action methods inside the ViewModel that the View can call from inside the code-behind event handlers. Another solution is attach commands on UI controls so that when an event fires on the UI a command defined inside the ViewModel gets executed. The second solution is the one we will look at in this article.
When attaching a RadContextMenu to a UIElement usually you handle the ItemClick event in the code-behind and process each menu-item clicked accordingly.
What we need to do is that when the ItemClick event is fired to execute a Command on the ViewModel. For this article, we will work with the Composite Application Library, the guidance or framework developed and released by Microsoft Patterns & Practices team which you can grab a copy from here: Composite WPF and Silverlight.
Composite Application Library provides support for Commands only for ButtonBase controls i.e. Button, RepeatButton, ToggleButton {CheckBox and RadioButton} and HyperlinkButton. At the same time, it has a very extendible Commanding framework to let developers provide commanding support for other types of controls. In this article, I will show you how to support Commanding for the Telerik RadContextMenu control by developing a command behavior for the RadContextMenu.
The type of Commanding that will be discussed in this article is the DelegateCommand that defines commands based on delegates. Initializing a command is done by providing two delegates: Execute and CanExecute:
- Execute delegate is nothing but an Action<T> delegate
- While CanExecute delegate is a Func<T, bool> delegate
Where T is the type of the parameter that could be passed into the Command handler.
To read more on DelegateCommands in the Composite Application Library, follow this link: Commands
Steps to enable Commanding for RadContextMenu as follows:
#1 – Create the RadContextMenuCommandBehavior class
The RadContextMenuCommandBehavior class adds the command behavior into the control in question by linking the fact of an event being fired to a command being executed. The class is simple and goes as follows:
public class RadContextMenuCommandBehavior : CommandBehaviorBase<RadContextMenu>
{
public RadContextMenuCommandBehavior(RadContextMenu source)
: base(source)
{
source.ItemClick += new Telerik.Windows.RadRoutedEventHandler(source_ItemClick);
}
void source_ItemClick(object sender, Telerik.Windows.RadRoutedEventArgs e)
{
var menu = sender as RadContextMenu;
if (menu != null)
{
// Set the parameter
this.CommandParameter = e;
// Execute the Command
this.ExecuteCommand();
}
}
}
The constructor of the RadContextMenuCommandBehavior class takes as input a RadContextMenu variable. Why? This class, through its constructor hooks into the ItemClick event of the RadContextMenu variables passed as input to the constructor.
The event handler for the RadContextMenu ItemClick event simply executes the Command property found in the CommandBehaviorBase class (inherited by RadContextMenuCommandBehavior class) and filling in the command parameter to the RadRoutedEventArgs value that gives information on the menu item clicked and will be passed internally to the Command to be executed.
#2 – Attach the Command to the RadContextMenu control
Before being able to attach a Command on the RadContextMenu we need to create 2 Attached Properties as follows:
public static class MenuItemClicked
{
private static readonly DependencyProperty MenuItemClickedCommandBehaviorProperty = DependencyProperty.RegisterAttached(
"MenuItemClickedCommandBehavior",
typeof(RadContextMenuCommandBehavior),
typeof(MenuItemClicked),
null);
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
"Command",
typeof(ICommand),
typeof(MenuItemClicked),
new PropertyMetadata(OnSetCommandCallback));
The CommandProperty property is an attached property that can be attached to a RadContextMenu. The MenuItemClickedCommandBehaviorProperty attached property is a private one and will see in few lines from here how is it used to populate the Command and CommandParameter properties of the RadContextMenuCommandBehavior class.
Notice how the CommandProperty specifies a PropertyMetaData instance. The PropertyMetaData instance can contain a PropertyChangedCallback, a property default value or both. In our case, we will have a PropertyChangedCallback for both of those properties as follows:
private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
RadContextMenu contextMenu = dependencyObject as RadContextMenu;
if (contextMenu != null)
{
RadContextMenuCommandBehavior behavior = GetOrCreateBehavior(contextMenu);
behavior.Command = e.NewValue as ICommand;
}
}
private static RadContextMenuCommandBehavior GetOrCreateBehavior(RadContextMenu contextMenu)
{
RadContextMenuCommandBehavior behavior = contextMenu.GetValue(MenuItemClickedCommandBehaviorProperty) as RadContextMenuCommandBehavior;
if (behavior == null)
{
behavior = new RadContextMenuCommandBehavior(contextMenu);
contextMenu.SetValue(MenuItemClickedCommandBehaviorProperty, behavior);
}
return behavior;
}
To start with, the GetOrCreateBehavior main purpose is to attach the RadContextMenuCommandBehavior on the RadContextMenu and populate both of its Command and CommandParameter properties. It first gets the Dependency Attached Property “MenuItemClickedCommandBehavior” on the RadContextMenu. If null, means the property hasn’t been attached before, so create a new instance of the RadContextMenuCommandBehavior and attach it into the RadContextMenu. However, if the property has been already attached, then simply returns it.
When the Command attached property is being attached to a RadContextMenu, the OnSetComamndCallback is fired. What this method does is simply populate the Command property on the RadContextMenuCommandBehavior class.
Let us recap a little bit, when the Command property is attached on the RadContextMenu control, the following happens:
- RadContextMenuCommandBehavior instance is created, hence hooking into the ItemClick event of the RadContextMenu control
- The instance of RadContextMenuCommandBehavior created above is attached as an attached property on the RadContextMenu control
- RadContextMenuCommandBehavior’s Command property is populated with the Command set on the RadContextMenu control
Now going back into the event handler for the ItemClick inside the RadContextMenuCommandBehavior class, when the RadContextMenu ItemClick event is fired, the ExecuteCommand() method is executed. Command executed means, the Execute Action<T> delegate is executed.
#2 – Define and Initialize the Command together with the Execute Action<T> delegate
Typically, we have been trying through out this article to prepare all the needed stuff to define the Command together with its Execute() method on the ViewModel. So now you know the answer, no need to continue explaining anything :), no wait!!!! Show us how to do it!
When developing with MVVM, each View might have a single ViewModel together with the IViewModel as follows:
public interface IContextMenuViewModel : IViewModel
{
DelegateCommand<object> MenuItemClickedCommand { get; set; }
ColorNames BackgroundColor { get; }
}
The ContextMenuViewModel implements the above interface:
public class ContextMenuViewModel : ViewModelBase, IContextMenuViewModel
{
public ContextMenuViewModel(
IEventAggregator eventAggregator,
IUnityContainer container, IModuleManager manager)
: base(eventAggregator, container, manager)
{
this.MenuItemClickedCommand = new DelegateCommand<object>(OnMenuItemClicked);
}
public DelegateCommand<object> MenuItemClickedCommand { get; set; }
private ColorNames backgroundColor;
public ColorNames BackgroundColor
{
get { return this.backgroundColor; }
private set
{
if (value == this.backgroundColor)
return;
this.backgroundColor = value;
FirePropertyChanged("BackgroundColor");
}
}
protected void OnMenuItemClicked(object parameter)
{
RadRoutedEventArgs args = parameter as RadRoutedEventArgs;
var radMenuItem = args.OriginalSource as RadMenuItem;
ColorNames color = ColorNames.White;
Enum.TryParse<ColorNames>(radMenuItem.Header.ToString(), out color);
this.BackgroundColor = color;
}
}
The class above defines a new property, MenuItemClickedCommand as a DelegateCommand<object> meaning that define for me a new Command that I can bind to on the RadContextMenu on the View that expects a parameter of type object in case a parameter is passed.
In the constructor of the ViewModel, we initialize the MenuItemClickedCommand by specifying the Execute method of type Action<object>() method, OnMenuItemClicked method. This method handles the execution of the Command. Before discussing its details, there is another public property on the ViewModel that is the BackgroundColor which is bound to the Background property of the Grid control on the View.
When a command is executed, the OnMenuItemClicked method gets executed which does the following:
- Extracts the RadMenuItem that was clicked from the passed in parameter. Remember when defining the RadContextMenuCommandBehavior class, we set the CommandParameter to an instance of the RadRoutedEventArgs value.
- In our case, the Header’s property of the RadMenuItem is a color name, hence we generate a new Color instance and set it on the BackgroundColor property.
- The “set” of the BackgroundColor property simply notifies the Data Binding engine in Silverlight that I have a new value and hence the UI I am bound to should be updated.
On the UI, how do we set the Command?
xmlns:infra="clr-namespace:bhaidar.Infrastructure;assembly=bhaidar.RCMInPrism.Infrastructure"
<Border BorderBrush="Blue"
BorderThickness="2"
CornerRadius="3">
<Grid x:Name="LayoutRoot"
Background="{Binding BackgroundColor, Converter={StaticResource colorConverter}}">
<TextBlock Text="Right-click anywhere here"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
<teleriknavigation:RadContextMenu.ContextMenu>
<teleriknavigation:RadContextMenu x:Name="cmMenu"
infra:MenuItemClicked.Command="{Binding MenuItemClickedCommand}">
<teleriknavigation:RadMenuItem Header="Red" />
<teleriknavigation:RadMenuItem Header="Blue" />
</teleriknavigation:RadContextMenu>
</teleriknavigation:RadContextMenu.ContextMenu>
</Grid>
</Border>
Notice first of all how a new XAML namespace has been defined reflecting the place where the Attached Properties have been defined. Once the namespace is added, notice how the RadContextMenu now has a new attached property as follows: infra:MenuItemClicked.Command=”{Binding MenuItemClickedCommand}”.
- The MenuItemClicked is the static class that defines the Attached Properties
- Command is an attached property
- The attached property Command is bound to the MenuItemClickedCommand command defined on the ViewModel.
The above means the following:
- The MenuItemClickedCommand command is initialized inside the ViewModel where the Execute method is defined.
- When the Command attached property is set, an instance of the RadContextMenuCommandBehavior class is instantiated and hooked into the ItemClick event of the RadContextMenu.
- The Command property defined on the RadContextMenuCommandBehavior object is populated with the Command defined in the ViewModel
- When an item on the RadContextMenu is clicked, the ItemClick is fired, hence the Command property on the RadContextMenuCommandBehavior object is executed, hence the Execute method of the MenuItemClickedCommand command defined inside the ViewModel gets called and executed.
Download source code
Bilal Haidar,
Regards
Tags: