воскресенье, 1 апреля 2012 г.

Возможности окон Windows

Это перевод Window Features. Автор: MSDN.

В этом обзоре рассматриваются особенности и возможности окон Windows - типы, состояния, размеры и положение.

Содержание:

Типы окон

Этот раздел содержит следующие подразделы, описывающие типы окон:

Overlapped окна

Overlapped окно ("перекрывающее окно") - это окно верхнего уровня (top-level window), у которого есть заголовок (title bar), рамка (border) и клиентская область (client area); предполагается, что окно этого типа будет использоваться в качестве главного окна приложения. У него также могут быть системное меню, кнопки максимизации и минимизации и полосы прокрутки. Overlapped окно обычно включает в себя все эти компоненты, когда используется в качестве главного окна приложения.

Приложение может создать overlapped окно, указывая стиль WS_OVERLAPPED или WS_OVERLAPPEDWINDOW в функции CreateWindowEx. Если вы использовали стиль WS_OVERLAPPED, то окно будет также иметь заголовок и рамку. Если вы использовали стиль WS_OVERLAPPEDWINDOW, то окно будет иметь заголовок, изменяющую размеры рамку, системное меню и кнопки минимизации и максимизации.
Примечания переводчика: флаг WS_OVERLAPPED равен нулю, поэтому если вы не укажете иной стиль, то окно по умолчанию будет иметь стиль WS_OVERLAPPED. Стиль WS_OVERLAPPEDWINDOW - это не самостоятельный стиль, он является простой комбинацией стилей WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX и WS_MAXIMIZEBOX.

Delphi создаёт overlapped-окна при BorderStyle = bsSingle, bsToolWindow, bsSizeable или bsSizeToolWin. Значение bsSizeable будет эквивалентно стилю WS_OVERLAPPEDWINDOW, если вы не отключали кнопки максимизации/минимизации.

Pop-up окна

Pop-up окно ("всплывающее окно") - это разновидность overlapped окна, используемая для диалоговых окон, окон-сообщений и других аналогичных временных окон, которые появляются вне главного окна приложения. Для них заголовок не обязателен; в противном случае pop-up окна не отличаются от overlapped окон со стилем WS_OVERLAPPED.

Вы можете создать pop-up окно, указывая стиль WS_POPUP в функции CreateWindowEx. Чтобы окно имело заголовок, вам нужно отдельно указать стиль WS_CAPTION. Для создания pop-up окна с рамкой и системным меню используйте стиль WS_POPUPWINDOW. Чтобы системное меню можно было увидеть, вам также нужно включить стиль WS_CAPTION вместе со стилем WS_POPUPWINDOW.

Примечания переводчика: стиль WS_POPUPWINDOW - это не самостоятельный стиль, он является простой комбинацией стилей WS_POPUP, WS_BORDER и WS_SYSMENU.

Delphi создаёт pop-up-окна при BorderStyle = bsNone или bsDialog. Стиль WS_POPUPWINDOW не используется Delphi, поскольку она никогда не указывает стиль WS_BORDER при установленном стиле WS_POPUP (вместо этого используется WS_THICKFRAME).

Child окна

Child окно ("дочернее окно") - имеет установленный стиль WS_CHILD и ограничивается клиентской областью его родительского окна (parent window). Как правило, child окна используются приложением, чтобы разделить клиентскую область окна по функциональным зонам. Вы можете создать child окно, указывая стиль WS_CHILD в функции CreateWindowEx.

У child окна обязательно должно быть родительское окно. Родительское окно может быть overlapped окном, pop-up окном или даже другим child окном. Вы указываете его во время вызова функции CreateWindowEx в параметре hWndParent. Если же вы укажете стиль WS_CHILD в CreateWindowEx, но не укажете родительское окно, то система откажется создавать окно.

Child окно имеет клиентскую область, но больше никаких других дополнительных возможностей, если только вы их явно не запросите. Вы можете добавить заголовок, системное меню, кнопки минимизации и максимизации, рамку и полосы прокрутки, но child окно не может иметь меню. Если вы укажете меню при создании child окна - оно будет проигнорировано. Если вы не укажете тип рамки, то система создаст окно вообще без рамки (borderless window). Приложение может использовать такие окна для разделения клиентской области родительского окна на части без видимого указания разделения пользователю.

Примечания переводчика: форма Delphi является pop-up или overlapped окном в 99% случаев. Вы можете положить форму на другую форму (отношения child-parent), но это используется крайне редко. Примером child окон могут служить фреймы (TFrame), панели (TPanel) и вообще практически любые другие оконные компоненты - например, кнопка (TButton). Тут нужно помнить, что "окно" в смысле пользователя - это "форма" в Delphi. Но термин "окно" в смысле системы - это вовсе не обязательно "окно" в смысле пользователя, это вообще некоторый элемент управления. Все элементы управления в Windows являются окнами в смысле системы. Кнопки, списки, строки ввода, полосы прокрутки, панели и тулбары - всё это "окна".

Кроме того, в этом разделе:

Позиционирование

Система всегда размещает child окно, используя относительные координаты - относительно левого-верхнего угла клиентской области родительского окна. Child окно не может выступать за границы своего родительского окна. Если приложение создаёт child окно больше размеров своего родительского окна, либо же размещает его так, что какая-то его часть выходит за границы родителя, то система обрезает child окно так, что часть child окна вне его родительского окна будет просто невидима.

Кроме того, действия с родительским окном также будут иметь эффект на все его child окна, как указано в следующей таблице:

