Наставление по Borland ObjectWindows for C++ 2.0

ObjectWindows 2.0 - это прикладная среда Borland С++ для Windows 3.1, Win32S и Windows NT. ObjectWindows позволяет быстро и легко строить полнофункциональные приложения Windows и обеспечивает следующие возможности:

Объекты приложений

ObjectWindows 2.0 инкапсулирует приложения Windows и DLL-модули с помощью классов TApplication и TModule соответственно. Объекты TModule инкапсулируют функции инициализации и закрытия DLL Windows. Объект TModule также содержит параметры hInstance и lpCmdLine, эквивалентные параметрам с тем же именем, передаваемых функции WinMain в приложениях, которые строятся без использования ObjectWindows.

Объект TAplication инкапсулирует инициализацию, управление этапом выполнения и функции закрытия приложения Windows. Объект TApplication содержит также значения параметров hPrevInstance и nCmdShow, которые эквивалентны параметрам с тем же именем, передаваемых функции WinMain в приложениях, которые строятся без использования ObjectWindows. Поскольку TApplication основывается на TModule, этот объект обладает всеми его функциональными возможностями. Кроме того, объект TApplication содержит функции для простой загрузки и использования библиотеки специализированных управляющих элементов Borland (Borland Custom Controls Library) и библиотеку трехмерных управляющих элементов Microsoft (Microsoft 3-D Controls Library).

Явной функции WinMain в приложения ObjectWindows 2.0 не требуется. Вместо нее используется функция OwlMain, которая позволяет вам указывать параметры int argc и char** argv и возвращать int, как и обычная программа Си и С++ с функцией main.

В данном разделе описывается, как использовать объекты TApplication. Если вы не работаете с DLL, самостоятельно создавать объект TModule вам не потребуется.

Чтобы использовать объект TApplication, вы должны включить нужный файл заголовка, создать объект и найти объект. TApplication определяется в файле заголовка owl\applicat.h. Так как TApplication является производным от TModule, owl\applicat.h включает файл owl\module.h.

Создать объект TApplication можно с помощью одного из двух конструкторов. Наиболее общим конструктором является конструктор, воспринимающий строку имени приложения, задавать которую не обязательно. Вторая версия конструктора позволяет задать ряд параметров, соответствующих параметрам, передаваемым обычно функции WinMain.

TApplication содержит обычно несколько функций-элементов и элементов данных, которые может потребоваться вызывать вне объектов приложений. Чтобы вы могли к ним обращаться, класс TWindow имеет функцию-элемент GetApplication, возвращающую указатель на объект приложения. Затем вы можете использовать этот указатель для вызова функций-элементов TApplication и доступа к элементам данных этого класса.

Минимальное приложение ObjectWindows имеет следующий вид: #include <owl\applicat.h> int OwlMain(int argc, char* argv[]) { return TApplication("ObjectWindows!").Run(); }

Оно создает приложение Windows, основное окно которого будет иметь заголовок "ObjectWindows!". Вы можете изменять размеры этого окна или закрыть его. В реальном приложении создается новый производный класс приложения с большими функциональными возможностями.

Инициализация приложений

Для инициализации приложения ObjectWindows требуется построить объект приложения и инициализировать приложение, каждый новый экземпляр и основное окно.

Для запуска приложения объект TApplication вызывает свои функции-элементы InitApplication, InitInstance и InitMainWindows. Вы можете переопределить любые из этих функций и задать собственную инициализацию. Для получения полезного приложения можно также переопределить InitMainWindow. Для переопределения функции TApplication нужно построить собственный производный от TApplication класс.

Конструктор класса приложения должен вызывать конструктор TApplication. Библиотека ObjectWindows обеспечивает собственные средства для обработки ошибок и исключительных ситуаций (в функции WinMain). Любую инициализацию можно выполнять в функции OwlMain, которая вызывается заданной по умолчанию функцией WinMain. Для построения объекта приложения создайте в функции OwlMain экземпляр класса приложения.

Хотя вы можете вызывать собственную функцию WinMain, поместив ее в исходный файл, делать этого не нужно. Все что требуется, можно сделать в вызываемой ей функции OwlMain или в функциях-элементах инициализации TApplication.

Если вы решили создать свою собственную функцию WinMain, TApplication поддерживает во втором своем конструкторе передачу обычных параметров функции WinMain.

Пользователи одновременно могут выполнять несколько копий приложения. С точки зрения 16-разрядных приложений инициализация первого экземпляра выполняется только когда не работает вторая копия приложения. Инициализация экземпляра происходит каждый раз, когда пользователь запускает приложение. Если пользователь запускает и закрывает приложение, запускает его снова и т.д., то каждый экземпляр будет первым экземпляром, так как они работают не одновременно.

В случае 32-разрядных приложений каждое приложение работает в своем собственном адресном пространстве без разделяемых данных, поэтому каждый экземпляр выглядит как первый экземпляр. Таким образом, при инициализации 32-разрядного приложения оно выполняет и инициализацию первого экземпляра, и инициализацию каждого экземпляра.

