2012-12-12 4 views
4

Delphi XE2를 사용하여 아래 코드를 작성했습니다. Form1을 만들고 Form1은 즉시 Form2의 인스턴스를 만듭니다. Form2의 단추를 누르면 두 번째 Form2가 만들어집니다.MainFormOnTaskbar + 툴팁으로 인한 포커스 도용

위로 가기이 두 번째, 최상위, Form2의 단추 위에 마우스를 놓고 툴팁이 나타날 때까지 툴팁이 나타나는 순간 첫 번째 Form2가 포커스를 훔칩니다.

Application.MainFormOnTaskbarTrue 인 경우에만 문제가 발생합니다. 또한 Form1의 FormCreate 메서드에서 만든 첫 번째 Form2를 사용합니다. PostMessage()을 사용하여 응용 프로그램의 초기화가 완료 될 때까지 첫 번째 Form2의 생성을 지연하면 문제가 해결됩니다.

왜 이런 일이 일어나고 있는지 알고 싶습니다. Delphi의 Application 객체가 힌트 표시를 포함한 많은 것을 처리한다는 것을 이미 알았습니다. 델파이가 초기화 과정에서 창 핸들을 다시 만들 수 있다는 것을 알고 있지만, 위에서 설명한 동작을 완전히 설명하지 못했습니다 (또는 실제로 위의 두 가지 사실이 관련이 있는지 여부).

PROJECT1.DPR

program Project1; 

uses 
    Vcl.Forms, 
    Unit1 in 'Unit1.pas' {Form1}, 
    Unit2 in 'Unit2.pas' {Form2}; 

{$R *.res} 

begin 
    Application.Initialize; 
    Application.MainFormOnTaskbar := True; // False makes problem go away 
    Application.CreateForm(TForm1, Form1); 
    Application.Run; 
end. 

로, Unit1.pas

unit Unit1; 
interface 
uses 
    Vcl.Forms, Unit2; 

type 
    TForm1 = class(TForm) 
    procedure FormCreate(Sender: TObject); 
    public 
    procedure CreateForm2; 
    end; 

var 
    Form1: TForm1; 

implementation 
{$R *.dfm} 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    CreateForm2; 
end; 

procedure TForm1.CreateForm2; 
var 
    frm : TForm2; 
begin 
    frm := TForm2.Create(Application); // (Could pass Self - makes no difference) 
    frm.Show; 
end; 

end. 

Unit2.pas

unit Unit2; 
interface 
uses 
    Vcl.Forms, System.Classes, Vcl.Controls, Vcl.StdCtrls, WinApi.Windows; 

type 
    TForm2 = class(TForm) 
    Button1: TButton; // This button has a hint 
    procedure Button1Click(Sender: TObject); 
    end; 

var 
    Form2: TForm2; 

implementation 
uses 
    System.SysUtils, Unit1; 

{$R *.dfm} 

procedure TForm2.Button1Click(Sender: TObject); 
begin 
    Form1.CreateForm2; 
end; 

end. 
+0

+1 매우 좋은 질문입니다. –

답변

6

핵심 문제는 여기에 있다는 것입니다 TForm2의 첫 번째 인스턴스는 응용 프로그램 창에서 소유 한 창 (Application.Handle)으로 만들어집니다. 그리고 여기서 나는 Windows meaning of owner을 언급하고 있습니다. VCL 언어에서는이를 팝업 부모라고합니다.

이제 TForm2 인스턴스를 처음 만들 때 Application.MainForm 속성은 여전히 ​​nil입니다. PopupParent을 명시 적으로 지정하지 않았으므로 TCustomForm.CreateParams의 코드는 소유자를 응용 프로그램 창으로 설정합니다.

숨겨진 응용 프로그램 창에서 창을 소유하는 것을 원하지 않습니다. 이러한 이유로 인해 첫 번째 TForm2 인스턴스가 다른 모든 창, 특히 기본 폼 뒤에 표시되는 경우가 있습니다. 그것은 단순히 잘못된 소유자와 함께 만들어졌습니다.

Application.Handle이 소유 한 양식은 THintWindow.ActivateHint에 표시됩니다. 이는 ParentWindow := Application.Handle이라는 줄 때문에 발생합니다. 그 다음에 SetWindowPos(Handle, ...)으로 전화하면 잘못 소유 된 양식이 앞으로 나옵니다. 아마 양식은 Application.Handle도 소유하고 있기 때문에 앞쪽으로옵니다. 지금은 정확한 메커니즘에 대한 명확한 설명이 없지만 형식이 잘못 설정되어 있기 때문에 그다지 재미 있지는 않습니다.

어쨌든 근본적인 문제는 잘못 소유 된 창을 만들었다는 것입니다. 따라서 솔루션은 창을 올바르게 소유하고 있는지 확인하는 것입니다. PopupParent을 지정하면됩니다.예 :

procedure TForm1.CreateForm2; 
var 
    frm : TForm2; 
begin 
    frm := TForm2.Create(Application); // (Could pass Self - makes no difference) 
    frm.PopupParent := Self; 
    frm.Show; 
end; 
+0

감사합니다. 결국 PopupParent 대신 TForm2.CreateParams에 명시 적으로 Params.WndParent를 설정합니다. 부모를 설정하지 않고 TForm2를 만들 수 없기 때문입니다. 내 TForm1은 실제로 숨겨져 있으며 (각 TForm2에는 고유 한 작업 표시 줄 단추가 있음) Form1.Handle이 반드시 적절하다고 확신하지 못했습니다. Raymond Chen은 [GetDesktopWindow를 절대로 사용하지 않습니다. 0을 사용하십시오 (http://blogs.msdn.com/b/oldnewthing/archive/2004/02/24/79212.aspx). 0 테스트, Form1.Handle 및 심지어 Application.Handle 모두 작동했습니다. 각 TForm2에는 _same_ 소유자가 필요합니다. 그래서 나는 0에 정착했습니다. –

+0

덧붙여 Peter Below [GetDesktopWindow를 사용하지 않는 것에 동의하지 않습니다] (https://groups.google.com/forum/?hl=en&fromgroups=#!msg/borland.public.delphi.winapi/7mNoFJpYTGQ/62DpjPRf9-kJ). –

+1

기본 폼이 숨겨져 있으면 '0'을 사용하는 것이 옳습니다. 'WndParent'를 통해 그것을 제어하는 ​​것은 소리의 움직임입니다. 'GetDesktopWindow'에 관해서는 Raymond가 정확합니다. 피터 아래는 많은 사람들이 저지르는 것과 똑같은 실수를 저지르고 있습니다. Raymond가 말한 실수. –

관련 문제