Hafedh TRIMECHE 1 Posted August 17, 2021 This overridden procedure wont prevent multiple clicks procedure TUButton.Click; var CanGo : Boolean; begin FLock.Lock; CanGo := (not FBusy); if CanGo then FBusy := True; Flock.Unlock; if (not CanGo) then Exit; inherited; FLock.Lock; FBusy := False; Flock.Unlock; end; Any possible solution? Thanks. Share this post Link to post
Darian Miller 361 Posted August 17, 2021 Managing multiple clicks is a common issue that offer many solutions...one simple way around it is the following code: procedure TUButton.Click; begin TUButton.Enabled := False; try //do stuff except on E:Exception do begin //handle errors end; TUButton.Enabled := True; end; Share this post Link to post
Hafedh TRIMECHE 1 Posted August 17, 2021 Thanks for your reply. Tried this solution but not solved the issue. Disabling and Enabling the button itself is only valid for OnClick handler which takes a time larger than the one elapsed between 2 clicks. I modified the code: procedure EmptyKeyQueue; var Msg : TMsg; begin while PeekMessage(Msg,0,WM_KEYFIRST,WM_KEYLAST,PM_REMOVE or PM_NOYIELD) do; end; procedure EmptyMouseQueue; var Msg : TMsg; begin while PeekMessage(Msg,0,WM_MOUSEFIRST, WM_MOUSELAST,PM_REMOVE or PM_NOYIELD) do; end; procedure FlushInput; begin EmptyKeyQueue; EmptyMouseQueue; end; procedure TUButton.Click; begin FlushInput; if MilliSecondsBetween(Now,FLastClicked)<600 then Exit; FLastClicked := Now; inherited; end; Share this post Link to post
David Schwartz 426 Posted August 18, 2021 Looks like you may have a bouncy mouse. I'd try another mouse and see if it works better. This shouldn't be an issue. Share this post Link to post
FPiette 383 Posted August 18, 2021 Which problem exactly are you trying to solve? Describe the scenario you are faced with. Solution given by @Darian Miller is good. One similar is: protected FClickRunning : Boolean; procedure TUButton.Click; begin if FClickRunning then Exit; try FClickRunning := TRUE; // Do the stuff, call inherited and more finally FClickRunning := FALSE; end; end; Share this post Link to post
Hafedh TRIMECHE 1 Posted August 18, 2021 I guess that more than one message events are already queued before TUbutton.Click starts executing. So, This approach is applicable only if previous messages are removed from the Queue ; otherwise, OnClick events will continue processing. Removing messages is done using FlushInput defined into my reply above. Share this post Link to post
David Heffernan 2345 Posted August 18, 2021 Set OnClick to nil in the event handler and leave it as nil until the program terminates. Then the event handler can never run again. I presume that you want the event to run no more than once during the life of the process. Or is that assumption incorrect. Share this post Link to post
Hafedh TRIMECHE 1 Posted August 18, 2021 The Event handler must resume execution. Share this post Link to post
mvanrijnen 123 Posted August 18, 2021 (edited) If you want to prevent doubleclicking by users who still don't know the difference between single and double click (yes, they are still around, we have some overhere 🙂 ), we use a simple trick. (Yelling to the users don't work 🙂 ) You set a timer on enabled in the onclick, something like (at start of program you set the interval to like 250 orso,). not a beauty, but it works here. (not exact code we use, but similar just out of the head): const CNST_PREVEN_DOUBLECLICK_INTERVAL_MSECS = 250; procedure TForm1.Button1Click(Sender : TObject); begin if not Button1Timer.Enabled then begin Button1Timer.Interval := CNST_PREVEN_DOUBLECLICK_INTERVAL_MSECS; Button1Timer.Enabled := True; DoButton1Code(); end; end; procedure TForm1.Button1TimerTimer(Sender : TObject); begin Botton1Timer.Enabled := False; end; [/code] maybe just use a "global" var, to remember lasttime clicked, and compare msecs to that could also be possible. Edited August 18, 2021 by mvanrijnen Share this post Link to post
Hafedh TRIMECHE 1 Posted August 18, 2021 It's already done: procedure TUButton.Click; begin FlushInput; if MilliSecondsBetween(Now,FLastClicked)<600 then Exit; FLastClicked := Now; inherited; end; No need to set a timer. Any click invoked within 600 milliseconds interval will be discarded. Thanks. 1 Share this post Link to post
David Heffernan 2345 Posted August 18, 2021 1 hour ago, Hafedh TRIMECHE said: The Event handler must resume execution. Oh, I am confused. You said you don't want it to run twice. Share this post Link to post
David Schwartz 426 Posted August 18, 2021 I think you are solving the wrong problem. Why are you getting multiple clicks enqueued that are so close together? They're supposed to be translated into double-clicks. Clicks at the short intervals you're showing are NOT from a single user! Find out what's spewing out all of those clicks and fix it. Share this post Link to post
Lars Fosdal 1792 Posted August 18, 2021 Can you verify that you are not calling Click from other methods / event handlers? Is it triggered from OnClick, or from OnMouseUp/OnMouseDown? Share this post Link to post
Guest Posted August 18, 2021 I had once implemented almost exactly the same solution OP pasted above with checking against an interval, my client asked my to solve this problem because there was few operations that took long time on a server, few of his clients complained about the delay in that process, which was including over TCP data transaction and update locally and on server it was took in some cases long time ( more than few seconds !), so they were clicking buttons (eg. refresh ) many times causing DoS and stressing the server and the connection even more, that wasn't the problem per se, the problem started when he explained this and asked them to please don't over click these buttons, then and there started the problem, when few of them started to use AutoIt for fun ! Anyway, the difference from the above was that i made these intervals and behavior configurable form the server side per group of buttons, and everyone lived happily after. ps: enabling and disabling the buttons didn't help as they were enabling them using tools and stuff, enabling and removing the event will do but will require timer to do it, in my opinion for this case the best solution was the interval as i didn't exit silently but a nag message made an educative lesson for the AutoIt users who hit it a button more than twice per second. Share this post Link to post
Darian Miller 361 Posted August 19, 2021 17 hours ago, Hafedh TRIMECHE said: It's already done: procedure TUButton.Click; begin FlushInput; if MilliSecondsBetween(Now,FLastClicked)<600 then Exit; FLastClicked := Now; inherited; end; No need to set a timer. Any click invoked within 600 milliseconds interval will be discarded. Thanks. Note: once you start down this path of customizing basic component behavior, it's best to derive a custom set of components. Derive your own component from TButton and reuse whatever custom code that ends up working out for you. You may find yourself with a custom version of all the basic components and you'll end up with customized behavior controlled in a central location. Whatever happens, please don't copy-n-paste this code into 27 different Click events throughout your app... Share this post Link to post
Hafedh TRIMECHE 1 Posted August 19, 2021 Please note that TUButton is a customized component derived from TButton. Share this post Link to post
Pat Foley 51 Posted August 20, 2021 Quote On 8/18/2021 at 9:00 PM, Darian Miller said: Note: once you start down this path of customizing basic component behavior, it's best to derive a custom set of components. Derive your own component from TButton and reuse whatever custom code that ends up working out for you. You may find yourself with a custom version of all the basic components and you'll end up with customized behavior controlled in a central location. Whatever happens, please don't copy-n-paste this code into 27 different Click events throughout your app... You could have single click event and assign all custom TUButtons onClick to it. This could stored in eventForm code. Somewhat like a DataModule Only we keep message handlers in there. Also panels to use as needed by setting panel parent property to different form. The DataModule would use the eventForm code for future events Say enable submit button when channel is on line and data fields all entered. Share this post Link to post
Guest Posted August 20, 2021 1 hour ago, Pat Foley said: You could have single click event and assign all custom TUButtons onClick to it. This could stored in eventForm code. Somewhat like a DataModule Only we keep message handlers in there. Also panels to use as needed by setting panel parent property to different form. The DataModule would use the eventForm code for future events Say enable submit button when channel is on line and data fields all entered. Share this post Link to post