RadControls for Silverlight

Attach a RadContextMenu to a RadGridView

In this tutorial a RadGridView will be used, which displays a list of Employee objects and has the following declaration:

CopyXAML
<telerik:RadGridView x:Name="radGridView"
                         AutoGenerateColumns="False">
    <telerik:RadGridView.Columns>
        <telerik:GridViewDataColumn DataMemberBinding="{Binding EmployeeID}"
                                        Header="ID" />
        <telerik:GridViewDataColumn DataMemberBinding="{Binding Name}"
                                        Header="Name" />
        <telerik:GridViewDataColumn DataMemberBinding="{Binding Title}"
                                        Header="Title" />
    </telerik:RadGridView.Columns>
</telerik:RadGridView>

In order to add a RadContextMenu to it, you have to just set the RadContextMenu.ContextMenu attached property.

CopyXAML
<telerik:RadGridView x:Name="radGridView"
                         AutoGenerateColumns="False">
    <telerik:RadContextMenu.ContextMenu>
        <telerik:RadContextMenu x:Name="GridContextMenu" />
    </telerik:RadContextMenu.ContextMenu>
    <telerik:RadGridView.Columns>
        <telerik:GridViewDataColumn DataMemberBinding="{Binding EmployeeID}"
                                        Header="ID" />
        <telerik:GridViewDataColumn DataMemberBinding="{Binding Name}"
                                        Header="Name" />
        <telerik:GridViewDataColumn DataMemberBinding="{Binding Title}"
                                        Header="Title" />
    </telerik:RadGridView.Columns>
</telerik:RadGridView>

Configure the ItemContainerStyle for RadContextMenu

The RadContextMenu will be populated with dynamic data, so you have to prepare an ItemContainerStyle that will display this data. The business object that will represent the data looks like this:

CopyC#
public class MenuItem : INotifyPropertyChanged
{
    private bool isEnabled = true;
    private string text;
    private ObservableCollection<MenuItem> subItems;
    public event PropertyChangedEventHandler PropertyChanged;
    public bool IsEnabled
    {
        get
        {
            return this.isEnabled;
        }
        set
        {
            if ( this.isEnabled != value )
            {
                this.isEnabled = value;
                this.OnNotifyPropertyChanged( "IsEnabled" );
            }
        }
    }
    public string Text
    {
        get
        {
            return this.text;
        }
        set
        {
            if ( this.text != value )
            {
                this.text = value;
                this.OnNotifyPropertyChanged( "Text" );
            }
        }
    }
    public ObservableCollection<MenuItem> SubItems
    {
        get
        {
            if ( this.subItems == null )
            {
                this.subItems = new ObservableCollection<MenuItem>();
            }
            return this.subItems;
        }
        set
        {
            if ( this.subItems != value )
            {
                this.subItems = value;
                this.OnNotifyPropertyChanged( "SubItems" );
            }
        }
    }
    private void OnNotifyPropertyChanged( string ptopertyName )
    {
        if ( this.PropertyChanged != null )
        {
            this.PropertyChanged( this, new PropertyChangedEventArgs( ptopertyName ) );
        }
    }
}
CopyVB.NET
Public Class MenuItem
 Implements INotifyPropertyChanged
 Private isEnabled As Boolean = True
 Private text As String
 Private subItems As ObservableCollection(Of MenuItem)
 Public Event PropertyChanged As PropertyChangedEventHandler
 Public Property IsEnabled() As Boolean
  Get
   Return Me.isEnabled
  End Get
  Set
   If Me.isEnabled <> value Then
    Me.isEnabled = value
    Me.OnNotifyPropertyChanged("IsEnabled")
   End If
  End Set
 End Property
 Public Property Text() As String
  Get
   Return Me.text
  End Get
  Set
   If Me.text <> value Then
    Me.text = value
    Me.OnNotifyPropertyChanged("Text")
   End If
  End Set
 End Property
 Public Property SubItems() As ObservableCollection(Of MenuItem)
  Get
   If Me.subItems = Nothing Then
    Me.subItems = New ObservableCollection(Of MenuItem)()
   End If
   Return Me.subItems
  End Get
  Set
   If Me.subItems <> value Then
    Me.subItems = value
    Me.OnNotifyPropertyChanged("SubItems")
   End If
  End Set
 End Property
 Private Sub OnNotifyPropertyChanged(ptopertyName As String)
  If Me.PropertyChanged <> Nothing Then
   Me.PropertyChanged(Me, New PropertyChangedEventArgs(ptopertyName))
  End If
 End Sub