Если текущий экземпляр является первым экземпляром (на что указывает hPrevInstance = 0), то вызывается InitApplication. В производном классе приложения вы можете переопределить InitApplication (по умолчанию InitApplication не несет никакой функциональной нагрузки).

Пользователи могут одновременно выполнять несколько копий (экземпляров) приложения. Для выполнения любой необходимой инициализации можно переопределить TApplication::InitInstance. InitInstance вызывает InitMainWindow, а затем создает и выводит на экран основное окно. Если вы переопределяете InitInstance, убедитесь, что новая функция InitInstance вызывает TAppliation::InitInstance.

Инициализация основного окна

По умолчанию TApplication::InitMainWindow создает окно-рамку с тем же именем, что и объект приложения. Это оно не особенно полезно, так как не может воспринимать и обрабатывать ввод от пользователя. Вы должны переопределить InitMainWindow и создать новый объект окна, обрабатывающий ввод от пользователя. Обычно InitMainWindow создает TFrameWindow или производный от него объект и вызывает функцию SetMainWindow, которая воспринимает один параметр (TFrameWindows*) и возвращает указатель на старое основное окно (в случае нового приложения возвращается 0).

С помощью элемента данных nCmdShow, соответствующего параметру с таким же именем функции WinMain, вы можете изменить характер вывода на экран основного окна приложения. Эту переменную можно установить, как только начинает выполнение функция Run, или пока вы не вызовите TApplication::InitInstance. По существу это означает, что вы можете установить значение nCmdShow либо в InitApplication, либо в функции InitMainWindow.

Для изменения основного окна при выполнении приложения вы можете использовать функцию SetMainWindow, которая имеет один параметр, TFrameWindow*, и возвращает указатель на старое основное окно (или 0). Этот указатель можно использовать для сохранения старого основного окна (и его последующего восстановления), либо для удаления объекта окна.

Обработка сообщений приложения

После инициализации приложения начинает выполняться цикл опроса сообщений объекта - MessageLoop. MessageLoop отвечает за обработку поступающих от Windows сообщений. Изменить обработку сообщений в ObjectWindows можно двумя способами: задать дополнительную обработку сообщений, переопределив функции обработки, и выполняя обработку во время простоя.

TApplication имеет функции-элементы MessageLoop, IdleAction, PreProcessMenu и ProcessAppMsg, обеспечивающие для любых приложений ObjectWindows функциональные возможности обработки сообщений.

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

Параметр функции IdleAction idleCount задает, сколько раз между сообщениями должна вызываться IdleAction. idleCount можно использовать для выбора между низкоприоритетной и высокоприоритетной обработкой во время простоя. Если idleCount достигает высокого значения, то ввода от пользователя не было уже длительный период, и надежнее выполнить низкоприоритетную обработку.

В дополнение к своей собственной обработке всегда следует вызывать функцию IdleAction базового класса. Если вы пишете приложения для Windows NT, то можете для фоновой обработки использовать несколько нитей.

Закрытие приложений

Пользователи обычно закрывают приложения с помощью команды File Exit или Alt+F4. Однако важно, чтобы приложения могли перехватывать такие попытки и давать пользователю возможность сохранить открытые файлы. TApplication позволяет вам это сделать.

TApplication и все оконные классы могут наследовать функцию-элемент CanClose. При попытке завершения приложения оно вызывает функцию CanClose основного окна и функции CanClose администратора документа. В случае наличия порожденных объектов вызывается функция CanClose каждого такого объекта. CanClose дает каждому объекту возможность подготовиться к завершению, а также при необходимости отменить завершение. Когда объект завершает очистку, его функция CanClose должна возвращать True. Если возвращается False, процедура завершения прерывается.

Механизм CanClose дает объекту приложения, основному окну и всем другим окнам возможность подготовиться к закрытию или предотвратить его. Нормальное закрытие имеет такую последовательность:

Функция CanClose по возможности должна выполнить все необходимые для возврата True действия. False следует возвращать только в том случае, если она не может выполнить необходимые для правильного завершения действия, или пользователь хочет сохранить приложение выполняющимся.

Использование библиотек управляющих элементов

TApplication имеет функции, необходимые для загрузки библиотеки специализированных управляющих элементов Borland (BWCC.DLL для 16-разрядных приложений и BWCC32.DLL для 32-разрядных) и библиотеки трехмерных управляющих элементов Microsoft (CTL3D.DLL). Эти DLL широко используются для обеспечения стандартного вида многих приложений.

BWCC можно открыть с помощью функции TApplication::EnableBWCC, которая воспринимает единственный параметр. Когда вы передаете ей True, функция загружает еще не загруженную DLL. При передаче False DLL выгружается.

После загрузки DLL вы можете использовать обычные функциональные возможности BWCC. EnableBWCC автоматически открывает корректную библиотеку (для 16- или 32-разрядных приложений).

Функция TApplication::EnableCtrl3d позволяет вам загружать и выгружать библиотеку CTL3D фирмы Microsoft. Определить, загружена ли эта библиотека, можно с помощью функции TApplication::Ctl2Enabled. Параметров эта функция не имеет, а возвращаемое значение True указывает на загрузку библиотеки.