Parent окноChild окно
УничтожаетсяУничтожается непосредственно перед удалением своего родительского окна.
СкрываетсяСкрывается непосредственно перед скрытием своего родительского окна. Всякое child окно видимо только если его родительское окно тоже видимо.
ПеремещаетсяПеремещается вместе с клиентской областью своего родителя так, что его относительное положение сохраняется. Child окно ответственно за перерисовку своей клиентской области после перемещения.
ПоказываетсяПоказывается сразу после показа своего родителя.


Обрезание

По умолчанию система не обрезает child окно, если оно размещено вне клиентской области своего родителя (если клиентская область родителя меньше размеров самого родительского окна, поскольку система всегда обрезает child окно, вылезающее за размеры своего родителя, как указано выше). Это означает, что родительское окно будет рисовать поверх child окна, если оно производит любую операцию рисования в той же области, что и child окно. Однако систему можно попросить обрезать child окно по клиентской области своего родителя, указав стиль WS_CLIPCHILDREN родительскому окну (не дочернему). Если child окно обрезается, то родительское окно не будет рисовать поверх его.

Child окно может пересекать другие child окна в той же клиентской области. Child окна, которые разделяют одного родителя, называются sibling окнами ("братские окна"). Пересекающиеся sibling окна могут рисовать в клиентской области друг друга, если только одно из child окон не имеет стиль WS_CLIPSIBLINGS. Если child окно указывает этот стиль, то любая часть любого другого sibling окна, лежащая в рамках этого child окна, будет обрезаться.

Установка стилей WS_CLIPCHILDREN или WS_CLIPSIBLINGS приводит к небольшому падению производительности. Каждое окно занимает системные ресурсы, так что приложение не должно использовать дочерние окна без разбора. Для достижения оптимальной производительности приложения, которые должны логически разделять своё главное окно, должны делать это в оконной процедуре главного окна, а не с помощью дочерних окон.

Отношение с родительским окном

Приложение может изменить родительское окно уже созданного child окна вызовом функции SetParent. В этом случае система удаляет child окно из клиентской области старого родительского окна и размещает его в клиентской области нового родительского окна. Если в SetParent указать 0 в качестве родителя, то новым родительским окном станет рабочий стол (desktop window). В этом случае child окно рисуется на рабочем столе, вне границ любого другого окна. Функция GetParent возвращает описатель родительского окна child окна.

Родительское окно уступает часть своей клиентской области child окну, и child окно получает весь ввод (input) с этой области. Родительское и child окно не обязаны иметь один и тот же оконный класс (window class). Это означает, что приложение может заполнить родительское окна child окнами, которые выглядят по-разному и выполняют разные задачи. Например, диалоговое окно может содержать несколько типов элементов управления, каждый из которых является child окном, которое принимает различные типы данных от пользователя.

Child окно имеет одно и только одно родительское окно, но родительское окно может иметь сколько угодно child окон. Каждое child окно, в свою очередь, тоже может иметь сколько угодно child окон. В такой цепочке окон каждое child окно называется descendant окном (окном-потомком) исходного родительского окна. Приложение может использовать функцию IsChild, чтобы определить, является ли заданное окно child окном или descendant окном другого окна.

Функция EnumChildWindows перечисляет все дочерние окна родительского окна. Функция EnumChildWindows передаёт описатель каждого найденного child окна в функцию обратного вызова приложения. Функция работает рекурсивно, поэтому она также перечисляет все descendant окна заданного родительского окна.

Сообщения

Система передаёт все сообщения ввода для child окна самому child окну напрямую, минуя окно родителя. Исключением для этого правила является случай, если дочернее окно было отключено вызовом функции EnableWindow. В этом случае система передаёт сообщения, предназначенные child окну, его родителю. Это позволяет родительскому окну проверить сообщения ввода и при необходимости включить child окно.

Child окно также может иметь уникальный числовой идентификатор. Идентификаторы child окон важны, если вы работаете с сообщениями. Приложение управляет элементами управления, отправляя им сообщения. Приложение может использовать идентификатор child окна для отправки в него сообщений. Кроме того, если элемент управления шлёт сообщения-уведомления своему родителю, то эти сообщения будут включать в себя идентификатор child-окна, что позволит родителю идентифицировать отправителя сообщения. Приложение указывает идентификатор child окна установкой параметра hMenu функции CreateWindowEx в число - значение идентификатора (а не описатель меню).

Layered окна

Использование layered окна ("окна со слоями") может здорово улучшить производительность и визуальные эффекты для окон, которые имеют сложную форму, анимацию или применяют альфа-каналы. Система автоматически производит композицию и перерисовку layered окон и окон под ним. В результате layered окна рисуются гладко, без эффектов мерцания на сложных регионах. Кроме того, layered окна могут быть сделаны частично прозрачными.

Чтобы создать layered окно, вам нужно указать флаг WS_EX_LAYERED расширенного стиля окна при вызове функции CreateWindowEx или вызвать функцию SetWindowLong уже после создания окна. После вызова CreateWindowEx layered окно не будет видимо до тех пор, пока вы не вызовите для него функцию SetLayeredWindowAttributes или функцию UpdateLayeredWindow.

Примечание: начиная с Windows 8 WS_EX_LAYERED может использоваться как для окон верхнего уровня, так и для дочерних окон (child окон). Предыдущие версии Windows поддерживают стиль WS_EX_LAYERED только для окон верхнего уровня.

Чтобы установить степень непрозрачности (opacity level) или цвет-маску (transparency color key) для заданного layered окна - вызовите функцию SetLayeredWindowAttributes. После этого вызова система всё ещё может запросить окно нарисовать себя при показе или перемещении. Однако, поскольку система запоминает растровое содержимое layered окна, то она не будет просить окно перерисовать себя при частичном перекрытии окна другими окнами или при его перемещении по рабочему столу. При этом устаревшим приложениям не нужно переделывать их код рисования, если они хотят добавить прозрачность или эффекты - потому что система перенаправляет рисование на окнах, которые вызвали SetLayeredWindowAttributes, на внеэкранный растр.

