左键单击显示ContextMenu仅使用XAML

tim*_*ath 7 .net wpf xaml contextmenu mvvm

WPF的默认行为ContextMenu是在用户右键单击时显示它.我希望在ContextMenu用户左键单击时显示.看起来这应该是一个简单的属性ContextMenu,但事实并非如此.

我操纵它,以便我LeftMouseButtonDown在代码隐藏中处理事件,然后显示上下文菜单.

我在我的项目中使用MVVM,这意味着我将DataTemplates用于具有上下文菜单的项目.摆脱代码隐藏并找到一种使用XAML中的触发器或属性显示上下文菜单的方法会更加优雅.

这个问题的任何想法或解决方案?

Cal*_*ear 9

我建议做的是创建一个带有DependencyProperty的新静态类.调用类LeftClickContextMenu和属性Enabled(只是想法).注册DependencyProperty时添加一个更改后的回调.然后在属性更改回调中,如果Enabled设置为true,则向LeftMouseButtonDown事件添加处理程序并在那里执行您的操作.如果Enabled设置为false,则删除处理程序.这可以让你只需在你的xaml中使用以下内容就可以将它设置为任何属性.

<Border namespace:LeftClickContextMenu.Enabled="True" />
Run Code Online (Sandbox Code Playgroud)

此技术称为附加行为,您可以在此代码项目文章中阅读更多相关信息:http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx


nig*_*der 8

我刚刚根据HK1的答案编写并测试了这个(您还可以阅读附加属性概述中的附加属性):

public static class ContextMenuLeftClickBehavior
{
    public static bool GetIsLeftClickEnabled(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsLeftClickEnabledProperty);
    }

    public static void SetIsLeftClickEnabled(DependencyObject obj, bool value)
    {
        obj.SetValue(IsLeftClickEnabledProperty, value);
    }

    public static readonly DependencyProperty IsLeftClickEnabledProperty = DependencyProperty.RegisterAttached(
        "IsLeftClickEnabled", 
        typeof(bool), 
        typeof(ContextMenuLeftClickBehavior), 
        new UIPropertyMetadata(false, OnIsLeftClickEnabledChanged));

    private static void OnIsLeftClickEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var uiElement = sender as UIElement;

        if(uiElement != null) 
        {
            bool IsEnabled = e.NewValue is bool && (bool) e.NewValue;

            if(IsEnabled)
            {
                if(uiElement is ButtonBase)
                    ((ButtonBase)uiElement).Click += OnMouseLeftButtonUp;
                else
                    uiElement.MouseLeftButtonUp += OnMouseLeftButtonUp;
            }
            else
            {
                if(uiElement is ButtonBase)
                    ((ButtonBase)uiElement).Click -= OnMouseLeftButtonUp;
                else
                    uiElement.MouseLeftButtonUp -= OnMouseLeftButtonUp;
            }
        }
    }

    private static void OnMouseLeftButtonUp(object sender, RoutedEventArgs e)
    {
        Debug.Print("OnMouseLeftButtonUp");
        var fe = sender as FrameworkElement;
        if(fe != null)
        {
            // if we use binding in our context menu, then it's DataContext won't be set when we show the menu on left click
            // (it seems setting DataContext for ContextMenu is hardcoded in WPF when user right clicks on a control, although I'm not sure)
            // so we have to set up ContextMenu.DataContext manually here
            if (fe.ContextMenu.DataContext == null)
            {
                fe.ContextMenu.SetBinding(FrameworkElement.DataContextProperty, new Binding { Source = fe.DataContext });
            }

            fe.ContextMenu.IsOpen = true;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

...

<Button Content="Do All" local:ContextMenuLeftClickBehavior.IsLeftClickEnabled="True" >
    <Button.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Make everything awesome" />
            <MenuItem Header="Control the World" />
        </ContextMenu>
    </Button.ContextMenu>
</Button>
Run Code Online (Sandbox Code Playgroud)

(注意OnMouseLeftButtonUp()方法中的注释)

  • 优秀的解决方案,为了解决您的评论中引用的绑定问题,您可以设置放置目标:`fe.ContextMenu.PlacementTarget = fe`然后`DataContext ="{Binding Path = PlacementTarget.DataContext,RelativeSource = {RelativeSource Self}}" >`然后,您可以使用ContextMenuService属性(如Placement和Horizo​​ntal/VerticalOffset)来定位它. (2认同)
  • 当上下文菜单已显示时,我不希望在单击按钮时再次打开它。因此,在 IsOpen=true 下面我添加了这两行: `fe.ContextMenu.Closed += ((s, a) =&gt; { fe.IsEnabled = true; }); fe.IsEnabled = false;` (2认同)