This answer does exactly the same job as the answer from @nightcoder (thanks for the inspiration!). It uses a Blend-style behavior which is a more modern approach compared to an attached property.
using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Interactivity;
/// <summary>
/// Add this to any button menu allow a left click to open the context menu as well as the right.
/// </summary>
public class ContextMenuLeftClickBehavior : Behavior<ButtonBase>
{
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.Loaded += this.OnWindowLoaded;
this.AssociatedObject.Unloaded += this.OnWindowUnloaded;
}
private void OnWindowLoaded(object sender, RoutedEventArgs e)
{
this.AssociatedObject.Click += OnMouseLeftButtonUp;
}
private void OnWindowUnloaded(object sender, RoutedEventArgs e)
{
this.AssociatedObject.Click -= OnMouseLeftButtonUp; // Cannot override OnDetached(), as this is not called on Dispose. Known issue in WPF.
}
private static void OnMouseLeftButtonUp(object sender, RoutedEventArgs e)
{
if (sender is ButtonBase fe && fe.ContextMenu != null)
{
if (fe.ContextMenu != 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? 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;
}
}
}
}
Then add the behavior to the button:
<Button>
<i:Interaction.Behaviors>
<attachedProperties:ContextMenuLeftClickBehavior/>
</i:Interaction.Behaviors>
<Button>
Elements such as an Ellipse or a Rectangle do not have an OnClick
event, which means nothing really works very well for anything interactive. So wrap everything in a button to get that OnClick
event. Might as well hint that the area is clickable by changing the mouse cursor to a hand on mouseover.
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Ellipse Fill="{TemplateBinding Background}" Width="16" Height="16"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<!-- Bind to custom color in ViewModel -->
<Setter Property="Background" Value="{Binding CustomBrush}"/>
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>