Чтобы реализовать более эффективную анимацию или если вам нужно попиксельное альфа-смешение цветов - вы можете вызвать UpdateLayeredWindow. UpdateLayeredWindow следует использовать, когда приложение хочет работать напрямую с формой и содержимым layered окна, минуя внеэкранный буфер, который система предоставляет через вызов SetLayeredWindowAttributes. Кроме того, использование UpdateLayeredWindow более эффективно, потому что системе не нужно выделять дополнительную память для хранения изображения перенаправленного окна. Обратите внимание, что если вы вызвали SetLayeredWindowAttributes, то все последующие вызовы UpdateLayeredWindow завершатся с ошибкой - до тех пор, пока вы не переустановите (сбросите и заново установите) флаг WS_EX_LAYERED.

Тестирование на попадание мышью (hit testing) для layered окна основывается на форме и прозрачности окна. Это означает, что зоны окна, которые раскрашены цветовым ключом или же их альфа-канал равен нулю, пропустят щелчки мыши мимо себя. Однако если в окне дополнительно установлен флаг WS_EX_TRANSPARENT, то всё окно целиком будет пропускать щелчки мыши сквозь себя.

Message-Only окна

Message-only окно ("окно для сообщений") позволяет вам принимать и отправлять оконные сообщения. Оно не видимо, не имеет Z-порядка, не появляется в перечислениях (enum) и не принимает широковещательные сообщения. Это окно просто диспетчеризирует сообщения.

Чтобы создать message-only окно, укажите константу HWND_MESSAGE или описатель на другое message-only окно в параметре hWndParent функции CreateWindowEx. Вы также можете сконвертировать любое существующее окно в message-only окно, указав HWND_MESSAGE в качестве параметра hWndNewParent функции SetParent.

Чтобы найти message-only окна - укажите HWND_MESSAGE в параметре hwndParent функции FindWindowEx. Кроме того, функция FindWindowEx будет искать message-only окна наравне с окнами верхнего уровня, если оба параметра hwndParent и hwndChildAfter будут равны нулю.

Отношения между окнами

Существует много способов, которыми одно окно может относится к пользователю или другому окну. Окно может быть владеемым, окном переднего или заднего плана, а также у него может быть задан Z-порядок по отношению к другим окнам:

Окна первого и заднего плана

Каждый процесс может иметь несколько потоков для выполнения, и каждый поток может создавать окна. Поток, который создал окно, с которым сейчас работает пользователь, называется потоком первого плана (foreground thread), а окно называется foreground окном (окном первого плана). Все прочие потоки называются потоками заднего плана (background thread), а все прочие окна - окнами заднего плана (background окнами).

Каждый поток имеет уровень приоритета, который определяет количество времени процессора, получаемое этим потоком. Хотя приложение может менять уровень приоритета своих потоков, обычно поток первого плана получает небольшую прибавку приоритета по отношению к потокам заднего плана. Поэтому поток первого плана будет получать больше процессорного времени, чем потоки заднего плана. Обычно поток первого плана имеет приоритет 9, в то время как обычное значение приоритета потока заднего плана - 7.

Пользователь задаёт окно переднего плана, щёлкая по окну, либо используя комбинации клавиш ALT+TAB или ALT+ESC. Чтобы получить описатель окна переднего плана - используйте функцию GetForegroundWindow.

Приложение может установить окно переднего плана, используя функцию SetForegroundWindow.

Система ограничивает процессы, которым разрешено менять окно переднего плана. Процесс сможет сделать это только если верны условия ниже:
  • процесс является процессом первого плана (т.е. окно первого плана принадлежит ему).
  • процесс был запущен процессом переднего плана.
  • процесс получил последнее сообщение ввода.
  • в системе нет процесса переднего плана.
  • процесс первого плана сейчас отлаживается.
  • смена окна первого плана не заблокирована (см. LockSetForegroundWindow).
  • истёк таймаут блокировки окна (см. SPI_GETFOREGROUNDLOCKTIMEOUT в SystemParametersInfo).
  • нет активных меню.
Процесс, которому разрешено менять окно первого плана, может передать это право другому процессу вызовом функции AllowSetForegroundWindow или функции BroadcastSystemMessage с флагом BSF_ALLOWSFW. Процесс первого плана также может отключить вызовы SetForegroundWindow, вызвав LockSetForegroundWindow function.

Owned окна

Overlapped или pop-up окно может владеть другим overlapped или pop-up окном. Отношение "владелец - owned окно" накладывают ограничения на последнее:
  • Owned окно всегда находится поверх своего владельца в Z-порядке.
  • Система автоматически уничтожает owned окна при уничтожении их владельца.
  • Owned окно скрывается при минимизации своего владельца.
Владельцем может быть только overlapped или pop-up окно; child окна владельцами быть не могут. Приложение может создать owned окно указанием описателя окна-владельца в параметре hwndParent функции CreateWindowEx, когда она создаёт окно со стилями WS_OVERLAPPED или WS_POPUP. Параметр hwndParent должен идентифицировать overlapped или pop-up окно. После создания owned окна приложение не может передать отношение владения другому окну.

Диалоговые окна и окна-сообщения (message box) являются owned окнами. Приложение указывает окно-владельца при вызове функции, создающей диалог или окно-сообщение.

Приложение может использовать функцию GetWindow с флагом GW_OWNER, чтобы получить описатель окна-владельца.

Z-порядок

Z-order (Z-порядок) окна указывает положение окна в стопке overlapped окон. Эта стопка окон ориентируется вдоль воображаемой оси (оси Z), идущей от экрана. Окно на вершине Z оси (Z-порядка) перекрывает все прочие окна. Окно на дне Z-порядка может быть перекрыто любым другим окном.

