steinonline
asked on
How to keep a double click on a button from firing the OnClick Event twice?
Hello experts, My question as best as I can put it, is:
I have numerous buttons in my application. Now, after limited commercial release, I have found out that certain users are using double clicks on these buttons. The OnClick event of course is happening twice. This is causing quite a memory leak in my program as there are a couple of forms created and much data loaded into memory. Is there any way I can cause double click events and single click events to be interpreted in exactly the same way? as a single click?
Users do the darndest things....
Has anyone encountered this issue before, if so what did you do to solve it?
Sounds simple but it's not.
My program has almost 200 different buttons in it, on various forms, all with the capability to spawn multiple instances of dialogs and pointers on a double click.
I have numerous buttons in my application. Now, after limited commercial release, I have found out that certain users are using double clicks on these buttons. The OnClick event of course is happening twice. This is causing quite a memory leak in my program as there are a couple of forms created and much data loaded into memory. Is there any way I can cause double click events and single click events to be interpreted in exactly the same way? as a single click?
Users do the darndest things....
Has anyone encountered this issue before, if so what did you do to solve it?
Sounds simple but it's not.
My program has almost 200 different buttons in it, on various forms, all with the capability to spawn multiple instances of dialogs and pointers on a double click.
ASKER
Yes that is a possible fix, one which I have already contemplated. Only problem is, nothing quick about it (so many procedures need retrofitting that this is sort of unweildy) I'm looking for something I can do at either form or, application level such as a WindowProc listener hook. Yours may actually be the only viable solution. This issue is of paramount importance to me, so I'm looking for the best possible technique to solve the problem. Not awarding points yet. Thanks for the advice.
If this is all you are trying to do
>> Is there any way I can cause double click events and single click events to be interpreted in exactly the same way? as a single click?
Why not just associate the single click function to the double click one as well.
Regardless of what they do it will fire off the same event.
Bear in mind that a double click should (if I remember right) fire off the single click event as well anyway.
>> Is there any way I can cause double click events and single click events to be interpreted in exactly the same way? as a single click?
Why not just associate the single click function to the double click one as well.
Regardless of what they do it will fire off the same event.
Bear in mind that a double click should (if I remember right) fire off the single click event as well anyway.
I think trying to do something at the WindowProc level would not be the way to go in my opinion.
I know the other way is a little unweildy as you will need to do this for every button (unless you made your own decendant of the button which automatically puts in the code for you), in the long run though it is probably the safest thing to do.
Remember for next time I guess :o)
I know the other way is a little unweildy as you will need to do this for every button (unless you made your own decendant of the button which automatically puts in the code for you), in the long run though it is probably the safest thing to do.
Remember for next time I guess :o)
Here is one possible solution that works pretty nicely, and doesn't force you to modify each and every button to implement. Let me know if there are questions / problems.
Regards,
Russell
----
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
// Private declarations
FLastButton: HWND;
FLastTime: LongWord;
protected
// Protected declarations
procedure AppOnMessage(var Msg: TMsg; var Handled: Boolean);
public
// Public declarations
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.AppOnMessage(var Msg: TMsg; var Handled: Boolean);
var ctlMessage: TWinControl;
begin
// Get the control from the window handle
ctlMessage:=FindControl(Ms g.hwnd);
// Check control
if Assigned(ctlMessage) then
begin
// Check class of control
if (ctlMessage is TButton) then
begin
// Check message
case Msg.Message of
// Single click
WM_LBUTTONDOWN :
begin
// Check against last state
if (FLastButton = ctlMessage.Handle) and (FLastTime + GetDoubleClickTime >= GetTickCount) then
begin
// Update last click time
FLastTime:=GetTickCount;
// Don't allow click to fire
Handled:=True;
end
else
begin
// Save last state
FLastButton:=ctlMessage.Ha ndle;
FLastTime:=GetTickCount;
// Allow click to fire
Handled:=False;
end;
end;
// Discard the double click message
WM_LBUTTONDBLCLK : Handled:=True;
end;
end
else
// Not handled
Handled:=False;
end
else
// Not handled
Handled:=False;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
// Set last state
FLastButton:=0;
FLastTime:=0;
// Bind application message handler
Application.OnMessage:=App OnMessage;
end;
procedure TForm1.Button1Click(Sender : TObject);
begin
Memo1.Lines.Add((Sender as TButton).Caption);
end;
end.
Regards,
Russell
----
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
// Private declarations
FLastButton: HWND;
FLastTime: LongWord;
protected
// Protected declarations
procedure AppOnMessage(var Msg: TMsg; var Handled: Boolean);
public
// Public declarations
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.AppOnMessage(var Msg: TMsg; var Handled: Boolean);
var ctlMessage: TWinControl;
begin
// Get the control from the window handle
ctlMessage:=FindControl(Ms
// Check control
if Assigned(ctlMessage) then
begin
// Check class of control
if (ctlMessage is TButton) then
begin
// Check message
case Msg.Message of
// Single click
WM_LBUTTONDOWN :
begin
// Check against last state
if (FLastButton = ctlMessage.Handle) and (FLastTime + GetDoubleClickTime >= GetTickCount) then
begin
// Update last click time
FLastTime:=GetTickCount;
// Don't allow click to fire
Handled:=True;
end
else
begin
// Save last state
FLastButton:=ctlMessage.Ha
FLastTime:=GetTickCount;
// Allow click to fire
Handled:=False;
end;
end;
// Discard the double click message
WM_LBUTTONDBLCLK : Handled:=True;
end;
end
else
// Not handled
Handled:=False;
end
else
// Not handled
Handled:=False;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
// Set last state
FLastButton:=0;
FLastTime:=0;
// Bind application message handler
Application.OnMessage:=App
end;
procedure TForm1.Button1Click(Sender
begin
Memo1.Lines.Add((Sender as TButton).Caption);
end;
end.
Russell, you didn't post the code for the GetDoubleClickTime function..
And what code would that be?
----
GetDoubleClickTime
The GetDoubleClickTime function retrieves the current double-click time for the mouse. A double-click is a series of two clicks of the mouse button, the second occurring within a specified time after the first. The double-click time is the maximum number of milliseconds that may occur between the first and second click of a double-click.
UINT GetDoubleClickTime(VOID);
Parameters
This function has no parameters.
Return Values
The return value specifies the current double-click time, in milliseconds.
Requirements
Windows NT/2000 or later: Requires Windows NT 3.1 or later.
Windows 95/98/Me: Requires Windows 95 or later.
Header: Declared in Winuser.h; include Windows.h.
Library: Use User32.lib.
----
GetDoubleClickTime
The GetDoubleClickTime function retrieves the current double-click time for the mouse. A double-click is a series of two clicks of the mouse button, the second occurring within a specified time after the first. The double-click time is the maximum number of milliseconds that may occur between the first and second click of a double-click.
UINT GetDoubleClickTime(VOID);
Parameters
This function has no parameters.
Return Values
The return value specifies the current double-click time, in milliseconds.
Requirements
Windows NT/2000 or later: Requires Windows NT 3.1 or later.
Windows 95/98/Me: Requires Windows 95 or later.
Header: Declared in Winuser.h; include Windows.h.
Library: Use User32.lib.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
p.s. same sollution can be applied to other buttons (not only TButton)
type
TSpeedButton = class(Buttons.TSpeedButton )
protected procedure Click; override;
end;
TForm1 = class(TForm)
SpeedButton1: TSpeedButton;
procedure SpeedButton1Click(Sender: TObject);
end;
...
implementation
...
procedure TSpeedButton.Click;
begin
if GetTickCount - Tag > GetDoubleClickTime then begin
inherited;
Tag := GetTickCount;
end;
end;
type
TSpeedButton = class(Buttons.TSpeedButton
protected procedure Click; override;
end;
TForm1 = class(TForm)
SpeedButton1: TSpeedButton;
procedure SpeedButton1Click(Sender: TObject);
end;
...
implementation
...
procedure TSpeedButton.Click;
begin
if GetTickCount - Tag > GetDoubleClickTime then begin
inherited;
Tag := GetTickCount;
end;
end;
Oh, cool, I was unaware of that WindowsAPI fnction.
ASKER
ZhaawZ, that is going to do the trick nicely, just decend a button with an overridden click method, count time and act accordingly. That is pretty slick. Works well. I also like the ability to inform users that their usage habits aren't quite correct. It is always a good thing to help the user understand the application. Thanks for your insightful solution. I appreciate it very much.
ASKER
Actually after a little tinkering, I have found that it works better for me to just simply disable the button, call inherited, re-enable the button, did it in one spot... 3 lines of code.... works for the entire application... 500 pts just for getting me out of the "box"
ASKER
My ultimate solution was this....
type TButton = Class(stdctrls.tbutton)
protected
Procedure Click; overrride;
end;
Procedure tButton.Click;
begin
enabled:=false;
inherited;
enabled:=true;
end;
This was actually all that was required to solve my problem.
type TButton = Class(stdctrls.tbutton)
protected
Procedure Click; overrride;
end;
Procedure tButton.Click;
begin
enabled:=false;
inherited;
enabled:=true;
end;
This was actually all that was required to solve my problem.
This way they can click as many times as they want and it should only fire once.
procedure TForm1.Button1Click(Sender
begin
try
Button1.Enabled := False;
// Original code here
finally
Button1.Enabled := True;
end
end
This is the sort of thing that happens on webpages when people submit data.