2009-02-17 17 views
6

El comportamiento predeterminado de un WPF ContextMenu es mostrarlo cuando el usuario hace clic con el botón derecho. Quiero que se muestre ContextMenu cuando el usuario hace clic con el botón izquierdo. Parece que esto debería ser una propiedad simple en ContextMenu, pero no lo es.Mostrar ContextMenu en el clic izquierdo usando solo XAML

Lo arreglé para que maneje el evento LeftMouseButtonDown en el código subyacente y luego visualice el menú contextual.

Estoy usando MVVM en mi proyecto, lo que significa que estoy usando DataTemplate s para los elementos que tienen los menús contextuales. Sería mucho más elegante deshacerse del código subyacente y encontrar una forma de mostrar el menú contextual usando activadores o propiedades en el XAML.

¿Alguna idea o solución a este problema?

+0

Es una desviación del estándar en Windows, ¿tiene buena justificación para hacer esto? –

+0

Ese es un buen punto, tal vez yo shou Debería usar algo que no sea el ContextMenu para hacer esto. Básicamente es un menú desplegable que aparece cuando haces clic en el elemento, no en un botón, sino en un botón.ContextMenu parecía una elección obvia, pero tal vez eso esté mal. – timothymcgrath

+0

Consulte mi respuesta, que utiliza los desencadenantes de Expression Blend aquí: http://stackoverflow.com/a/4917707/87912 –

Respuesta

8

Lo que sugeriría hacer es crear una nueva clase estática con DependencyProperty adjunto. Llame a la clase LeftClickContextMenu y a la propiedad Enabled (solo ideas). Cuando registre DependencyProperty, agregue una devolución de llamada modificada. Luego, en la propiedad se cambió la devolución de llamada si Enabled se establece en true, luego se agrega un controlador al evento LeftMouseButtonDown y se hacen las cosas allí. Si Enabled está configurado como falso, elimine el controlador. Esto le permite configurarlo como una propiedad en cualquier cosa simplemente usando lo siguiente en su xaml.

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

Esta técnica se llama un comportamiento adjunto y se puede leer más sobre ello en este artículo del proyecto de código: http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx

+0

Creo que esta solución es más fácil: http://uxpassion.com/blog/old-blog/how-to-enable-and-show-context-menu-on-left-click-in-wpf – Sonhja

+0

Definitivamente es una enfoque más fácil y si solo lo necesita un punto, le recomendaría poner el código en el código. Sin embargo, si necesita este comportamiento en algunos lugares, el método de comportamiento adjunto que he sugerido es una OMI más agradable. –

+0

Ah, y si quería una solución aún más fácil con un botón, puede usar un ToggleButton y enlazar su propiedad IsChecked a la propiedad IsOpen del menú contextual. –

3

Si bien la respuesta de Caleb es correcta, no incluye el código de trabajo. Configuré un ejemplo usando VB.NET (lo siento) así que lo estoy publicando aquí.

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

olvidar lo "única xaml". Esto se puede resolver muy bien cuando lo envuelves en el comportamiento adjunto.

Aquí está una manera de mostrar menú contextual de clic izquierdo:

Crear un nuevo controlador de botón izquierdo sobre el elemento Border:

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

y luego añadir lo siguiente:

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

Lo que hace, básicamente asigna el clic izquierdo al botón derecho. Para la reutilización, puede ajustar esto en un comportamiento adjunto.

3

acabo de escribir y probado esto basado en la respuesta de HK1 (también se puede leer acerca de las propiedades asociadas en 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'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; 
     } 
    } 

} 

...

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

(tenga en cuenta el comentario dentro de la Método OnMouseLeftButtonUp()

+0

Excelente solución, para resolver el problema vinculante al que se hace referencia en sus comentarios, puede establecer el destino de ubicación: 'fe.ContextMenu.PlacementTarget = fe' luego' DataContext = "{Binding Path = PlacementTarget.DataContext, RelativeSource = {RelativeSource Self}} ">' Luego puede usar las propiedades ContextMenuService como Placement y Horizontal/VerticalOffset para posicionarlo. – DoubleJ

Cuestiones relacionadas