Group Checkable Menu Items into Radio Group

This tutorial will show you how to group your checkable menu items into a radio group. This will allow you to have only one of them checked at a time.

Creating a sample RadMenu and defining the Radio Groups

Here is a sample RadMenu and one of its items has two radio groups. Notice that the Tag property of the RadMenuItem is used to store the name of the Radio Group. In this case the groups are named "1" and "2".

<telerik:RadMenu x:Name="radMenu"> 
    <telerik:RadMenuItem Header="File"> 
        <telerik:RadMenuItem Header="Exit" /> 
    </telerik:RadMenuItem> 
    <telerik:RadMenuItem Header="Options"> 
        <telerik:RadMenuItem Header="Radio Button 1.1" 
                             Tag="1" 
                             IsCheckable="True" 
                             StaysOpenOnClick="True" /> 
        <telerik:RadMenuItem Header="Radio Button 1.2" 
                             Tag="1" 
                             IsCheckable="True" 
                             StaysOpenOnClick="True" /> 
        <telerik:RadMenuItem Header="Radio Button 1.3" 
                             Tag="1" 
                             IsCheckable="True" 
                             StaysOpenOnClick="True" /> 
        <telerik:RadMenuItem IsSeparator="True" /> 
        <telerik:RadMenuItem Header="Radio Button 2.1" 
                             Tag="2" 
                             IsCheckable="True" 
                             StaysOpenOnClick="True" /> 
        <telerik:RadMenuItem Header="Radio Button 2.2" 
                             Tag="2" 
                             IsCheckable="True" 
                             StaysOpenOnClick="True" /> 
        <telerik:RadMenuItem Header="Radio Button 2.3" 
                             Tag="2" 
                             IsCheckable="True" 
                             StaysOpenOnClick="True" /> 
    </telerik:RadMenuItem> 
</telerik:RadMenu> 

Handling the ItemClick Event

The next step is to handle the ItemClick event of the RadMenu. It gets fired each time an item gets clicked .

<telerik:RadMenu x:Name="radMenu1" ItemClick="radMenu_ItemClick"> 
    ... 
</telerik:RadMenu> 

In the event handler you get the item that has been clicked and check if it is checkable or is placed in a group.

private void radMenu_ItemClick(object sender, RadRoutedEventArgs e) 
{ 
    var currentItem = e.OriginalSource as RadMenuItem; 
    if (currentItem.IsCheckable && currentItem.Tag != null) 
    { 
        //the place for the radio items logic 
    } 
} 
Private Sub radMenu_ItemClick(sender As Object, e As RadRoutedEventArgs) 
    Dim currentItem = TryCast(e.OriginalSource, RadMenuItem) 
    If currentItem.IsCheckable AndAlso currentItem.Tag <> Nothing Then 
        'the place for the radio items logic 
    End If 
End Sub 

The next step is to get the sibling items of the clicked one, which are from the same group.

Getting All Sibling Items from the same Group

In order the uncheck the other items from the same group, you have to get them first. Here is a method that takes as a parameter the clicked item and returns the sibling items which are from the same group.

private List<RadMenuItem> GetSiblingGroupItems(RadMenuItem currentItem) 
{ 
    var parentItem = currentItem.ParentOfType<RadMenuItem>(); 
    if (parentItem == null) 
    { 
        return null; 
    } 
    List<RadMenuItem> items = new List<RadMenuItem>(); 
    foreach (var item in parentItem.Items) 
    { 
        RadMenuItem container = parentItem.ItemContainerGenerator.ContainerFromItem(item) as RadMenuItem; 
        if (container == null || container.Tag == null) 
        { 
            continue; 
        } 
        if (container.Tag.Equals(currentItem.Tag)) 
        { 
            items.Add(container); 
        } 
    } 
    return items; 
} 
Private Function GetSiblingGroupItems(currentItem As RadMenuItem) As List(Of RadMenuItem) 
    Dim parentItem = currentItem.ParentOfType(Of RadMenuItem)() 
    If parentItem Is Nothing Then 
        Return Nothing 
    End If 
    Dim items As New List(Of RadMenuItem)() 
    For Each item In parentItem.Items 
        Dim container As RadMenuItem = TryCast(parentItem.ItemContainerGenerator.ContainerFromItem(item), RadMenuItem) 
        If container Is Nothing OrElse container.Tag = Nothing Then 
            Continue For 
        End If 
        If container.Tag.Equals(currentItem.Tag) Then 
            items.Add(container) 
        End If 
    Next 
    Return items 
