代码之家  ›  专栏  ›  技术社区  ›  Peter Turner

Delphi:如何在不调用onchange事件的情况下在tedit/tmaskedit中设置文本

  •  2
  • Peter Turner  · 技术社区  · 15 年前

    我有一个很大的设置窗体,我想用一个类中的数据填充它。所以我做了很多

    Edt1.text := ASettings.FirstThing; 
    

    我想避免

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

    如何更改文本框中的文本并回避onchange事件。

    4 回复  |  直到 10 年前
        1
  •  11
  •   jasonpenny    15 年前

    我使用了一些类似于更改onchange处理程序的方法,但更常见的是,我使用了一个标志。

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

    然后

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


    另外,我不会硬编码onchange实际的onchange过程,而是存储编辑控件的当前值,然后重置该值(如果未设置该值,或者如果另一个位置已更改该值,则该值将起作用)等。

    oldOnChange := Edt1.OnChange;
    Edt1.OnChange := nil;
    Edt1.Text := ASettings.FirstThing; 
    Edt1.OnChange := oldOnChange;
    
        2
  •  6
  •   AlexV    15 年前

    据我所知,如果对象的onchange设计为在文本属性更改时激发,则必须暂时将事件设置为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;
    
        3
  •  6
  •   Deltics    15 年前

    您可以考虑使用一个对象来管理事件的零化,并恢复以前安装的事件处理程序。假设要还原的事件恰好是在设计时分配的事件/恰好具有“适合的名称”,这有点危险——为了安全起见,应该始终保存/还原当前分配的处理程序。

    这将提供比 不更改的设置文本 ()程序:

      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;
    

    因为“你不能使用” 具有 “拥挤”——无论如何,要声明自己是一个额外的局部变量,如果它能帮助你晚上更容易入睡的话,就用它。:)

    或者,为了更方便使用和消除” 具有 “,我会做 悬浮物 类一个接口对象,并将其使用包装在一个函数中,该函数生成了一个接口引用,该引用可以允许“活动在作用域中”, as exemplified by my AutoFree() implementation. In fact, you could use AutoFree () as-is 要管理此项,请执行以下操作:

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

    为超出单个过程范围的时间段设置事件需要比任何辅助实用程序都可能以通用的方式提供更多的管理,我认为,至少在没有明确恢复事件的特定方法的情况下,而不是自动恢复事件。

    如果你只是包装 悬浮物 在它自己的接口生成函数中,遵循与 自动装置 ()您可以进一步将其简化为:

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

    最后一点,我认为应该很容易理解,如果需要的话,这可以非常简单地扩展为支持在单个调用中挂起一个对象上的多个事件,例如:

      SuspendEvent(Edit1, ['OnChange', 'OnEnter']);
    
        4
  •  1
  •   PhilipO    10 年前

    我知道这是一个老问题,但我想我会添加我的解决方案,不涉及前面答案中概述的任何复杂过程,以防在另一个搜索中出现。

    问题在于onchange事件本身。我根本不把它用于文本字段。

    移除所有onchange并使用onexit代替,并将其绑定到onkeyup。

    所有编辑都有一个共同的祖先tcustoment。

    我通常使用一个名为customditkeyup的方法,并将表单上的所有编辑指向此单个方法(tedit、tlabellededit等)。

    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在tcustoment中是私有的,因此需要进行黑客攻击。如果您知道编辑来自不同的路径,其中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;
    ....
    

    对我来说,最大的好处是,当我分析输入的有效数字时,我只需要在编辑完成时执行一次,而不是对每个退格键、删除插入等都用try包围,除了各种格式化函数出错,因为它们不理解空格等。对于数据库等,调用次数将大大减少。

    那是我的两便士。

    推荐文章