Определить, загружена ли библиотека CTL3D, можно с помощью TApplication::Ctl3dEnabled, которая в этом случае возвращает True. Чтобы использовать функцию EnableCtl3dAutosubclass воспринимает единственный параметр. Если передаете ей True, функция разрешает выделение подклассов (в этом случае все отличные от ObjectWindows диалоги будут иметь трехмерный вид), а если False запрещает.

Интерфейсные объекты

Объекты, представляющие окна, блоки диалога и управляющие элементы называются объектами интерфейса пользователя, или просто интерфейсными объектами. В этом разделе обсуждаются общие свойства и требования интерфейсных объектов и их связь с реальными окнами, блоками диалога и управляющими элементами, появляющимися на экране. Здесь также объясняется взаимосвязь между различными интерфейсными объектами прикладной программы, и описывается механизм ответа на сообщения Windows.

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

Назначение интерфейсных объектов

Одна из наибольших трудностей программирования в Windows состоит в том, что можно запутаться в управляющих элементах. Иногда вы посылаете окну сообщение, в другой раз вызываете функцию API. При работе с различными видами элементов внешне похожие операции часто различаются. ObjectWindows значительно уменьшает эти трудности, предусматривая объекты, инкапсулирующие интерфейсные элементы. Это избавляет вас от необходимости иметь дело непосредственно с Windows и обеспечивает более единообразный интерфейс для управления интерфейсными элементами.

Интерфейсные объекты предусматривают функции-элементы для создания, инициализации, уничтожения и управления соответствующим интерфейсным элементом. Многие детали программирования в Windows берут на себя функции-элементы.

Интерфейсные объекты инкапсулируют также данные, необходимые для взаимодействия с интерфейсным элементом. Связь между интерфейсным объектом и интерфейсными элементами аналогична связи между файлом на диске и потоком С++. Объект потока только представляет файл на диске: вы можете манипулировать этим файлом, манипулируя объектом потока.

Объект TWindow

Все интерфейсные объекты ObjectWindows являются производными от TWindow. Этот класс определяет базовое поведение всех окон, диалоговых блоков и объектов управляющих элементов. TWindow поддерживает взаимосвязь между интерфейсными объектами и интерфейсными элементами, включая создание и уничтожение объектов и элементов. Он управляет всеми взаимодействиями "предок-потомок" между интерфейсными элементами, а также регистрирует новые оконные классы Windows.

Создание интерфейсных объектов

Создание интерфейсного объекта с соответствующими интерфейсными элементами требует следующих шагов:

Связь между интерфейсным объектом и интерфейсным элементом поддерживается через элемент данных HWindow - описатель окна.

Обычно в Windows вновь создаваемый интерфейсный элемент получает от Windows сообщение WM_CREATE и отвечает на него своей инициализацией. Интерфейсный объекты ObjectWindows перехватывают это сообщение и отвечают на него вызовом SetupWindow, где вы можете выполнить собственную инициализацию.

Если при инициализации интерфейсного объекта требуется описатель окна интерфейсного элемента, то вы должны выполнить эту инициализацию после вызова функции SetupWindow базового класса. До вызова функции SetupWindow базового класса и создания его дочерних окон HWindow будет недействительным (имеет значение NULL).

Из-за того, что большая часть инициализации выполняется в конструкторе интерфейсного объекта, после создания этого объекта вы не сможете изменить его характеристики. Таким образом, требуется двухэтапная инициализация: до создания интерфейсного элемента (конструктор) и после него (SetupWindow).

Отображение интерфейсного элемента

Создание объекта и соответствующего элемента не означает, что вы будете что-то видеть на экране. Когда создается интерфейсный элемент, Windows проверяет, включает ли стиль элемента WS_VISIBLE. Если да, то он выводится на экран. В противном случае элемент создается, но не выводится. WS_VISIBLE и другие стили устанавливаются или сбрасываются в конструкторе, путем их установки или сброса в элементе данных интерфейсного объекта Attr.Style. Создаваемый интерфейсный элемент выводится на экран только в случае установки стиля WS_VISIBLE.

Определить видимость интерфейсного объекта можно с помощью вызова IsWindowVisible. В случае видимости объекта эта функция возвращает True.

В любое время после создания, элемент может быть показан или спрятан вызовом функции-элемента интерфейсного объекта Show.

Характеристики объекта

Кроме атрибутов интерфейсного элемента интерфейсные объекты имеют характеристики объекта ObjectWindows. С помощью функций SetFlag, ClearFlag и IsFlagSet вы можете запросить и изменить эти характеристики. При работе с данными функциями можно использовать следующие флаги:

Флаг Значени
wfAlias Указывает, является ли объект псевдонимом
wfAutoCreate Указывает, разрешено ли для объект автоматическое создание.
wfFromResource Показывает, загружен ли интерфейсны элемент из ресурса
wfShrinkToClient Задает сжатие окна-рамки до размер клиентного окна
wfMainWindow Указывает, что окно является основным
wfPredefinedClass Определяет, что окно является предопределенным классом Windows.
wfTransfer Показывает, может ли окно использовать механизм передачи данных.