End Function 

First you get the parent item of the clicked one and then you iterate through its Items collection. The Items collection doesn't hold the container itself, so you have to get the container of each item and check if it belongs to the same group as the clicked item. The matching containers are returned as List.

Checking only the Clicked Item

Now as the sibling items from the same group are available, the only things that's left is to unmark those of them which are different from the clicked one as unchecked.

private void radMenu_ItemClick(object sender, Telerik.Windows.RadRoutedEventArgs e) 
{ 
    var currentItem = e.OriginalSource as RadMenuItem; 
    if (currentItem.IsCheckable && currentItem.Tag != null) 
    { 
        var siblingItems = this.GetSiblingGroupItems(currentItem); 
        if (siblingItems != null) 
        { 
            foreach (var item in siblingItems) 
            { 
                if (item != currentItem) 
                { 
                    item.IsChecked = false; 
                } 
            } 
        } 
    } 
} 
Private Sub radMenu_ItemClick(ByVal sender As Object, ByVal e As Telerik.Windows.RadRoutedEventArgs) 
    Dim currentItem = TryCast(e.OriginalSource, RadMenuItem) 
    If currentItem.IsCheckable AndAlso currentItem.Tag IsNot Nothing Then 
        Dim siblingItems = Me.GetSiblingGroupItems(currentItem) 
        If siblingItems IsNot Nothing Then 
            For Each item In siblingItems 
                If item IsNot currentItem Then 
                    item.IsChecked = False 
                End If 
            Next item 
        End If 
    End If 
End Sub 

Using this Logic with Dynamic Data

Before you continue, please, Take a look at the topic about Binding to Dynamic Data.

An entirely business object oriented approach about handling radio groups within dynamic data can be found in the online demo.

When having RadMenu with dynamic data in it, the logic remains the same, but you have to modify the data items a bit, so they can provide the needed information. You need your business object to expose several properties.

  • Header - indicates header text of the item. It will be bound to the Header property of the RadMenuItem.

  • IsCheckable - indicates whether the item is checkable. It will be bound to the IsCheckable property of the RadMenuItem.

  • IsSeparator - indicates whether the item is a separator. It will be bound to the IsSeparator property of the RadMenuItem.

  • RadioGroup - represents the radio group to which the item belongs. It will be bound to the Tag property of the RadMenuItem.

  • SubMenuItems - represents a collection with the sub menu items of the item. It will be bound to the ItemsSource property of the RadMenuItem.

public class MenuItem 
{ 
    public string Header { get; set; } 
    public bool IsCheckable { get; set; } 
    public string RadioGroup { get; set; } 
    public bool IsSeparator { get; set; } 
    public ObservableCollection<MenuItem> SubMenuItems { get; set; } 
} 
Public Class MenuItem 
    Public Property Header() As String 
    Public Property IsCheckable() As Boolean 
    Public Property RadioGroup() As String 
    Public Property IsSeparator() As Boolean 
    Public Property SubMenuItems() As ObservableCollection(Of MenuItem) 
End Class 

These properties should be bound in the Style for the RadMenuItem container to its respective properties:

