Главная » Delphi » Сворачивание приложения в Tray Ваг при потере фокуса

0

Наметим план действий в этом простейшем случае. Нам надо: при потере фокуса свернуть программу в иконку, при щелчке на иконке вновь распахнуть окно. Сразу заметим, что последнее сделать очень просто — достаточно вызвать метод show формы. А вот первое действие раскладывается как минимум на два: собственно размещение иконки и скрытие окна.

Перед тем как вносить изменения, скопируйте проект SlideShow из папки Glava2 в новую папку, на прилагаемом диске это папка с именем "1" внутри папки Glava3 (с соответствующими исправлениями в файле DSK, как описано во введении). Не забудем изменить номер версии — пусть это будет 1.10 (еще раз напомню, что изменения вносятся в двух местах: в заголовке и в пункте меню Project | Options | Version Info).

Заметки на полях

Далее мы часто будем употребпять термин "дескриптор", что требует комментариев. Термин handle, который имеется в виду, дословно переводится как "приводная ручка". В русскоязычной литературе по программироаанию его переводят как "описетель" [1], "дескриптор" [2,3], иногда "идентификатор". На азгляд автора, подкрепленный авторитетом автора книги [13], пераые два случая только затемняют смысл термина. Основное предназначение "хендпа" — однозначно идентифицировать объект, к которому производится обращение, и тут пучше всего подходит по смыслу слово "идентификатор", "указатель", тем более, что, как и обычный указатель (pointer) в языке Pascal, он есть просто число (индекс в обслуживаемой Windows таблице указателей на физические адреса памяти). Но оба указанных термина уже заняты. В свою очередь и слово "дескриптор" имеет свой отдельный более узкий смысл — как некая структура данных, действительно "описатепь", например, бпока в памяти (segment handle) ипи файла (file handle), ипи физического устройства (device handle), в этом смысле наш handle есть указатель на подобную структуру. Однако не будем нарушать традиций, просто помните, что употребление этого термина с семантической точки зрения неоднознвчно.

Разберем сначала самое сложное: манипуляции с иконкой. Для этого в Win32 API имеется функция sheii Notifyicon с двумя параметрами: сообщением о том, что собственно нужно делать с иконкой при размещении в Tray, и дескриптором структуры TNotifyiconData (PNotifyiconData в нотации Windows API). Сообщение (как и вообще любое сообщение Windows) имеет тип "двойное слово" (DWord), что вполне можно заменить на integer (Longint) в Object Pascal. В таких случаях надо быть внимательным — несмотря на формальное совпадение типов, Delphi иногда выдает сообщение об ошибке, если вы указали не тот тип при обращении к API. Само сообщение может иметь всего три предопределенных значения: num add, ncjk delete и num_modify, смысл которых понятен без особых пояснений.

Заметки на полях

Отметим тут еще одно преимущество языка Pascal перед С-ориентированными языками: в нем заглавные и строчные буквы в идентификаторах заведомо не различаются, и мы не задумываясь можем писать мнемонические наименования, как нам удобно. В то время, как е С это зависит от воли разработчиков конкретной программы и часто служит источником неудобств: так, все разработчики сайтов знают, что index.html, lndex.html ипи INDEX.HTML есть совершенно разные файпы, при совершенной бессмыслице этого с практической точки зрения. В тексте данной книги я буду придерживаться примерно того же написания, которое предлагается в справке по Win32, просто чтобы не смущать читетеля, но указанную особенность следует иметь в виду.

Структура TNotifyiconData содержит несколько составляющих. Вначале (как обычно для подобных структур) идет размер самой структуры cbsize. Затем последовательно: дескриптор wnd типа hwnd окна, с которым связана иконка, идентификатор самого значка uid типа integer (значков может быть несколько), переменная флагов UFiags, идентификатор определяемого нами сообщения ОТ ИКОНКИ приложению uCallBackMessage (и ТО И другое типа integer), наконец, дескриптор иконки Н1соп(типа hicon—извините за тавтологическое использование идентификаторов, но у разработчиков API подобное встречается сплошь и рядом) и текст всплывающей подсказки szTlp (подробности см. в справке по Win32, а также в [1]). Те переменные, которые участвуют в нашем процессе, следует объявить в качестве глобальных:

var

Ico_Message: i nt едеr=wrn_Useг; {сообщение} noIconData: TNotifyiconData; {дескриптор структуры} Hlconl: hicon; {дескриптор иконки) FHandle: HWnd; {дескриптор окна (формы))

Обратите внимание на то, что мы сразу инициализировали сообщение от иконки приложению значением wm user. В принципе можно и другое значение, но нам важно, чтобы оно не совпало с уже имеющимися в данном приложении значениями, а инициализация его значением 0 (как было бы, если бы мы оставили его вовсе без инициализации) недопустима. Подробнее о пользовательских сообщениях мы еще поговорим.

