2009-02-17 22 views
6

Domyślnym zachowaniem pliku WPF ContextMenu jest wyświetlenie go, gdy użytkownik kliknie prawym przyciskiem myszy. Chcę, aby ContextMenu wyświetlał się, gdy użytkownik kliknie lewym przyciskiem myszy. Wygląda na to, że powinna to być prosta właściwość pod numerem ContextMenu, ale tak nie jest.Pokaż menu kontekstowe lewym przyciskiem myszy, używając tylko XAML

Podsunąłem to, aby obsłużyć zdarzenie LeftMouseButtonDown w kodzie z tyłu, a następnie wyświetlić menu kontekstowe.

Używam MVVM w moim projekcie, co oznacza, że ​​używam DataTemplate s dla elementów, które mają menu kontekstowe. Bardziej elegancko byłoby pozbyć się kodu z tyłu i znaleźć sposób na wyświetlenie menu kontekstowego za pomocą wyzwalaczy lub właściwości w XAML.

Wszelkie pomysły lub rozwiązania tego problemu?

+0

To odejście od standardu w Windows, czy masz dobre uzasadnienie dla tego? –

+0

To jest dobra uwaga, może ja Używaj czegoś innego niż ContextMenu, aby to zrobić. Zasadniczo jest to menu rozwijane, które pojawia się po kliknięciu elementu, a nie przycisku, ale rodzaju buttony.ContextMenu wydawało się oczywistym wyborem, ale może to jest złe. – timothymcgrath

+0

Zobacz moją odpowiedź, która używa Expression Blend Triggers tutaj: http://stackoverflow.com/a/4917707/87912 –

Odpowiedz

8

Zalecam wykonanie nowej klasy statycznej z dołączoną funkcją DependencyProperty. Wywołaj klasę LeftClickContextMenu i właściwość Enabled (tylko pomysły). Po zarejestrowaniu DependencyProperty dodaj zmieniony oddzwanianie. Następnie w właściwości zmieniono wywołanie zwrotne, jeśli Enabled jest ustawione na true, a następnie dodaj program obsługi do zdarzenia LeftMouseButtonDown i zrób tam swoje rzeczy. Jeśli opcja Enabled jest ustawiona na false, usuń moduł obsługi. Dzięki temu możesz ustawić go jak właściwość na cokolwiek, po prostu używając następujących w swoim Xaml.

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

Technika ta nazywana jest załączony zachowań i można przeczytać więcej na ten temat w tym artykule kod projektu: http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx

+0

Myślę, że to rozwiązanie jest łatwiejsze: http://uxpassion.com/blog/old-blog/how-to-enable-and-show-context-menu-on-left-click-in-wpf – Sonhja

+0

To zdecydowanie łatwiejsze podejście i jeśli potrzebujesz go tylko w jednym miejscu, polecam po prostu umieszczenie kodu w kodzie. Jeśli jednak potrzebujesz tego zachowania w kilku miejscach, sugerowana przeze mnie metoda zachowania jest lepsza IMO. –

+0

Aha i gdybyś chciał jeszcze łatwiejszego rozwiązania za pomocą przycisku, możesz po prostu użyć ToggleButton i powiązać właściwość IsChecked z właściwością IsOpen menu kontekstowego. –

3

Chociaż odpowiedź Kaleba jest poprawna, to nie obejmuje działający kod. Przygotowałem przykład używając VB.NET (przepraszam), więc zamieszczam go tutaj.

<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

Zapomnij o "tylko" rzecz XAML. To może być ładnie rozwiązane po zawinięciu go do przywiązanego zachowania.

Oto sposób, aby pokazać menu kontekstowego kliknąć lewym przyciskiem:

Utwórz nowy obsługi lewy przycisk na elemencie Border:

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

a następnie dodać:

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

To, co robi, w zasadzie odwzorowuje kliknięcie lewym przyciskiem myszy w prawy przycisk myszy. W celu ponownego użycia można zawinąć to w dołączone zachowanie.

3

Właśnie napisane i przetestowane w oparciu o odpowiedzi na hk1 (można również przeczytać o dołączonych właściwości w 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> 

(zwrócić uwagę na komentarz wewnątrz Metoda OnMouseLeftButtonUp()

+0

Doskonałe rozwiązanie, aby rozwiązać problem wiążący, do którego odnoszą się komentarze, możesz ustawić cel umieszczania: 'fe.ContextMenu.PlacementTarget = fe' then' DataContext = "{Binding Path = PlacementTarget.DataContext, RelativeSource = {RelativeSource Self}} ">' Następnie możesz użyć właściwości ContextMenuService, takich jak rozmieszczenie i Horizontal/VerticalOffset, aby je ustawić. – DoubleJ