在 wpf 上下文菜单中禁用多重检查

发布于 2024-11-02 03:16:42 字数 33 浏览 0 评论 0原文

我想在上下文菜单项中禁用多重检查,只检查一项,如何?

I want to disable multicheck in context Menu items just check on one item how?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

临走之时 2024-11-09 03:16:42

您可以在代码隐藏中执行此操作,如 DarkSquirrel42 的答案中所述。但如果您想要一个可重用的解决方案,最好的方法可能是将其实现为附加行为,以便您可以直接在 XAML 中使用它。下面是一个基本实现:

public static class MenuBehavior
{
    [AttachedPropertyBrowsableForType(typeof(MenuItem))]
    public static string GetOptionGroupName(MenuItem obj)
    {
        return (string)obj.GetValue(OptionGroupNameProperty);
    }

    public static void SetOptionGroupName(MenuItem obj, string value)
    {
        obj.SetValue(OptionGroupNameProperty, value);
    }

    public static readonly DependencyProperty OptionGroupNameProperty =
        DependencyProperty.RegisterAttached(
          "OptionGroupName",
          typeof(string),
          typeof(MenuBehavior),
          new UIPropertyMetadata(
            null,
            OptionGroupNameChanged));

    private static void OptionGroupNameChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var menuItem = o as MenuItem;
        if (menuItem == null)
            return;

        var oldValue = (string)e.OldValue;
        var newValue = (string)e.NewValue;

        if (!string.IsNullOrEmpty(oldValue))
        {
            RemoveFromOptionGroup(menuItem);
        }
        if (!string.IsNullOrEmpty(newValue))
        {
            AddToOptionGroup(menuItem);
        }
    }

    private static Dictionary<string, HashSet<MenuItem>> GetOptionGroups(DependencyObject obj)
    {
        return (Dictionary<string, HashSet<MenuItem>>)obj.GetValue(OptionGroupsPropertyKey.DependencyProperty);
    }

    private static void SetOptionGroups(DependencyObject obj, Dictionary<string, HashSet<MenuItem>> value)
    {
        obj.SetValue(OptionGroupsPropertyKey, value);
    }

    private static readonly DependencyPropertyKey OptionGroupsPropertyKey =
        DependencyProperty.RegisterAttachedReadOnly("OptionGroups", typeof(Dictionary<string, HashSet<MenuItem>>), typeof(MenuBehavior), new UIPropertyMetadata(null));

    private static HashSet<MenuItem> GetOptionGroup(MenuItem menuItem, bool create)
    {
        string groupName = GetOptionGroupName(menuItem);
        if (groupName == null)
            return null;

        if (menuItem.Parent == null)
            return null;

        var optionGroups = GetOptionGroups(menuItem.Parent);
        if (optionGroups == null)
        {
            if (create)
            {
                optionGroups = new Dictionary<string, HashSet<MenuItem>>();
                SetOptionGroups(menuItem.Parent, optionGroups);
            }
            else
            {
                return null;
            }
        }

        HashSet<MenuItem> group;
        if (!optionGroups.TryGetValue(groupName, out group) && create)
        {
            group = new HashSet<MenuItem>();
            optionGroups[groupName] = group;
        }
        return group;
    }

    private static void AddToOptionGroup(MenuItem menuItem)
    {
        var group = GetOptionGroup(menuItem, true);
        if (group == null)
            return;

        if (group.Add(menuItem))
        {
            menuItem.Checked += menuItem_Checked;
            menuItem.Unchecked += menuItem_Unchecked;
        }
    }

    private static void RemoveFromOptionGroup(MenuItem menuItem)
    {
        var group = GetOptionGroup(menuItem, false);
        if (group == null)
            return;

        if (group.Remove(menuItem))
        {
            menuItem.Checked -= menuItem_Checked;
            menuItem.Unchecked -= menuItem_Unchecked;
        }
    }

    static void menuItem_Checked(object sender, RoutedEventArgs e)
    {
        MenuItem menuItem = sender as MenuItem;
        if (menuItem == null)
            return;

        string groupName = GetOptionGroupName(menuItem);
        if (groupName == null)
            return;

        // More than 1 checked option is allowed
        if (groupName.EndsWith("*") || groupName.EndsWith("+"))
            return;

        var group = GetOptionGroup(menuItem, false);
        if (group == null)
            return;

        foreach (var item in group)
        {
            if (item != menuItem)
                item.IsChecked = false;
        }
    }

    static void menuItem_Unchecked(object sender, RoutedEventArgs e)
    {
        MenuItem menuItem = sender as MenuItem;
        if (menuItem == null)
            return;

        string groupName = GetOptionGroupName(menuItem);
        if (groupName == null)
            return;

        // 0 checked option is allowed
        if (groupName.EndsWith("*") || groupName.EndsWith("?"))
            return;

        var group = GetOptionGroup(menuItem, false);
        if (group == null)
            return;

        if (!group.Any(item => item.IsChecked))
            menuItem.IsChecked = true;
    }
}

