2009-02-17 5 views
6

Hành vi mặc định của WPF ContextMenu là hiển thị nó khi người dùng nhấp chuột phải. Tôi muốn ContextMenu hiển thị khi người dùng nhấp chuột trái. Có vẻ như đây là một tài sản đơn giản trên ContextMenu, nhưng nó không phải là.Hiển thị ngữ cảnhMenu trên nhấp chuột trái chỉ sử dụng XAML

Tôi đã gian lận nó, để tôi xử lý sự kiện LeftMouseButtonDown trong mã sau và sau đó hiển thị menu ngữ cảnh.

Tôi đang sử dụng MVVM trong dự án của mình có nghĩa là tôi đang sử dụng DataTemplate giây cho các mục có menu ngữ cảnh. Nó sẽ thanh lịch hơn nhiều để loại bỏ mã phía sau và tìm cách hiển thị menu ngữ cảnh bằng cách sử dụng trình kích hoạt hoặc thuộc tính trong XAML.

Bất kỳ ý tưởng hoặc giải pháp nào cho vấn đề này?

+0

Đó là một sự khởi đầu từ tiêu chuẩn trong Windows, bạn có biện minh tốt cho việc này không? –

+0

Đó là một điểm tốt, có thể tôi là shou ld được sử dụng một cái gì đó khác hơn là ContextMenu để thực hiện điều này. Về cơ bản nó là một trình đơn thả xuống xuất hiện khi bạn nhấp vào mục đó, không phải là một nút, mà là kiểu nút.ContextMenu có vẻ như là một lựa chọn hiển nhiên, nhưng có lẽ điều đó là sai. – timothymcgrath

+0

Xem câu trả lời của tôi sử dụng Expression Blend Triggers tại đây: http://stackoverflow.com/a/4917707/87912 –

Trả lời

8

Điều tôi đề nghị làm là tạo một lớp tĩnh mới với DependencyProperty đính kèm. Gọi lớp LeftClickContextMenu và thuộc tính Enabled (chỉ là ý tưởng). Khi bạn đăng ký DependencyProperty, hãy thêm một callback đã thay đổi. Sau đó, trong tài sản đã thay đổi gọi lại nếu Enabled được đặt thành true sau đó thêm một trình xử lý vào sự kiện LeftMouseButtonDown và thực hiện công cụ của bạn ở đó. Nếu Enabled được đặt thành false, hãy xóa trình xử lý. Điều này có thể cho phép bạn thiết lập nó như một thuộc tính trên bất cứ thứ gì bằng cách sử dụng những điều sau đây trong xaml của bạn.

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

Kỹ thuật này được gọi là một hành vi đính kèm và bạn có thể đọc thêm về nó trong bài viết dự án mã này: http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx

+0

Tôi nghĩ rằng giải pháp này dễ dàng hơn: http://uxpassion.com/blog/old-blog/how-to-enable-and-show-context-menu-on-left-click-in-wpf – Sonhja

+0

Nó chắc chắn là một phương pháp tiếp cận dễ dàng hơn và nếu bạn chỉ cần một điểm, tôi khuyên bạn chỉ nên đặt mã trong mã phía sau. Tuy nhiên nếu bạn cần hành vi này ở một vài nơi, phương pháp hành vi đính kèm mà tôi đã đề xuất là IMO đẹp hơn. –

+0

Oh và nếu bạn muốn có một giải pháp dễ dàng hơn với một nút, bạn chỉ có thể sử dụng một ToggleButton và liên kết nó là tài sản IsChecked vào thuộc tính IsOpen của menu ngữ cảnh. –

3

Trong khi câu trả lời Caleb là đúng, nó không bao gồm mã làm việc. Tôi thiết lập một ví dụ bằng cách sử dụng VB.NET (xin lỗi) vì vậy tôi đăng nó ở đây.

<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

Hãy quên đi "chỉ XAML" điều. Điều này có thể được giải quyết độc đáo khi bạn quấn nó vào hành vi đính kèm.

Đây là một cách để hiển thị menu ngữ cảnh bên trái nhấp chuột:

Tạo một handler nút bên trái mới trên Border phần tử:

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

và sau đó thêm này:

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

Những gì nó làm, nó về cơ bản bản đồ nhấp chuột trái vào nhấp chuột phải. Để có thể sử dụng lại, bạn có thể quấn nó vào một hành vi đính kèm.

3

Tôi vừa mới được viết và thử nghiệm này dựa trên câu trả lời của HK1 (bạn cũng có thể đọc về tài sản gắn liền trong 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> 

(lưu ý những nhận xét bên trong Phương pháp OnMouseLeftButtonUp())

+0

Giải pháp tuyệt vời, để giải quyết vấn đề ràng buộc được tham chiếu trong các nhận xét của bạn, bạn có thể đặt mục tiêu vị trí: 'fe.ContextMenu.PlacementTarget = fe' rồi' DataContext = "{Binding Path = PlacementTarget.DataContext, RelativeSource = {RelativeSource Self}} Sau đó, bạn có thể sử dụng các thuộc tính ContextMenuService như Placement và Horizontal/VerticalOffset để định vị nó. – DoubleJ