複数のアプリウィンドウのアクティベーションが正しく機能しない
質問
メイン フォームとしてドキュメント ブラウザを持つ Delphi アプリケーションがあります。ユーザーがドキュメントを開くと、エディタ ウィンドウが開きます。各エディターのタスク バーとメイン フォームにボタンを配置したいと考えています。これを行うために通常のコードを適用しました (下記) が、エディター ウィンドウを使用した後にメイン フォームをクリックすると、エディターが上部に残ったままになり、フォーカスはメイン フォームにあります。この動作の原因を突き止めることができません。
舞台設定:メインフォームとドキュメントフォームを開きます。
別のアプリをクリックし、メイン フォームをクリックしても、メイン フォームはフォーカスされたままになります。(期待どおりの動作をしています。)
ドキュメントフォームをクリックし、メインフォームをクリックし、ドキュメントフォームが前面に戻りますが、不活性が表示されます。(写真は結果を示しています)
代替テキスト http://www.matthew-jones.com/temp_xfer/titlebarfailure.jpg
最初のステップ、これは Delphi 2007 で、プロジェクトには次のものがあります。
Application.MainFormOnTaskBar := True;
メインフォームには追加のコードはありません。
文書フォームについては、
procedure TCommonEditForm.CreateParams(var params: TCreateParams);
begin
inherited;
params.WndParent := 0; // GetDeskTopWindow; no diff
end;
これを引き起こしているメッセージがあるかどうか調べてみましたが、適切なものが見つかりませんでした。「アクティブ化」に関連するコードを検索しました。手がかりは大歓迎です!
解決
私のアプリケーションはあなたが説明した方法で動作します。これが私がとったアプローチです。もっと簡単なアプローチを見つけたかったのですが、そうすることはできませんでした。
私はこれらの記事を読むことから始めました。この最初の記事は、Peter Below による素晴らしい記事です。
http://groups-beta.google.com/group/borland.public.delphi.winapi/msg/e9f75ff48ce960eb?hl=ja
他の情報もここにありましたが、これは有効な解決策であるとは証明されませんでした。私の使用のために: http://blogs.teamb.com/DeepakShenoy/archive/2005/04/26/4050.aspx
最終的に行き着いたのがこちらです。
私のスプラッシュ画面はアプリケーションのメインフォームとしても機能します。メイン フォームには、アプリケーション オブジェクトとの特別な関係があります。すべての二次形式を使用すると、探していた動作が得られます。
タスク バーに表示したい各フォームで、CreateParams をオーバーライドします。私はこれを編集フォームとユーザーが「メインフォーム」として見るもので行います。
procedure TUaarSalesMain.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
Params.WndParent := GetDesktopWindow;
end;
Delphi に関する限り、私の「メイン」フォームは、Activitate 関数で真のメイン フォームを読み込みます。最初のアクティブ化を追跡するためにメンバー変数を使用します。次に、関数の最後にスプラッシュ フォームを非表示にしますが、閉じないでください。これは私にとって重要でした。ユーザーがドキュメントを編集中にメイン フォームを閉じた場合に、編集画面が同時に強制的に閉じられることを望まなかったからです。このようにして、目に見えるフォームはすべて同じように扱われます。
if FFirstActivate = false then
exit;
FFristActivate := false;
/*
Main Load code here
Update Splash label, repaint
Application.CreateForm
etc.
*/
// I can't change visible here but I can change the size of the window
Self.Height := 0;
Self.Width := 0;
Self.Enabled := false;
// It is tempting to set Self.Visible := false here but that is not
// possible because you can't change the Visible status inside this
// function. So we need to send a message instead.
ShowWindow(Self.Handle, SW_HIDE);
end;
しかし、まだ問題があります。他のすべてのフォームを閉じたときに、メイン/スプラッシュ ウィンドウを閉じる必要があります。私はフォームをプラグインとして使用しているため、閉じるルーチンに Parent <> nil の追加チェックを入れています (私の目的からすると、フレームよりもフォームの方がうまく機能します)。
Idle イベントを使用するのはあまり好きではありませんでしたが、これが CPU に負担をかけていることに気づきませんでした。
{
TApplicationManager.ApplicationEventsIdle
---------------------------------------------------------------------------
}
procedure TApplicationManager.ApplicationEventsIdle(Sender: TObject;
var Done: Boolean);
begin
if Screen.FormCount < 2 then
Close;
end;
{
TApplicationManager.FormCloseQuery
---------------------------------------------------------------------------
}
procedure TApplicationManager.FormCloseQuery(Sender: TObject;
var CanClose: Boolean);
var
i: integer;
begin
for i := 0 to Screen.FormCount - 1 do
begin
if Screen.Forms[i] <> self then
begin
// Forms that have a parent will be cleaned up by that parent so
// ignore them here and only attempt to close the parent forms
if Screen.Forms[i].Parent = nil then
begin
if Screen.Forms[i].CloseQuery = false then
begin
CanClose := false;
break;
end;
end;
end;
end;
end;
{
TApplicationManager.FormClose
---------------------------------------------------------------------------
}
procedure TApplicationManager.FormClose(Sender: TObject;
var Action: TCloseAction);
var
i: integer;
begin
for i := Screen.FormCount - 1 downto 0 do
begin
if Screen.Forms[i] <> self then
begin
// Forms that have a parent will be cleaned up by that parent so
// ignore them here and only attempt to close the parent forms
if Screen.Forms[i].Parent = nil then
begin
Screen.Forms[i].Close;
end;
end;
end;
end;
これはこれまでのところ私にとってうまく機能しています。「メイン/スプラッシュ」画面のアイコンがまだ表示されていたため、Vista 用に小さな変更を加えました。それが何だったのか覚えていないけど。おそらく、幅、高さを設定して有効にし、スプラッシュ画面で非表示メッセージを送信する必要はありません。それが表示されないことを確認したかっただけです:-)。
近い出来事への対処が必要だった。私の記憶が正しければ、これは Windows がシャットダウン メッセージを送信するときに必要でした。メインフォームだけがそのメッセージを受け取ると思います。
他のヒント
これが本当に馬鹿げているのに申し訳ありませんが、フォームスタイルをfsStayOnTopに設定していない場合はどうでしょうか?これにより、この動作が説明されます。
おそらくcreateparamsにこれを追加します
Params.ExStyle := Params.ExStyle OR WS_EX_APPWINDOW;
またはコードの任意の場所でこれを試してください。フォーム.OnCreateイベントで使用します。
SetWindowLong(Wnd, GWL_EXSTYLE,
GetWindowLong(Wnd, GWL_EXSTYLE) or WS_EX_APPWINDOW) ;
この欠点は、メインフォームが最小化されている場合、他のフォームも同様に非表示になりますが、メインフォームが非表示になったときに復元されることです。