tags:

views:

148

answers:

4

Given the following code:

<MenuItem x:Name="MenuItem_Root" Header="Root">
    <MenuItem x:Name="MenuItem_Item1" IsCheckable="True" Header="item1" />
    <MenuItem x:Name="MenuItem_Item2" IsCheckable="True" Header="item2"/>
    <MenuItem x:Name="MenuItem_Item3" IsCheckable="True" Header="item3"/>
</MenuItem>

In XAML, is there a way to create checkable menuitem's that are mutually exclusive? Where is the user checks item2, item's 1 and 3 are automatically unchecked.

I can accomplish this in the code behind by monitoring the click events on the menu, determining which item was checked, and unchecking the other menuitems. I'm thinking there is an easier way.

Any ideas?

+1  A: 

There is not a built-in way to do this in XAML, you will need to roll your own solution or get an existing solution if available.

Chris Taylor
A: 

This may not be what you're looking for, but you could write an extension for the MenuItem class that allows you to use something like the GroupName property of the RadioButton class. I slightly modified this handy example for similarly extending ToggleButton controls and reworked it a little for your situation and came up with this:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace WpfTest
{
     public class MenuItemExtensions : DependencyObject
     {
           public static Dictionary<MenuItem, String> ElementToGroupNames = new Dictionary<MenuItem, String>();

           public static readonly DependencyProperty GroupNameProperty =
               DependencyProperty.RegisterAttached("GroupName",
                                            typeof(String),
                                            typeof(MenuItemExtensions),
                                            new PropertyMetadata(String.Empty, OnGroupNameChanged));

           public static void SetGroupName(MenuItem element, String value)
           {
                element.SetValue(GroupNameProperty, value);
           }

           public static String GetGroupName(MenuItem element)
           {
                return element.GetValue(GroupNameProperty).ToString();
           }

           private static void OnGroupNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
           {
                //Add an entry to the group name collection
                var menuItem = d as MenuItem;

                if (menuItem != null)
                {
                     String newGroupName = e.NewValue.ToString();
                     String oldGroupName = e.OldValue.ToString();
                     if (String.IsNullOrEmpty(newGroupName))
                     {
                          //Removing the toggle button from grouping
                          RemoveCheckboxFromGrouping(menuItem);
                     }
                     else
                     {
                          //Switching to a new group
                          if (newGroupName != oldGroupName)
                          {
                              if (!String.IsNullOrEmpty(oldGroupName))
                              {
                                   //Remove the old group mapping
                                   RemoveCheckboxFromGrouping(menuItem);
                              }
                              ElementToGroupNames.Add(menuItem, e.NewValue.ToString());
                               menuItem.Checked += MenuItemChecked;
                          }
                     }
                }
           }

           private static void RemoveCheckboxFromGrouping(MenuItem checkBox)
           {
                ElementToGroupNames.Remove(checkBox);
                checkBox.Checked -= MenuItemChecked;
           }


           static void MenuItemChecked(object sender, RoutedEventArgs e)
           {
                var menuItem = e.OriginalSource as MenuItem;
                foreach (var item in ElementToGroupNames)
                {
                     if (item.Key != menuItem && item.Value == GetGroupName(menuItem))
                     {
                          item.Key.IsChecked = false;
                     }
                }
           }
      }
 }

Then, in the XAML, you'd write:

        <MenuItem x:Name="MenuItem_Root" Header="Root">
            <MenuItem x:Name="MenuItem_Item1" YourNamespace:MenuItemExtensions.GroupName="someGroup" IsCheckable="True" Header="item1" />
            <MenuItem x:Name="MenuItem_Item2" YourNamespace:MenuItemExtensions.GroupName="someGroup" IsCheckable="True" Header="item2"/>
            <MenuItem x:Name="MenuItem_Item3" YourNamespace:MenuItemExtensions.GroupName="someGroup" IsCheckable="True" Header="item3"/>
        </MenuItem>

It's a bit of a pain, but it offers the perk of not forcing you to write any additional procedural code (aside from the extension class, of course) to implement it.

Credit goes to Brad Cunningham who authored the original ToggleButton solution.

Patrick
A: 

Simply create a Template for MenuItem which will contain a RadioButton with a GroupName set to some value. You can also change the template for the RadioButtons to look like the MenuItem's default check glyph (which can be easily extracted with Expression Blend).

That's it!

Captain
A: 

You could do something like this:

        <Menu>
            <MenuItem Header="File">
                <ListBox BorderThickness="0" Background="Transparent">
                    <ListBox.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel />
                        </ItemsPanelTemplate>
                    </ListBox.ItemsPanel>
                    <ListBox.ItemContainerStyle>
                        <Style TargetType="{x:Type ListBoxItem}">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate>
                                        <MenuItem IsCheckable="True" IsChecked="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Header="{Binding Content, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" />
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </ListBox.ItemContainerStyle>
                    <ListBox.Items>
                        <ListBoxItem Content="Test" />
                        <ListBoxItem Content="Test2" />
                    </ListBox.Items>
                </ListBox>
            </MenuItem>
        </Menu>

It has some weird side effect visually (you'll see when you use it), but it works nonetheless

Carrotman