XAML 用法:

<ContextMenu>
    <MenuItem Header="Choose one" IsEnabled="False" />
    <MenuItem Header="Option 1.1" IsCheckable="True" IsChecked="True"
                my:MenuBehavior.OptionGroupName="group1"/>
    <MenuItem Header="Option 1.2" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group1"/>
    <MenuItem Header="Option 1.3" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group1"/>
    <Separator />
    <MenuItem Header="Choose zero or one" IsEnabled="False" />
    <MenuItem Header="Option 2.1" IsCheckable="True" IsChecked="True"
                my:MenuBehavior.OptionGroupName="group2?"/>
    <MenuItem Header="Option 2.2" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group2?"/>
    <MenuItem Header="Option 2.3" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group2?"/>
    <Separator />
    <MenuItem Header="Choose one or more" IsEnabled="False" />
    <MenuItem Header="Option 3.1" IsCheckable="True" IsChecked="True"
                my:MenuBehavior.OptionGroupName="group3+"/>
    <MenuItem Header="Option 3.2" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group3+"/>
    <MenuItem Header="Option 3.3" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group3+"/>
    <Separator />
    <MenuItem Header="Choose any number" IsEnabled="False" />
    <MenuItem Header="Option 4.1" IsCheckable="True" IsChecked="True"
                my:MenuBehavior.OptionGroupName="group4*"/>
    <MenuItem Header="Option 4.2" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group4*"/>
    <MenuItem Header="Option 4.3" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group4*"/>
</ContextMenu>

my 是映射到您在其中声明 MenuBehavior 类的 CLR 命名空间的 XML 命名空间)

当然有有改进的余地:

  • 您可能希望强制选中一个选项(即不可能取消选中所有内容) DONE
  • 当前组名称是全局的,即如果您在不同的环境中使用相同的组名称菜单,“单选”规则将应用于所有菜单。您可能希望将其限制为当前菜单 完成

