【问题标题】:WPF Context menu on left click左键单击 WPF 上下文菜单
【发布时间】:2011-05-17 08:59:49
【问题描述】:

我有一个 WPF 应用程序。在其中我有一个 Xaml 文件中的图像控件。

右键单击此图像,我有一个上下文菜单。

我也想在“左键”上显示相同的内容。

我如何以 MVVM 方式做到这一点?

【问题讨论】:

  • 虽然有可能,但在左键单击时显示上下文菜单会违反标准 Windows 的预期。
  • 关于制作这个 MVVM,我相信 XAML 将在您的“视图”中,Image_MouseDown C# 代码将在您的“视图模型”中,并且您的“模型”不应该对上下文一无所知菜单。

标签: wpf mvvm contextmenu mouseleftbuttondown


【解决方案1】:

这是一个仅限 XAML 的解决方案。 只需将此样式添加到您的按钮。 这将导致上下文菜单在左右单击时打开。尽情享受吧!

<Button Content="Open Context Menu">
    <Button.Style>
        <Style TargetType="{x:Type Button}">
            <Style.Triggers>
                <EventTrigger RoutedEvent="Click">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
                                    <DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
                                </BooleanAnimationUsingKeyFrames>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
            </Style.Triggers>
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu>
                        <MenuItem />
                        <MenuItem />
                    </ContextMenu>
                </Setter.Value>
            </Setter>
        </Style>
    </Button.Style>
</Button>

【讨论】:

  • 样式正确,但是,当我将命令添加到 MenuItem 时,它不会触发事件。我已经验证 - 绑定是正确的。有什么想法吗?
  • 我对@utkarsh 有同样的问题,当我尝试将命令绑定到MenuItem 时,&lt;MenuItem Header="Download" Command="{Binding DownloadButtonCommand}"/&gt; 它不起作用,有什么建议吗?
  • 似乎上下文菜单的数据绑定只在右键单击时设置。首先右键单击打开它后,它也适用于左键单击。这就是我最终使用此解决方案的原因:stackoverflow.com/a/29123964/4550393
  • 有问题,ContextMenuService.Placement不起作用。
【解决方案2】:

你可以通过像这样使用 Image 的 MouseDown 事件来做到这一点

<Image ... MouseDown="Image_MouseDown">
    <Image.ContextMenu>
        <ContextMenu>
            <MenuItem .../>
            <MenuItem .../>
        </ContextMenu>
    </Image.ContextMenu>
</Image>

然后在后面的代码中显示EventHandler中的ContextMenu

private void Image_MouseDown(object sender, MouseButtonEventArgs e)
{
    if (e.ChangedButton == MouseButton.Left)
    {
        Image image = sender as Image;
        ContextMenu contextMenu = image.ContextMenu;
        contextMenu.PlacementTarget = image;
        contextMenu.IsOpen = true;
        e.Handled = true;
    }
}

【讨论】:

  • 此实现会导致上下文菜单出现并立即消失。我在 DataTemplate 中使用控件及其上下文菜单,不确定这是否重要...
  • 这似乎不会引发 ContextMenuOpening 事件,因此依赖于该事件的代码不会运行..
  • 只需使用 MouseUp 事件
  • 如果您要将此添加到按钮(不是图像)中,那么只需将与此类似的代码放入 Button_Click 事件本身。
【解决方案3】:

您可以发明自己的 DependencyProperty,它会在单击图像时打开一个上下文菜单,就像这样:

  <Image Source="..." local:ClickOpensContextMenuBehavior.Enabled="True">
      <Image.ContextMenu>...
      </Image.ContextMenu>
  </Image>

这是该属性的 C# 代码:

public class ClickOpensContextMenuBehavior
{
  private static readonly DependencyProperty ClickOpensContextMenuProperty =
    DependencyProperty.RegisterAttached(
      "Enabled", typeof(bool), typeof(ClickOpensContextMenuBehavior),
      new PropertyMetadata(new PropertyChangedCallback(HandlePropertyChanged))
    );

  public static bool GetEnabled(DependencyObject obj)
  {
    return (bool)obj.GetValue(ClickOpensContextMenuProperty);
  }

  public static void SetEnabled(DependencyObject obj, bool value)
  {
    obj.SetValue(ClickOpensContextMenuProperty, value);
  }

  private static void HandlePropertyChanged(
    DependencyObject obj, DependencyPropertyChangedEventArgs args)
  {
    if (obj is Image) {
      var image = obj as Image;
      image.MouseLeftButtonDown -= ExecuteMouseDown;
      image.MouseLeftButtonDown += ExecuteMouseDown;
    }

    if (obj is Hyperlink) {
      var hyperlink = obj as Hyperlink;
      hyperlink.Click -= ExecuteClick;
      hyperlink.Click += ExecuteClick;
    }
  }

  private static void ExecuteMouseDown(object sender, MouseEventArgs args)
  {
    DependencyObject obj = sender as DependencyObject;
    bool enabled = (bool)obj.GetValue(ClickOpensContextMenuProperty);
    if (enabled) {
      if (sender is Image) {
        var image = (Image)sender;
        if (image.ContextMenu != null)
          image.ContextMenu.IsOpen = true;
      }
    }
  } 

