Board index » cppbuilder » Catching mouse scroll wheel events in panel descendant

Catching mouse scroll wheel events in panel descendant


2003-11-19 09:00:49 AM
cppbuilder33
Okay, I have a component which descends from TCustomPanel, within which I
would like to handle the OnMouseWheel event. I've searched and trialled
and cursed and read, and have been unable to catch the event. I've
reduced the test code to this:
/**********MouseWheelPanel.h************/
#include <SysUtils.hpp>
#include <Controls.hpp>
#include <Classes.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
class PACKAGE TMouseWheelPanel : public TCustomPanel
{
private:
protected:
public:
__fastcall TMouseWheelPanel(TComponent* Owner);
__published:
__property OnMouseWheel;
};
/*********MouseWheelPanel.cpp***********/
#include <vcl.h>
#pragma hdrstop
#include "MouseWheelPanel.h"
#pragma package(smart_init)
static inline void ValidCtrCheck(TMouseWheelPanel *)
{
new TMouseWheelPanel(NULL);
}
__fastcall TMouseWheelPanel::TMouseWheelPanel(TComponent* Owner)
: TCustomPanel(Owner)
{
}
namespace Mousewheelpanel
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TMouseWheelPanel)};
RegisterComponents("shaftest", classes, 0);
}
}
That's it for the component. Installing that is successful. I then have an
empty application with a single form, to drop my new component onto. That
goes well, and the single event "OnMouseWheel" appears in the Object
Inspector. Double clicking this entry allows me to enter code as such:
/*********main.cpp*********/
#include <vcl.h>
#pragma hdrstop
#include "main.h"
#pragma package(smart_init)
#pragma link "MouseWheelPanel"
#pragma resource "*.dfm"
TfrmMain *frmMain;
__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TfrmMain::MouseWheelPanel1MouseWheel(TObject *Sender,
TShiftState Shift, int WheelDelta, const TPoint &MousePos,
bool &Handled)
{
ShowMessage("Seen!");
}
This all compiles and runs fine, but the event handler never executes. I
don't see the message and setting a breakpoint on it never triggers. For
comparison sake, I can do the same with OnDblClick (add __property
OnDblClick to the __published section of MouseWheelPanel.h, and then use
the object inspector to put some code behind the event) and the double
click event is caught without problem.
Do I need to do something special to catch these mouse wheel
events? OnMouseWheel is defined in TWinControl, and incidentally, works
fine if I implement the OnMouseWheel event of the form, but I'd like to
keep the behaviour to my component.
Advice would be very much appreciated.
Heath
 
 

Re:Catching mouse scroll wheel events in panel descendant

"Heath Raftery" < XXXX@XXXXX.COM >wrote in message
Quote
This all compiles and runs fine, but the event handler never executes.
I don't see the message and setting a breakpoint on it never triggers.
The only way the OnMouseWheel event would not be triggered in your code is
if the component is never receiving the WM_MOUSEWHEEL or CM_MOUSEWHEEL
messages to begin with. WM_MOUSEWHEEL is sent automatically by Windows to
whatever focused window is underneath the mouse. Does your panel ever have
the input focus at all? Only by having the focus can the component ever
receive WM_MOUSEWHEEL or CM_MOUSEWHEEL messages. If the component does not
receive the WM_MOUSEWHEEL message directly, the form will send CM_MOUSEWHEEL
to whichever component is in focus. Otherwise, the form itself will handle
the message and trigger its own OnMouseWheel event, not any component.
Gambit
 

Re:Catching mouse scroll wheel events in panel descendant

"Remy Lebeau \(TeamB\)" < XXXX@XXXXX.COM >wrote:
Quote

"Heath Raftery" < XXXX@XXXXX.COM >wrote in message
news: XXXX@XXXXX.COM ...

>This all compiles and runs fine, but the event handler never executes.
>I don't see the message and setting a breakpoint on it never triggers.

