Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to redirect mouse events to another control?

I have a situation where I have a TImage and on top of it a TPanel covering it partially and they share the same parent:

------------------
|  Image1        |
|  ------------  |
|  |  Panel1  |  |
|  ------------  |
|                |
------------------

Panel1 is receiving mouse down/move/up events and processing it (so does Image1), but in some situation I would like to "redirect" the mouse down message to Image1 as if to simulate that Image1 was clicked rather than Panel1.

Here is what I did:

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if (ssLeft in Shift) then
    Beep;
end;

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; 
  X, Y: Integer);
begin
  //...
end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  ShowMessage('boo!');
end;

procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  P: TPoint;
begin
  if FRedirectToImage then begin
    ReleaseCapture; // do I need to send a WM_LBUTTONUP as well to the panel?        
    GetCursorPos(P);
    P := ScreenToClient(P);
    Image1.Perform(WM_LBUTTONDOWN, MK_LBUTTON, Longint(PointToSmallPoint(P)));
    Exit;
  end;

  // Normal handling
  if (ssLeft in Shift) then begin
    // ...
  end;
end;

It works as expected but I'm not sure It's the right way.
My question is, am I doing it right? is there a better or recommended way of doing it?


Update (1) : Handling WM_NCHITTEST as suggested is a valid answer and I thought about it also. even setting Panel1.Enabled to False will route the mouse messages to the underlying Image1 control.

But (!) consider this situation where I click the x location on the Panel and still need to route the message to Image1:

------------------
|  Image1        |
|          --------------
|          |  Panel1  x |
|          --------------
|                |
------------------

My method works, but WM_NCHITTEST is not applicable in the described scenario. I still didn't get an answer if my method is valid or not. (or maybe I should ask another question with the above scenario?)

like image 848
Vlad Avatar asked Dec 13 '12 20:12

Vlad


1 Answers

Handle wm_NCHitTest messages sent to the panel and return htTransparent. The OS will send the mouse message to the next control down without any further processing required from your program. (From the OS perspective, the "next control down" is the parent control of both the panel and the image; the VCL takes care of routing the mouse message back to the image control, as it does with all TGraphicControl descendants, since they aren't real windowed controls.)

Something like this:

procedure TParentForm.PanelWindowProc(var Msg: TMessage);
begin
  FPrevPanelWindowProc(Msg);
  if (Msg.Message = wm_NCHitTest) and FRedirectToImage then
    Msg.Result := htTransparent;
end;

Assign that method to the panel's WindowProc method. Store the previous value of the property in a field of the form.

var
  FPrevPanelWindowProc: TWndMethod;

FPrevPanelWindowProc := Panel.WindowProc;
Panel.WindowProc := Self.PanelWindowProc;
like image 194
Rob Kennedy Avatar answered Oct 07 '22 05:10

Rob Kennedy