Характеристики окна

TWindow предусматривает также пару функций, которые позволяют вам изменять ресурсы и характеристики интерфейсного элемента. Так как TWindow обеспечивает общие для большого числа объектов функции, никаких специальных функций для управления характеристиками и ресурсами в нем не предусмотрено. Дополнительно к функциональным возможностям TWindow предусмотрены следующие функции:

Уничтожение интерфейсных объектов

Уничтожение интерфейсного объекта предусматривает два этапа: уничтожение интерфейсного элемента и удаление интерфейсного объекта. Если создавать и выводить интерфейсный элемент требуется снова, вы можете уничтожить интерфейсный элемент, не удаляя интерфейсного объекта.

Уничтожение интерфейсного элемента выполняет функция-элемент Destroy интерфейсного объекта. Для уничтожения интерфейсных элементов Destroy вызывает функцию API DestroyWindow. При уничтожении интерфейсного элемента элемент данных HWindow устанавливается в 0.

При закрытии пользователем окна происходит следующее:

Интерфейсный объект - это обычный объект С++, и вы можете удалить его с помощью оператора delete.

Родительские и дочерние интерфейсные элементы

В Windows-приложениях интерфейсные элементы (окна, блоки диалога и управляющие элементы) связаны между собой отношениями родитель-потомок. Два интерфейсных элемента являются связанными, когда один из них является родительским окном, а другой дочерним. Не путайте эти родительские взаимоотношения с наследованием или владением экземпляров, которые являются отношениями между объектами. Дочернее окно не обязательно происходит из своего родительского окна или наследует его свойства.

Дочернее окно - это интерфейсный элемент, управляемый другим интерфейсным элементом. Например, блоки списков управляются окном или блоком диалога в котором они появляются. Они выводятся только в случае вывода их родительских окон. И наоборот, блоки диалога являются дочерними окнами, управляемыми порождающими их окнами. При закрытии родительского окна, дочерние окна закрываются автоматически. Аналогично, при перемещении родительского окна, все его дочерние окна перемещаются вместе с ним.

Все интерфейсные элементы (окна, блоки диалога и управляющие элементы) могут выступать как родительские и как дочерние окна.

Списки дочерних окон

Вы задаете родителя интерфейсного элемента во время его конструирования. Родительский оконный объект является параметром конструктора интерфейсного объекта. Дочерний оконный объект хранит в своем элементе данных Parent адрес своего родительского оконного объекта как указатель на этот объект. Он также автоматически сохраняет адреса своих дочерних оконных объектов в частном элементе данных ChildList.

Получить доступ к дочерним окнам объекта вы можете с помощью функций-итераторов. Функции элементы ForEach и FirstThat получают в качестве первого аргумента указатель на функцию. Подробнее о них рассказывается ниже.

Создание дочерних интерфейсных элементов

Как и все интерфейсные объекты, объекты дочерних окон создаются в два этапа: построение интерфейсного объекта и создание интерфейсного элемента. Если вы строите дочерний объект в конструкторе родительского окна, их интерфейсные элементы создаются автоматически (при разрешении автоматического создания для дочерних окон). По умолчанию автоматическое создание разрешено для всех объектов ObjectWindows на базе TWindow, за исключением TDialog.

Если вы не строите в родительском окне дочерние объекты, они не будут автоматически создаваться и выводиться вместе с родительским окном. Вы можете создать их самостоятельно с помощью Create или (в случае режимного окна) Execute. При этом создание означает создание экземпляра интерфейсного элемента.

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

Уничтожение родительского окна означает уничтожение всех его дочерних окон. Уничтожать дочерние окна или интерфейсные объекты дочерних окон явным образом не требуется. Это же касается механизма CanClose: CanClose для родительского окна может вызывать CanClose для всех дочерних окон.

Когда вы уничтожаете интерфейсный элемент объекта, он разрешает автоматическое создание всех дочерних объектов независимо от того, было ли оно до этого разрешено. Таким образом, при создании родительского объекта все родительские объекты восстанавливаются в то состояние, которое они имели перед уничтожением родительского объекта. Вы можете использовать это для уничтожения интерфейсного элемента и воссоздания его в том же состоянии, какое он имел при уничтожении. Чтобы предотвратить это, вы должны явно отключить автоматическое создание для всех дочерних объектов, которые не хотите автоматически создавать.

Автоматическое создание

При разрешении автоматического создания дочерний объект создается одновременно с родительским. Чтобы исключить дочернее окно из механизма автоматического создания и вывода, вызовите в конструкторе дочернего объекта функцию-элемент DisableAutoCreate. Обратное действие выполняет EnableAutoCreate. По умолчанию автоматическое создание разрешено для всех классов кроме диалоговых блоков.

Манипуляция дочерними окнами

Вы можете написать функции элементы, осуществляющие итерацию по дочерним окнам некоторого окна с целью выполнения некоторых действий. Например, вам нужно будет проверить все дочерние блоки проверки в окне. В этом случае, используйте наследуемую из TWindowsObject функцию-элемент ForEach. Функции-элементы ForEach и FirstThat позволяют вам выполнять операции со всеми окнами в списке дочерних окон или с каждым окном по очереди. TWindow предусматривает также ряд других функций, которые позволяют вам определить число дочерних окон в списке или перемещать их.

