Search code examples
delphiuser-interfacedelphi-xevcl

Prevent double click on TButton


We are having an issue in an application with users double-clicking on almost everything including buttons. In some circumstances that leads to something that was not intended by the user since it will fire the OnClick event on the button twice. One idea to prevent this for the entire application was raised and I want to know if there are any pitfalls with using it.

Add a TApplicationEvents and implement OnMessage to set Handled for WM_LBUTTONDBLCLK on TButton to True.

Something like this:

function GetWindowClassName(Handle: HWND): String;
var
  Buffer: array[0..255] of Char;
begin
  if GetClassName(Handle, @Buffer, 255) = 0 then
    RaiseLastOSError;
  Result := Buffer;
end;

procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
begin
  if Msg.message = WM_LBUTTONDBLCLK then
    Handled := (GetWindowClassName(msg.hwnd) = 'TButton');
end;

One obvious drawback is if you have a situation where a user actually wants to click on the same button with some haste. It remains to be investigated if that is the case in this particular application.


Solution

  • To avoid that use a constant in your OnClickEvent:

     procedure TMyForm.Button1OnClick(sender:TObject);
     const
        {$J+}
        IsInside:Boolean=False;
        {$J-}
     begin
         if IsInSide then Exit;
         IsInside:=True;
         try
             do your code;
         finally
             IsInside:=False;
         end;
     end;
    

    If you have a lot of buttons I suggest to use a TActionList for those buttons and capture de OnActionExecute of ActionList event. Not tested, see TActionList for help. I've used it sometime ago.

    procedure TMyForm.ActionList1Execute(Action: TBasicAction; var Handled: Boolean);
    const
        {$J+}
        IsInside:Boolean=False;
        {$J-}
    begin
        if IsInSide then Exit;
        IsInside:=True;
        try
           ActionList1.ExecuteAction(Action);
        finally
           IsInside:=False;
        end;
    end;