Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Делфи _ Немодальное дочернее окно всегда сверху

Автор: TarasBer 2.11.2010 21:47


uses
Windows, Messages;

var
WC: TWndClass;
H1, H2: hWND;
Message: TMsg;

function WP(Handle: HWND; Message: UINT; WP: WParam; LP: LParam): longint; stdcall;
begin
case Message of
wm_Destroy: if Handle = H1 then begin
PostQuitMessage(0);
Result := 0;
Exit;
end;
end;
Result := DefWindowProc(Handle, Message, WP, LP);
end;

begin
with WC do begin
Style := cs_HRedraw or cs_VRedraw or cs_OwnDC;
lpfnWndProc := @WP;
cbClsExtra := 0;
cbWndExtra := 0;
hInstance := MainInstance;
hIcon := 0;
hCursor := LoadCursor(0, idc_Arrow);
hbrBackground := Color_BtnFace + 1;
lpszMenuName := nil;
lpszClassName := 'Test';
end;

RegisterClass(WC);

H1 := CreateWindow('Test', 'Test1', ws_OverlappedWindow,
100, 100, 320, 200,
0, 0, MainInstance, nil);

H2 := CreateWindow('Test', 'Test2', ws_OverlappedWindow,
110, 110, 320, 200,
H1, 0, MainInstance, nil);

ShowWindow(H1, cmdShow);
UpdateWindow(H1);

ShowWindow(H2, sw_ShowNormal);

while GetMessage(Message, 0, 0, 0) do begin
TranslateMessage(Message);
DispatchMessage(Message);
end;

end.



Второе окно всегда сверху, даже когда первое активно.
Если у второго окна задавать родителем 0, то в панели задач будут 2 иконки, тоже не катит.
Что делать?
Как делать правильно?

Автор: volvo 2.11.2010 22:24

Абсолютно правильное поведение:
http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#owned_windows

Цитата
An owned window is always above its owner in the z-order.

Автор: TarasBer 2.11.2010 22:51

А мне что делать, чтобы сверху было активное окно? Первое окно наверх даже BringWindowToTop не вытаскивает, кстати.

Автор: volvo 3.11.2010 1:32

Цитата
А мне что делать, чтобы сверху было активное окно?
Можно объяснить, что именно тебе нужно? У тебя есть два окна. Какая между ними связь?

Если нужно именно два независимых окна, то я бы создал первое, потом - промежуточное, скрытое, и уже к скрытому бы прикрепил потомка - второе окно. Так как окна, которые скрыты (или унаследованы от скрытых) не имеют кнопки на таскбаре - будет то, что нужно.

Автор: TarasBer 3.11.2010 1:49

> Какая между ними связь?

Да никакой, на самом деле. Они равноправны, разница лишь в том, что когда закрывается H1, должно завершаться приложение, ну так это и так в оконной функции указано.

Короче, надо выкручиваться.

Я поступил по-VCLовски: в качестве главного создал видимое окно нулевого размера, а у тех двух указал родителем его.

Автор: volvo 3.11.2010 3:40

Цитата
Я поступил по-VCLовски: в качестве главного создал видимое окно нулевого размера, а у тех двух указал родителем его.
Это нехорошее решение. Оно сбивает работу на новых ОСях (работа Aero ломается. В частности, live thumbnails, Dynamic Windows, Windows Flip и Windows Flip 3D работают не так, как ожидается. Попробуй например запустить свое приложение под Win7 и минимизировать/максимизировать его. Эффект будет НЕ тот, что при минимизации/максимизации, скажем, NotePad-а. Твое окно будет плавно исчезать/проявляться вместо анимации сворачивания в таскбар).

Поэтому, чтобы заставить программу, компилируемую Дельфями ниже 2007, работать нормально под Вистой/Семеркой, нужно вносить некоторые изменения в код. Об этом есть статья на Королевстве Дельфи, если не ошибаюсь, Д. Ларионов написал.

А ты в свой проект намеренно включаешь этот глюк от VCL.

Автор: TarasBer 3.11.2010 14:08

А, вот оно как.
Тогда сделаю во-твоему.

Вот они, преимущества безVCLного кода... Захотел - юникодом все компоненты задал, захотел - окна как надо сделал.

Ещё оффтоп, но тоже об окнах - у меня в одной программе главное окно без рамки (ws_Popup), она страшно глючит при запуске под линуксом через WINE, это известный глюк WINE или в программе что-то не так?

Автор: TarasBer 3.11.2010 16:50

Теперь у меня новая проблема.
Если переключиться на другое приложение, закрывающее окно2, а потом снова на это, то окно2 пропадает.
Надо ловить wm_Activate и руками что-то писать?
И ещё, надо, чтобы при соваривании окна1 сворачивалось и окно2 (да, я забыл это сказать, думал, что такой эффект сам собой получится).


uses
Windows, Messages;

var
WC: TWndClass;
H1, BK, H2: hWND;
Message: TMsg;