Функция-итератор ForEach воспринимает в качестве параметра указатель на функцию. Это может быть функция-элемент или автономная функция. ForEach вызывает функцию (с аргументами TWindow* и void*) один раз для каждого дочернего объекта. Дочернее окно передается как TWindow*.

Вам может потребоваться написать функции-элементы, осуществляющие итерацию по списку дочерних окон для поиска определенного дочернего окна. Например, в окне с несколькими дочерними окнами кнопок с независимой фиксацией вам понадобилось определить первый установленный блок проверки. Для этого, используйте наследуемую из TWindowsObject функцию-элемент FirstThat.

Кроме функций-итераторов TWindow предусматривает ряд функций для поиска и манипуляции с одним дочерним окном:

Функция Действие
NumChildren Возвращает значение, указывающее общее число дочерних окон в списке
GetFirstChild Возвращает TWindows*, указывающий на первую запись в списке дочерних окон.
GetLastChild Возвращает TWindow*, указывающий на последнюю запись в списке дочерних окон.
Next Возвращает TWindow*, указывающий на следующую запись в списке дочерних окон.
Previous Возвращает TWindow*, указывающий на предыдущую запись в списке дочерних окон.

Регистрация оконных классов

Когда вы создаете интерфейсный элемент из интерфейсного объекта с помощью Create или Execute, объект проверяет, имеется ли другой объект того же зарегистрированного в Windows типа. Если да, то элемент создается на основе существующего класса регистрации. Если нет, то объект автоматически регистрируется, а затем создает на базе только что зарегистрированного класса. Это освобождает программиста от необходимости регистрировать все оконные классы перед их использованием.

Обработка событий

В данной главе описывается, как использовать таблицы реакции ObjectWindows 2.0. Таблицы реакции - это методы, используемые для обработки событий в приложении ObjectWindows. Для использования таблицы реакции ObjectWindows нужно выполнить следующие шаги:

Для использования описываемых в данном разделе макрокоманд вы должны включить файл заголовка owl\eventhan.h. Этот файл уже включается файлами owl\module.h и owl\window.h, поэтому явно данный файл обычно включать не требуется.

Таблицы реакции ObjectWindows 2.0 - это значительное усовершенствование по сравнению с методами обработки событий Windows и сообщениями, включая оператор switch (как в стандартных программах Си для Windows) и схемы, используемые в других прикладных средах. Таблицы реакции ObjectWindows 2.0 предусматривают:

Описание и определение таблиц реакции

Поскольку таблица реакции является членом класса ObjectWindows, вы должны описать эту таблицу при определении класса. ObjectWindows предусматривает для этого макрокоманду DECLARE_RESPONSE_TABLE, имеющую единственный аргумент - имя класса, для которого описывается таблица реакции. Добавьте эту макрокоманду в конец определения класса.

После описания таблицы реакции вы должны определить ее (вне определения класса). Помочь вам в этом может макрокоманда DEFINE_RESPONSE_TABLEx, где x зависит от наследования класса и равно числу его непосредственных базовых классов. Данная макрокоманда имеет x+1 аргумент - имя класса, для которого определяется таблицей, и имена каждого непосредственного базового класса. Таблица реакции заполняется записями и завершается макрокомандой END_RESPONSE_TABLE. Приведем пример: DEFINE_RESPONSE_TABLE(TMyFrame, TFrameWindow) EV_WM_LBUTTONDOWN, EV_WM_LBUTTONUP, EV_WM_MOOUSEMOVE, EV_WM_RBUTTONDOWN, EV_WM_RBUTTONUP, END_RESPONSE_TABLE;

Определение записей таблицы реакции

Записи таблицы реакции связывают событие Windows с конкретной функцией. Когда окно или управляющий элемент получает сообщение, оно проверяет свою таблицу реакции и находит запись для данного сообщения. Если она присутствует, то сообщение передается данной функции. Если нет, то оно передается выше родительскому объекту, и выполняется обработка, предусмотренная в ObjectWindows по умолчанию. ObjectWindows предусматривает для таблицы реакции набор макрокоманд:

Макрокоманды командных сообщений

ObjectWindows предусматривает большое число макрокоманд, которые называются макрокомандами командных сообщений и позволяют вам присваивать любой функции командные сообщения. Единственное требование состоит в том, чтобы сигнатура заданной вами функции обработки сообщения соответствовала сигнатуре, требуемой макрокомандой. Некоторые макрокоманды позволяют передавать функции обработки сообщений необработанное сообщение. Эти макрокоманды работают с любым общим и зарегистрированным сообщением.

Макрокоманды сообщений Windows

ObjectWindows предусматривает макрокоманды для всех стандартных сообщений Windows. Вы можете использовать их для обработки стандартных сообщений в любой их своих функций-элементов. Эти макрокоманды имеют префикс EV_.

