Mega Code Archive

 
Categories / Delphi / Graphic
 

Handling mouse messages in overlapped TGraphicControl descendants

Title: Handling mouse messages in overlapped TGraphicControl descendants. Question: This is the problem I've encountered devloping small CAD application. Draggable elements are implemented as TGraphicControl descendants. They do not occupy all the client rectangle, but if two components overlap, and a user clicks on the visible but overlapped part of the lowest control, mouse message still goes to the topmost. Answer: The reason is that when parent TWinControl handles incoming messages in its WndProc, this message is posted to the correspondiong child control by calling IsControlMouseMsg: procedure TWinControl.WndProc(var Message: TMessage); ... case Message.Msg of ... WM_MOUSEFIRST..WM_MOUSELAST: if IsControlMouseMsg(TWMMouse(Message)) then begin if Message.Result = 0 then DefWindowProc(Handle, Message.Msg, Message.wParam, Message.lParam); Exit; end; ... end; IsControlMouseMsg tries to find a corresponding child control by calling ControlAtPos. ControlAtPos browses all the child controls and checks if message belongs to control by calling GetControlAtPos function. function GetControlAtPos(AControl: TControl): Boolean; begin with AControl do begin P := Point(Pos.X - Left, Pos.Y - Top); Result := PtInRect(ClientRect, P) and ((csDesigning in ComponentState) and (Visible or not (csNoDesignVisible in ControlStyle)) or (Visible and (Enabled or AllowDisabled) and (Perform(CM_HITTEST, 0, Longint(PointToSmallPoint(P))) 0))); if Result then vControl := AControl; end; end; As you may see from the code of the GetControlAtPos, the function checks if control's clientrect contains the point and also performs a CM_HITTEST message on the control. By default TControl handles this message returning the result that indicates that hit was in control's client area regardless the message coordinates: procedure TControl.CMHitTest(var Message: TCMHitTest); begin Message.Result := HTCLIENT; end; In our case we got two overlapped controls and the problem is that message ALWAYS goes to the topmost control if its coordinates are in control's client rectangle. By overriding handling of the CM_HITTEST message we may provide additional checking. For example, if we have calculated a region that our control's graphics occupy, we may implement this check as follows: TMyControl = class(TGraphicControl) ... private procedure CMHitTest(var Message: TCMHitTest); message CM_HITTEST; ... end; ... procedure TMyControl.CMHitTest(var Message: TCMHitTest); begin with Message do if PtInRegion(Region, XPos, YPos) then Result := HTCLIENT else Result := HTNOWHERE; end; In this case a mouse message will be passed to the control only if its coordinates hits region defined by Region variable. p.s. This article demonstrates a method for TGraphicControl descendants. If you inherit from TWinControl, you should use SetWindowRGN etc.