Как Вы предотвращаете новую обработку событий для запуска, когда обработка событий уже работает?
Я нажимаю button1, и обработчик событий запускают, например, замедляют задание печати. Существует несколько средств управления в кнопках формы, редактирований, комбинаций, и я хочу это, новое событие позволило только после того, как рабочий обработчик закончен.
Я использовал fRunning переменную для блокировки обработчика в общем обработчике событий. Там более умный путь состоит в том, чтобы обработать это?
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;
Другим вариантом (не требующим поля флага) было бы временное присвоение NIL событию:
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;
Для удобства это можно обернуть в интерфейс:
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;
Ваше решение в порядке. Вы также можете связать нажатия кнопок с действиями и включать/выключать действия в обработчике события 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;
Вам вообще не нужно этого делать, поскольку все это происходит в главном (VCL) потоке: Никакое другое событие кнопки (VCL) не может быть введено, пока не вернется обработчик предыдущего (VCL) события... Одновременное выполнение обработчика другого события может произойти неожиданно только в том случае, если какой-то другой поток упреждающе вводит второе событие кнопки (до завершения первого), но этого не может произойти, поскольку существует только один поток VCL.
Теперь, если то, что вы делаете, выполняется в другом потоке, потому что вы не хотите, чтобы это блокировало GUI, то вы можете просто установить свойство Button.Enabled в false, пока ваша обработка не завершится.
А если вы решите просто оставить событие кнопки до тех пор, пока все не будет завершено, используйте application.processmessages достаточно часто в цикле обработки, чтобы предотвратить зависание графического интерфейса. В этом случае, да, вы должны отключить исходную кнопку, чтобы предотвратить повторный вход.
Как уже упоминал Gerry в одном из комментариев, вы можете отключить всю форму:
procedure TFormFoo.Button_Click(Sender: TObject);
begin
try
Enabled := False;
//...
finally
Enabled := True;
end;
end;
Если ваше приложение однопоточное, то пока работает код обработчика событий, ваше приложение не может выполнять другие коды, поэтому все вызовы этого обработчика событий будут сериализованы, и вам не нужно беспокоиться.
Если ваш event-handler выполняет какое-либо асинхронное задание, то вы можете использовать технику, представленную в вашем вопросе.