Delphi и предотвращает обработку событий

Как Вы предотвращаете новую обработку событий для запуска, когда обработка событий уже работает?

Я нажимаю 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;
5
задан pKarelian 7 March 2010 в 09:57
поделиться

5 ответов

Другим вариантом (не требующим поля флага) было бы временное присвоение 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;
7
ответ дан 18 December 2019 в 14:44
поделиться

Ваше решение в порядке. Вы также можете связать нажатия кнопок с действиями и включать/выключать действия в обработчике события 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
ответ дан 18 December 2019 в 14:44
поделиться

Вам вообще не нужно этого делать, поскольку все это происходит в главном (VCL) потоке: Никакое другое событие кнопки (VCL) не может быть введено, пока не вернется обработчик предыдущего (VCL) события... Одновременное выполнение обработчика другого события может произойти неожиданно только в том случае, если какой-то другой поток упреждающе вводит второе событие кнопки (до завершения первого), но этого не может произойти, поскольку существует только один поток VCL.

Теперь, если то, что вы делаете, выполняется в другом потоке, потому что вы не хотите, чтобы это блокировало GUI, то вы можете просто установить свойство Button.Enabled в false, пока ваша обработка не завершится.
А если вы решите просто оставить событие кнопки до тех пор, пока все не будет завершено, используйте application.processmessages достаточно часто в цикле обработки, чтобы предотвратить зависание графического интерфейса. В этом случае, да, вы должны отключить исходную кнопку, чтобы предотвратить повторный вход.

2
ответ дан 18 December 2019 в 14:44
поделиться

Как уже упоминал Gerry в одном из комментариев, вы можете отключить всю форму:

procedure TFormFoo.Button_Click(Sender: TObject);    
begin
  try
    Enabled := False;
    //...
  finally
    Enabled := True;
  end;
end;
2
ответ дан 18 December 2019 в 14:44
поделиться

Если ваше приложение однопоточное, то пока работает код обработчика событий, ваше приложение не может выполнять другие коды, поэтому все вызовы этого обработчика событий будут сериализованы, и вам не нужно беспокоиться.

Если ваш event-handler выполняет какое-либо асинхронное задание, то вы можете использовать технику, представленную в вашем вопросе.

0
ответ дан 18 December 2019 в 14:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: