Delphi 应用程序是否可以接收包含对象、其属性和事件分配的 dfm 文件,并加载所有这些信息,就像它们如何处理用它编译的内部 dfm 时一样?
我们怎么能这样做?有直接的方法吗?
注意:应用程序已经拥有包含正确类和方法的代码,包括事件。我们还可以远程接收某种脚本,该脚本可以由我的应用程序读取,该脚本将创建匹配 dfm 文件规范所需的对象。就像 Web 浏览器解释 HTML、css 和 JS 文件一样......
Delphi 应用程序是否可以接收包含对象、其属性和事件分配的 dfm 文件,并加载所有这些信息,就像它们如何处理用它编译的内部 dfm 时一样?
我们怎么能这样做?有直接的方法吗?
注意:应用程序已经拥有包含正确类和方法的代码,包括事件。我们还可以远程接收某种脚本,该脚本可以由我的应用程序读取,该脚本将创建匹配 dfm 文件规范所需的对象。就像 Web 浏览器解释 HTML、css 和 JS 文件一样......
确实可以在运行时加载 .dfm 文件并创建由该 dfm 文件表示的表单。
我已经写了一些代码来做到这一点:
但是:请注意:您需要在 RegisterNecessaryClasses 过程中添加更多的 RegisterClass(TSomeComponent) 行。如前所述,例如,如果您尝试加载包含 TSpeedbutton 的 .dfm 文件,您将得到一个异常:只需将 RegisterClass(TSpeedbutton) 添加到 RegisterNecessaryClasses 过程。
unit DynaFormF; // This is a normal Delphi form - just an empty one (No components dropped on the form)
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TfrmDynaForm = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmDynaForm: TfrmDynaForm;
implementation
{$R *.dfm}
end.
// :
unit DynaLoadDfmU;
{$O-}
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ComCtrls, utils08, DynaFormF;
var
DebugSL : TStrings;
procedure ShowDynaFormModal(Filename:String);
implementation
procedure RegisterNecessaryClasses;
begin
RegisterClass(TfrmDynaForm);
RegisterClass(TPanel);
RegisterClass(TMemo);
RegisterClass(TTimer);
RegisterClass(TListBox);
RegisterClass(TSplitter);
RegisterClass(TEdit);
RegisterClass(TCheckBox);
RegisterClass(TButton);
RegisterClass(TLabel);
RegisterClass(TRadioGroup);
end;
type
TCrackedTComponent = class(TComponent)
protected
procedure UpdateState_Designing;
end;
var
ClassRegistered : Boolean;
procedure RemoveEventHandlers(SL:TStrings);
const
Key1 = ' On';
Key2 = ' = ';
var
i, k1,k2 : Integer;
S : String;
begin
for i := SL.Count-1 downto 0 do begin
S := SL[i];
k1 := pos(Key1, S);
k2 := pos(Key2, S);
if (k1 <> 0) AND (k2 > k1) then begin
// remove it:
SL.Delete(i);
end;
end;
end;
procedure ReportBoolean(S:String; B:Boolean);
const
Txts : Array[Boolean] of String = (
'Cleared', 'Set'
);
begin
if Assigned(DebugSL) then begin
S := S + ' : ' + Txts[B];
DebugSL.Add(S);
end;
end;
procedure SetComponentStyles(AForm:TForm);
var
AComponent : TComponent;
i : Integer;
B1, B2 : Boolean;
begin
for i := 0 to AForm.ComponentCount-1 do begin
AComponent := AForm.Components[i];
if AComponent is TTimer then begin
// TTIMER:
B1 := csDesigning in AComponent.ComponentState;
// Does not work: an attempt to make the TTimer visible like it is in Delphi IDE's form designer.
TCrackedTComponent(AComponent).UpdateState_Designing;
B2 := csDesigning in AComponent.ComponentState;
ReportBoolean('Before setting it: ', B1);
ReportBoolean('After setting it: ', B2);
end;
end;
end;
procedure ShowDynaFormModalPrim(Filename:String);
var
FormDyna : TfrmDynaForm;
S1 : TFileStream;
S1m : TMemoryStream;
S2 : TMemoryStream;
S : String;
k1, k2 : Integer;
Reader : TReader;
SLHelper : TStringlist;
OK : Boolean;
MissingClassName, FormName, FormTypeName : String;
begin
FormName := 'frmDynaForm';
FormTypeName := 'TfrmDynaForm';
FormDyna := NIL;
OK := False;
S1 := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
try
S1m := TMemoryStream.Create;
try
SLHelper := TStringlist.Create;
try
SLHelper.LoadFromStream(S1);
S := SLHelper[0];
k1 := pos(' ', S);
k2 := pos(': ', S);
if (k1 <> 0) AND (k2 > k1) then begin
// match:
SetLength(S, k2+1);
S := 'object ' + FormName + ': ' + FormTypeName;
SLHelper[0] := S;
end;
RemoveEventHandlers(SLHelper);
SLHelper.SaveToStream(S1m);
finally
SLHelper.Free;
end;
S1m.Position := 0;
S2 := TMemoryStream.Create;
try
ObjectTextToBinary(S1m, S2);
S2.Position := 0;
Reader := TReader.Create(S2, 4096);
try
try
FormDyna := TfrmDynaForm.Create(NIL);
Reader.ReadRootComponent(FormDyna);
OK := True;
SetComponentStyles(FormDyna);
except
on E:Exception do begin
S := E.ClassName + ' ' + E.Message;
if Assigned(DebugSL) then begin
DebugSL.add(S);
if (E.ClassName = 'EClassNotFound') then begin
// the class is missing - we need one more "RegisterClass" line in the RegisterNecessaryClasses procedure.
MissingClassName := CopyBetween(E.Message, 'Class ', ' not found');
S := ' RegisterClass(' + MissingClassName + ');';
DebugSL.Add(S);
end;
end;
end;
end;
finally
Reader.Free;
end;
finally
S2.Free;
end;
finally
S1m.Free;
end;
finally
S1.Free;
end;
if OK then begin
try
FormDyna.Caption := 'Dynamically created form: ' + ' -- ' + FormDyna.Caption;
FormDyna.ShowModal;
finally
FormDyna.Free;
end;
end else begin
// failure:
S := 'Dynamic loading of form file failed.';
if Assigned(DebugSL)
then DebugSL.Add(S)
end;
end;
procedure ShowDynaFormModal(Filename:String);
begin
if NOT ClassRegistered then begin
ClassRegistered := True;
RegisterNecessaryClasses;
end;
ShowDynaFormModalPrim(Filename);
end;
{ TCrackedTComponent }
procedure TCrackedTComponent.UpdateState_Designing;
begin
SetDesigning(TRUE, FALSE);
end;
end.
您收到了一个 DFM 表单设计文件,并且您想要实例化它?
如果没有随附的 PAS 源文件,这是不可能的。您需要实现类的行为和交互方式。(如果没有实现,即 DFM 不引用事件处理程序,那么如果您有或解析来自 DFM 的类名,则您可以在运行时创建一个类。但您必须知道或解析表单的所有已发布成员类,从而使这种解决方案具有学术性)。
即使您有源文件,在编译时也需要它才能创建类。
如果您在编译时同时拥有设计文件和源文件,则将它们添加到项目中,并且您不需要从文件加载表单,因为它包含在可执行文件的资源中。只需在运行时使用默认构造函数Create
来创建表单。
当您有一个用于单个 PAS 源文件的辅助 DFM 表单文件时,请使用这种技巧使用构造函数创建CreateNew
基于相同代码的替代表单对象(例如,隐藏或设置某些控件)。
如果您的 DFM 文件是在运行时从表单的特定状态创建的,则照常创建表单,但使用此问题的答案之一恢复该特定状态。