Use Commands with the RadContextMenu
As the RadMenuItem implements the ICommandSource interface, you are able to use any kind of commands that inherit from the ICommand interface with it. This tutorial will show you how to use the RadContextMenu with RoutedUICommands combined with the MVVM pattern. Two commands are going to be exposed - one for moving an item in a ListBox up and one for moving an item down. The following things will come in focus:
Attaching a RadContextMenu to a ListBox control
Before getting to the commands, you have to prepare the UI on which they will get executed. In this tutorial a ListBox and a RadContextMenu are used.
<ListBox x:Name="listBox">
<telerik:RadContextMenu.ContextMenu>
<telerik:RadContextMenu x:Name="radContextMenu">
</telerik:RadContextMenu>
</telerik:RadContextMenu.ContextMenu>
</ListBox>
Having the UI prepared, you have to add some data to it.
Populating the ListBox with data via a ViewModel
As the MVVM pattern should be used, you have to create a ViewModel for your UserControl, which will control its behavior. In it you will store the data which the View is using. Here is the declaration of the ViewModel class. It has a constructor, a method that initializes the items for the ListBox and an Items property, that stores them. Additionally create a SelectedItem property that will hold the selected item of the ListBox.
public class ExampleViewModel : INotifyPropertyChanged
{
private DataItem selectedItem;
public ExampleViewModel()
{
this.InitItems();
}
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<DataItem> Items
{
get;
set;
}
public DataItem SelectedItem
{
get
{
return this.selectedItem;
}
set
{
if (this.selectedItem != value)
{
this.selectedItem = value;
this.OnNotifyPropertyChanged("SelectedItem");
}
}
}
private void OnNotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private void InitItems()
{
ObservableCollection<DataItem> items = new ObservableCollection<DataItem>();
items.Add(new DataItem("Item 1"));
items.Add(new DataItem("Item 2"));
items.Add(new DataItem("Item 3"));
this.Items = items;
}
}
Public Class ExampleViewModel
Implements INotifyPropertyChanged
Private selectedItemField As DataItem
Public Sub New()
Me.InitItems()
End Sub
Public Property Items() As ObservableCollection(Of DataItem)
Get
End Get
Set(value As ObservableCollection(Of DataItem))
End Set
End Property
Public Property SelectedItem() As DataItem
Get
Return Me.selectedItemField
End Get
Set(value As DataItem)
If Me.selectedItemField Is value Then
Me.selectedItemField = value
Me.NotifyPropertyChanged("SelectedItem")
End If
End Set
End Property
Private Sub InitItems()
Dim items As New ObservableCollection(Of DataItem)()
items.Add(New DataItem("Item 1"))
items.Add(New DataItem("Item 2"))
items.Add(New DataItem("Item 3"))
Me.Items = items
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
In the constructor of the UserControl you have to create an instance of the ViewModel, store it in a field and pass it as a DataContext of the entire UserControl.
private ExampleViewModel viewModel;
public Default_Cs()
{
InitializeComponent();
this.viewModel = new ExampleViewModel();
this.DataContext = viewModel;
}
Private viewModel As ExampleViewModel
Public Sub New()
InitializeComponent()
Me.viewModel = New ExampleViewModel()
Me.DataContext = viewModel
End Sub
In the XAML you have to set the SelectedItem, the DisplayMemberPath and the ItemsSource properties of the ListBox in order to visualize the data.
<ListBox x:Name="listBox1"
DisplayMemberPath="Value"
ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<telerik:RadContextMenu.ContextMenu>
<telerik:RadContextMenu x:Name="radContextMenu1">
</telerik:RadContextMenu>
</telerik:RadContextMenu.ContextMenu>
</ListBox>
Selecting the right-clicked ListBoxItem
Before continuing, there is one more thing to be done. When right-clicking to open the RadContextMenu, the clicked item should get selected, or if no item was clicked, the selection should be removed. This is done by handling the Opened event of the RadContextMenu.
<ListBox x:Name="listBox2"
DisplayMemberPath="Value"
ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<telerik:RadContextMenu.ContextMenu>
<telerik:RadContextMenu x:Name="radContextMenu2"
Opened="RadContextMenu_Opened">
</telerik:RadContextMenu>
</telerik:RadContextMenu.ContextMenu>
</ListBox>
private void RadContextMenu_Opened(object sender, RoutedEventArgs e)
{
System.Windows.Controls.ListBoxItem item = this.radContextMenu.GetClickedElement<System.Windows.Controls.ListBoxItem>();
if (item != null)
{
this.listBox.SelectedItem = item.DataContext;
}
else
{
this.listBox.SelectedItem = null;
}
}
Private Sub RadContextMenu_Opened(sender As Object, e As RoutedEventArgs)
Dim item As System.Windows.Controls.ListBoxItem = Me.radContextMenu.GetClickedElement(Of System.Windows.Controls.ListBoxItem)()
If item Is Nothing Then
Me.listBox.SelectedItem = item.DataContext
Else
Me.listBox.SelectedItem = Nothing
End If
End Sub
Preparing the RoutedUICommands
The next step is to create your commands. They will be host by the ViewModel.
public RoutedUICommand MoveUpCommand
{
get;
private set;
}
public RoutedUICommand MoveDownCommand
{
get;
private set;
}
Public Property MoveUpCommand() As RoutedUICommand
Get
End Get
Private Set(value As RoutedUICommand)
End Set
End Property
Public Property MoveDownCommand() As RoutedUICommand
Get
End Get
Private Set(value As RoutedUICommand)
End Set
End Property
Initialize them in the constructor of the ViewModel:
public ExampleViewModel()
{
this.MoveUpCommand = new RoutedUICommand("Move Up", "MoveUp", typeof(ExampleViewModel));
this.MoveDownCommand = new RoutedUICommand("Move Down", "MoveDown", typeof(ExampleViewModel));
this.InitItems();
}
Public Sub New()
Me.MoveUpCommand = New RoutedUICommand("Move Up", "MoveUp", GetType(ExampleViewModel))
Me.MoveDownCommand = New RoutedUICommand("Move Down", "MoveDown", GetType(ExampleViewModel))
Me.InitItems()
End Sub
Bind them in the View.
<ListBox x:Name="listBox3"
DisplayMemberPath="Value"
ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<telerik:RadContextMenu.ContextMenu>
<telerik:RadContextMenu x:Name="radContextMenu3"
Opened="RadContextMenu_Opened">
<telerik:RadMenuItem Header="{Binding MoveUpCommand.Text}"
Command="{Binding MoveUpCommand}" />
<telerik:RadMenuItem Header="{Binding MoveDownCommand.Text}"
Command="{Binding MoveDownCommand}" />
</telerik:RadContextMenu>
</telerik:RadContextMenu.ContextMenu>
</ListBox>
You will also need methods that will get called when the command is executed. In the next section is explained how to connect the methods to the command. Here are sample methods for the two commands.
public void MoveUp(object sender, ExecutedRoutedEventArgs e)
{
if (this.SelectedItem == null || this.Items.IndexOf(this.SelectedItem as DataItem) == 0)
{
return;
}
DataItem item = this.SelectedItem;
int index = this.Items.IndexOf(item as DataItem);
this.Items.Remove(item as DataItem);
this.Items.Insert(index - 1, item as DataItem);
this.SelectedItem = item;
}
public void MoveDown(object sender, ExecutedRoutedEventArgs e)
{
if (this.SelectedItem == null || this.Items.IndexOf(this.SelectedItem as DataItem) == this.Items.Count - 1)
{
return;
}
DataItem item = this.SelectedItem;
int index = this.Items.IndexOf(item as DataItem);
this.Items.Remove(item as DataItem);
this.Items.Insert(index + 1, item as DataItem);
this.SelectedItem = item;
}
Public Sub MoveUp(sender As Object, e As ExecutedRoutedEventArgs)
If Me.SelectedItem Is Nothing OrElse Me.Items.IndexOf(TryCast(Me.SelectedItem, DataItem)) = 0 Then
Return
End If
Dim item As DataItem = Me.SelectedItem
Dim index As Integer = Me.Items.IndexOf(TryCast(item, DataItem))
Me.Items.Remove(TryCast(item, DataItem))
Me.Items.Insert(index - 1, TryCast(item, DataItem))
Me.SelectedItem = item
End Sub
Public Sub MoveDown(sender As Object, e As ExecutedRoutedEventArgs)
If Me.SelectedItem Is Nothing OrElse Me.Items.IndexOf(TryCast(Me.SelectedItem, DataItem)) = Me.Items.Count - 1 Then
Return
End If
Dim item As DataItem = Me.SelectedItem
Dim index As Integer = Me.Items.IndexOf(TryCast(item, DataItem))
Me.Items.Remove(TryCast(item, DataItem))
Me.Items.Insert(index + 1, TryCast(item, DataItem))
Me.SelectedItem = item
End Sub
Creating the CommandBindings
In order to use the commands in the UI you have to provide a CommandBinding for each of the commands. The CommandBinding binds the command to a method that is called when the command gets executed. The CommandBidnings get set via the CommandManager. As the CommandManager is called by the View you have to expose a method in your ViewModel that returns a collection of its CommandBindings.
public CommandBindingCollection GetCommandBindings()
{
CommandBindingCollection bindings = new CommandBindingCollection();
bindings.Add(new CommandBinding(this.MoveUpCommand, this.MoveUp));
bindings.Add(new CommandBinding(this.MoveDownCommand, this.MoveDown));
return bindings;
}
Public Function GetCommandBindings() As CommandBindingCollection
Dim bindings As New CommandBindingCollection()
bindings.Add(New CommandBinding(Me.MoveUpCommand, Me.MoveUp))
bindings.Add(New CommandBinding(Me.MoveDownCommand, Me.MoveDown))
Return bindings
End Function
Setting the CommandBindings
In the View get the CommandBindingsCollection and set it through the CommandManager.
public Default_Cs()
{
InitializeComponent();
this.viewModel = new ExampleViewModel();
this.DataContext = viewModel;
CommandManager.SetCommandBindings(this, this.viewModel.GetCommandBindings());
}
Public Sub New()
InitializeComponent()
Me.viewModel = New ExampleViewModel()
Me.DataContext = viewModel
CommandManager.SetCommandBindings(Me, Me.viewModel.GetCommandBindings())
End Sub