Эти стандартные макрокоманды передают сообщения функциям с предопределенными именами, имя которой соответствует имени сообщения, но вместо WM_ подставляется Ev, например, WM_LBUTTONDOWN передается EvButtonDown.

Благодаря этим макрокомандам Windows автоматически обрабатывают сообщение, то есть сообщения, которые обычно записываются как параметры LPARAM и WPARAM разбиваются на составные части. Они позволяют также переносить приложения с 16- на 32-битовую платформу и наоборот. Чтобы использовать стандартную макрокоманду сообщения Windows, добавьте в свой класс функцию-элемент с корректным именем и сигнатурой.

Макрокоманды уведомления от дочерних объектов

Макрокоманды уведомления от дочерних объектов предусматривают несколько различных способов обработки сообщений уведомления от этих объектов. В одной функции вы можете обрабатывать уведомляющие функции от нескольких дочерних объектов, передавать все уведомляющие коды от дочернего окну отвечающему окну или обрабатывать код уведомления в дочернем объекте.

Чтобы облегчить связь с дочерними управляющими элементами, можно использовать макрокоманды. Эти макрокоманды предусматривают различные методы для обработки уведомляющих кодов дочерних объектов. Если вы хотите обрабатывать уведомления в родительском окне, используйте EV_CHILD_NOTIFY, которые передает код уведомления в качестве параметра и допускает обработку уведомления нескольких дочерних объектов в одной функции. Это позволяет также избежать обработки каждого уведомляющего сообщения в отдельной таблице реакции для каждого управляющего элемента. Вместо этого сообщение обрабатывается в родительском объекте, позволяя, например, диалоговому окну обрабатывать все свои управляющие элементы в одной таблице реакции.

Оконные объекты

Оконные объекты - это интерфейсные объекты высокого уровня, облегчающие работу с окнами, дочерними окнами и управляющими элементами. ObjectWindows предусматривает несколько различных типов оконных объектов:

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

Использование оконных объектов

Ниже поясняется как создавать, выводить на экран и заполнять оконные объекты, а также как строить оконные объекты, управлять атрибутами создания и создавать интерфейсные элементы окна. Все обсуждаемые типы окон являются примерами оконных объектов.

Построение оконных объектов

Оконные объекты представляют интерфейсные элементы. Объекты подключаются к элементу через описатель, записанный в элемент данных HWindow, который наследуется из TWindow. Когда вы строите оконный объект, интерфейсный элемент еще не существует. Вы должны создать его на отдельном шаге. TWindow создает также конструктор, который можно использовать в DLL для создания оконного объекта с уже существующим интерфейсным элементом.

Некоторые классы ObjectWindows 2.0 (TDialog, TMDIFrame, TTinyCaption, TMDIChild, TDecoratedFrame, TLayoutWindow, TClipboardViewer, TKeyboardModeTracker и TFrameWindow) используют TWindow или TFrameWindow в качестве виртуально базы. В C++ сначала строятся виртуальные базовые классы. Это означает, что конструктор производного класса не может задавать для конструктора базового класса аргументы, используемые по умолчанию. Решить эту проблему можно двумя способами:

Установка атрибутов создания

Обычное Windows приложение имеет много различных стилей окон: перекрывающиеся или всплывающие, с рамкой, прокручиваемые и т.д. Эти атрибуты стиля, а также и другие атрибуты создания, обычно устанавливаются при конструировании оконного объекта, и используются при создании представляемого им отображаемого элемента.

Атрибуты создания оконного объекта, такие как стиль, заголовок и меню, хранятся в его элементе данных Attr, имеющем тип TWindowAttr. TWindowAttr включает следующие элементы данных:

Элемент данных Использование
Style Типа DWORD, содержит константу комбинированного стиля.
ExStyle Типа DWORD, содержит расширенный стиль.
Menu Типа LPSTR, идентифицирует ресурс меню.
X Типа int, задает горизонтальную координату начального местоположения окна. Является горизонтальной координатой левого верхнего угла окна на экране.
Y Типа int, задает вертикальную координату начального местоположения окна. Является вертикальной координатой левого верхнего угла окна на экране.
W Типа int, задает начальную ширину окна в экранных координатах.
H Типа int, задает начальную высоту окна в экранных координатах.
Param Типа LPSTR, будет передаваться окну при его создании.
Id Типа int, задает идентификатор дочернего окна, исползуемого для связи между управляющим элементом и его родительским окном или диалогом. Id должен быть разным для всех дочерних окон одного родителя. Если управляющий элемент определяется в ресурсе, то его Id должен совпадать с идентификатором ресурса. Окно никогда не имеет оба набора Menu и Id.
AccelTable Типа TResId. Идентификатор ресурса таблицы акселераторов окна.

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

В следующей таблице показаны некоторые заданные по умолчанию значения, которые вы можете переопределить для элементов Attr:

Элементы Attr Значение по умолчанию
Style WS_CHILD WS_VISIBLE
ExStyle 0
X 0
Y 0
W 0
H 0
Menu 0
Id 0
Param 0
AccelTable 0

Создание интерфейсных элементов окна