End Class

Here is the ItemContainerStyle:

CopyXAML
<Style x:Key="MenuItemContainerStyle" TargetType="telerik:RadMenuItem">
    <Setter Property="Header" Value="{Binding Text}"/>
    <Setter Property="ItemsSource" Value="{Binding SubItems}"/>
    <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
</Style>

Display different menu items depending on which part of the RadGridView is clicked

Tha RadContextMenu should display different items, depending on which part of it is clicked. Here are the possibilities:

  • GridView Header
  • Sort
  • Ascending
  • Descending
  • None
  • Move Left
  • Move Right
  • GridView Row
  • Add
  • Edit
  • Delete
  • Anything Else
  • Add
  • Edit (Disabled)
  • Delete (Disabled)

As you can see, two data sources have to be provided for the RadContextMenu - one when a header is clicked and one when a row is clicked. For that purpose, create two properties in your UserControl as follows:

CopyC#
private ObservableCollection<MenuItem> HeaderContextMenuItems
{
    get;
    set;
} 
private ObservableCollection<MenuItem> RowContextMenuItems
{
    get;
    set;
}
CopyVB.NET
Private Property HeaderContextMenuItems() As ObservableCollection(Of MenuItem)
 Get
 End Get
 Set
 End Set
End Property 
Private Property RowContextMenuItems() As ObservableCollection(Of MenuItem)
 Get
 End Get
 Set
 End Set
End Property

And initialize their data by using methods like these one.

CopyC#
public RadGridViewIntegration()
{
    InitializeComponent();
    this.radGridView.ItemsSource = RadGridViewSampleData.GetEmployees();

    this.InitializeHeaderContextMenuItems();
    this.InitializeRowContextMenuItems();
}
private void InitializeRowContextMenuItems()
{
    ObservableCollection<MenuItem> items = new ObservableCollection<MenuItem>();
    MenuItem addItem = new MenuItem();
    addItem.Text = "Add";
    items.Add( addItem );
    MenuItem editItem = new MenuItem();
    editItem.Text = "Edit";
    items.Add( editItem );
    MenuItem deleteItem = new MenuItem();
    deleteItem.Text = "Delete";
    items.Add( deleteItem );
    this.RowContextMenuItems = items;
}
private void InitializeHeaderContextMenuItems()
{
    ObservableCollection<MenuItem> headerItems = new ObservableCollection<MenuItem>();
    ObservableCollection<MenuItem> sortItems = new ObservableCollection<MenuItem>();
    MenuItem sortAscItem = new MenuItem();
    sortAscItem.Text = "Ascending";
    sortItems.Add( sortAscItem );
    MenuItem sortDescItem = new MenuItem();
    sortDescItem.Text = "Descending";
    sortItems.Add( sortDescItem );
    MenuItem sortNoneItem = new MenuItem();
    sortNoneItem.Text = "None";
    sortItems.Add( sortNoneItem );
    MenuItem sortItem = new MenuItem();
    sortItem.Text = "Sort";
    sortItem.SubItems = sortItems;
    headerItems.Add( sortItem );
    MenuItem moveLeftItem = new MenuItem();
    moveLeftItem.Text = "Move Left";
    headerItems.Add( moveLeftItem );
    MenuItem moveRightItem = new MenuItem();
    moveRightItem.Text = "Move Right";
    headerItems.Add( moveRightItem );
    this.HeaderContextMenuItems = headerItems;
}
CopyVB.NET
Public Sub New()
 InitializeComponent()
 Me.radGridView.ItemsSource = RadGridViewSampleData.GetEmployees()
 Me.InitializeHeaderContextMenuItems()
 Me.InitializeRowContextMenuItems()
