5

Delphi アプリケーションは、オブジェクト、そのプロパティ、およびイベントの割り当てを含む dfm ファイルを受け取り、それでコンパイルされた内部 dfm を使用する場合と同じように、これらすべての情報をロードできますか?

どうすればそれができますか?直接的な方法はありますか?

注: アプリケーションには、正しいクラスとメソッド、イベントが含まれたコードが既に含まれています。dfm ファイルの仕様に一致するために必要なオブジェクトを作成する、アプリケーションで読み取ることができる何らかの種類のスクリプトをリモートで受け取ることもできます。Web ブラウザーが HTML、css、および JS ファイルを解釈するのと同じように...

4

2 に答える 2

8

実行時に .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.
于 2014-04-19T19:17:39.617 に答える
3

DFM フォーム デザイン ファイルが表示され、それをインスタンス化しますか?

付属の PAS ソース ファイルがなければ、これは不可能です。クラスがどのように動作し、相互作用するかを実装する必要があります。(実装がない場合、つまり DFM がイベント ハンドラーを参照しない場合、DFM からクラス名を取得または解析すれば、実行時にクラスを作成できます。ただし、フォームの公開されたすべてのメンバーを把握または解析する必要があります。クラス、したがって、このソリューションは学術的なものになります)。

ソース ファイルがある場合でも、クラスを作成するためにコンパイル時に必要になります。

コンパイル時にデザイン ファイルとソース ファイルの両方がある場合は、それらをプロジェクトに追加します。フォームは実行可能ファイルのリソースに含まれているため、ファイルからフォームをロードする必要はありません。実行時にデフォルトのコンストラクターを使用Createしてフォームを作成するだけです。

単一の PAS ソース ファイルに対して 2 番目の DFM フォーム ファイルがある場合は、この種のトリックCreateNewを使用して、同じコードに基づいてコンストラクターを使用して別のフォーム オブジェクトを作成します (たとえば、一部のコントロールを非表示にしたり、スタイルを設定したりします)。

DFM ファイルが実行時にフォームの特定の状態から作成された場合、通常どおりフォームを作成しますが、この質問に対する回答の 1 つを使用してその特定の状態を復元します。

于 2013-11-15T21:58:24.780 に答える