После построения оконного объекта вам нужно сообщить Windows о создании соответствующего интерфейсного элемента. Это можно сделать путем вызова функции-элемента Create объекта, которая делает следующее:

При создании интерфейсного элемента оконного объекта возможны две особых ситуации С++. Таким образом, для обработки проблем с ресурсами или памятью вы должны заключить вызовы Create в блок try/catch. Если окно создать невозможно, Create порождает особую ситуацию TXInvalidWindow, а при невозможности создания дочернего окна - TXInvalidChildWindow. Обе особых ситуации обычно вызываются нехваткой памяти или других ресурсов.

Основное окно приложения автоматически создается TApplication::InitInstance. Для создания основного окна вызов Create не требуется.

Окна схемы

Окна схемы (разметки) инкапсулируют класс TLayoutWindow, инкапсулирующий производный от TWindow класс TLayoutWindow. Как и TFrameWindow, TLayoutWindow создает основу для декорированных окон-рамок и возможности упорядочения элементов в области рамки.

Окна схемы (или разметочные окна) называются так потому, что могут упорядочивать дочерние окна в своей клиентной области. Расположение дочерних окон определяется относительно окна схемы или другого дочернего окна ("братского" окна - окна равного уровня). Расположение дочернего окна зависит от параметров схемы, которые включат в себя ряд правил, описывающих координаты X и Y окон, их высоту и ширину. Эти правила обычно основываются на координатах братского окна и, в итоге, на размере и организации окна схемы.

Параметры схемы для дочерних окон содержатся в классе TLayoutMetrics. Объекты параметров схемы состоят из ряда ограничений схемы. Каждое ограничение описывает правило определения конкретного изменения окна, например X-координаты или ширины окна. Эти ограничения хранятся в структуре TLayoutConstraints, но обычно используются производные от нее классы, такие как TEdgeConstraint.

Ограничения схемы определяют соотношение между краем или размером одного окна и краем или размером братского или родительского окна схемы. Это соотношение может быть достаточно гибким. Например, можно задать ширину окна как процент от ширины родительского окна. Ограничения задаются обычно с помощью функции Set, которая определяется в классе TEdgeConstraint и в результате наследуется из TEdgeOrWidthConstraint и TEdgeOrHeightConstraint.

Параметр edge функции Set определяет, какую часть окна вы ограничиваете. Для этого используется enum TEdge с 5 возможными значениями:

С помощью enum TWidthHeight можно задать ограничение высоты или ширины окна:

TEdgeConstraint содержит также ряд функций, которые вы можете использовать для задания предопределенных соотношений, тесно связанных с теми, которые задаются в Set.

Для задания схемы окна недостаточно одного ограничения расположения. Например, указание того, что окно должно находиться на 10 элементов изображения ниже другого окна ничего не говорит о высоте и ширине окна, расположении его левого или правого края или нижней границы. Комбинация ограничений схемы расположения может полностью определить расположение окна. TLayoutMetrics использует 4 ограничения расположения: два объекта TEdgeConstraint (X и Y), TEdgeOrWidthConstraint (Width) и TEdgeOrHeightConstraint (Height).

TLayoutMetrics - достаточно простой класс. Его конструктор не имеет параметров и только устанавливает элемент каждого ограничения. Для каждого ограничения схемы ограничивающее окно обнуляется, соотношение устанавливается в lmAsIs, единицы устанавливаются в lmLayoutUnits, а значение - в 0.

После построения объекта TLayoutMetrics вам нужно задать ограничения схемы нужного окна. Для установки каждого ограничения можно использовать соответствующую функцию.

Чтобы лучше понять, как совместно работают ограничения, выполните пример приложения LAYOUT из каталога примеров программ. Его диалоговое окно позволяет изменить ограничения каждого из окон и увидеть результаты. Однако ограничения нужно описывать полностью. Неполное описание может приводить к аварийному завершению.

Окна-рамки

Окна-рамки (объекты класса TFrameWindow) - это специализированные окна, поддерживающие клиентное окно. Они составляют основу окон-рамок MDI и SDI, дочерних окон MDI и (вместе с TLayoutWindow) декорированных окон-рамок. Окна-рамки играют в ObjectWindows важную роль и используются для управления такими элементами как меню и полосы прокрутки. Их клиентные окна могут специализироваться для выполнения конкретных задач. Вносимые в окно-рамку изменения (например, добавление строки состояния) на клиентное окно не влияет.

Построение объектов TFrameWindow

Вы можете построить окно-рамку с помощью одного из двух конструкторов TFrameWindow. Эти конструкторы позволяют создавать новые окна-рамки с новыми интерфейсными элементами и связывать их с существующими интерфейсными элементами. В параметрах первого конструктора вы можете задать объект родительского окна, заголовок, клиентное окно, режим сжатия до размеров клиентного окна и конструктор базового класса. Второй конструктор используется для привязки существующего интерфейсного элемента к новому объекту TFrameWindow, который называется псевдонимом существующего окна. В его параметрах задается описатель окна существующего интерфейсного элемента и конструктор базового класса. Второй конструктор устанавливает флаг wfAlias.

Модификация окон-рамок

