[DUG] Component that knows when a click occurs elsewhere (secondtry)

Karl Reynolds karlreynolds at xtra.co.nz
Fri Nov 25 18:00:52 NZDT 2005


Hmmm, I think you'll have to hook mouse clicks for the application your
component is on if you want the ability to respond to a click on a control
that doesn't take the focus (like a speedbutton), and you want to
encapsulate the code for that within your component code.  I tried it out:
here's the source for what I did:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton; // Some components for testing
    CheckBox1: TCheckBox; // Some components for testing
    SpeedButton1: TSpeedButton; // Some components for testing
    procedure FormShow(Sender: TObject);
  private
    procedure WCLGotClick(Sender: TObject; APoint: TPoint);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  TWCLExternalButtonClick = procedure (Sender: TObject; APoint: TPoint) of
object;

  TWCL = class(TButton) // In my test I made TWCL a button descendant
  private
    FOnExternalButtonClick: TWCLExternalButtonClick; // An event for you to
respond to
  protected
    destructor Destroy; override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property OnExternalButtonClick: TWCLExternalButtonClick read
FOnExternalButtonClick write FOnExternalButtonClick;
  end;

var
  MHookCount: Integer = 0; // Need to keep count of the number of TWCL's you
drop around the place - this is a global count
  MMouseHook: HHOOK; // Your event hook

{
CatchMouseDown: The procedure hooked for mouse events.
In this example, I am only interested in left mouse clicks,
which I pass off to the focused TWCL component (if there is
one) as an OnExternalButtonClick event
}
function CatchMouseDown(nCode: Integer; wParam: WPARAM; lParam: LPARAM):
LRESULT; stdcall;
var i: Integer;
begin
  if nCode >= 0 then
  begin
    if wParam = WM_LBUTTONDOWN then
    begin
      with Screen do
        if Assigned(ActiveForm) then
          with ActiveForm do
            for i := 0 to ComponentCount - 1 do
              if Components[i] is TWCL then
                with TWCL(Components[i]) do
                  if Focused then // Only raise the event if a TWCL is the
focused control
                    if Assigned(FOnExternalButtonClick) then
                    begin
                      FOnExternalButtonClick(ActiveForm.Components[i],
                        PMouseHookStruct(lParam)^.pt); // nb. pt is in
screen co-ordinates
                    end;
    end;
  end;
  Result := CallNextHookEx(MMouseHook, nCode, wParam, lparam);
end;

procedure HookMouseEvents;
begin
  if MHookCount = 0 then
    MMouseHook := SetWindowsHookEx(WH_MOUSE, @CatchMouseDown, 0,
GetCurrentThreadID);
  Inc(MHookCount);
end;

procedure UnhookMouseEvents;
begin
  Dec(MHookCount);
  if MHookCount = 0 then
    UnhookWindowsHookEx(MMouseHook);
end;

{ TWCL }

constructor TWCL.Create(AOwner: TComponent);
begin
  inherited;
  HookMouseEvents;
end;

destructor TWCL.Destroy;
begin
  UnhookMouseEvents;
  inherited;
end;

// And some code to check that everything works:

{ TForm1 }

procedure TForm1.FormShow(Sender: TObject);
begin
  with TWCL.Create(Self) do
  begin
    Left := 10;
    Top := 10;
    Caption := 'WCL';
    OnExternalButtonClick := WCLGotClick;
    Parent := Self;
    SetFocus;
  end;
end;

procedure TForm1.WCLGotClick(Sender: TObject; APoint: TPoint);
var LControl: TControl;
begin
{
  I decided to add the name of the control clicked on to a memo here.
  But you could call TWCL(Sender).OnExit, or whatever
}
  LControl := ControlAtPos(ScreenToClient(APoint), True, True);
  if Assigned(LControl) then
    Memo1.Lines.Add(LControl.Name);
end;

end.

HTH.

Cheers,
Carl

><snip>
> I'm writing a subclass of tComponent,   tWCL,   that calls
> reactionMethod   when the user clicks one something else - on another
> component, but also on the form..
>
> When I originally wrote   tWCL,   I gave it an onExit
> handler. When the
> user clicked on some other component, the tWCL object's
> onExit handler
> was called automatically, and this called   reactionMethod.   I also
> added the handler    form.onClick,   which explicitly set the tWCL
> object's enabled property to FALSE when the user clicked on the form,
> thereby also invoking the object's onExit handler, which in turn
> called    reactionMethod,    as required
>
> Now, however, I want to turn    tWCL    into a component, and I don't
> want the user to have to write a    form.onClick    handler.
> How can I
> get my component to detect that the user has clicked on the form, so
> that it can call   reactionMethod?
>
> Thanks
> Paul



More information about the Delphi mailing list