2010-12-16 4 views
1

J'ai le contrôle WPF suivant, qui devrait se comporter comme, par ex. GoogleMaps ne (zoom sur double clic gauche, dézoomer sur double clic droit):Tunneling events et ContextMenu

<Grid> 
    <ScrollViewer x:Name="scrollViewer"> 
     <Canvas x:Name="myCanvas"/> 
    </ScrollViewer> 
</Grid> 

Et un code:

void OnScrollViewerPreviewMouseDoubleClick(object sender, MouseButtonEventArgs e) 
{ 
    //this.myCanvas.ContextMenu = null; 
    if (e.OriginalSource is Canvas) 
    { 
     // zoom on left doubleClick 
     if (e.ChangedButton == MouseButton.Left) 
     { 
      ZoomOnMouseOnce(); 
     } // UNzoom on right doubleClick 
     else if (e.ChangedButton == MouseButton.Right) 
     { 
      UnzoomOnMouseOnce(); 
     } 
     e.Handled = true; 
    } 
} 

Le problème est que lorsque myCanvas ont un ContextMenu la méthode dézoomer ne fonctionne pas, car l'événement DoubleClick est pas attrappe plus sur ScrollViewer ...

Réglage this.myCanvas.ContextMenu = null; résout le problème, mais est-il un moyen de contourner cela? ...

+0

Pourquoi ne pas ignorer le menu contextuel et utiliser une fenêtre contextuelle pour construire la fonctionnalité exacte que vous désirez ? –

+0

@Aaron: cela ne change rien. Le problème reste le même, menu contextuel ou popup ... – serhio

Répondre

1

Mise à jour
Projet de petit échantillon téléchargé démontrant l'effet.
http://www.mediafire.com/?du2jr18khx8ooy9

J'ai essayé un tas de différentes approches pour cela, et la chose la plus proche que j'ai trouvé pour compenser la HorizontalOffset par 1 pour le ContextMenu. Cela permettait un double-clic droit lorsque le ContextMenu était ouvert. Cela ne fonctionnait toujours pas lorsque le ContextMenu n'était pas ouvert car le ScrollViewer ne recevait que le premier clic. Vous pouvez contourner ce problème en utilisant votre code temporel pour le premier clic droit de la souris et si un autre clic droit de la souris se produit avant l'expiration du thread, vous simulez un autre clic droit en utilisant SendInput. Bien que ce ne soit pas si joli, je fais le travail.

<Canvas ...> 
    <Canvas.ContextMenu> 
     <ContextMenu Placement="RelativePoint" HorizontalOffset="1"> 
      <MenuItem Header="MenuItem 1"/> 
     </ContextMenu> 
    </Canvas.ContextMenu> 

code derrière

private bool m_waitingForRightMouseDoubleClick = false; 
private void scrollViewer_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) 
{ 
    if (e.ChangedButton == MouseButton.Right) 
    { 
     if (m_waitingForRightMouseDoubleClick == false) 
     { 
      m_waitingForRightMouseDoubleClick = true; 
      Thread thread = new Thread(new System.Threading.ThreadStart(delegate() 
       { 
        Thread.Sleep(System.Windows.Forms.SystemInformation.DoubleClickTime); 
        this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, 
         new Action(delegate() 
          { 
           m_waitingForRightMouseDoubleClick = false; 
          } 
          )); 
       })); 
      thread.Start(); 
     } 
     else 
     { 
      MouseSimulator.ClickRightMouseButton(); 
     } 
    } 
} 

MouseSimulator.cs

public class MouseSimulator 
{ 
    [DllImport("user32.dll", SetLastError = true)] 
    static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize); 

    [StructLayout(LayoutKind.Sequential)] 
    struct INPUT 
    { 
     public SendInputEventType type; 
     public MouseKeybdhardwareInputUnion mkhi; 
    } 
    [StructLayout(LayoutKind.Explicit)] 
    struct MouseKeybdhardwareInputUnion 
    { 
     [FieldOffset(0)] 
     public MouseInputData mi; 

     [FieldOffset(0)] 
     public KEYBDINPUT ki; 

     [FieldOffset(0)] 
     public HARDWAREINPUT hi; 
    } 
    [StructLayout(LayoutKind.Sequential)] 
    struct KEYBDINPUT 
    { 
     public ushort wVk; 
     public ushort wScan; 
     public uint dwFlags; 
     public uint time; 
     public IntPtr dwExtraInfo; 
    } 
    [StructLayout(LayoutKind.Sequential)] 
    struct HARDWAREINPUT 
    { 
     public int uMsg; 
     public short wParamL; 
     public short wParamH; 
    } 
    struct MouseInputData 
    { 
     public int dx; 
     public int dy; 
     public uint mouseData; 
     public MouseEventFlags dwFlags; 
     public uint time; 
     public IntPtr dwExtraInfo; 
    } 
    [Flags] 
    enum MouseEventFlags : uint 
    { 
     MOUSEEVENTF_MOVE = 0x0001, 
     MOUSEEVENTF_LEFTDOWN = 0x0002, 
     MOUSEEVENTF_LEFTUP = 0x0004, 
     MOUSEEVENTF_RIGHTDOWN = 0x0008, 
     MOUSEEVENTF_RIGHTUP = 0x0010, 
     MOUSEEVENTF_MIDDLEDOWN = 0x0020, 
     MOUSEEVENTF_MIDDLEUP = 0x0040, 
     MOUSEEVENTF_XDOWN = 0x0080, 
     MOUSEEVENTF_XUP = 0x0100, 
     MOUSEEVENTF_WHEEL = 0x0800, 
     MOUSEEVENTF_VIRTUALDESK = 0x4000, 
     MOUSEEVENTF_ABSOLUTE = 0x8000 
    } 
    enum SendInputEventType : int 
    { 
     InputMouse, 
     InputKeyboard, 
     InputHardware 
    } 

    public static void ClickRightMouseButton() 
    { 
     INPUT mouseDownInput = new INPUT(); 
     mouseDownInput.type = SendInputEventType.InputMouse; 
     mouseDownInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_RIGHTDOWN; 
     SendInput(1, ref mouseDownInput, Marshal.SizeOf(new INPUT())); 

     INPUT mouseUpInput = new INPUT(); 
     mouseUpInput.type = SendInputEventType.InputMouse; 
     mouseUpInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_RIGHTUP; 
     SendInput(1, ref mouseUpInput, Marshal.SizeOf(new INPUT())); 
    } 
} 
+0

intéressant. Jusqu'à présent, enfin j'ai utilisé ce qui suit: http://stackoverflow.com/questions/4492996/invoke-a-contextmenu-to-open – serhio

+0

ah, je vois, vous "attendez" aussi ... le "inconvénient" de la méthode nous utilisons tous les deux le délai perceptible entre le clic et l'ouverture du menu. – serhio

+0

@serhio: Avec cette méthode, le ContextMenu s'ouvre instantanément. Et à partir de là, il attendra un autre clic, et si cela se produit, il envoie deux clics. Par conséquent, nous obtenons le droit-double-cliquez sur –