Delphi: Как установить текст в TEdit / TMaskEdit, не вызывая событие onchange - PullRequest
3 голосов
/ 06 января 2010

У меня довольно большая форма установки, которую я хотел бы заполнить данными из класса. так что я делаю много

Edt1.text := ASettings.FirstThing; 

Я бы хотел избежать

Edt1.onchange := nil;
Edt1.text := ASettings.FirstThing; 
Edt1.onchange := edt1Onchange;

Как изменить текст в текстовом поле и обойти событие onchange.

Ответы [ 5 ]

11 голосов
/ 06 января 2010

Я использовал что-то вроде изменения обработчика OnChange, но чаще я использую флаг.

updatingFromCode := true;
Edt1.Text := ASettings.FirstThing;
updatingFromCode := false;

тогда

procedure TForm1.OnChange(...);
begin
  if updatingFromCode then
    Exit;
  ...


Кроме того, вместо жесткого кодирования OnChange фактической процедуры OnChange, я бы сохранял текущее значение элемента управления Edit, а затем сбрасывал его (что будет работать, если оно не установлено, или если другое место изменило его и т. 1010 *

oldOnChange := Edt1.OnChange;
Edt1.OnChange := nil;
Edt1.Text := ASettings.FirstThing; 
Edt1.OnChange := oldOnChange;
7 голосов
/ 24 июня 2010

Вы можете рассмотреть возможность использования объекта для управления NIL'ом события и восстановления ранее установленного обработчика события. Немного опасно предполагать, что восстанавливаемое событие просто оказывается назначенным во время разработки / которое имеет «подходящее имя» - вы всегда должны сохранять / восстанавливать назначенный в данный момент обработчик, просто чтобы быть в безопасности .

Это обеспечит еще более многократное использование утилиты, чем SetTextWithoutOnChange ():

  TSuspendEvent = class
  private
    fObject: TObject;
    fEvent: String;
    fHandler: TMethod;
  public
    constructor Create(const aObject: TObject; aEvent: String);
    destructor Destroy; override;
  end;

  constructor TSuspendEvent.Create(const aObject: TObject; aEvent: String);
  const
    NILEvent  : TMethod = (Code: NIL; Data: NIL);
  begin
    inherited Create;

    fObject := aObject;
    fEvent  := aEvent;

    fHandler := GetMethodProp(aObject, aEvent);

    SetMethodProp(aObject, aEvent, NILEvent);
  end;


  destructor TSuspendEvent.Destroy;
  begin
    SetMethodProp(fObject, fEvent, fHandler);

    inherited;
  end;

При использовании это будет выглядеть примерно так:

  with TSuspendEvent.Create(Edit1, 'OnChange') do
  try
    Edit1.Text := 'Reset!';
  finally
    Free;
  end;

Для "Не используйте ' с " толпой "- непременно объявите себе дополнительную локальную переменную и используйте ее, если она поможет вам спать спокойно ночью. :)

Или, чтобы сделать его еще более удобным для использования и исключения " с ", я бы сделал класс TSuspendEvent интерфейсным объектом и включил бы его использование в функцию, которая давала ссылка на интерфейс, к которой можно было бы разрешить «жить в области видимости», , как показано в моей реализации AutoFree (). Фактически, вы могли бы использовать AutoFree () как есть , чтобы управлять этим уже:

  AutoFree(TSuspendEvent.Create(Edit1, 'OnChange'));
  Edit1.Text := 'Reset!';

Для включения событий на период, выходящий за рамки одной процедуры, требуется больше управления, чем, вероятно, могут оказать какие-либо вспомогательные утилиты в общем виде, по крайней мере, не имея специальных средств для явного восстановления событий. , а не автоматически.

Если вы просто обернули TSuspendEvent в его собственную функцию выдачи интерфейса, следуя тому же шаблону, что и AutoFree (), вы можете упростить это далее до:

  SuspendEvent(Edit1, 'OnChange');
  Edit1.Text := 'Reset!';

В качестве заключительного замечания, я думаю, должно быть довольно легко увидеть, как это можно довольно просто расширить для поддержки приостановки нескольких событий на объекте за один вызов, если требуется, например:

  SuspendEvent(Edit1, ['OnChange', 'OnEnter']);
6 голосов
/ 06 января 2010

Насколько я знаю, если OnChange вашего объекта предназначен для запуска при изменении свойства Text, вы должны придерживаться установки события на nil временно. Я сам делаю это так (наконец-то попробую):

Edt1.onchange := nil;
try
    Edt1.text := ASettings.FirstThing;
finally
    Edt1.onchange := edt1Onchange;
end;

Вы могли бы также сделать некоторую процедуру, чтобы справиться с этим для вас:

procedure SetTextWithoutOnChange(anEdit: TEdit; str: String);
var
    anEvent: TNotifyEvent;
begin
    anEvent := anEdit.OnChange;
    anEdit.OnChange := nil;
    try
        anEdit.Text := str;
    finally
        anEdit.OnChange := anEvent;
    end;
end;
1 голос
/ 25 апреля 2015

Я знаю, что это старый вопрос, но я подумал, что добавлю свое решение, которое не включает ни одной из сложных процедур, описанных в предыдущих ответах, на случай, если оно будет найдено в другом поиске.

Проблема в самом событии onChange. Я вообще не использую его для текстовых полей.

удалите все OnChange и используйте вместо них OnExit и свяжите его с OnKeyUp.

Все правки имеют общего предка TCustomEdit.

Обычно я использую один метод CustomEditKeyUp и указываю все изменения в форме на этот единственный метод (TEdit, TLabeledEdit и т. Д. И т. Д.)

type THack = class(TCustomEdit);

procedure TForm1.CustomeEditKeyUP(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if (Key=VK_RETURN) and (Sender is TCustomEdit) then
    {This is just to isolate the one occasion when they press the
    enter but don't exit immediately}
    if Assigned(THack(Sender).OnExit) then THack(Sender).OnExit(Sender);
end;

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

Тогда Для каждого элемента управления «Правка» используйте определенный OnExit

.
procedure TForm1.MyEditExit(Sender: TObject);
begin
  if MyEdit.Modified then
  begin
    {Do Something here}
    MyEdit.Modified := false;
  end;
end;

Если вы хотите изменить значение программно без его срабатывания, OnExit

....
MyEdit.Text :='I've changed it'
MyEdit.Modified := false;
....

Большим преимуществом для меня является то, что когда я анализирую ввод для допустимого числа, мне нужно сделать это только один раз, когда редактирование завершено, а не для каждого отдельного возврата, удалить вставку и т. Д., Все окруженные попыткой, кроме как выдают ошибки различные функции форматирования, поскольку они не понимают пробелы и т. д. Для базы данных и т. д. количество вызовов будет значительно уменьшено.

Это мои два пеннета.

0 голосов
/ 15 мая 2019

Другой способ - использование помощников классов, представленных в Delphi 8. http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Class_and_Record_Helpers_(Delphi)

Вы можете написать:

type
  TEditHelper = class helper for TEdit
  public
    procedure SetTextDisableOnChange(const AText: string);
  end;

{ TEditHelper }

procedure TEditHelper.SetTextDisableOnChange(const AText: string);
var
  OnChangeTmp: TNotifyEvent;
begin
  OnChangeTmp:=OnChange;
  try
    OnChange:=nil;
    Text:=AText;
  finally
    OnChange:=OnChangeTmp;
  end;
end;

и затем:

EditCtrl.SetTextDisableOnChange('I do not invoke OnChange!');
EditCtrl.Text:='I invoke OnChange';
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...