End Sub
Private Sub InitializeRowContextMenuItems()
 Dim items As New ObservableCollection(Of MenuItem)()
 Dim addItem As New MenuItem()
 addItem.Text = "Add"
 items.Add(addItem)
 Dim editItem As New MenuItem()
 editItem.Text = "Edit"
 items.Add(editItem)
 Dim deleteItem As New MenuItem()
 deleteItem.Text = "Delete"
 items.Add(deleteItem)
 Me.RowContextMenuItems = items
End Sub
Private Sub InitializeHeaderContextMenuItems()
 Dim headerItems As New ObservableCollection(Of MenuItem)()
 Dim sortItems As New ObservableCollection(Of MenuItem)()
 Dim sortAscItem As New MenuItem()
 sortAscItem.Text = "Ascending"
 sortItems.Add(sortAscItem)
 Dim sortDescItem As New MenuItem()
 sortDescItem.Text = "Descending"
 sortItems.Add(sortDescItem)
 Dim sortNoneItem As New MenuItem()
 sortNoneItem.Text = "None"
 sortItems.Add(sortNoneItem)
 Dim sortItem As New MenuItem()
 sortItem.Text = "Sort"
 sortItem.SubItems = sortItems
 headerItems.Add(sortItem)
 Dim moveLeftItem As New MenuItem()
 moveLeftItem.Text = "Move Left"
 headerItems.Add(moveLeftItem)
 Dim moveRightItem As New MenuItem()
 moveRightItem.Text = "Move Right"
 headerItems.Add(moveRightItem)
 Me.HeaderContextMenuItems = headerItems
End Sub

Next you will need two properties that will return the clicked row and the clicked header. Define them in your UserControl as follows.

CopyC#
private GridViewHeaderCell ClickedHeader
{
    get
    {
        return this.GridContextMenu.GetClickedElement<GridViewHeaderCell>();
    }
}
private GridViewRow ClickedRow
{
    get
    {
        return this.GridContextMenu.GetClickedElement<GridViewRow>();
    }
}
CopyVB.NET
Private ReadOnly Property ClickedHeader() As GridViewHeaderCell
 Get
  Return Me.GridContextMenu.GetClickedElement(Of GridViewHeaderCell)()
 End Get
End Property
Private ReadOnly Property ClickedRow() As GridViewRow
 Get
  Return Me.GridContextMenu.GetClickedElement(Of GridViewRow)()
 End Get
End Property

The last thing to do is to attach an event handler to the Opened event of the RadContextMenu. There you can implement the logic around changing the ItemsSource of the RadContextMenu depending on the clicked element.

CopyXAML
<telerik:RadContextMenu x:Name="GridContextMenu"
                                  ItemContainerStyle="{StaticResource MenuItemContainerStyle}"
                                  Opened="GridContextMenu_Opened" />
CopyC#
private void GridContextMenu_Opened( object sender, RoutedEventArgs e )
{
    if ( this.ClickedHeader != null )
    {
        this.GridContextMenu.ItemsSource = this.HeaderContextMenuItems;
    }
    else if ( this.ClickedRow != null )
    {
        this.radGridView.SelectedItem = this.ClickedRow.DataContext;
        foreach ( var item in this.RowContextMenuItems )
        {
            item.IsEnabled = true;
        }
        this.GridContextMenu.ItemsSource = this.RowContextMenuItems;
    }
    else
    {
        foreach ( var item in this.RowContextMenuItems )
        {
            if ( !item.Text.Equals( "Add" ) )
            {
                item.IsEnabled = false;
            }
        }
        this.GridContextMenu.ItemsSource = this.RowContextMenuItems;
    }
}
CopyVB.NET
Private Sub GridContextMenu_Opened(sender As Object, e As RoutedEventArgs)
 If Me.ClickedHeader <> Nothing Then
  Me.GridContextMenu.ItemsSource = Me.HeaderContextMenuItems
 ElseIf Me.ClickedRow <> Nothing Then
  Me.radGridView.SelectedItem = Me.ClickedRow.DataContext
  For Each item As var In Me.RowContextMenuItems
   item.IsEnabled = True
  Next
  Me.GridContextMenu.ItemsSource = Me.RowContextMenuItems
 Else
  For Each item As var In Me.RowContextMenuItems
   If Not item.Text.Equals("Add") Then
    item.IsEnabled = False
   End If
  Next
  Me.GridContextMenu.ItemsSource = Me.RowContextMenuItems
 End If
