Туннельные события и ContextMenu - PullRequest
1 голос
/ 16 декабря 2010

У меня есть следующий элемент управления 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 не работает, потому что событие DoubleClick больше не перехватывается в ScrollViewer ...

Настройка this.myCanvas.ContextMenu = null; решает проблему, но есть ли способ обойти это? ...

1 Ответ

1 голос
/ 20 декабря 2010

Обновление
Загруженный небольшой пример проекта, демонстрирующий эффект.
http://www.mediafire.com/?du2jr18khx8ooy9

Я попробовал несколько разных подходов для этого, и самым близким, что я нашел, былосмещение HorizontalOffset на 1 для ContextMenu.Это позволило дважды щелкнуть правой кнопкой мыши, когда было открыто ContextMenu.Он все еще не работал, когда ContextMenu не был открыт, так как ScrollViewer получал только первый щелчок.

Вы можете обойти это, используя свой временной код для первого щелчка правой кнопкой мыши и, если другое право-mouse-click происходит до того, как закончится поток, вы имитируете еще один щелчок правой кнопкой мыши с помощью 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()));
    }
}
...