Система хранит Z-порядок в едином списке. Она добавляет окна в список в зависимости от их типа: topmost окна, top-level окна и child окна. Окно "поверх всех" (topmost окно) перекрывает все прочие окна (не topmost окна), вне зависимости от того, является ли topmost окно активным. У topmost окон устанавливается стиль WS_EX_TOPMOST. Все topmost окна всегда располагаются в списке Z-порядка перед любыми другими не topmost окнами. Child окна группируются вместе с их родителями.

Когда приложение создаёт окно, система помещает окно в список Z-order - на вершину цепочки окон того же типа. Вы можете использовать функцию BringWindowToTop, чтобы переместить окно на вершину списка Z-order (но только в рамках окон того же типа). Вы можете переупорядочивать окна, используя функции SetWindowPos или DeferWindowPos.

Пользователь может изменить Z-порядок активируя различные окна. Система перемещает активное окно на вершину Z-порядка для окон того же типа. Child окна перемещаются на вершину вместе со своим родителем. Вы можете использовать функцию GetTopWindow для поиска всех child окон родительского окна и получения описателя child окна на вершине Z-порядка. Функция GetNextWindow возвращает описатель следующего или предыдущего окна в Z-порядке.

Состояния отображения окон

В любой момент времени произвольное окно может быть активным или не активным; скрытым или видимым; и либо минимизированным, либо максимизированным, либо развёрнутым. Все эти характеристики называются window show state (состояния отображения окон). Они обсуждаются ниже:

Активное окно

Активное окно (active window) - это окно верхнего уровня (top-level) приложения, с которым сейчас работает пользователь. Чтобы пользователь мог опознать активное окно, система размещает активное окно на вершине Z-порядка, а также изменяет вид заголовка (выделяя его) и рамки окна. Активным окном может быть только окно верхнего уровня. Если пользователь работает с child окном, то система активирует окно верхнего уровня, ассоциированное с текущим child окном.

В любой момент времени в системе может быть только одно активное окно. Пользователь может активировать окно (верхнего уровня) просто щёлкая по нему (или по одному из его child окон), либо используя комбинации клавиш для переключения окон (например, ALT+ESC или ALT+TAB). Приложение может сделать окно (верхнего уровня) активным, вызывая функцию SetActiveWindow. Активация окна также может происходить при использовании других функций, включая SetWindowPos, DeferWindowPos, SetWindowPlacement и DestroyWindow. Хотя приложение может менять активное окно в любое время, но чтобы не смущать пользователя, лучше всего это делать в ответ на действия пользователя. Приложение может узнать текущее активное окно вызовом функции GetActiveWindow.

Когда активное окно изменяется с окна одного приложения на окно другого приложения, система посылает сообщение WM_ACTIVATEAPP обоим приложениям, уведомляя их об изменении активного окна. Если же активное окно изменяется с одного окна приложения на другое окно этого же приложения, то система посылает сообщение WM_ACTIVATE обоим окнам этого приложения.

Отключенные окна

Окно может быть отключено. Отключенное окно (disabled window) не может получать ввод пользователя (сообщения клавиатуры и мыши), но может получать сообщения от других окон, приложений и системы. Обычно приложение отключает окно для предотвращения взаимодействия с ним пользователя. К примеру, приложение может отключить кнопку в диалоге, чтобы не дать пользователю на неё нажать. Приложение может включить (enable) отключенное окно в любое время; включение окна восстановит возможность ввода.

По умолчанию окно создаётся в доступном состоянии. Но приложение может указать стиль WS_DISABLED для создания окна в отключенном состоянии. Приложение может включить или отключить окно вызовом функции EnableWindow. Непосредственно перед изменением состояния окна система отправляет ему сообщение WM_ENABLE. Приложение может узнать статус окна вызовом функции IsWindowEnabled.

Когда child окно отключается, система передаёт сообщения мыши от child окна его родителю. Родительское окно может использовать эти сообщения для определения того, не нужно ли включить child окно. Подробнее - см. Mouse Input.

В каждый момент времени только одно окно в системе может принимать клавиатурный ввод; говорят, что такое окно имеет фокус (keyboard focus). Если приложение использует функцию EnableWindow для отключения окна, которое имеет фокус ввода, то, кроме собственно отключения, окно также потеряет фокус ввода. Функция EnableWindow установит клавиатурный фокус в 0, что означает, что в системе нет окна с фокусом ввода. Аналогично, если фокус имеет child окно или descendant окно, а родитель отключается, то descendant окно теряет фокус ввода. Подробнее - см. Keyboard Input.

Видимость окон

Окно может быть либо видимым, либо невидимым (скрытым). Система показывает на экране только видимые окна (visible window). Все скрытые окна (hidden window) не отображаются (не рисуются). Если окно видимо, то пользователь может взаимодействовать с окном (ввод и чтение содержимого). Если окно скрыто, то оно отключается. Скрытое окно может получать сообщения от других окон, приложений или системы, но оно не может получать ввод от пользователя или отображать себя на экране (рисовать). Приложение указывает видимость окон на экране при их создании. Приложение также может менять видимость окон после создания.

Окно видимо, если у него установлен стиль WS_VISIBLE. По умолчанию функция CreateWindowEx создаёт скрытое окно, если только приложение явно не укажет стиль WS_VISIBLE. Обычно приложение создаёт окна скрытыми, а затем показывает их - после настройки окна, чтобы пользователь не видел процесс создания окна. Если при вызове функции CreateWindowEx указан стиль WS_VISIBLE, то система отправляет окну сообщение WM_SHOWWINDOW после его создания, но до отображения на экране.