End Sub

Handle the menu items' clicks

The last thing to do in this tutorial is to handle the menu items actions. For this purpose attach an event handler to the ItemClick event of the RadContextMenu. In it get the clicked item and, depending on its value, execute the appropriate code.

CopyXAML
<telerik:RadContextMenu x:Name="GridContextMenu"
                                  ItemContainerStyle="{StaticResource MenuItemContainerStyle}"
                                  Opened="GridContextMenu_Opened"
                                  ItemClick="GridContextMenu_ItemClick" />
CopyC#
private void GridContextMenu_ItemClick( object sender, Telerik.Windows.RadRoutedEventArgs e )
{
    MenuItem item = ( e.OriginalSource as RadMenuItem ).DataContext as MenuItem;
    switch ( item.Text )
    {
        case "Add":
            this.radGridView.BeginInsert();
            break;
        case "Edit":
            this.radGridView.BeginEdit();
            break;
        case "Delete":
            this.radGridView.Items.Remove( this.radGridView.SelectedItem );
            break;
        case "Ascending":
            this.radGridView.SortDescriptors.Clear();
            this.radGridView.SortDescriptors.Add( new SortDescriptor()
            {
                Member = this.ClickedHeader.Column.UniqueName,
                SortDirection = ListSortDirection.Ascending
            } );
            break;
        case "Descending":
            this.radGridView.SortDescriptors.Clear();
            this.radGridView.SortDescriptors.Add( new SortDescriptor()
            {
                Member = this.ClickedHeader.Column.UniqueName,
                SortDirection = ListSortDirection.Descending
            } );
            break;
        case "None":
            this.radGridView.SortDescriptors.Clear();
            break;
        case "Move Left":
            if ( this.ClickedHeader.Column.DisplayIndex > 0 )
                this.ClickedHeader.Column.DisplayIndex -= 1;
            break;
        case "Move Right":
            if ( this.ClickedHeader.Column.DisplayIndex < this.radGridView.Columns.Count - 1 )
                this.ClickedHeader.Column.DisplayIndex += 1;
            break;
    }
}
CopyVB.NET
Private Sub GridContextMenu_ItemClick(sender As Object, e As Telerik.Windows.RadRoutedEventArgs)
 Dim item As MenuItem = TryCast((TryCast(e.OriginalSource, RadMenuItem)).DataContext, MenuItem)
 Select Case item.Text
  Case "Add"
   Me.radGridView.BeginInsert()
   Exit Select
  Case "Edit"
   Me.radGridView.BeginEdit()
   Exit Select
  Case "Delete"
   Me.radGridView.Items.Remove(Me.radGridView.SelectedItem)
   Exit Select
  Case "Ascending"
   Me.radGridView.SortDescriptors.Clear()
   Me.radGridView.SortDescriptors.Add(New SortDescriptor())
   Exit Select
  Case "Descending"
   Me.radGridView.SortDescriptors.Clear()
   Me.radGridView.SortDescriptors.Add(New SortDescriptor())
   Exit Select
  Case "None"
   Me.radGridView.SortDescriptors.Clear()
   Exit Select
  Case "Move Left"
   If Me.ClickedHeader.Column.DisplayIndex > 0 Then
    Me.ClickedHeader.Column.DisplayIndex -= 1
   End If
   Exit Select
  Case "Move Right"
   If Me.ClickedHeader.Column.DisplayIndex < Me.radGridView.Columns.Count - 1 Then
    Me.ClickedHeader.Column.DisplayIndex += 1
   End If
   Exit Select
 End Select
End Sub

See Also