<Style x:Key="MenuItemStyle" TargetType="telerik:RadMenuItem"> 
    <Setter Property="Header" Value="{Binding Header}" /> 
    <Setter Property="IsCheckable" Value="{Binding IsCheckable}" /> 
    <Setter Property="StaysOpenOnClick" Value="{Binding IsCheckable}" /> 
    <Setter Property="IsSeparator" Value="{Binding IsSeparator}" /> 
    <Setter Property="Tag" Value="{Binding RadioGroup}" /> 
    <Setter Property="ItemsSource" Value="{Binding SubMenuItems}" /> 
</Style> 

Next step will be to initialize the collection with the MenuItem objects which will be bound to ItemsSource property of the RadMenu.

The ViewModel should look as shown below:

public class ViewModel 
{ 
    public ObservableCollection<MenuItem> MenuItems { get; set; } 
 
    public ViewModel() 
    { 
        this.MenuItems = new ObservableCollection<MenuItem> 
    { 
        new MenuItem { Header = "File", SubMenuItems = new ObservableCollection<MenuItem> 
        { 
            new MenuItem { Header = "Exit", IsCheckable = true, RadioGroup = "1" }, 
        }}, 
        new MenuItem { Header = "Options", SubMenuItems = new ObservableCollection<MenuItem> 
        { 
            new MenuItem { Header = "Radio Button 1.1", IsCheckable = true, RadioGroup = "1" }, 
            new MenuItem { Header = "Radio Button 1.2", IsCheckable = true, RadioGroup = "1" }, 
            new MenuItem { Header = "Radio Button 1.3", IsCheckable = true, RadioGroup = "1" }, 
            new MenuItem { IsSeparator = true }, 
            new MenuItem { Header = "Radio Button 2.1", IsCheckable = true, RadioGroup = "2" }, 
            new MenuItem { Header = "Radio Button 2.2", IsCheckable = true, RadioGroup = "2" }, 
            new MenuItem { Header = "Radio Button 2.3", IsCheckable = true, RadioGroup = "2" }, 
        }}, 
    }; 
    } 
} 
Public Class ViewModel 
    Public Property MenuItems() As ObservableCollection(Of MenuItem) 
 
    Public Sub New() 
        Me.MenuItems = New ObservableCollection(Of MenuItem) From { 
            New MenuItem With {.Header = "File", .SubMenuItems = New ObservableCollection(Of MenuItem) From { 
                    New MenuItem With {.Header = "Exit", .IsCheckable = True, .RadioGroup = "1"}}}, 
            New MenuItem With {.Header = "Options", .SubMenuItems = New ObservableCollection(Of MenuItem) From { 
                    New MenuItem With {.Header = "Radio Button 1.1", .IsCheckable = True, .RadioGroup = "1"}, 
                    New MenuItem With {.Header = "Radio Button 1.2", .IsCheckable = True, .RadioGroup = "1"}, 
                    New MenuItem With {.Header = "Radio Button 1.3", .IsCheckable = True, .RadioGroup = "1"}, 
                    New MenuItem With {.IsSeparator = True}, 
                    New MenuItem With {.Header = "Radio Button 2.1", .IsCheckable = True, .RadioGroup = "2"}, 
                    New MenuItem With {.Header = "Radio Button 2.2", .IsCheckable = True, .RadioGroup = "2"}, 
                    New MenuItem With {.Header = "Radio Button 2.3", .IsCheckable = True, .RadioGroup = "2"}}} 
        } 
    End Sub 
End Class 

Finally you need to set the created style to the ItemContainerStyle property and bind the collection to the ItemsSource property of the RadMenu.

<telerik:RadMenu x:Name="radMenu2"  
                 VerticalAlignment="Top"   
                 ItemClick="radMenu_ItemClick" 
                 Orientation="Horizontal" 
                 ItemsSource="{Binding MenuItems}" 
                 ItemContainerStyle="{StaticResource MenuItemStyle}" /> 

From here on, the things work the same as in the scenario with the static items.

This will be the result:

Rad Menu How To Group Checkable Menu Items

See Also

In this article