Приложение может определить, видимо ли окно, используя функцию IsWindowVisible. Приложение может изменить видимость окон вызовом функций ShowWindow, SetWindowPos, DeferWindowPos, SetWindowPlacement или SetWindowLong. Любая из этих функций может скрыть или показать окно - удалением или добавлением стиля WS_VISIBLE. Они также отправят окну сообщение WM_SHOWWINDOW непосредственно перед его показом или скрытием.

Когда окно-владелец (owner) минимизируется, система автоматически скрывает ассоциированные с ним owned окна. Аналогично, когда owner окно восстанавливается, система автоматически показывает ассоциированные owned окна. В обоих случаях система посылает owned окнам сообщения WM_SHOWWINDOW. Иногда приложению может понадобится скрыть owned окна не минимизируя владельца. В этом случае приложение может использовать функцию ShowOwnedPopups. Эта функция устанавливает или удаляет стиль WS_VISIBLE для всех owned окон, а также рассылает им сообщения WM_SHOWWINDOW. Учтите, что скрытие окна-владельца не влияет на видимость ассоциированных с ним owned окон.

Когда родительское окно видимо, то его child окна также видимы. Если родительское окно скрывается, то дочерние окна также скрываются. Минимизация родительского окна не влияет на видимость дочерних окон; т.е. child окна исчезают с экрана вместе с родителем, но стиль WS_VISIBLE для них не удаляется.

Даже если у окна есть стиль WS_VISIBLE, пользователь может не видеть окна на экране; окно может быть перекрыто другими окнами или быть расположено за границами экрана. Кроме того, видимое child окно может обрезаться, как того диктуют отношения с родительским окном. И если родительское окно не видимо, то child окно также не будет видимо. Если родительское окно будет расположено за границами экрана, то его child окна также будут расположены за границами экрана, потому что child окно перемещается вместе с родительским окном, ограничен им и располагается в относительных координатах.

Минимизированные, максимизированные и развёрнутые окна

Максимизированное окно (maximized window) - это окно, у которого установлен стиль WS_MAXIMIZE. По умолчанию система увеличивает максимизированное окно так, чтобы оно заполняло экран или, в случае child окна, клиентскую область родительского окна. Хотя вы также можете установить размеры и положение окна так, чтобы они совпали с положением максимизированного окна, эти состояния отличаются. Система отключает возможность перемещения окна и изменения его размера для максимизированных окон. Кроме того, кнопка максимизации изменяется на кнопку восстановления размера.

Минимизированное окно (minimized window) - это окно, у которого установлен стиль WS_MINIMIZE. По умолчанию система уменьшает окно до размеров его кнопки на панели задач и перемещает окно на панель задач.

Развёрнутое окно (restored window) - это окно, которое было восстановлено в исходные размер и положение после минимизации или максимизации.

Если приложение укажет стили WS_MAXIMIZE или WS_MINIMIZE в функции CreateWindowEx, окно создастся изначально максимизированным или минимизированным. После создания окна приложение может использовать функцию CloseWindow для минимизации окна. Функция ArrangeIconicWindows упорядочивает свёрнутые окна на рабочем столе или в родительском окне. Функция OpenIcon восстанавливает минимизированное окно.

Дополнительно: Почему все свёрнутые окна имеют размер 160x31?.

Функция ShowWindow может минимизировать, максимизировать и восстанавливать окна. Она также может менять видимость окон и задавать активное окно. Функция SetWindowPlacement содержит ту же функциональность, что и ShowWindow, но она также может менять размещение окна по умолчанию в положениях минимизации, максимизации и восстановления.

Функции IsZoomed и IsIconic определяют, является ли заданное окно максимизированным или минимизированным соответственно. Функция GetWindowPlacement может получить положения минимизации, максимизации и восстановления для окна, а также определить состояние отображения окна.

Когда система получает команду на максимизацию или восстановление минимизированного окна, она отправляет окну сообщениеWM_QUERYOPEN. Если оконная процедура вернёт 0, то система проигнориует команду максимизации или восстановления.

Система автоматически устанавливает размеры и положение максимизированного окна. Чтобы изменить системные умолчания, приложение может либо использовать функцию SetWindowPlacement, либо обрабатывать сообщение WM_GETMINMAXINFO, которое отправляется окну непосредственно перед максимизацией. Сообщение WM_GETMINMAXINFO содержит указатель на запись MINMAXINFO, содержащую значения размера и положения окна, присвоенные системой. Приложение может изменить эти значения.

Размер и положение окна

Размер и положение окна выражаются как ограничивающий прямоугольник (bounding rectangle), задавая его координаты относительно экрана (для окон верхнего уровня) или родительского окна (для child окон). Приложение указывает размер и положение окна при его создании, но может изменить их в любое время. Подробнее - см. Filled Shapes.

В этой секции также рассматриваются:

Размеры и положение по умолчанию

Приложение может разрешить системе задать начальные положение и размер окна верхнего уровня, указав CW_USEDEFAULT в функции CreateWindowEx. Если приложение установит координаты окна в значение CW_USEDEFAULT, то если приложение не создало других окон верхнего уровня, то система будет размещать окно относительно экрана; в противном случае система будет размещать окно относительно последнего созданного окна верхнего уровня. Если приложение также укажет CW_USEDEFAULT для ширины и высоты окна, то размеры окна будут определяться системой. Если приложение до этого создавало окна верхнего уровня, то система будет определять размер окна по последнему созданному окну верхнего уровня. Указание CW_USEDEFAULT для child или pop-up окна создаст окно с минимальным размером.

Отслеживание размера

