Show ContextMenu on the left side of the screen using only XAML

The default behavior of WPF ContextMenu is to display it when the user right-clicks. I want ContextMenu appear when the user left-clicked. It looks like this should be a simple property on ContextMenu , but it is not.

I bound it to handle the LeftMouseButtonDown event in the code, and then display the context menu.

I use MVVM in my project, which means that I use a DataTemplate for items that have context menus. It would be much more elegant to get rid of the code and find a way to display the context menu using triggers or properties in XAML.

Any ideas or solutions to this problem?

+6
wpf mvvm xaml contextmenu
source share
4 answers

I would suggest making a new static class with a DependencyProperty attached. Call the LeftClickContextMenu class and the Enabled property (ideas only). When you register DependencyProperty, add the modified callback. Then the callback is changed in the property if Enabled is set to true, then add a handler to the LeftMouseButtonDown event and make your stuff there. If Enabled is false, remove the handler. This will allow you to set it as a property to anything just by using the following in your xaml.

 <Border namespace:LeftClickContextMenu.Enabled="True" /> 

This method is called attached behavior, and you can learn more about this in this code draft article: http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx

+8
source share

While Caleb's answer is correct, it does not include working code. I am setting up an example using VB.NET (sorry), so I post it here.

 <Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:AttachedBehaviorTest.AttachedBehaviorTest" Title="MainWindow" Height="350" Width="525"> <Grid> <StackPanel> <TextBlock local:ContextMenuLeftClickBehavior.IsLeftClickEnabled="True">Some Text Goes Here <TextBlock.ContextMenu> <ContextMenu> <MenuItem Header="Test1" /> </ContextMenu> </TextBlock.ContextMenu> </TextBlock> </StackPanel> </Grid> </Window> 
 Namespace AttachedBehaviorTest Public NotInheritable Class ContextMenuLeftClickBehavior Private Sub New() End Sub Public Shared Function GetIsLeftClickEnabled(obj As DependencyObject) As Boolean Return CBool(obj.GetValue(IsLeftClickEnabled)) End Function Public Shared Sub SetIsLeftClickEnabled(obj As DependencyObject, value As Boolean) obj.SetValue(IsLeftClickEnabled, value) End Sub Public Shared ReadOnly IsLeftClickEnabled As DependencyProperty = _ DependencyProperty.RegisterAttached("IsLeftClickEnabled", GetType(Boolean), GetType(ContextMenuLeftClickBehavior), New UIPropertyMetadata(False, AddressOf OnIsLeftClickEnabled)) Private Shared Sub OnIsLeftClickEnabled(sender As Object, e As DependencyPropertyChangedEventArgs) Dim fe As FrameworkElement = TryCast(sender, FrameworkElement) If fe IsNot Nothing Then Dim IsEnabled As Boolean = CBool(e.NewValue) If IsEnabled = True Then AddHandler fe.MouseLeftButtonUp, AddressOf OnMouseLeftButtonUp Debug.Print("Added Handlers") Else RemoveHandler fe.MouseLeftButtonUp, AddressOf OnMouseLeftButtonUp Debug.Print("RemovedHandlers") End If End If End Sub Private Shared Sub OnMouseLeftButtonUp(sender As Object, e As RoutedEventArgs) Debug.Print("OnMouseLeftButtonUp") Dim fe As FrameworkElement = TryCast(sender, FrameworkElement) If fe IsNot Nothing Then 'Next Line is Needed if Context Menu items are Data Bound 'fe.ContextMenu.DataContext = fe.DataContext fe.ContextMenu.IsOpen = True End If End Sub End Class End Namespace 
+3
source share

I just wrote and tested this based on HK1's answer (you can also read about attached properties in the Attached Properties Overview) :

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

...

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

(note the comment inside the OnMouseLeftButtonUp () method)

+3
source share

Forget about "just haml". This can be easily solved when you wrap it in the attached behavior.

The following is a way to display the context menu by left-clicking:

Create a new left button handler in the Border element:

 <Border x:Name="Win" Width="40" Height="40" Background="Purple" MouseLeftButtonUp="UIElement_OnMouseLeftButtonUp"> 

and then add the following:

 private void UIElement_OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { e.Handled = true; var mouseDownEvent = new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Right) { RoutedEvent = Mouse.MouseUpEvent, Source = Win, }; InputManager.Current.ProcessInput(mouseDownEvent); } 

What he does, he basically displays the left click with the right mouse button. For reuse, you can wrap this in the attached behavior.

-2
source share

All Articles