As nobody offered any proper solution, I am posting my own. The solution is not perfect but at least it does what it needs to do - mouse wheel scrolls all controls under it, including the VirtualTreeView
controls. The code in solution is in C++ but Delphi version is very similar (it only needs to be translated).
My current solution is to grab WM_MOUSEWHEEL
events and translate them into WM_VSCROLL
or WM_HSCROLL
to which VirtualTreeView
reacts and scrolls the content. Additionally, it needs to take into account high-precision mouse wheels which can have smaller value than WHEEL_DELTA (which is set to 120). Finally, it needs to take into account user setting for number of lines to scroll (set in Control Panel in Windows). So here goes:
Put a TApplicationEvents
to a form and in the OnMessage
event do this:
void __fastcall TFormMain::ApplicationEventsMessage(tagMSG &Msg, bool &Handled)
{
// Check these 3 messages because some mouse drivers may use VSCROLL instead of MOUSESWHEEL message
if (Msg.message == WM_MOUSEWHEEL || Msg.message == WM_VSCROLL || Msg.message == WM_HSCROLL)
{
TPoint pnt;
TWinControl *ctrl;
if (!GetCursorPos(&pnt)) return;
ctrl = FindVCLWindow(pnt);
if (ctrl != NULL)
{
// ToDo: implement if user needs wheel-click - then we also need KEYSTATE but for this example it is not needed
// int fwKeys = GET_KEYSTATE_WPARAM(Msg.wParam);
int zDelta = GET_WHEEL_DELTA_WPARAM(Msg.wParam),
pvParam = 3; // Windows default value
unsigned MyMsg = WM_VSCROLL;
// ToDo: extract SystemParametersInfo somewhere else so it is not extracted for each WM_MOUSEWHEEL message which may not be needed
switch (Msg.message)
{
// This will translate WM_MOUSEWHEEL into WM_VSCROLL
case WM_MOUSEWHEEL:
case WM_VSCROLL:
// Windows setting which determines how many lines to scroll - we'll send that many WM_VSCROLL or WM_HSCROLL messages
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &pvParam, 0);
MyMsg = WM_VSCROLL;
break;
case WM_HSCROLL:
// Same as above but for WM_HSCROLL (horizontal wheel)
SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &pvParam, 0);
MyMsg = WM_HSCROLL;
break;
}
// This calculation takes into account high-precision wheels with delta smaller than 120
// Possible TODO: Round up values smaller than 1 (e.g. 0.75 * pvParam) if pvParam is 1
int ScrollBy = ((double)zDelta / (double)WHEEL_DELTA) * pvParam;
// Send multiple messages based on how much the zDelta value was
if (zDelta > 0)
{
do
{
SendMessage(ctrl->Handle, MyMsg, SB_LINEUP, 0);
}
while (--ScrollBy > 0);
}
else
{
do
{
SendMessage(ctrl->Handle, MyMsg, SB_LINEDOWN, 0);
}
while (++ScrollBy < 0);
}
Handled = true;
}
}
}