После построения объекта многие атрибуты окна-рамки можно изменить (для этого используются специальные функции, в том числе функции TWindow). В TFrameWindow с этой целью предусмотрен набор дополнительных функций:

Функция Назначение
AssignMenu Обычно используется перед созданием интерфейсного элемента для задания меню окна.
SetMenu Устанавливает описатель меню.
SetMenuDescr Задает описание меню.
GetMenuDescr Возвращает описание меню.
MergeMenu Объединяет текущее описание меню с заданным.
RestoreMenu Восстанавливает меню окна из Attr.Menu.
SetIcon Устанавливает пиктограмму согласно заданному ресурсу.

Декорированные окна-рамки

Декорированные окна-рамки инкапсулируются в TDecoratedFrame (класс, производный от TFrameWindow и TLayoutWindow). Декорированные окна-рамки обеспечивают все функциональные возможности окон-рамок и окон разметки, но кроме них поддерживают дополнительные управляющие элементы (декорирующие элементы) и автоматически настраивают дочерние окна соответственно размещению дополнительных элементов.

Построение объектов декорированных окон-рамок

TDecoratedFrame имеет только один конструктор, почти идентичный конструктору TFrameWindow, за исключением одного параметра, который позволяет задать отслеживание команд меню. Если отслеживание разрешено, окно передает строку в строку состояния окна. Эта строка имеет то же имя ресурса, что и выбранный пункт меню.

Добавление декорирующих элементов

Для модификации атрибутов декорированных окон-рамок можно использовать те же базовые методы, что и для модификации окон. Функция-элемент Insert в TDecaratedFrame дает здесь дополнительную гибкость и позволяет добавлять декорирующие элементы: строки состояния (TControlBar), строки состояния (TStatusBar), кнопки реквизитов (TButtonGadget) и другие управляющие элементы на базе TWindow.

Окна MDI

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

Приложения MDI

В каждом приложении MDI присутствуют определенные компоненты. Это основное окно (окно-рамка MDI), клиентное окно MDI в окне-рамке, содержащее дочерние окна (окна MDI) и др. При использовании классов Doc/View приложение может помещать в окна MDI отображаемые элементы.

Приложение MDI обычно имеет пункт меню Window, который управляет дочерними окнами MDI. Меню Window обычно имеет такие пункты как Tile, Cascade, Arrange и Close All. Имя каждого открытого окна MDI автоматически добавляется в конец данного меню, а текущее выбранное окно отмечается.

Дочерние окна MDI имеют некоторые характеристики перекрывающегося окна. Их можно максимизировать до полного размера клиентного окна MDI или минимизировать в пиктограмму внутри клиентного окна. Дочерние окна MDI никогда не выводятся вне своих клиентных окон и окон-рамок. Хотя дочерние окна MDI не могут иметь меню, можно использовать меню окна-рамки.

Для каждого типа окна MDI в ObjectWindows определены классы TMDIFrame, TMDIClient и TMDIChild. В ObjectWindows окно-рамка MDI владеет клиентным окном MDI, а клиентное окно MDI является владельцем каждого дочернего окна MDI.

Функции-элементы TMDIFrame управляют окном-рамкой и его меню. ObjectWindows сначала передает команды активному окну, а затем - его родительскому окну, поэтому клиентное окно может обрабатывать команды окна-рамки. Обычно создается экземпляр TMDIFrame. Так как TMDIChild является производным от TFrameWindow, оно может быть окном-рамкой с клиентным окном. Таким образом, вы можете создать специализированные окна TMDIChild. Стиль их может зависеть от конкретного приложения.

Построение приложений MDI

Для построения приложения MDI в ObjectWindows выполните следующие шаги:

Специфическое для MDI поведение реализуется классами TMDIXxx, поэтому вы можете сосредоточиться на специфическом для приложения поведения.

Окно-рамка MDI всегда является основным окном приложения, так что вы можете построить его в функции-элементе InitMainWindow объекта приложения. Окна-рамки MDI отличаются от других окон-рамок (обрамляющих окон) следующим:

Поскольку в качестве окна-рамки MDI обычно используется экземпляр TMDIFrame, вам нужно добавить к своим клиентным оконным классам MDI специфическое для приложения поведение. Окно-рамка является владельцем меню и комплектов инструментальных средств и передает в клиентное окно и приложение генерируемые ими команды.

Для манипуляции с дочерними окнами MDI TMDIClient имеет несколько функций-элементов. Команды меню дочернего окна приложения MDI управляют клиентными окнами. TMDIClient автоматически отвечает на эти команды и выполняет соответствующие действия, задавая вывод окон с перекрытием или без, упорядочивая пиктограммы или закрывая их. Команды меню определены в файле заголовка ресурса mdi.rh. При построении меню в сценарии ресурса не забудьте включить этот файл.

Дочерние окна MDI не должны отвечать на команды меню какого-либо дочернего окна. Это делает клиентное окно MDI. Создать дочерние окна MDI можно двумя способами: автоматически в TMDIClient::InitChild или в другом месте.

Назад | Содержание | Вперед

Copyright © CIT