2009-02-17 9 views
6

Il comportamento predefinito di un WPF ContextMenu è di visualizzarlo quando l'utente fa clic con il pulsante destro del mouse. Voglio il ContextMenu da mostrare quando l'utente fa clic-sinistro. Sembra che questa dovrebbe essere una semplice proprietà su ContextMenu, ma non lo è.Mostra menu contestuale su clic sinistro utilizzando solo XAML

L'ho attrezzato, in modo da gestire l'evento LeftMouseButtonDown nel code-behind e quindi visualizzare il menu di scelta rapida.

Sto utilizzando MVVM nel mio progetto, il che significa che sto usando DataTemplate s per gli elementi che hanno i menu di scelta rapida. Sarebbe molto più elegante sbarazzarsi del code-behind e trovare un modo per visualizzare il menu contestuale utilizzando i trigger o le proprietà in XAML.

Qualche idea o soluzione a questo problema?

+0

È una deviazione dallo standard di Windows, hai una buona giustificazione per fare questo? –

+0

Questo è un buon punto, forse io shou Utilizzerei qualcosa di diverso dal ContextMenu per fare questo. È fondamentalmente un menu a discesa che appare quando si fa clic sull'elemento, non su un pulsante, ma su un pulsante.ContextMenu sembrava una scelta ovvia, ma forse è sbagliato. – timothymcgrath

+0

Vedere la mia risposta che utilizza Expression Blend Triggers qui: http://stackoverflow.com/a/4917707/87912 –

risposta

8

Quello che suggerirei di fare è creare una nuova classe statica con DependencyProperty. Chiama la classe LeftClickContextMenu e la proprietà Enabled (solo idee). Al momento della registrazione, DependencyProperty aggiunge una richiamata modificata. Quindi nella proprietà cambiata la funzione callback se Enabled è impostata su true, quindi aggiungi un gestore all'evento LeftMouseButtonDown e fai i tuoi contenuti lì. Se Abilitato è impostato su falso, rimuovere il gestore. Questo ti permetterà di impostarlo come una proprietà su qualsiasi cosa semplicemente usando quanto segue in xaml.

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

Questa tecnica è chiamata un comportamento allegato e si può leggere di più su di esso in questo articolo del progetto di codice: http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx

+0

Penso che questa soluzione sia più semplice: http://uxpassion.com/blog/old-blog/how-to-enable-and-show-context-menu-on-left-click-in-wpf – Sonhja

+0

È decisamente un approccio più semplice e se ti serve solo un punto, ti consiglio di inserire il codice nel codice. Tuttavia, se hai bisogno di questo comportamento in alcuni punti, il metodo di comportamento allegato che ho suggerito è IMO più bello. –

+0

Oh e se si desidera una soluzione ancora più semplice con un pulsante, è sufficiente utilizzare un ToggleButton e associare la proprietà IsChecked alla proprietà IsOpen del menu di scelta rapida. –

3

Mentre la risposta di Caleb è corretta, non include il codice di lavoro. Ho impostato un esempio utilizzando VB.NET (mi dispiace) quindi lo sto postando qui.

<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

Dimenticate la cosa "solo XAML". Questo può essere risolto bene quando lo si avvolge in un comportamento collegato.

Ecco un modo per mostrare il menu contestuale a sinistra-clic:

Creare un nuovo gestore di pulsante sinistro sull'elemento Border:

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

e quindi aggiungere questo:

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

Che cosa fa, fondamentalmente esegue il mapping del tasto sinistro del mouse in clic con il pulsante destro del mouse. Per la riusabilità, puoi avvolgere questo in un comportamento collegato.

3

che ho appena scritto e testato questo in base alla risposta del HK1 (si può anche leggere su proprietà associate a 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> 

(notare il commento all'interno del Metodo OnMouseLeftButtonUp())

+0

Soluzione eccellente, per risolvere il problema di binding a cui si fa riferimento nei commenti, è possibile impostare il target di posizionamento: 'fe.ContextMenu.PlacementTarget = fe' then' DataContext = "{Binding Path = PlacementTarget.DataContext, RelativeSource = {RelativeSource Self}} ">' Puoi quindi utilizzare le proprietà ContextMenuService come Placement e Horizontal/VerticalOffset per posizionarlo. – DoubleJ