45 votes

Mutuellement exclusives vérifiable éléments de menu?

Étant donné le code suivant:

<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>

En XAML, il est un moyen de créer vérifiable menuitem qui sont mutuellement exclusifs? Où est l'utilisateur vérifie item2, de l'élément 1 et 3 sont automatiquement désactivées.

Je peux le faire dans le code-behind, par la surveillance des événements de clic sur le menu, la détermination de l'élément a été vérifié, et en décochant les autres menuitems. Je pense il y a un moyen plus facile.

Des idées?

48voto

Patrick Points 627

Cela peut ne pas être ce que vous cherchez, mais vous pourriez écrire une extension pour l' MenuItem classe qui vous permet d'utiliser quelque chose comme l' GroupName de la propriété de l' RadioButton classe. J'ai légèrement modifié cette pratique exemple même d'étendre ToggleButton contrôles et retravaillé un peu de votre situation et est venu avec ceci:

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;
                     }
                }
           }
      }
 }

Ensuite, dans le code XAML, vous devez écrire:

        <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>

C'est un peu de douleur, mais il offre l'avantage de ne pas vous forcer à écrire supplémentaires code de procédure (à l'exception de la classe d'extension, bien sûr) pour la mettre en œuvre.

Le crédit va à Brad Cunningham qui est l'auteur de l'original ToggleButton solution.

9voto

user1182735 Points 180

Vous pouvez également utiliser un Comportement. Comme celui-ci:

<MenuItem Header="menu">

    <MenuItem x:Name="item1" Header="item1" IsCheckable="true" ></MenuItem>
    <MenuItem x:Name="item2" Header="item2" IsCheckable="true"></MenuItem>
    <MenuItem x:Name="item3" Header="item3" IsCheckable="true" ></MenuItem>

    <i:Interaction.Behaviors>
    <local:MenuItemButtonGroupBehavior></local:MenuItemButtonGroupBehavior>
    </i:Interaction.Behaviors>

</MenuItem>


public class MenuItemButtonGroupBehavior : Behavior<MenuItem>
{
    protected override void OnAttached()
    {
        base.OnAttached();

        GetCheckableSubMenuItems(AssociatedObject)
            .ToList()
            .ForEach(item => item.Click += OnClick);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        GetCheckableSubMenuItems(AssociatedObject)
            .ToList()
            .ForEach(item => item.Click -= OnClick);
    }

    private static IEnumerable<MenuItem> GetCheckableSubMenuItems(ItemsControl menuItem)
    {
        var itemCollection = menuItem.Items;
        return itemCollection.OfType<MenuItem>().Where(menuItemCandidate => menuItemCandidate.IsCheckable);
    }

    private void OnClick(object sender, RoutedEventArgs routedEventArgs)
    {
        var menuItem = (MenuItem)sender;

        if (!menuItem.IsChecked)
        {
            menuItem.IsChecked = true;
            return;
        }

        GetCheckableSubMenuItems(AssociatedObject)
            .Where(item => item != menuItem)
            .ToList()
            .ForEach(item => item.IsChecked = false);
    }
}

5voto

Rhyous Points 2163

J'ai juste pensé que je voudrais jeter dans ma solution, étant donné qu'aucune des réponses à mes besoins. Ma solution est ici...

WPF MenuItem comme un RadioButton

Cependant, l'idée de base est d'utiliser ItemContainerStyle.

<MenuItem.ItemContainerStyle>
    <Style TargetType="MenuItem">
        <Setter Property="Icon" Value="{DynamicResource RadioButtonResource}"/>
        <EventSetter Event="Click" Handler="MenuItemWithRadioButtons_Click" />
    </Style>
</MenuItem.ItemContainerStyle>

Et l'événement suivant cliquez sur doit être ajoutée afin que la case d'option est cochée lorsque le MenuItem est cliqué (sinon, vous devez cliquer exactement sur la case d'option):

private void MenuItemWithRadioButtons_Click(object sender, System.Windows.RoutedEventArgs e)
{
    MenuItem mi = sender as MenuItem;
    if (mi != null)
    {
        RadioButton rb = mi.Icon as RadioButton;
        if (rb != null)
        {
            rb.IsChecked = true;
        }
    }
}

4voto

Chris Taylor Points 25865

Il n'y a pas un moyen intégré pour ce faire, dans le code XAML, vous aurez besoin pour restaurer votre propre solution ou d'obtenir une solution existante si elle est disponible.

0voto

Captain Points 325

Il suffit de créer un Modèle pour MenuItem qui contiennent un composant RadioButton avec un nom de groupe réglé à une certaine valeur. Vous pouvez également modifier le modèle pour les composants radiobutton pour ressembler à la MenuItem par défaut de vérifier glyphe (qui peut être facilement extrait avec Expression Blend).

Ça y est!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X