2009-02-17 14 views
6

Das Standardverhalten eines WPF ContextMenu ist es anzuzeigen, wenn der Benutzer mit der rechten Maustaste klickt. Ich möchte die ContextMenu zeigen, wenn der Benutzer Linksklick. Es scheint, dass dies eine einfache Eigenschaft auf ContextMenu sein sollte, aber es ist nicht.ContextMenu bei Linksklick nur mit XAML anzeigen

Ich habe es manipuliert, so dass ich das LeftMouseButtonDown Ereignis im Code-Behind behandeln und dann das Kontextmenü anzeigen.

Ich verwende MVVM in meinem Projekt, was bedeutet, ich verwende DataTemplate s für die Elemente, die die Kontextmenüs haben. Es wäre viel eleganter, den Code-Behind loszuwerden und eine Möglichkeit zu finden, das Kontextmenü mit Triggern oder Eigenschaften im XAML anzuzeigen.

Irgendwelche Ideen oder Lösungen zu diesem Problem?

+0

Es ist eine Abweichung von der Norm in Windows, haben Sie gute Gründe dafür zu tun? –

+0

Das ist ein guter Punkt, vielleicht ich Shou Ich würde etwas anderes als das ContextMenu verwenden, um dies zu tun. Es ist im Grunde ein Dropdown-Menü, das angezeigt wird, wenn Sie auf das Element klicken, nicht auf eine Schaltfläche, sondern auf eine Schaltfläche.ContextMenu schien eine offensichtliche Wahl zu sein, aber vielleicht ist das falsch. – timothymcgrath

+0

Siehe meine Antwort, die Expression Blend Trigger hier verwendet: http://stackoverflow.com/a/4917707/87912 –

Antwort

8

Was ich vorschlagen würde, ist eine neue statische Klasse mit angehängter DependencyProperty. Rufen Sie die Klasse LeftClickContextMenu und die Eigenschaft Enabled (nur Ideen) auf. Wenn Sie DependencyProperty registrieren, fügen Sie einen geänderten Callback hinzu. Dann in der Eigenschaft Callback geändert, wenn Enabled auf True gesetzt ist, dann fügen Sie einen Handler zum LeftMouseButtonDown-Event hinzu und machen Sie Ihre Sachen dort. Wenn Enabled auf false gesetzt ist, entfernen Sie den Handler. Dies ermöglicht es Ihnen, es als eine Eigenschaft für alles zu setzen, indem Sie einfach Folgendes in Ihrem XAML verwenden.

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

Diese Technik wird ein angeschlossenes Verhalten und Sie können mehr darüber in diesem Code-Projekt Artikel lesen genannt: Antwort des http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx

+0

Ich denke, diese Lösung ist einfacher: http://uxpassion.com/blog/old-blog/how-to-enable-and-show-context-menu-on-left-click-in-wpf – Sonhja

+0

Es ist definitiv ein einfachere Vorgehensweise und wenn Sie nur eine Stelle benötigen, würde ich empfehlen, den Code einfach in den Code dahinter zu setzen. Wenn Sie dieses Verhalten jedoch an einigen Stellen benötigen, ist die von mir vorgeschlagene Verhaltensmethode "Schöner IMO". –

+0

Oh, und wenn Sie eine noch einfachere Lösung mit einer Schaltfläche wollten, können Sie einfach einen ToggleButton verwenden und seine IsChecked-Eigenschaft an die IsOpen-Eigenschaft des Kontextmenüs binden. –

3

Während Caleb richtig ist, es funktioniert Code nicht enthalten. Ich habe ein Beispiel mit VB.NET eingerichtet (sorry), also poste ich es hier.

<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

Vergessen Sie die "nur XAML" Sache. Dies kann gut gelöst werden, wenn Sie es in angehängtes Verhalten umbrechen.

ist hier ein Weg Kontextmenü anzuzeigen auf Linksklick:

auf dem Border Element eine neue linke Taste Handler erstellen:

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

und fügen Sie dann dieses:

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

Was es tut, es mappt im Grunde den Linksklick in Rechtsklick. Zur Wiederverwendbarkeit können Sie dies in ein angehängtes Verhalten einfügen.

3

Ich habe gerade geschrieben und testeten diese auf Basis von HK1 Antwort (Sie können auch in Attached Properties Overview) über angefügten Eigenschaften lesen:

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> 

(beachten Sie den Kommentar innerhalb der OnMouseLeftButtonUp() Methode)

+0

Ausgezeichnete Lösung, um das in Ihren Kommentaren referenzierte Bindungsproblem zu lösen, können Sie das Placement-Ziel festlegen: 'fe.ContextMenu.PlacementTarget = fe' dann' DataContext = "{Binding Path = PlacementTarget.DataContext, RelativeSource = {RelativeSource Self}} ">' Sie können dann die ContextMenuService-Eigenschaften wie Placement und Horizontal/VerticalOffset verwenden, um sie zu positionieren. – DoubleJ

Verwandte Themen