编辑:我更新了代码以包含上述改进:

  • 组现在仅限于同一菜单中的 MenuItem
  • 您现在可以定义在每组的基础上,检查选项的规则:
    • 必须仅选中一个选项(默认)
    • 必须选中零个或一个选项(在组名称末尾添加 ?
    • 必须选中一个或多个选项(在组名称末尾添加 +
    • 可以选中任意数量的选项(在组名称末尾添加 *)。这实际上与根本不使用附加行为相同,但为了完整性我还是将其包含在内......

XAML 使用示例说明了各种规则

You could do it in code-behind as explained in DarkSquirrel42's answer. But if you want a reusable solution, the best approach is probably to implement it as an attached behavior, so that you can use it directly in XAML. Here's a basic implementation:

public static class MenuBehavior
{
    [AttachedPropertyBrowsableForType(typeof(MenuItem))]
    public static string GetOptionGroupName(MenuItem obj)
    {
        return (string)obj.GetValue(OptionGroupNameProperty);
    }

    public static void SetOptionGroupName(MenuItem obj, string value)
    {
        obj.SetValue(OptionGroupNameProperty, value);
    }

    public static readonly DependencyProperty OptionGroupNameProperty =
        DependencyProperty.RegisterAttached(
          "OptionGroupName",
          typeof(string),
          typeof(MenuBehavior),
          new UIPropertyMetadata(
            null,
            OptionGroupNameChanged));

    private static void OptionGroupNameChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var menuItem = o as MenuItem;
        if (menuItem == null)
            return;

        var oldValue = (string)e.OldValue;
        var newValue = (string)e.NewValue;

        if (!string.IsNullOrEmpty(oldValue))
        {
            RemoveFromOptionGroup(menuItem);
        }
        if (!string.IsNullOrEmpty(newValue))
        {
            AddToOptionGroup(menuItem);
        }
    }

    private static Dictionary<string, HashSet<MenuItem>> GetOptionGroups(DependencyObject obj)
    {
        return (Dictionary<string, HashSet<MenuItem>>)obj.GetValue(OptionGroupsPropertyKey.DependencyProperty);
    }

    private static void SetOptionGroups(DependencyObject obj, Dictionary<string, HashSet<MenuItem>> value)
    {
        obj.SetValue(OptionGroupsPropertyKey, value);
    }

    private static readonly DependencyPropertyKey OptionGroupsPropertyKey =
        DependencyProperty.RegisterAttachedReadOnly("OptionGroups", typeof(Dictionary<string, HashSet<MenuItem>>), typeof(MenuBehavior), new UIPropertyMetadata(null));

    private static HashSet<MenuItem> GetOptionGroup(MenuItem menuItem, bool create)
    {
        string groupName = GetOptionGroupName(menuItem);
        if (groupName == null)
            return null;

        if (menuItem.Parent == null)
            return null;

        var optionGroups = GetOptionGroups(menuItem.Parent);
        if (optionGroups == null)
        {
            if (create)
            {
                optionGroups = new Dictionary<string, HashSet<MenuItem>>();
                SetOptionGroups(menuItem.Parent, optionGroups);
            }
            else
            {
                return null;
            }
        }

        HashSet<MenuItem> group;
        if (!optionGroups.TryGetValue(groupName, out group) && create)
        {
            group = new HashSet<MenuItem>();
            optionGroups[groupName] = group;
        }
        return group;
    }

    private static void AddToOptionGroup(MenuItem menuItem)
    {
        var group = GetOptionGroup(menuItem, true);
        if (group == null)
            return;

        if (group.Add(menuItem))
        {
            menuItem.Checked += menuItem_Checked;
            menuItem.Unchecked += menuItem_Unchecked;
        }
    }

    private static void RemoveFromOptionGroup(MenuItem menuItem)
    {
        var group = GetOptionGroup(menuItem, false);
        if (group == null)
            return;

        if (group.Remove(menuItem))
        {
            menuItem.Checked -= menuItem_Checked;
            menuItem.Unchecked -= menuItem_Unchecked;
        }
    }

    static void menuItem_Checked(object sender, RoutedEventArgs e)
    {
        MenuItem menuItem = sender as MenuItem;
        if (menuItem == null)
            return;

        string groupName = GetOptionGroupName(menuItem);
        if (groupName == null)
            return;

        // More than 1 checked option is allowed
        if (groupName.EndsWith("*") || groupName.EndsWith("+"))
            return;

        var group = GetOptionGroup(menuItem, false);
        if (group == null)
            return;

        foreach (var item in group)
        {
            if (item != menuItem)
                item.IsChecked = false;
        }
    }

    static void menuItem_Unchecked(object sender, RoutedEventArgs e)
    {
        MenuItem menuItem = sender as MenuItem;
        if (menuItem == null)
            return;

        string groupName = GetOptionGroupName(menuItem);
        if (groupName == null)
            return;

        // 0 checked option is allowed
        if (groupName.EndsWith("*") || groupName.EndsWith("?"))
            return;

        var group = GetOptionGroup(menuItem, false);
        if (group == null)
            return;

        if (!group.Any(item => item.IsChecked))
            menuItem.IsChecked = true;
    }
}

XAML usage:

<ContextMenu>
    <MenuItem Header="Choose one" IsEnabled="False" />
    <MenuItem Header="Option 1.1" IsCheckable="True" IsChecked="True"
                my:MenuBehavior.OptionGroupName="group1"/>
    <MenuItem Header="Option 1.2" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group1"/>
    <MenuItem Header="Option 1.3" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group1"/>
    <Separator />
    <MenuItem Header="Choose zero or one" IsEnabled="False" />
    <MenuItem Header="Option 2.1" IsCheckable="True" IsChecked="True"
                my:MenuBehavior.OptionGroupName="group2?"/>
    <MenuItem Header="Option 2.2" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group2?"/>
    <MenuItem Header="Option 2.3" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group2?"/>
    <Separator />
    <MenuItem Header="Choose one or more" IsEnabled="False" />
    <MenuItem Header="Option 3.1" IsCheckable="True" IsChecked="True"
                my:MenuBehavior.OptionGroupName="group3+"/>
    <MenuItem Header="Option 3.2" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group3+"/>
    <MenuItem Header="Option 3.3" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group3+"/>
    <Separator />
    <MenuItem Header="Choose any number" IsEnabled="False" />
    <MenuItem Header="Option 4.1" IsCheckable="True" IsChecked="True"
                my:MenuBehavior.OptionGroupName="group4*"/>
    <MenuItem Header="Option 4.2" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group4*"/>
    <MenuItem Header="Option 4.3" IsCheckable="True"
                my:MenuBehavior.OptionGroupName="group4*"/>
</ContextMenu>

(my is an XML namespace mapped to the CLR namespace where you declared the MenuBehavior class)

Of course there is room for improvements:

  • You might want to enforce that one option is checked (i.e. impossible to uncheck everything) DONE
  • Currently the group names are global, i.e. if you use the same group name in different menus, the "single choice" rule will be applied across all the menus. You might want to restrict it to the current menu DONE

EDIT: I updated the code to include the improvements mentioned above:

  • Groups are now restricted to MenuItems in the same menu
  • You can now define on a per-group basis the rules for checked options:
    • Exactly one option must be checked (default)
    • Zero or one option must be checked (add a ? at the end of the group name)
    • One or more options must be checked (add a + at the end of the group name)
    • Any number of options can be checked (add a * at the end of the group name). That's actually the same as not using the attached behavior at all, but I included it anyway for completeness...

The XAML usage example illustrates the various rules

有深☉意 2024-11-09 03:16:42

处理 MenuItems 的 Click 事件...

private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (sender is MenuItem)
            {
                IEnumerable<MenuItem> menuItems = null;
                var mi = (MenuItem)sender;
                if (mi.Parent is ContextMenu)
                    menuItems = ((ContextMenu)mi.Parent).Items.OfType<MenuItem>();
                if (mi.Parent is MenuItem)
                    menuItems = ((MenuItem)mi.Parent).Items.OfType<MenuItem>();
                if(menuItems!=null)
                    foreach (var item in menuItems)
                    {
                        if (item.IsCheckable && item != mi)
                            item.IsChecked = false;
                    }
            }
        }

handle the Click event of the MenuItems ...

private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (sender is MenuItem)
            {
                IEnumerable<MenuItem> menuItems = null;
                var mi = (MenuItem)sender;
                if (mi.Parent is ContextMenu)
                    menuItems = ((ContextMenu)mi.Parent).Items.OfType<MenuItem>();
                if (mi.Parent is MenuItem)
                    menuItems = ((MenuItem)mi.Parent).Items.OfType<MenuItem>();
                if(menuItems!=null)
                    foreach (var item in menuItems)
                    {
                        if (item.IsCheckable && item != mi)
                            item.IsChecked = false;
                    }
            }
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文