隧道事件和ContextMenu

时间:2010-12-16 20:13:39

标签: .net wpf events canvas

我有以下WPF控件,它应该表现得像,例如GoogleMaps会这样做(放大左键双击,右键双击取消缩放):

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

还有一些代码:

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

问题是当myCanvas有ContextMenu时,UnZoom方法不起作用,因为在ScrollViewer上不再捕获DoubleClick事件......

设置this.myCanvas.ContextMenu = null;可以解决问题,但有没有办法绕过这个?...

1 个答案:

答案 0 :(得分:1)

<强>更新
上传的小样本项目展示了这种效果。
http://www.mediafire.com/?du2jr18khx8ooy9

我为此尝试了一些不同的方法,我发现最接近的事情是将ContextMenu的Horizo​​ntalOffset偏移1。这允许在ContextMenu打开时右键双击。由于ScrollViewer仅收到第一次点击,因此在ContextMenu未打开时仍然无效。

您可以通过使用第一次右键单击的计时代码解决此问题,如果在线程用完之前发生另一次右键单击,则使用SendInput模拟另一次右键单击。虽然它可能不那么漂亮,但我完成了工作。

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

代码

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