Система хранит максимальный и минимальный размеры окон со стилем WS_THICKFRAME; окно с этим стилем имеет рамку с возможностью изменения размера. Минимальный размер (minimum tracking size) - это наименьший размер окна, который пользователь сможет задать, перетаскивая рамку окна. Аналогично максимальный размер (maximum tracking size) - это наибольший размер окна, который сможет получить пользователь при изменении размеров окна.

Минимальные и максимальные размеры окна устанавливаются в системные значения по умолчанию при создании окна. Приложение может узнать эти значения и заместить их на свои при обработке сообщения WM_GETMINMAXINFO. Подробнее - см. ниже.

Системные команды

Приложение, которое имеет оконное меню ("системное меню"), может изменить размеры окна отправкой системный команд. Системные команды отправляются, когда пользователь выбирает команды из оконного меню. Приложение может эмулировать действия пользователя отправкой сообщения WM_SYSCOMMAND окну. Следующие системные команды влияют на размер и положение окна:

КомандаОписание
SC_CLOSEЗакрывает окно. Эта команда отправляет сообщение WM_CLOSE окну. Дальнейшие действия зависят от окна (обработчика сообщения WM_CLOSE).
SC_MAXIMIZEМаксимизирует окно.
SC_MINIMIZEМинимизирует окно.
SC_MOVEПеремещает окно.
SC_RESTOREВосстанавливает минимизированное или максимизированное окно на его исходные положение и размер.
SC_SIZEНачинает процедуру изменения размера. Для изменения размера окна можно использовать мышь или стрелки на клавиатуре.


Функции для размера и положения

После создания окна приложение может установить размер или положение окна вызовом одной из следующих функций: SetWindowPlacement, MoveWindow, SetWindowPos и DeferWindowPos. SetWindowPlacement может установить минимизированный размер, максимизированный размер, обычный размер и положение окна, а также состояние видимости. Функции MoveWindow и SetWindowPos похожи; они обе устанавливают размер или положение одного окна в приложении. Но функция SetWindowPos допускает изменение видимости окна; а MoveWindow - нет. Используя функции BeginDeferWindowPos, DeferWindowPos и EndDeferWindowPos, приложение может одновременно изменить атрибуты нескольких окон сразу - включая положение, размеры, Z-порядок и видимость.

Приложение может получить ограничивающий прямоугольник вызовом функции GetWindowRect. GetWindowRect заполняет запись TRect координатами левого-верхнего и правого-нижнего углов окна. Координаты вычисляются относительно левого-верхнего угла экрана - даже для child окон (позиционирование которых осуществляется относительно родительского окна). Функции ScreenToClient или MapWindowPoints могут быть использованы для перевода экранных координат в относительные координаты для child окон.

Функция GetClientRect возвращает координаты клиентской области окна. GetClientRect заполняет TRect координатами левого-верхнего и правого-нижнего углов клиентской области окна, но координаты считаются относительно самой клиентской области окна. Т.е. левый-верхний угол всегда имеет координаты (0, 0), а координаты правого-нижнего угла - это ширина и высота клиентской области окна.

Функция CascadeWindows раскладывает каскадом (cascade) окна на рабочем столе или child окна в родительском окне. Функция TileWindows раскладывает окна черепицей (tiles).

Сообщения для размера и положения

Окну, чьё положение должно измениться, система отправляет сообщение WM_GETMINMAXINFO. К примеру, это сообщение будет отправлено, если пользователь щёлкнул по командам Переместить или Изменить размер из оконного меню или щёлкает по рамке окна или заголовку; также это сообщение будет отправлено, если приложение вызовет функцию SetWindowPos для перемещения или изменения размера окна. Сообщение WM_GETMINMAXINFO передаёт указатель на запись TMinMaxInfo, которая содержит размер и положение максимизированного окна по умолчанию, равно как и минимальные и максимальные размеры окна. Приложение может изменить эти значения, обрабатывая сообщение WM_GETMINMAXINFO. Чтобы получать сообщения WM_GETMINMAXINFO, окно должно иметь стиль WS_THICKFRAME или WS_CAPTION. Окно со стилем WS_THICKFRAME получит это сообщение во время создания окна, а также при изменениях размера и положения.

Окну, чьи размеры, положение, Z-порядок или видимость должны измениться, система отправит сообщение WM_WINDOWPOSCHANGING. Это сообщение содержит указатель на запись TWindowsPos, которая указывает новые положения, размер, Z-порядок и видимость окна. Изменяя поля записи TWindowsPos, приложение может изменить способ отображения окна.

После того, как изменились размер, положение, Z-порядок или видимость окна, система отправляет окну сообщение WM_WINDOWPOSCHANGED. Это сообщение содержит указатель на запись TWindowPos, которая информирует окно о его новых атрибутах. Изменение этой записи не приведёт к изменению атрибутов окна. Окно, которому нужно обрабатывать сообщения WM_SIZE и WM_MOVE, должно передавать сообщение WM_WINDOWPOSCHANGED в функцию DefWindowProc; иначе система не будет рассылать сообщения WM_SIZE и WM_MOVE.

Когда окно создаётся или изменяет размер, система также отправляет окну сообщение WM_NCCALCSIZE. Это сообщение используется системой для вычисления размера и относительного положения клиентской области окна (относительно левого-верхнего угла самого окна). Как правило, окно просто передаёт это сообщение обработчику по умолчанию; однако это сообщение может быть использовано приложениями, которые хотят изменить размеры клиентской области окна. Для более подробной информации - см. Painting and Drawing.

Анимация окон


Вы можете создавать специальные эффекты при показе или скрытии окон, используя функцию AnimateWindow. При использовании этой функции вы можете указать эффекты roll, slide, collapse, expand или fade.

По умолчанию система использует анимацию roll. При этой анимации окно "раскатывается" от одного края до противоположного. Вы можете указать в флагах направление анимации: горизонтально, вертикально или по диагонали.