function WP(Handle: HWND; Message: UINT; WP: WParam; LP: LParam): longint; stdcall;
begin
case Message of
wm_Activate: if (Handle = H1) and (LoWord(WP) <> wa_Inactive) then begin
// ???
end;
wm_Destroy: if Handle = H1 then begin
PostQuitMessage(0);
Result := 0;
Exit;
end;
end;
Result := DefWindowProc(Handle, Message, WP, LP);
end;

begin
with WC do begin
Style := cs_HRedraw or cs_VRedraw or cs_OwnDC;
lpfnWndProc := @WP;
cbClsExtra := 0;
cbWndExtra := 0;
hInstance := MainInstance;
hIcon := 0;
hCursor := LoadCursor(0, idc_Arrow);
hbrBackground := Color_BtnFace + 1;
lpszMenuName := nil;
lpszClassName := 'Test';
end;

RegisterClass(WC);

H1 := CreateWindow('Test', 'Test1', ws_OverlappedWindow,
100, 100, 320, 200,
0, 0, MainInstance, nil);

BK := CreateWindow('Test', 'Test1', 0,
100, 100, 320, 200,
0, 0, MainInstance, nil);

H2 := CreateWindow('Test', 'Test2', ws_OverlappedWindow,
110, 110, 320, 200,
BK, 0, MainInstance, nil);

ShowWindow(H1, cmdShow);
UpdateWindow(H1);

ShowWindow(H2, sw_ShowNormal);

while GetMessage(Message, 0, 0, 0) do begin
TranslateMessage(Message);
DispatchMessage(Message);
end;

end.


Автор: volvo 3.11.2010 23:07

Цитата
Теперь у меня новая проблема.
Если переключиться на другое приложение, закрывающее окно2, а потом снова на это, то окно2 пропадает.
Надо ловить wm_Activate и руками что-то писать?
И ещё, надо, чтобы при соваривании окна1 сворачивалось и окно2 (да, я забыл это сказать, думал, что такой эффект сам собой получится).
Вот теперь - "вот тебе и преимущества безVCL-ного кода". Само сделается? Не сделается. Надо вручную прописывать. Ловишь WM_SYSCOMMAND, и если WPARAM = SC_MINIMIZE, то сворачиваешь главное окно и скрываешь (повторяю, скрываешь а не сворачиваешь, если свернешь - оно свернется аккуратно над таскбаром, слева, я не думаю, что это зрелище кого-нибудь обрадует, тебя - в первую очередь) второе. Как только получаешь SC_RESTORE - показываешь второе окно и разворачиваешь первое.

То же самое касается и обработки WM_ACTIVATE... Проверить какая именно активация была, если по какому-то окну щелкнули мышой (WA_CLICKACTIVE), то делать BringWindowToTop(другое_окно), потом BringWindowToTop(то_по_которому_щелкнули). Чтобы окна расположились в правильном порядке. Если ткнули на кнопку в таскбаре (это уже случай WA_ACTIVE, если не произошло WA_CLICKACTIVE перед этим), то поднимать окна в порядке "сначала H2, потом H1", ибо кнопка в таскбаре связана с H1, значит, оно должно оказаться выше. Если хочется особо поизвращаться - заведи булеву переменную IsFirstWindowAbove, и поднимай окна в нужном порядке, при активации к тому же меняя значение этой переменной.

Ну, как тебе безVCL-ный код?

Автор: TarasBer 3.11.2010 23:46

> это уже случай WA_ACTIVE, если не произошло WA_CLICKACTIVE перед этим

Не, так не прокатило - при старте как раз происходит wa_Active без wa_ClickActive и если в обработчике "wa_Active, перед которым не было wa_ClickActive" написать подъём окон, то при старте они дёргаются.

> Ну, как тебе безVCL-ный код?

Документацию рыть много надо, а так разницы мало - у меня всё равно болшая часть кода не относится к интерфейсу.

Добавлено через 1 мин.
Или я не так понял? Оконна функция:



function WP(Handle: HWND; Message: UINT; WP: WParam; LP: LParam): longint; stdcall;
const
WasCA: byte = 0;
begin
if WasCA > 0 then Dec(WasCA);
case Message of
wm_SysCommand: if Handle = H1 then begin
if WP = sc_Minimize then ShowWindow(H2, sw_Hide)
else if WP = sc_Restore then ShowWindow(H2, sw_Show);
end;
wm_Activate: if LoWord(WP) = wa_ClickActive then begin
if Handle = H1 then begin
BringWindowToTop(H2);
BringWindowToTop(H1);
end else begin
BringWindowToTop(H1);
BringWindowToTop(H2);
end;
WasCA := 2;
end else if (Handle = H1) and (LoWord(WP) = wa_Active) and (WasCA = 0) then begin
BringWindowToTop(H2);
BringWindowToTop(H1);
end;
wm_Destroy: if Handle = H1 then begin
PostQuitMessage(0);
Result := 0;
Exit;
end;
end;
Result := DefWindowProc(Handle, Message, WP, LP);
end;



Автор: volvo 4.11.2010 0:33

Это я что-то ступил... Есть же еще WM_ACTIVATEAPP. Вот его и надо ловить, когда переключаемся с другого приложения.

var
WasCA: byte = 0; // Перенес сюда, лень лезть в настройки компилятора