Для того чтобы вызвать какую-нибудь функцию API, нужно в предложение uses в начале текста программы включить модуль shellApi. После этого у нас все готово для написания процедуры размещения иконки, осталось только решить, к какому событию ее привязать. При потере фокуса формой должно произойти событие onDeactivate, однако на самом деле не все так просто — происходит лишь событие, связанное с приложением в целом, и чтобы происходило именно событие деактивации конкретного окна, нам требуется это явно указать. Удобно сделать это в самом начале программы: создать обработчик события onCreate и включить в него нужный оператор:

procedure TForml.FormCreate{Sender: TObject); begin

Application.onDeactivate := FonnDeactivate; end;

Нам еще нужно разместить в памяти дескриптор окна, чтобы было куда посылать сообщения и получить дескриптор иконки. Первое делается с помощью функции AiiocateHWnd, а второе заключается в том, что мы скопируем иконку приложения через функцию copyicon. Теперь мы спокойно можем писать обработчик события onDeactivate формы:

procedure TForml.FormDeactivate(Sender: TObject); begin

FHandle := AiiocateHWnd(WndProc); (получаем дескриптор окна} Hlconl:=CopyIcon(Application.Icon.Handle);

(получаем дескриптор иконки}

with noIconData do begin

cbSize:=Sizeof(TNotifylconData); (размер структуры} Wnd:=FHandle; {дескриптор окна} uID:=0; (единственная иконка}

UFlags:=NIF_MESSAGE or NIF_ICON or NIF_TIP; (взводим все флаги) SzTip: =1 SlideShow'; (всплывахчцая подсказка} HIcon:=HIconl; {дескриптор иконки} uCallBackMessage:-Ico_Messaga;

{Определяемое пользователем сообщение}

end;

Shell_NotifyIcon(NIM_ADD,SnoIconData); {создали иконку}

Forrol.Hide; (скрыли окно}

end;

Для параметра UFlags мы указали все возможные флаги, т. е. мы будем посылать пользовательское сообщение (nif_message), рисовать иконку (nif icon) и привязывать к ней всплывающую подсказку (nif_tip).

Но это только полдела — если мы теперь попробуем запустить приложение, то при потере фокуса оно свернется в иконку и останется там навсегда. Надо его как-то оттуда извлечь. Для этого нам нужно обработать сообщение ico Message, которое посылается приложению каждый раз, когда в области иконки в Tray происходит что-то, связанное с мышью. Как его поймать? Для этого можно перекрыть или переопределить метод WndProc формы, который служит для вызова оконной процедуры (т. е. главной процедуры окна по обработке сообщений — подробнее см. в главе 5). Для переопределения достаточно объявить свою процедуру по такому шаблону:

type

TForml = olaee(TForm) procedure WndProc(var Message: TMessage);

Включим вручную соответствующую строку в секцию объявления типов в интерфейсной части модуля slide, туда же, где находятся все остальные процедуры. Запись Message типа TMessage основана на определенной в Windows структуре Msg (TMsg в нотации Delphi — подробнее со структурой Msg можно ознакомиться в любой справке по WinAPI, см. также главу 5), которая содержит параметры сообщения, для каждого их типов определенных в Windows сообщений эти параметры имеют свой особенный смысл. Из них нас сейчас будет интересовать lParam (нам еще предстоит очень близко познакомиться и с этим, и некоторыми другими параметрами настоящей структуры). Для данного типа сообщения lParam будет содержать номер, соответствующий идентификатору события, вызвавшего наше сообщение от иконки к окну. Теперь также полностью вручную (или через нажатие клавиш <Ctrl>+<Shift>+<C>) напишем текст самой процедуры в исполняемой части модуля (в любом месте после слова implementation):

procedure TForml.WndProc(var Message: TMessage); {обработка пользовательских сообщений^

j

begin

if Message.Msg = IcoJMassage then begin

if Massage.lParam=WM_LBUTTONUP then

{если была отпущена кнопка)

begin

Formi.Show; {восстанавливаем окно}

Application.BringToFront; (помещаем его поверх всех окон) DeallocateHWnd(FHandle);

(убираем из памяти дескриптор окна) Shell_NotifyIcon(NIM_Delete,@noIconData); (удаляем иконку) Application.ProcessMessages;

(на всякий случай обрабатываем системные сообщения)

end; end; end;

Вот теперь мы закончили — можно запустить приложение (<F9>). Как только оно потеряет фокус, окно просто исчезнет, а в Tray появится иконка со всплывающей подсказкой "SlideShow". При щелчке на этой иконке она пропадет, а окно восстановится поверх всех открытых на данный момент окон. В этом случае восстановление произойдет при отпускании левой кнопки (сообщение wm lbuttonup). Отметим, что всегда, кроме каких-то специальных случаев, при обработке событий мыши принято использовать событие именно отпускания, а не нажатия (в отличие от событий клавиатуры).

Имея такой базовый набор отлаженных действий, мы можем теперь перейти к случаям, чаще встречающимся на практике.

Источник: Ревнч Ю. В.  Нестандартные приемы программирования на Delphi. — СПб.: БХВ-Петербург, 2005. — 560 е.: ил.

По теме:

  • Комментарии