2

左側の treeView と右側のパネルに基づくアプリケーション レイアウトがあります。パネルは、選択したツリー ノードに応じて異なる TForm クラスをホストします (一種の「フォーム エクスプローラー」)。一度に表示されるフォームは 1 つだけで、別の場所に保存されている基になるデータを公開し、新しいツリー ノードがクリックされるたびにフォーム インスタンスが作成および破棄されます。

次のシナリオを除いて、これはすべて正常に機能します。1 秒ほどかかるアクションを起動するフォームのボタンをクリックします。このアクション中に、Application.ProcessMessages が呼び出される場合があります。このアクションが実際に完了する直前に、ユーザーは新しいツリー ノードをクリックします。この wmMousedown メッセージが処理され、フォームがすぐに解放されます。その後、アクション コードはフォーム コードに戻り、self が変更され、AV が発生したことを検出します。

私の質問は、フォームの解放を許可する前に、フォームのメッセージがすべて処理され完了したことを知る方法はありますか? モーダルフォームは、忙しい場合に閉じる前に一時停止するため、閉じるボタンがクリックされたときにこれを行うようです...

ありがとうブライアン

4

5 に答える 5

7

Application.ProcessMessagesの使用は絶対に避けてください。

私が目にする最も一般的な不適切な使用法は、ラベルのキャプションを更新した後など、GUI を再描画できるようにすることです。GUI を確実に更新する最も安全な方法は、影響を受けるコントロールをUpdateメソッドを使用して明示的に再描画することです (これにより、描画メッセージがバイパスされ、コントロールが直接再描画されます)。または、Refreshメソッドである場合もあれば、どちらかまたは両方である場合もあります。頭のてっぺんから思い出せないのは悲しいことです!

Application.ProcessMessagesは、発見したように「再入可能性」の問題を引き起こし、コード内に潜在的な新しい短命の「メインメッセージループ」を効果的に作成し、診断が困難で再現が困難な問題につながる可能性があります。

この場合のApplication.ProcessMessagesの使用を調べて、Application.ProcessMessagesの問題に対処するためだけに他の多くのコードにパッチを当てようとするのではなく、コードからその使用を排除するための代替アプローチを考案できないかどうかを確認します。.ProcessMessagesの原因。

注: ルールの 1 つの例外は、Application.ProcessMessagesを使用して「進行状況」メッセージ/ダイアログ ボックスの「キャンセル」ボタンで応答性を維持することは、その進行状況メッセージ/ダイアログ ボックスがモーダルであり、残りの部分がそのダイアログが表示されている間、アプリケーションは事実上無効になっているため、その「キャンセル」ボタンのみがメッセージに応答する可能性があります

于 2009-09-09T11:48:08.407 に答える
4

最後の段落で実際の質問に答えるには... :)

フォームでReleaseを呼び出すと、フォームにメッセージが投稿され、そのメッセージを受信したときにそれ自体がFreeになります。

メッセージはメッセージキューに投稿されるため、そのフォームの他の現在のメッセージが処理された後にのみ到着し、あなたが求めているものを正確に達成すると思います.

于 2009-09-09T11:59:38.037 に答える
1

配置されたすべてのフォームのクラスに IsBusy プロパティを実装します (継承を使用 - このプロパティを親フォームに実装します)。ホスティング パネルからフォームを削除する前に (Free を呼び出すか、単にパネルから移動するかに関係なく)、IsBusy が true でないかどうかを確認してください。ホストされているフォームがビジー状態の場合は、終了するまで待ってから削除してください。ホストされているフォームに通知する方法を追加して、実行時間の長いタスクを中止することもできます。

現在の問題を解決するだけでなく、フォーム内のビジネス ロジックをサニタイズすることもできます。

したがって、TreeView のフォーム変更コードには、次のコードが含まれている必要があります。

    {FCurrentForm is a reference to currently placed form on panel}
    if (FCurrentForm.IsBusy) do
    begin
      {remember some information that will be used to create new form}
      FNewFormToBeAdded := ... 
    end
    else
    begin
      FreeCurrentForm();
      PlaceNewFormOnPanel();
    end;

したがって、次のようなルーチンが必要です。

   procedure THostForm.NotifyMeAboutTaskFinished;
   begin
     if FNewFormToBeAdded <> 0 than 
     begin
       FreeCurrentForm();
       PlaceNewFormOnPanel();
     end; 
   end;

そして、あなたの HOSTED フォームでは、あなたが持つことができます

procedure TSomeHostedForm.btnDoLongTaskClick(Sender : TObject);
begin
  IsBusy := true;
  try
    {... do some tikme taking task ...}
  finally
    IsBusy :=false;
    NotifyHostingFormIAMNotBusyAnymore();
  end;
end;
于 2009-09-09T11:32:53.950 に答える
0

TreeView.OnClick は、現在アクティブなフォームの CloseQuery メソッドを呼び出すことができます。CloseQuery が false を返す場合は、フォームを交換しないでください。その後、必要なフォームで標準の CloseQuery を処理できます。

パネルに表示されているフォームでは、実際に閉じることができるかどうかを知るために、いくつかの状態を追跡する必要があります。長時間実行されているプロセスにも停止条件をチェックさせます。通常はキャンセル ボタンもありますが、CloseQuery を呼び出すと、実行時間の長いプロセスも中止されます。私の CloseQuery は通常、次のようになります。

procedure TBatchPoster.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin inherited;

  if FProcessRunning = true then
  begin
    FStop := true;
    CanClose := false;
  end;
end;

通常、ユーザーが 1 回クリックしても何も変わらない場合、2 回目にクリックしようとすると、長時間実行されているプロセスが停止し、2 回目のクリックでフォームの変更に成功します。

実際、これはsmok1の回答と同じですが、すでにフォームを使用しているため、新しいプロパティを追加する必要はありません.

于 2009-09-09T18:49:57.210 に答える
0

「Deltics」の両方の回答に同意しますが、フォームを解放する必要があるかどうかに応じて、別のオプションがあります。

フォームの FormClose イベントで、Action を caHide に設定します。これにより、フォームが破壊されるのではなく、非表示になります。割り当てたフォームを追跡する必要があります (おそらく TTreeNode の「データ」ポインタを使用します)。

于 2009-09-09T22:05:00.420 に答える