С флагом AW_SLIDE система использует анимацию slide. При этом окно "выезжает". Анимация похожа на roll анимацию. roll анимация использует сглаженный край окна, имитируя рулон, а slide использует чёткие края, имитируя плоское окно. Аналогично, вы можете указать в флагах направление анимации: горизонтально, вертикально или по диагонали.

Флаг AW_BLEND задаёт анимацию alpha-blended fade. При этом окно постепенно "проявляется" или исчезает. У этой анимации нет направления.

Вы также можете использовать флаг AW_CENTER для анимации разворота окна из центра окна. Анимация похожа на roll анимацию, но roll анимация показывает окно от границы к границе окна, а center анимация - от центра окна до его границ. У этой анимации нет направления.

Компоновка и отражение

Компоновка окна (window layout) определяет способ размещения в окне или контексте устройства (DC - Device Context) текста и объектов GDI. Некоторые языки (вроде английского, французского, русского и немецкого) требуют компоновку left-to-right (LTR) - "слева направо". Другие языки (арабский, иврит) требуют компоновки right-to-left (RTL) - "справа налево". Компоновка окна применяется к тексту, но она также влияет и на другие элементы окна, включая растры, значки, расположение кнопок, многоуровневых деревьев, то, будет ли увеличиваться или уменьшаться горизонтальная координата при сдвиге вправо и т.п. К примеру, после того, как приложение установит компоновку RTL, начало координатной сетки в окне будет расположено в правом-верхнем углу окна (или контекста устройства) - в то время как обычно точка отсчёта располагается в левом-верхнем угле; а горизонтальная координата будет увеличиваться при движении влево, а не вправо, как это было с LTR. Однако не все объекты подчиняются компоновке окна. К примеру, компоновки диалоговых окон, окон-сообщений (message box) и контекстов устройств, не ассоциированных с окном (метафайлы, принтеры и другие) - обрабатываются отдельно. Некоторые такие особенности также упомянуты ниже.

Оконные функции позволяют вам указать или изменить компоновку окна. Заметьте, что изменение компоновки на RTL (этот процесс также называется зеркалированием или отражением окна - mirroring) не поддерживается окнами, которые установили стиль CS_OWNDC, а также контекстами устройств в режиме GM_ADVANCED.

По умолчанию окно и контексты устройств имеют компоновку left-to-right (LTR). Чтобы изменить компоновку окна на RTL - вызовите функцию CreateWindowEx с флагом WS_EX_LAYOUTRTL. Также, по умолчанию, child окна (т.е. окна, создаваемые со стилем WS_CHILD) будут иметь ту же компоновку, что и их родитель. Чтобы отключить наследование зеркалирования для child окон и задавать компоновку явно - добавьте стиль WS_EX_NOINHERITLAYOUT в вызове CreateWindowEx. Заметьте, что компоновка не наследуется owned окнами (создаваемыми без стиля WS_CHILD) или окнами, чей параметр hWnd в функции CreateWindowEx был установлен в 0. Чтобы отключить наследование зеркалирования в отдельном окне, обработайте сообщение WM_NCCREATE, где используйте функции GetWindowLong и SetWindowLong для замены флага WS_EX_LAYOUTRTL.

Вы также можете изменить умолчание для всего процесса вызовом функции SetProcessDefaultLayout. Все окна, создаваемые после вызова SetProcessDefaultLayout(LAYOUT_RTL), будут создаваться зеркалированными (без указания флага), но уже существующие окна не изменятся. Чтобы отключить зеркалирование по умолчанию - используйте SetProcessDefaultLayout(0).

Заметьте, что функция SetProcessDefaultLayout отразит контексты устройств только в отзеркалированных окнах. Чтобы отразить произвольный DC - вызовите функцию SetLayout. См. также ниже обсуждение отражённых контекстов устройств, не связанных с окнами.

Растры (bitmap) и значки (icon) в отражённом окне также по умолчанию зеркалируются. Однако это не всегда бывает необходимо. К примеру, бизнес-лого или аналоговые часы не должны зеркалироваться. Чтобы отключить зеркалирование растра, вызовите функцию SetLayout с установленным флагом LAYOUT_BITMAPORIENTATIONPRESERVED в dwLayout. Чтобы отключить зеркалирование в DC, вызовите SetLayout(DC, 0).

Чтобы узнать текущую компоновку по умолчанию, вызовите функцию GetProcessDefaultLayout. При успешном вызове pdwDefaultLayout будет содержать LAYOUT_RTL или 0. Чтобы узнать компоновку контекста устройства, вызовите функцию GetLayout. При успешном вызове она вернёт набор флагов, управляющих компоновкой, которые вы можете проверить на LAYOUT_RTL и LAYOUT_BITMAPORIENTATIONPRESERVED.

Изменить компоновку после создания окна можно функцией SetWindowLong. К примеру, это может понадобится при смене языка UI в run-time. Однако при этом вы также должны обновить (перерисовать) содержимое окна.

При отражении окна вы должны думать в терминах "ближе" и "дальше", а не "левее" и "правее". Иначе у вас могут быть проблемы. Частой ошибкой является сценарий конвертирования координат между экранными и клиентскими. К примеру, приложение может использовать такой код для расположения элемента управления в окне:
// КОД НИЖЕ - ОШИБОЧЕН

// Получаем координаты окна в экранных координатах 
GetWindowRect(hControl, rControlRect);  