function WP(Handle: HWND; Message: UINT; WP: WParam; LP: LParam): longint; stdcall;
begin
if WasCA > 0 then Dec(WasCA);
case Message of
wm_SysCommand:
if Handle = H1 then begin
if WP = sc_Minimize then ShowWindow(H2, sw_Hide)
else if WP = sc_Restore then ShowWindow(H2, sw_Show);
end;
wm_Activate:
if LoWord(WP) = wa_ClickActive then begin
if Handle = H1 then begin
BringWindowToTop(H2);
BringWindowToTop(H1);
end else begin
BringWindowToTop(H1);
BringWindowToTop(H2);
end;
WasCA := 2;
end
// Внимательнее. Здесь обработка должна быть только когда WasCA > 0, а не = ...
else if (Handle = H1) and (LoWord(WP) = wa_Active) and (WasCA > 0) then begin
BringWindowToTop(H2);
BringWindowToTop(H1);
end;

// Собственно, переключение с другого приложения
WM_ACTIVATEAPP:
if WP <> 0 then // Активируется? Да, при деактивации WP = 0
begin
BringWindowToTop(H2);
BringWindowToTop(H1);
end;

wm_Destroy: if Handle = H1 then begin
PostQuitMessage(0);
Result := 0;
Exit;
end;
end;
Result := DefWindowProc(Handle, Message, WP, LP);
end;
Как-то так.

Автор: TarasBer 4.11.2010 1:18

Обнаружил, что если WM_ACTIVATEAPP написать наоборот, то есть


WM_ACTIVATEAPP:
if WP <> 0 then // Активируется? Да, при деактивации WP = 0
begin
BringWindowToTop(H1);
BringWindowToTop(H2); // сверху ставим второе окно!
end;



То всё равно после переключения сверху оказывается первое окно.
То есть какое-то событие после этого ещё делает окно H1 сверху.
А я, допустим, хочу, чтобы после переключения именно второе окно сверху было.
(Да, я решил поизвращаться, чтобы после активации сверху было то окно, которое было сверху до деактивации).

Автор: volvo 4.11.2010 2:24

Цитата
А я, допустим, хочу, чтобы после переключения именно второе окно сверху было.
Автоматически этого не будет. Потому что кнопка в таскбаре связана с первым окном, у него приоритет, после клика по кнопке активируется именно то окно, которое с этой кнопкой связано. Тебе надо это поведение как-то отменить. Как - пока не придумалось.

Автор: volvo 4.11.2010 4:11

Так. Ну, допустим, в WM_ACTIVATEAPP проверять, если это не самая первая активация (то есть, не та, которая при создании приложения, а та, которая уже при клике на таскбаре, там разрулишь флагами) - то просто посылать

PostMessage(H1, WM_ACTIVATE, WA_CLICKACTIVE, H1); // если надо активным сделать окно №1
// или
PostMessage(H2, WM_ACTIVATE, WA_CLICKACTIVE, H2); // если надо сделать активным второе окно

, и пусть обработчик WM_ACTIVATE делает свою работу...

Автор: TarasBer 4.11.2010 16:35

Заработал такой вариант оконной функции:


function WP(Handle: HWND; Message: UINT; WP: WParam; LP: LParam): longint; stdcall;
const
WasH1: boolean = false;
begin
Result := 0;
case Message of
wm_SysCommand: if Handle = H1 then begin
if WP = sc_Minimize then ShowWindow(H2, sw_Hide)
else if WP = sc_Restore then ShowWindow(H2, sw_Show);
end;
wm_Activate: if LoWord(WP) = wa_ClickActive then WasH1 := Handle = H1;
wm_ActivateApp: if WP <> 0 then begin
if WasH1 then begin
BringWindowToTop(H2); // тут небольшая проблема
BringWindowToTop(H1);
end else PostMessage(H2, wm_Activate, wa_ClickActive, H2);
end;
wm_Destroy: if Handle = H1 then begin
PostQuitMessage(0);
Exit;
end;
end;
Result := DefWindowProc(Handle, Message, WP, LP);
end;



Осталась одна проблема - если оставить активным первое окно, а потом переключиться на другое приложение и обратно, то заметно небольшое передёргивание - ведь сначала второе окно вытягивают наверх, а потом сразу первое. Нельзя ли сразу как-нибудь вывести второе окно именно на 2 место?

Автор: volvo 4.11.2010 16:46

Вот этого никогда не делал, хотя теоретически знаю, что в этом может помочь функция http://msdn.microsoft.com/en-us/library/ms633545%28VS.85%29.aspx (в данном конкретном случае особенно интересен второй параметр hWndInsertAfter, который указывает, после какого окна в Z-последовательности будет установлено hWnd).

К сожалению, я сейчас не под Windows, так что проверить возможности не имею.

Автор: TarasBer 4.11.2010 17:14


BringWindowToTop(H2);
BringWindowToTop(H1);


Заменил на

BringWindowToTop(H1);
SetWindowPos(H2, H1, 0, 0, 0, 0, swp_NoMove or swp_NoSize);


Теперь всё ништяк.