  private static void ExecuteClick(object sender, RoutedEventArgs args)
  {
    DependencyObject obj = sender as DependencyObject;
    bool enabled = (bool)obj.GetValue(ClickOpensContextMenuProperty);
    if (enabled) {
      if (sender is Hyperlink) {
        var hyperlink = (Hyperlink)sender;
        if(hyperlink.ContextMenu != null)
          hyperlink.ContextMenu.IsOpen = true;
      }
    }
  } 
}

【讨论】:

  • 这很有帮助,但我需要将ContextMenu 的PlacementTarget 分配回DependencyObject(在我的情况下为Button),以便填充菜单正确。这是为 ListView 中的每个项目填充的动态上下文菜单。
  • 我在我的 MenuItem 中使用了它(如果单击,将显示子菜单)但不起作用。我仍然需要右键单击才能显示菜单
  • myFrameworkElement.ContextMenu.PlacementTarget = myFrameworkElement 为我做了。谢谢@EricP。
【解决方案4】:

如果您只想在 Xaml 中执行此操作而不使用代码隐藏,您可以使用 Expression Blend 的触发器支持:

...
xmlns:i="schemas.microsoft.com/expression/2010/interactivity"
...

<Button x:Name="addButton">
    <Button.ContextMenu>
        <ContextMenu ItemsSource="{Binding Items}" />
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Click">
                <ei:ChangePropertyAction TargetObject="{Binding ContextMenu, ElementName=addButton}" PropertyName="PlacementTarget" Value="{Binding ElementName=addButton, Mode=OneWay}"/>
                <ei:ChangePropertyAction TargetObject="{Binding ContextMenu, ElementName=addButton}" PropertyName="IsOpen" Value="True"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </Button.ContextMenu>
</Button>

【讨论】:

  • 能否在示例中指定 xml 命名空间?
  • 这与 epalm 在其他答案中提到的问题相同
  • 我对这个答案的问题是没有完整的代码清单,所以“”没有意义。此外,如果我将其插入我拥有的沙盒应用程序中,无论如何我都会遇到语法错误。我用的是.Net4.5,VS2013,所以很确定不是这样的!
  • 适用于我在 WindowsFormsHost 中右键单击 - TargetObject 更改为 WindowsFormsHost (DockPanel) 之外的元素
【解决方案5】:

您只需将代码添加到函数 Image_MouseDown

e.Handled = true;

那么它就不会消失了。

【讨论】:

    【解决方案6】:

    嘿,我在寻找我在这里没有找到的解决方案时遇到了同样的问题。

    我对 MVVM 一无所知,所以它可能不符合 MVVM,但它对我有用。

    第 1 步:为上下文菜单命名。

    <Button.ContextMenu>
        <ContextMenu Name="cmTabs"/>
    </Button.ContextMenu>
    

    第 2 步:双击控件对象并插入此代码。订单很重要!

    Private Sub Button_Click_1(sender As Object, e As Windows.RoutedEventArgs)
            cmTabs.StaysOpen = True
            cmTabs.IsOpen = True
        End Sub
    

    第 3 步:享受

    这将对左键和右键单击做出反应。它是一个带有 ImageBrush 和 ControlTemplate 的按钮。

    【讨论】:

    • 不必为上下文菜单命名。您可以从按钮获取它:``` private void cmdExportButton_Click(object sender, RoutedEventArgs e) { Button exportButton = (Button)sender; exportButton.ContextMenu.PlacementTarget = 出口按钮; exportButton.ContextMenu.Placement = PlacementMode.Bottom; exportButton.ContextMenu.StaysOpen = true; exportButton.ContextMenu.IsOpen = true; } ```
    【解决方案7】:

    您可以将 contextMenu 的 Isopen 属性绑定到 viewModel 中的某个属性,例如“IsContextMenuOpen”。 但问题是您不能直接将 contextmenu 绑定到您的 viewModel,因为它不是您的 userControl hiarchy 的一部分。因此,要解决此问题,您应该将 tag 属性绑定到视图的 dataontext。

    <Image Tag="{Binding DataContext, ElementName=YourUserControlName}">
    <ContextMenu IsOpen="{Binding PlacementTarget.Tag.IsContextMenuOpen,Mode=OneWay}" >
    .....
    </ContextMenu>
    <Image>
    

    祝你好运。

    【讨论】:

      【解决方案8】:

      XAML

          <Button x:Name="b" Content="button"  Click="b_Click" >
              <Button.ContextMenu >
                  <ContextMenu   >
                      <MenuItem Header="Open" Command="{Binding OnOpen}" ></MenuItem>
                      <MenuItem Header="Close" Command="{Binding OnClose}"></MenuItem>                    
                  </ContextMenu>
              </Button.ContextMenu>
          </Button>
      

      C#

          private void be_Click(object sender, RoutedEventArgs e)
              {
              b.ContextMenu.DataContext = b.DataContext;
              b.ContextMenu.IsOpen = true;            
              }
      

      【讨论】:

      • OP 询问图像控件上的点击事件。您正在为按钮控件提供解决方案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多