The only way the OnMouseWheel event would not be triggered in your code is
if the component is never receiving the WM_MOUSEWHEEL or CM_MOUSEWHEEL
messages to begin with. WM_MOUSEWHEEL is sent automatically by Windows to
whatever focused window is underneath the mouse. Does your panel ever have
the input focus at all? Only by having the focus can the component ever
receive WM_MOUSEWHEEL or CM_MOUSEWHEEL messages. If the component does not
receive the WM_MOUSEWHEEL message directly, the form will send CM_MOUSEWHEEL
to whichever component is in focus. Otherwise, the form itself will handle
the message and trigger its own OnMouseWheel event, not any component.
Ah, that's it! The tiny tidbit of information I was missing was the
focus part. After reading your response, I immediately dropped a TEdit
box onto my Panel component. Running the program again, and scrolling
the mouse *while the edit box has focus" runs the OnMouseWheel event
handler as you would expect.
I was thinking of behaviour like you see in multipane eMail clients
(like ThunderBird) which scroll the window the mouse is over when the
wheel is scrolled. I guess the technique here is to get the Form to catch
the mousewheel event and check the position of the mouse, forwarding the
information on to the appropriate control. Ideally, I'd like to avoid
this, since I want the Component to be self contained. I guess that is
not what MouseWheel events are designed for though.
Ah, I've just discovered another way to do this: implement an OnMouseEnter
event handler for the component, which sets the ActiveControl property
of the Form containing the component to the component. This has the
disadvantage of removing focus from whatever previous had focus, without
a click. Makes sense in Focus-Follows-Mouse environments of course,
but perhaps not in general in Windows.
Thanks very much for your help.
Heath
 

{smallsort}

Re:Catching mouse scroll wheel events in panel descendant

"Heath Raftery" < XXXX@XXXXX.COM >wrote in message
Quote
Ah, that's it! The tiny tidbit of information I was missing was the
focus part. After reading your response, I immediately dropped a TEdit
box onto my Panel component. Running the program again, and scrolling
the mouse *while the edit box has focus" runs the OnMouseWheel event
handler as you would expect.
If you read the documentation for WM_MOUSEWHEEL, it specifically states:
"The WM_MOUSEWHEEL message is sent to the **focus window** when the
mouse wheel is rotated."
If you trace through the VCL's source code, you would see all the checks
that are done to make sure the target component has focus before receiving
the CM_MOUSEWHEEL message to trigger the OnMouseWheel event.
As for as the TEdit, the reason that works is because TEdit does not handle
WM_MOUSEWHEEL, and DefWindowProc() automatically iterates through a window's
chain of parent windows until it finds a window that handles the message.
Since your component was the parent of the TEdit, and it does handle the
message, it triggered its OnMouseWheel event.
Quote
I was thinking of behaviour like you see in multipane eMail clients
(like ThunderBird) which scroll the window the mouse is over
when the wheel is scrolled.
Do do that, the parent window that actually handles the message has to
determine which component is actually underneath the mouse and manually give
it focus so that it can receive the notifications directly from that point
on. Unless the mouse driver itself is designed to handle that internally on
behalf of applications, which I suspect is probably more likely.
Quote
Ideally, I'd like to avoid this, since I want the Component to
be self contained.
It will work fine, as long as it has focus. That is the key, and that is
how the message is designed to work.
Quote
Ah, I've just discovered another way to do this: implement an
OnMouseEnter event handler for the component, which sets
the ActiveControl property of the Form containing the component
to the component.
One alternative would be to use a local WH_MOUSE message hook via
SetWindowsHookEx(). The WM_MOUSEHOOK message does contain absolute screen
coordinates, so you could see if the those coordinates fall within your
component's area, and if so then handle the message and discard it so that
it is not processed any further.
Quote
This has the disadvantage of removing focus from whatever previous
had focus, without a click. Makes sense in Focus-Follows-Mouse
environments of course, but perhaps not in general in Windows.
Probably not. Some people are going to be annoyed by that, since it is not
standard Windows behavior that everyone has grown accustomed to.
Gambit