2

さて、私の問題は次のとおりです。

基本的にDelphi 2010に移植しているDelphi 5アプリケーションがあります(古いコンポーネントを最新バージョンに置き換え、避けられないAnsi / Unicode文字列の問題を修正するなど)、ある種の問題に遭遇しました。

フォームの 1 つを作成すると、アクセス違反が発生します。調べてみたところ、この理由は、Create で呼び出されたセッターの 1 つが、フォーム上でまだ作成されていないオブジェクトのプロパティを変更しようとしたためであるという結論に達しました。

私はそれを少しトリミングしましたが、コードは基本的に次のようになります。

フォーム宣言:

property EnGrpSndOption:boolean read fEnGrpSndOption write SetGrpSndOption;

フォームの作成:

EnGrpSndOption := false;

実装中:

procedure Myform.SetGrpSndOption(const Value: boolean);
begin
  fEnGrpSndOption := Value;
  btGrpSnd.Visible := Value;
end;

btGrpSnd.Visible := Value の直前に ShowMessage(BooltoStr(Assigned(btGrpSend), true)) を投げ込むことで、btGrpSnd がまだ作成されていないことが問題であることを確認しました。

btGrpSend は LMDButton ですが、まだ作成されていないため、あまり関係がないと確信しています。

コントロールが割り当てられていることを確認してから値を割り当てる必要があると思いますが、実際のコントロールに設定されていない create で設定された値が発生するだけです。

だから私がやりたいことは、作成が実行される前に、フォーム上のすべてのコントロールが確実に作成されるようにする方法を見つけることです。

これを行う際の支援、または Delphi がフォームを作成する方法に関する情報をいただければ幸いです。それは Delphi 5 で機能していたので、この原因はバージョン間の変更点のリストのどこかに記載されているはずだと思います。結局、Delphi 2010 は Delphi 5 よりもかなり新しいものです。

4

5 に答える 5

3

Tobias が言及したように (ただし反対を主張)、作成順序を変更できます (作成順序の変更のフォームで)。

ただし、setter メソッドでフォームが作成中かどうかを確認することもできます (form.componentstate の csCreating)。その場合は、そのプロパティ値を自分で保存し、AfterConstruction で処理する必要があります。

于 2009-12-14T12:20:22.477 に答える
2

設計時にAVを配置するときにAVを取得しているというコメントから、それはコントロール自体に問題があり、適切に移植されていないことを意味します. 制御された状況下で実行時に再現するには、次のような小さなプログラムを作成する必要があります。

単一のフォームで新しい VCL アプリを作成します。フォームに TButton を配置します。ボタンの OnClick で、次のようにします。

var
   newButton: TLMDButton;
begin
   newButton := TLMDButton.Create(self);
   newButton.Parent := self;
   //assign any other properties you'd like here
end;

コンストラクターにブレークポイントを設定し、アクセス違反の原因が見つかるまでトレースします。

編集:OK、コメントを見て、私たちはあなたの問題を見つけたと思います!

フォームのサブコントロールは、DFM ファイルを読み取ることによって初期化されます。コントロールを TCustomForm に変更したとき、それを定義するために新しい DFM を提供しましたか? そうでない場合は、フォームのコンストラクターをオーバーライドしてコントロールを作成し、それらのプロパティを手動で定義する必要があります。あなたのためにそれを初期化する「魔法」はありません。

于 2009-12-14T13:08:59.010 に答える
1

YourCreateは常に先祖コンストラクターの前に最初に呼び出されます。それがコンストラクタの仕組みです。残りの初期化を行う前に、継承されたコンストラクターを呼び出すことができるはずです。

constructor MyForm.Create(Owner: TComponent);
begin
  inherited;
  EnGrpSndOption := False;
end;

ただし、実現しようとしていることを示すより良い方法があります。クラスは DFM リソースからプロパティをロードしています。完了すると、 という名前の仮想メソッドが呼び出されLoadedます。これは通常、すべての子にすべての準備が整ったことを通知するために使用されます。そのため、それらのいずれかがフォーム上の他の子への参照を保持している場合、その時点でそれらの参照を使用しても安全であることがわかります。フォームでオーバーライドすることもできます。

procedure MyForm.Loaded;
begin
  inherited;
  EnGrpSndOption := False;
end;

ただし、それは通常、あなたの場合には大きな違いはありません。Loadedフォームが DFM リソースからのロードを終了した直後に、コンストラクターから呼び出されます。そのリソースは、それ自体のために作成する必要があるすべてのコントロールをフォームに伝えます。ボタンが作成されていない場合は、DFM に正しくリストされていない可能性があります。クラスに対応するフィールドがないコントロールが DFM にリストされる可能性があります。一方、DFM に対応するエントリがないパブリッシュ済みフィールドがある場合、IDE はそのことを警告し、フォーム デザイナでそのフィールドを表示するたびに宣言を削除するよう提案する必要があります。DFM をテキストとして表示し、 という名前のコントロールのエントリが実際にあることを確認しますbtGrpSnd

于 2009-12-14T16:06:29.853 に答える
0

2つの可能性があります。VisibleプロパティにValueを割り当てる前に、btGrpSndがnilであるかどうかを確認してください。nilの場合は、次のいずれかを実行できます。

  • プロパティを設定しない
  • btGrpSndを作成します

私は作成順序をいじりません。それはより複雑であり、さらなる変更で壊れることがあります。


コメントから:デザインモードかランタイムモードかを確認できます。可視性を設定する前に、設計時にいるかどうかを確認してください。

if not (csDesigning in Componentstate) then
begin
  btGrpSnd:=Value;
end;

あなたのコメントへの回答:

これのために行く:

procedure Myform.SetGrpSndOption(const Value: boolean); 
begin 
  fEnGrpSndOption := Value; 
  if btGrpSnd<>nil then btGrpSnd.Visible := Value; 
end; 

および1つの追加のプロパティ設定btGrpSnd。値<>nilに設定されている場合は、fEnGrpSndOptionでも可視性を保護するように設定します。

Myformの外部でbtGrpSndを設定する必要がない場合は、すべてを作成するinit-procedureを作成します。例えば:

constructor Myform.Create(...)
begin
  init;
end;

procedure init
begin
  btGrpSnd:=TButton.Create;
  ...
end;

procedure Myform.SetGrpSndOption(const Value: boolean); 
begin 
 fEnGrpSndOption := Value; 
 if btGrpSnd<>nil then init;
 btGrpSnd.Visible := Value; 
end; 

これは、将来壊れてしまう可能性のあるいくつかの変更されたinit-code-hackに依存する場合よりもさらに優れています。

于 2009-12-14T12:06:12.770 に答える
0

これで十分ですか?

Assigned(btGrpSnd) と btGrpSnd.HandleAllocated の場合、btGrpSnd.Visible := ...

于 2009-12-14T15:19:04.893 に答える