36

I have a WPF application..In which I have an Image control in Xaml file.

On right click of this image I have a context menu.

I would like to have same to be displayed on "Left click" also.

How do I do this in MVVM way ?

2
  • 1
    Although it is possible, it would be against standard windows expectations to display a context menu on left click. Commented Nov 29, 2010 at 16:14
  • Regarding making this MVVM, I believe the XAML would be in your "view", the Image_MouseDown C# code would be in your "view model", and your "model" should not know anything about the context menu. Commented Nov 29, 2010 at 20:07

9 Answers 9

58

Here is a XAML only solution. Just add this style to your button. This will cause the context menu to open on both left and right click. Enjoy!

<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>
Sign up to request clarification or add additional context in comments.

4 Comments

The styling came correct, however, when I add Command to MenuItem, it is not triggering the event. I have verified - bindings are correct. Any idea?
I have same issue with @utkarsh, when I try to binding command to MenuItem, <MenuItem Header="Download" Command="{Binding DownloadButtonCommand}"/> it does not work, any advise?
It seems that the databinding of the contextmenu is only set on right click. After right-click-opening it first it works also on left click. That's why I ended up using this solution: stackoverflow.com/a/29123964/4550393
There is a problem, ContextMenuService.Placement does not work.
21

You can do this by using the MouseDown event of an Image like this

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

And then show the ContextMenu in the EventHandler in code behind

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.DataContext = image.DataContext;
        contextMenu.IsOpen = true;
        e.Handled = true;
    }
}

5 Comments

This implementation causes the context menu to appear and immediately disappear. I'm using the control and its context menu in a DataTemplate, not sure if that matters...
this doesn't seem to raise the ContextMenuOpening event so code depending on that will not run..
Just use MouseUp event
If you're adding this to a Button (not an Image) then just put code similar to this inside the Button_Click event itself.
I love you!!!!!
13

You can invent your own DependencyProperty which opens a context menu when image is clicked, just like this:

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

And here is a C# code for that property:

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

3 Comments

This was very helpful, but I needed to assign the PlacementTarget of the ContextMenu back to the DependencyObject (in my case, Button), in order for the menu to populate correctly. This was for a dynamic context menu populated for each item in a ListView.
I used this in my MenuItem (if clicked, will show sub menus) but is not working. I still have to right-click to make the menus show
myFrameworkElement.ContextMenu.PlacementTarget = myFrameworkElement did for me. thanks @EricP.
3

If you want to do this just in Xaml without using code-behind you can use Expression Blend's triggers support:

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

5 Comments

Could you specify the xml namespaces in your example?
This has the same issue epalm mentions in the other answer
My issue with this answer is there isn't a full code listing so "<ContextMenu ItemsSource="{Binding Items}" />" doesn't make sense. Also, if I plug this into a sandbox app I have, I get errors with the syntax anyway. I'm using .Net4.5, VS2013, so pretty sure it isn't that!
works for me for right click in WindowsFormsHost - TargetObject changed to an element outside the WindowsFormsHost (DockPanel)
3

you only need add the code into function Image_MouseDown

e.Handled = true;

Then it will not disappear.

Comments

3

Interactivity is old and not support any more. The new approach for the implementation is:

 xmlns:b="http://schemas.microsoft.com/xaml/behaviors"

 <Button x:Name="ConvertVideoButton">
            <Button.ContextMenu>
                <ContextMenu VerticalContentAlignment="Top" >
                    <MenuItem Header="Convert  1"  Command="{Binding ConvertMkvCommand}" />
                    <MenuItem Header="Convert 2"  Command="{Binding ConvertMkvCommand}" />
                </ContextMenu>
            </Button.ContextMenu>

            <b:Interaction.Triggers>
                <b:EventTrigger  EventName="Click">
                    <b:ChangePropertyAction TargetObject="{Binding ContextMenu, ElementName=ConvertVideoButton}" PropertyName="PlacementTarget" Value="{Binding ElementName=ConvertVideoButton, Mode=OneWay}"/>
                    <b:ChangePropertyAction TargetObject="{Binding ContextMenu, ElementName=ConvertVideoButton}" PropertyName="IsOpen" Value="True"/>
                </b:EventTrigger>
            </b:Interaction.Triggers>
        </Button>

Comments

1

Hey I came across the same problem looking for a solution which I didn't find here.

I don't know anything about MVVM so it's probably not MVVM conform but it worked for me.

Step 1: Give your context menu a name.

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

Step 2: Double click the control object and insert this code. Order matters!

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

Step 3: Enjoy

This will react for left & right click. It's a button with a ImageBrush with a ControlTemplate.

1 Comment

It's not necessary to give a name for your context menu. You could get it from button as: ``` private void cmdExportButton_Click(object sender, RoutedEventArgs e) { Button exportButton = (Button)sender; exportButton.ContextMenu.PlacementTarget = exportButton; exportButton.ContextMenu.Placement = PlacementMode.Bottom; exportButton.ContextMenu.StaysOpen = true; exportButton.ContextMenu.IsOpen = true; } ```
0

you can bind the Isopen Property of the contextMenu to a property in your viewModel like "IsContextMenuOpen". but the problem is your can't bind directly the contextmenu to your viewModel because it's not a part of your userControl hiarchy.So to resolve this you should bing the tag property to the dataontext of your view.

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

Good luck.

Comments

-1

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

1 Comment

OP is asking about a clickevent on an image control. You are serving a solution for a button control.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.