2

I have these links with code:

WMMouseWheel not working in Delphi

How to disable MouseWheel if mouse is not over VirtualTreeView (TVirtualStringTree)

Translated it to C++ Builder but it doesn't work:

UPDATE: After narrowing the problem down it appears that WM_MOUSEWHEEL messages don't work over unfocused TVirtualStringTree control only, they work over other controls. When focus is on e.g. TMemo control, other TMemo control scrolls on wheel but not TVirtualStringTree control. When focus is on TVirtualStringTree it scrolls TVirtualStringTree but not other controls. So the problem is now specific to TVirtualStringTree only.

void __fastcall TForm1::ApplicationEventsMessage(tagMSG &Msg, bool &Handled)
{
TPoint Pt;
HWND   Wnd;

if (Msg.message == WM_MOUSEWHEEL ||
    Msg.message == WM_VSCROLL    ||
    Msg.message == WM_HSCROLL)
    {
    if (GetCursorPos(&Pt))
        {
        Wnd = WindowFromPoint(Pt);
        // It must be a VCL control otherwise we could get access violations
        if (IsWindowEnabled(Wnd) && FindControl(Wnd) != NULL)
            {
            Msg.hwnd = Wnd; // change the message receiver to the control under the cursor
            }
        }
    }
}

Different version of the similar code, also doesn't work:

TPoint       pnt;
TWinControl *ctrl;

if ((Msg.message == WM_MOUSEWHEEL ||
     Msg.message == WM_VSCROLL    ||
     Msg.message == WM_HSCROLL) &&
    GetCursorPos(&pnt))
    {
    ctrl = FindVCLWindow(pnt);
    if (ctrl != NULL)
        {
        SendMessage(ctrl->Handle, Msg.message, Msg.wParam, Msg.lParam); // No effect
//      SendMessage(ctrl->Handle, WM_VSCROLL, 1, 0); // This is the only thing that actually moves scrollbars but this is not exactly the same message like above

//      Msg.hwnd = ctrl->Handle; // No effect
        this->Caption=ctrl->Name; // This shows correct control name so the message IS GETTING THROUGH!
        Handled = true;
        }
    }

It should work but it doesn't. Tried other code as well. No effect - mouse wheel does not operate on unfocused control. As you can see, I checked for all 3 variants of wheel messages, it gets correct control under the mouse, it shows that control name but the control doesn't receive wheel messages.

Any ideas what piece of the puzzle am I missing to get it to work?

Community
  • 1
  • 1
Coder12345
  • 3,431
  • 3
  • 33
  • 73
  • What is the target control? What does it do when it receives the message? – David Heffernan Jul 10 '13 at 18:27
  • VirtualTreeView is target control. It doesn't do anything, scrollbar doesn't move on mousewheel. Same code works in Delphi. – Coder12345 Jul 10 '13 at 18:33
  • What is `ApplicationEventsMessage` attached to? – David Heffernan Jul 10 '13 at 18:34
  • It is dropped onto a main form (TForm1). – Coder12345 Jul 10 '13 at 18:36
  • Looks like I found another problem. Tried simpler code and the code now it works in C++ builder. something else is taking over the message. I may have to implement this on lower level. – Coder12345 Jul 10 '13 at 18:40
  • It is dropped onto a main form makes no sense. It's an event handler. It needs to be attached to an event. Which event is it attached to? Implementing on a lower level doesn't make much sense to me. – David Heffernan Jul 10 '13 at 18:43
  • Sorry, it is `TApplicationEvents` - `OnMessage` event. But as I said, just tried the same thing in much simpler application and there it works. It is possible the message is being handled somewhere before it reaches treeview control. By lower level I meant mouse hook. – Coder12345 Jul 10 '13 at 18:46
  • You won't need a mouse hook! First of all you need to work out what is happening to the message. Don't settle on solutions until you've isolated the problem. – David Heffernan Jul 10 '13 at 18:50
  • 2
    I believe I did narrow it down. Placed 2 `TMemo` and 1 `TListView` and 1 `TVirtualStringTree` controls on the form. When focus is on `TMemo`, wheel scrolls everything except `TVirtualStringTree`. When focus is on `TVirtualStringTree`, wheel scrolls only `TVirtualStringTree`, but not other unfocused controls. – Coder12345 Jul 10 '13 at 19:23
  • "WM_MOUSEWHEEL messages don't work over unfocused TVirtualStringTree control only, they work over other controls." I made some quick tests and on my system (Win7 64Bit, Delphi XE2) with TListView and TCheclListbox and both only scroll if they have the focus. The same appplies for the tree and list controls in Windows Explorer by the way. – Joachim Marder Jul 11 '13 at 14:07
  • You probably did tests without the above code which redirects WM_MOUSEWHEEL messages to a control which is under the mouse (by default Delphi works like this - take a look at CodeRage4 session - http://bit.ly/v4wPR7 This redirection works for all controls except for TVirtualStringTree. The fact that in explorer it doesn't work is just explorer thing. Try Outlook Express for example - it works there. It works, no question about it, I just need to override default behaviour of TVirtualStringTree which "eats" these messages. – Coder12345 Jul 11 '13 at 14:34
  • See also [How to add mouse wheel support to a component descended from TGraphicControl?](http://stackoverflow.com/a/34463279/757830), and [How to direct the mouse wheel input to control under cursor instead of focused?](http://stackoverflow.com/a/34386680/757830) – NGLN Dec 25 '15 at 13:43

1 Answers1

1

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;
        }
    }
}
Coder12345
  • 3,431
  • 3
  • 33
  • 73