Delphi and event handling prevention

How to prevent new event processing from starting when event processing starts?

I press button 1 and fires an event handler, for example. Slow print job. There are several controls in the form buttons, changes, combos, and I want the new event to be resolved only after the processing of the handler.

I used the fRunning variable to lock the handler in the general event handler. Is there a smarter way to handle this?

procedure TFormFoo.Button_Click(Sender: TObject);    
begin
  if not fRunning then
  try
    fRunning := true;
    if (Sender = Button1) then // Call something slow ...
    if (Sender = Button2) then // Call something ...
    if (Sender = Button3) then // Call something ...
  finally
    fRunning := false;
  end;
end;
+5
source share
5 answers

Another option (which does not require a flag field) is to temporarily assign NIL to the event:

procedure TForm1.Button1Click(Sender: TObject);
var
  OldHandler: TNotifyEvent;
begin
  OldHandler := (Sender as TButton).OnClick;
  (Sender as TButton).OnClick := nil;
  try
    ...
  finally
    (Sender as TButton).OnClick := OldHandler;
  end;
end;

For convenience, this can be wrapped in an interface:

interface

function TempUnassignOnClick(_Btn: TButton): IInterface;

implementation

type
  TTempUnassignOnClick = class(TInterfacedObject, IInterface)
  private
    FOldEvent: TNotifyEvent;
    FBtn: TButton;
  public
    constructor Create(_Btn: TButton);
    destructor Destroy; override;
  end;

constructor TTempUnassignOnClick.Create(_Btn: TButton);
begin
  Assert(Assigned(_Btn), 'Btn must be assigned');

  inherited Create;
  FBtn := _Btn;
  FOldEvent := FBtn.OnClick;
  FBtn.OnClick := NIL;
end;

destructor TTempUnassignOnClick.Destroy;
begin
  FBtn.OnClick := FOldEvent;
  inherited;
end;

function TempUnassignOnClick(_Btn: TButton): IInterface;
begin
  Result := TTempUnassignOnClick(_Btn);
end;

:

procedure TForm1.Button1Click(Sender: TObject);
begin
   TempUnassignOnClick(Sender as TButton);
   ...
end;
+6

, (VCL) : (VCL) , (VCL) ... , - ( ), , VCL.

, , , , , GUI, Button.Enabled false , .
, , application.processmessages , gui . , , .

+2

. / TAction.OnUpdate, fRunning. "if no fRunning" , , :

// Button1.Action = acButton1, Button2.Action = acButton2, etc

procedure TForm1.acButtonExecute(Sender: TObject);
begin
  if not fRunning then

  try
    fRunning:= True;
    if (Sender = acButton1) then // Call something slow ...
    if (Sender = acButton2) then // Call something ...
    if (Sender = acButton3) then // Call something ...
  finally
    fRunning:= False;
  end;

end;

procedure TForm1.acButtonUpdate(Sender: TObject);
begin
  (Sender as TAction).Enabled:= not fRunning;
end;
+2

As Jerry mentioned in one of the comments, you can disable the whole form:

procedure TFormFoo.Button_Click(Sender: TObject);    
begin
  try
    Enabled := False;
    //...
  finally
    Enabled := True;
  end;
end;
+2
source

If your application is single-threaded, then when your event handler code is running, your application cannot run other codes, so all calls to this event handler will be serialized and you won’t need to worry.

If any asynchronous task is running in your event handler, you can use the technique presented in your question.

0
source

All Articles