// Проецируем координаты экрана в клиентские координаты диалога
ScreenToClient(hDialog, rControlRect.left); 
ScreenToClient(hDialog, rControlRect.right);
Этот код вызовет проблемы в отзеркалированных окнах, потому что в них "лево" становится "право" и наоборот. Чтобы избежать этой проблемы, замените вызовы функции ScreenToClient на вызовы MapWindowPoints:
// Корректный код
GetWindowRect(hControl, ControlRect);
MapWindowPoints(0, hDialog, rControlRect, 2);
Этот код будет работать корректно, потому что функция MapWindowPoints умеет работать с прямоугольниками (ScreenToClient и ClientToScreen работают только с точками), поэтому она может учесть зеркалированность окон и поменять местами левые и правые грани прямоугольника при необходимости.

Ещё одна практика, которая может вызвать проблемы в отзеркалированных окнах - позиционирование элементов управления, используя экранные координаты вместо клиентских. К примеру, код ниже использует разницу в экранных коодинатах для размещения элемента управления в диалоговом окне:
// КОД НИЖЕ - ОШИБОЧЕН
var 
  rdDialog: TRect;
  rcControl: TRect;
  hControl: HWND;
begin
  GetWindowRect(hDlg, rcDialog);              // Получение прямоугольника в экранных координатах
  GetWindowRect(hControl, rcControl);
  MoveWindow(hControl,
             rcControl.left - rcDialog.left,  // Получение позиции x в клиентских координатах
             rcControl.top - rcDialog.top,
             nWidth,
             nHeight,
             False);
Этот код будет работать для диалогового окна с компоновкой left-to-right (LTR) и режимом проецирования (mapping mode) элемента управления MM_TEXT, потому что новая позиция по X в клиентских координатах соответствует разнице в левых краях элемента управления и диалога в экранных координатах. Но для отзеркалированного диалога вам нужно использовать MapWindowPoints, как показано ниже:
// Корректный код
var
  rcDialog: TRect;
  rcControl: TRect;
  hControl: HWND;
begin
  ..
  GetWindowRect(hControl, rcControl);

  // MapWindowPoints работает корректно в обоих режимах (прямом и зеркальном)
  MapWindowPoints(0, hDlg, rcControl, 2);

  // Теперь rcControl - в клиентских координатах
  MoveWindow(hControl, rcControl.left, rcControl.top, nWidth, nHeight, False);

Отражение диалоговых окон и окон-сообщений

Диалоговые окна и окна-сообщения (message box) не наследуют компоновку окна, поэтому вам нужно устанавливать компоновку явно. Чтобы отразить окно-сообщени, используйте флаг MB_RTLREADING при вызове функций MessageBox или MessageBoxEx. Чтобы изменить компоновку диалога - укажите стиль WS_EX_LAYOUTRTL в шаблоне диалога. Вкладки свойств (property sheets) являются частным случаем диалогов. Каждая вкладка трактуется как отдельный диалог, поэтому вам нужно включать стиль WS_EX_LAYOUTRTL для каждой вкладки отдельно.

Отражение контекстов устройств, не ассоциированных с окном

Контексты устройств (DC), которые не ассоциированы с окном, не наследуют компоновку, поэтому вам нужно указывать её явно. Для изменения компоновки контекста устройства используйте функцию SetLayout.

Функция SetLayout редко используется с окнами. Как правило, окна получают ассоциированный DC только при обработке сообщения WM_PAINT. Иногда приложение может создавать DC для окна вызовом GetDC. В любом случае, компоновка этих DC устанавливается в соответствии с компоновкой целевого окна.

Вызов функции SetLayout не влияет на значения, возвращаемые функциями GetWindowOrgEx, GetWindowExtEx, GetViewportOrgEx и GetViewportExtEx.

При RTL компоновке функция GetMapMode вернёт MM_ANISOTROPIC вместо MM_TEXT. Вызов SetMapMode с MM_TEXT будет работать корректно; будет изменено только возвращаемое значение GetMapMode. Аналогично, вызов SetLayout(hdc, LAYOUT_RTL) при режиме MM_TEXT приведёт к смене режима на MM_ANISOTROPIC.

Уничтожение окна

В целом, приложение должно уничтожать все окна, которые оно создало. Оно может сделать это вызывая функцию DestroyWindow. Когда окно уничтожается, система скрывает окно (если оно было видимо), а затем удаляет все внутренние данные, ассоциированные с окном. Описатель окна (handle) становится недопустимым и не должен более использоваться приложением.

Обычно приложения удаляет окна вскоре после их создания. К примеру, приложение удаляет диалоговое окно со всеми дочерними окнами как только оно получит достаточно информации от пользователя. В конечном итоге приложение уничтожит и главное окно (непосредственно перед выходом).

Перед уничтожением окна приложение должно удалить любые данные, ассоциированные с окном (если они есть), а также освободить системные ресурсы, выделенные для окна. Если приложение не освободит ресурсы, то они будут освобождены системой при завершении приложения.

Уничтожение окна не влияет на оконный класс этого окна. Приложение может создавать окна данного оконного класса и после удаления окна этого класса, равно как и продолжить использование других окон этого класса. Уничтожение окна также уничтожит все его descendant окна (т.е. все child окна, child окна child окон и так далее). Функция DestroyWindow отправляет сообщение WM_DESTROY сначала самому окну, затем всем его child окнам, а затем, аналогично, и descendant окнам.

Окно с оконным меню также получит сообщение WM_CLOSE, когда пользователь щёлкнет по Закрыть. Приложение может запросить у пользователя подтверждение закрытия окна в обработчике этого сообщения. Если пользователь согласен с закрытием окна, то приложение может вызвать функцию DestroyWindow для уничтожения окна.

Если уничтожаемое окно является активным, то активным окном становится другое окно. Оно же получает фокус ввода. Окно, которое станет активным, определяется по порядку окон в списке ALT+ESC.

Комментариев нет:

Отправить комментарий

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.

Примечание. Отправлять комментарии могут только участники этого блога.