Глава 9. Интерфейсные объекты


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

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

    Примечание: Приводимый здесь материал относится к


окнам, диалоговым блокам и управляющим элементам.

Для чего нужны интерфейсные объекты?


    Для чего нужны интерфейсные объекты, если в Windows уже есть окна, диалоговые блоки и управляющие элементы?

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

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

Что делают интерфейсные объекты?


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

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

Общий интерфейсный объект


    Все интерфейсные объекты ObjectWindows наследуют из единственного абстрактного объектного типа TWindowsObject, который определяет поведение, общее для окна, диалога и объектов управляющих элементов, видоизменяемых и специализируемых в производных объектных типах TDialog, TWindow и TControl.

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

    Новые типы, производные от TWindowsObject, определяются редко, но он служит основой объектно-ориентированной модели окон. Он определяет большинство наследуемых объектами функциональных возможностей, когда вы получаете из TWindow и TDialog новые типы.

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


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

    Первым шагом является вызов конструктора Init, который строит интерфейсный объект и устанавливает его атрибуты, такие как стиль и меню.

    Второй шаг заключается в вызове метода создания окна объекта приложения, MakeWindow, связывающего интерфейсный объект с новым элементом экрана. Эта связь поддерживается полем HWindow экрана (описателем окна).

    Примечание: Об описателях окон рассказывается в Главе 7


"Иерархия ObjectWindows".

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

Допустимость описателя окна


    Обычно в Windows вновь созданный интерфейсный элемент получает (от Windows) сообщение wm_Create, на которое требуется ответить инициализацией. Интерфейсный объект ObjectWindows не будет получать сообщений wm_Create, поэтому не забудьте определить для инициализации метод SetupWindow.

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


Рис. 9.1 Когда окно имеет допустимый описатель.

Видимость на экране


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

    ws_Visible и другие стили окна обычно устанавливаются или сбрасываются конструктором Init в поле Attr.Style объекта.

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

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


    Как и в случае создания интерфейсный объектов, их уничтожение предполагает выполнение двух шагов:

    Уничтожением экранного элемента занимается метод Destroy интерфейсного объекта, который делает следующее: он вызывает функцию Windows DestroyWindow, чтобы избавиться от элемента экрана, и устанавливает поле HWindow объекта в 0. Таким образом, проверив указатель, вы можете сообщить, связан ли еще объект с элементом экрана.

    Уничтожить элемент экрана вы можете без уничтожения объекта (если хотите создавать и выводить его снова).

    Примечание: Уничтожение самого окна обычно не требуется. Это делается автоматически при закрытии окна.

    Когда пользователь закрывает на экране окно, ObjectWindows обнаруживает, что данный элемент экрана уничтожен, устанавливает поле HWindow соответствующего объекта в 0 и вызывает деструктор объекта Done.

Связь порождающего и дочернего объектов


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

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

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

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

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


    Когда вы строите объект дочернего окна, то можете в качестве параметра конструктора Init можете задать порождающее окно (пример вы можете найти в Главе 10). Объект дочернего окна отслеживает свое порождающее окно через указатель на его поле Parent. Он отслеживает также объекты его дочерних окон, сохраненные в поле ChildList. Дочернее окно, на которое в данный момент установлен ChildList, является последним созданным дочерним окном.

Построение дочерних окон


    Как и в случае интерфейсных объектов, объекты дочерних окон создаются в два этапа (построение объекта и создание элемента экрана). Объекты порожденного окна следует строить с помощью конструктора Init порождающего окна. Например, объект окна, наследующий из TWindow и содержащий командную кнопку должен иметь примерно следующий вид:

     constructor TMyWindow.Init(AParent: PWindowsObject;
                                ATitle: PChar);
     begin
       inherited Init(AParent, ATitle);
       TheButton := New(PButton, Init(@Self, id_TheButton,
         'Текст кнопки', 20, 10, 100, 25, True));
     end;

    Обратите внимание на использование указателя Self для связи дочернего объекта (TheButton) с порождающим (экземпляром TMyWindow). Конструктор интерфейсного объекта автоматически добавляет к своему списку дочерних окон новые объекты.

Создание дочерних элементов экрана


    Когда построен список дочерних элементов интерфейсного объекта, создание элементов экрана для дочерних окон выполняется автоматически. Создание родительского окна (через вызов MakeWindow) включает в себя вызов метода SetupWindow порождающего окна. Одним из наследуемых действий SetupWindow является вызов для каждого из окон в списке дочерних окон методов SetupWindow.

    Примечание: Автоматическое создание можно запретить. См. ниже раздел "Запрещение автоматического создания".

    При создании нового производного объектного типа нужно помнить об инициализации объекта в SetupWindow после вызова наследуемого метода SetupWindow, например:

     procedure TMyCheckBox.SetupWindow;
     begin
inherited SetupWindow;           { сначала по умолчанию }
      .
      .
      .                  { выполнить инициализацию объекта }
     end;

Уничтожение дочерних окон


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

Запрещение автоматического создания


    Чтобы явно исключить дочернее окно из механизма автоматического создания и вывода, вызовите после его создания метод DisableAutoCreate. Чтобы явно добавить в механизм создания и вывода дочернее окно (такое как диалоговый блок, который при нормальном выводе будет исключен), вызовите после построения его метод EnableAutoCreate.

Итерация дочерних окон


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

     procedure TMyWindow.CheckAllBoxes;
       procedure CheckTheBox(ABox: PWindowsObject); far;
       begin
         PCheckBox(ABox)^.Check;
       end;
     begin
       ForEach(@CheckTheBox);
     end;

    Использование метода ForEach (и аналогичных методов FirstThat и LastThat) похоже на применение методов с аналогичными названиями в TCollection. Хотя ObjectWindows не использует наборы для обслуживания дочерних окон, методы итерации работают аналогично.

Поиск определенного дочернего окна


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

     function TMyWindow.GetFirstChecked: PWindowsObject;
       function IsThisOneChecked(ABox: PWindowsObject): Boolean;
              far;
       begin
         IsThisOneChecked := (ABox^.GetCheck = bf_Checked);
       end;
     begin
       GetFirstChecked := FirstThat(@IsThisOneChecked);
     end;