Tunneling events and ContextMenu
I have the following WPF Control, that should behave like, by e.g. GoogleMaps does(zoom on left double click, unzoom on right double click):
<Grid>
<ScrollViewer x:Name="scrollViewer">
<Canvas x:Name="myCanvas"/>
</ScrollViewer>
</Grid>
And some code:
void OnScroll开发者_开发技巧ViewerPreviewMouseDoubleClick(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;
}
}
The problem is that When myCanvas have a ContextMenu the UnZoom method does not work, because DoubleClick event is not catched anymore on ScrollViewer...
Setting this.myCanvas.ContextMenu = null;
solves the problem, but Is there a way to bypass this?...
Update
Uploaded small sample project demonstrating the effect.
http://www.mediafire.com/?du2jr18khx8ooy9
I tried a bunch of different approaches for this and the closest thing I found was to offset the HorizontalOffset by 1 for the ContextMenu. This allowed for right-double-click when the ContextMenu was open. It still didn't work when the ContextMenu wasn't open since the ScrollViewer only recieved the first click.
You could work around this by using your timing code for the first right-mouse-click and if another right-mouse-click occurs before the Thread runs out, you simulate another right-mouse-click using SendInput. Although it may not be so pretty, I gets the job done.
<Canvas ...>
<Canvas.ContextMenu>
<ContextMenu Placement="RelativePoint" HorizontalOffset="1">
<MenuItem Header="MenuItem 1"/>
</ContextMenu>
</Canvas.ContextMenu>
Code behind
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()));
}
}
精彩评论