我有一个非常大的设置表单,我想填充一个类的数据。所以我做了很多
Edt1.text := ASettings.FirstThing;
我想避免
Edt1.onchange := nil;
Edt1.text := ASettings.FirstThing;
Edt1.onchange := edt1Onchange;
如何更改文本框中的文本并回避onchange事件。
答案 0 :(得分:11)
我使用过类似OnChange处理程序的东西,但更常见的是,我使用了一个标志。
updatingFromCode := true;
Edt1.Text := ASettings.FirstThing;
updatingFromCode := false;
然后
procedure TForm1.OnChange(...);
begin
if updatingFromCode then
Exit;
...
另外,我不是硬编码OnChange实际的OnChange过程,而是存储Edit控件的当前值,然后重置它(如果没有设置它会工作,或者如果另一个地方改变了它,等等。)< / p>
oldOnChange := Edt1.OnChange;
Edt1.OnChange := nil;
Edt1.Text := ASettings.FirstThing;
Edt1.OnChange := oldOnChange;
答案 1 :(得分:7)
您可以考虑使用对象来管理事件的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;
对于“你不能使用'与'人群” - 无论如何都要声明自己一个额外的局部变量并使用它来帮助你在晚上更容易入睡。 :)
或者,为了使它更方便使用并消除“ with ”,我会将 TSuspendEvent 类作为一个接口对象并将其包含在一个函数中产生了一个接口参考,可以让它“活在范围内”,as exemplified by my AutoFree() implementation. In fact, you could use AutoFree() as-is来管理它:
AutoFree(TSuspendEvent.Create(Edit1, 'OnChange'));
Edit1.Text := 'Reset!';
在超出单个过程范围的时间段内启用事件需要比任何帮助程序实用程序可能以通用方式提供的更多管理,我认为,至少在没有明确恢复事件的特定方法的情况下而不是自动。
如果你只是将 TSuspendEvent 包装在它自己的界面产生函数中,遵循与 AutoFree ()相同的模式,你可以进一步简化为:
SuspendEvent(Edit1, 'OnChange');
Edit1.Text := 'Reset!';
作为最后一点,我认为如果需要,可以很容易地看到如何将其简单地扩展到支持在单个调用中暂停对象上的多个事件,例如:
SuspendEvent(Edit1, ['OnChange', 'OnEnter']);
答案 2 :(得分:6)
据我所知,如果对象的OnChange设计为在Text属性更改时触发,则必须坚持将事件设置为暂时为零。我自己,我这样做(终于试一试):
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 :(得分:1)
我知道这是一个老问题,但我想我会添加我的解决方案,不涉及之前答案中列出的任何复杂程序,以防它出现在另一个搜索中。
问题是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公开的其他路线,则进行不同的投射,并且不需要Hack。
然后对于每个Edit控件,使用特定的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;
....
对我来说最大的好处是,当我解析输入说有效数字时,我只需要在编辑完成后执行一次,而不是每个退格,删除插入等所有包围的尝试除了作为各种格式化函数出错,因为它们不了解空格等。对于数据库等,调用次数将大大减少。
这是我的两个问题。
答案 4 :(得分:0)
另一种方法是使用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';