WM_MOUSEWHEEL
messages are sent to the control with the focus. My application has a complex control hierarchy, with controls containing other controls, some of which are invisible or overlapping. I would like the mouse wheel to scroll a specific ScrollableControl
.
This question has an answer with a IMessageFilter
implementation that catches WM_MOUSEWHEEL
messages. This works well and I see the messages being caught. I tried manipulating ScrollableControl's VerticalScroll
property to scroll its contents, by changing the value of VerticalScroll.Value
. Unfortunately, there are some undesirable side effects like the mouse thumb in the scrollbar becoming unsynchronized with the ScrollableControl's contents. Perhaps this is because this work is being done inside the message pump instead of in an event handler.
This post describes a technique where WM_MOUSEWHEEL messages are reposted to another window. I would like to implement a IMessageFilter
that catches WM_MOUSEWHEEL
messages, and forwards them to a designated recipient.
I create the following IMessageFilter
that tries to do this. I can see the forwarded message being caught by my filter, and I return false
from the filter to tell the control to handle the message. The target control does not receive an OnMouseWheel
event.
Can this filter be modified to to allow my targetControl
to be scrolled using redirected messages?
public static class MouseWheelMessageRedirector
{
public static void Add(Control rootControl, ScrollableControl targetControl)
{
var filter = new MouseWheelMessageFilter(rootControl, targetControl);
Application.AddMessageFilter(filter);
rootControl.Disposed += (sender, args) => Application.RemoveMessageFilter(filter);
targetControl.MouseWheel += (sender, args) =>
{
// ... this code never executes
System.Diagnostics.Trace.WriteLine("WHEEL ON TARGET");
};
}
private class MouseWheelMessageFilter : IMessageFilter
{
const int WM_MOUSEWHEEL = 0x020A;
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
public MouseWheelMessageFilter(Control rootControl, ScrollableControl targetControl)
{
_rootControl = rootControl;
_targetControl = targetControl;
_targetWindowHandle = _targetControl.Handle;
}
public bool PreFilterMessage(ref Message m)
{
if (m.Msg != WM_MOUSEWHEEL)
return false;
if (m.HWnd == _targetWindowHandle)
return false;
// ... get the control that the mouse is over
// ... determine if this is a control that we want to handle the message for
// ... (omitted)
PostMessage(_targetWindowHandle, m.Msg, m.WParam, m.LParam);
return true;
}
private Control _rootControl;
private ScrollableControl _targetControl;
private IntPtr _targetWindowHandle;
}
}
答案 0 :(得分:0)
我刚做了同样的事情。这是我做的:
public bool PreFilterMessage(ref Message m)
{
if ((WM)m.Msg == WM.MOUSEWHEEL)
{
// if mouse is over a certain component, prevent scrolling
if (comboBoxVendors.Bounds.Contains(PointToClient(Cursor.Position)))
{
// return true which says the message is already processed
return true;
}
// which direction did they scroll?
int delta = 0;
if ((long)m.WParam >= (long)Int32.MaxValue)
{
var wParam = new IntPtr((long)m.WParam << 32 >> 32);
delta = wParam.ToInt32() >> 16;
}
else
{
delta = m.WParam.ToInt32() >> 16;
}
delta = delta*-1;
var direction = delta > 0 ? 1 : 0;
// post message to the control I want scrolled (I am converting the vertical scroll to a horizontal, bu you could just re-send the same message to the control you wanted
PostMessage(splitContainerWorkArea.Panel2.Handle, Convert.ToInt32(WM.HSCROLL), (IntPtr) direction, IntPtr.Zero);
// return true to say that I handled the message
return true;
}
// message was something other than scroll, so ignore it
return false;
}
另外,我使用了来自PInvoke.net的windows消息枚举: http://www.pinvoke.net/default.aspx/Enums.WindowsMessages
最后,请确保在关闭表单时删除邮件过滤器,或者当您不再需要处理邮件时删除邮件过滤器:
Application.RemoveMessageFilter(thisForm)