Глава 14. Объекты MDI


    Многодокументальный интерфейс (MDI) - это стандарт интерфейса для приложений Windows, которые позволяют пользователю одновременно работать с несколькими открытыми документами. Документ, в этом смысле, это обычно связанная с файлом задача, например, редактирование текстового файла или работа с файлом электронной таблицы. В приложениях MDI пользователь может, например, иметь несколько открытых файлов в одном приложении. Возможно, что вы уже использовали приложения MDI: Microsoft Exel, администратор программ Windows, администратор файлов Windows. Стандарт MDI является также частью спецификации общего доступа пользователя (CUA) фирмы IBM. ObjectWindows предусматривает объекты, позволяющие легко писать приложения MDI.

Что такое приложение MDI?


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


Рис. 14.1 Компоненты приложения MDI.

Меню дочернего окна


    Строка меню окна-рамки содержит меню, управляющее дочерними окнами MDI. Меню дочернего окна содержит такие элементы как Tile (Вывод без перекрытия), Cascade (Вывод с перекрытием), Arrange (Упорядочить) и Close All (Закрыть все). Имя каждого открытого окна MDI автоматически добавляется к концу этого меню с выбором текущего окна.

Дочерние окна MDI


    Каждое дочернее окно MDI имеет некоторые характеристики перекрывающего окна. Его можно максимизировать до полного размера окна клиента MDI или минимизировать в пиктограмму, которая помещается к нижней границе окна-рамки. Дочернее окно MDI никогда не выходит за границы его окна-рамки (обрамляющего окна). Дочернее окно MDI не может иметь меню, поэтому все его функции реализуются меню окна-рамки. Заголовок каждого дочернего окна MDI часто представляет собой имя открытого файла, связанного с этим окном, хотя его поведение заранее неизвестно и определяется программой. Можно рассматривать приложение MDI как мини-сеанс Windows, когда несколько приложений представлены окнами или пиктограммами.

Окна MDI в ObjectWindows


    ObjectWindows определяет типы для представления рамок MDI и клиентов MDI. Это соответственно TMDIWindow и TMDIClient. TMDIWindow является производным от TWindow, но TMDIClient на самом деле представляет собой управляющий элемент и является производным от TControl. В приложении MDI ObjectWindows, окно-рамки владеет своим окном клиента MDI и хранит его в поле ClientWnd. Окно-рамка также содержит каждое из дочерних окон MDI в связанном списке ChildList. Дочерние окна MDI являются экземплярами типа объекта, производного от написанного вами TWindow.

    Методы TMDIWindow занимаются в основном конструированием и управлением дочерними окнами MDI, окном клиента MDI и обработкой выбора в меню. Главная работа TMDIClient происходит скрытно от пользователя и состоит в управлении дочерними окнами MDI. При разработке приложений MDI вы в общем случае будете создавать новые производные типы для своих рамок и дочерних окон соответственно от TMDIWindow и TWindow.

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


    Построение приложения MDI в ObjectWindows представляет собой относительно простую задачу:

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

Построение рамки MDI


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

    * Рамка MDI всегда является основным окном, поэтому оно никогда не имеет порождающего окна. Таким образом, TMDIWindow.Init нет необходимости воспринимать в качестве параметра указатель порождающего окна.

    * Окно-рамка MDI всегда должно иметь меню, так что вторым параметром Init является описатель меню. Для основных окон, отличных от MDI и производных от TWindows, вы определяете Init для установки Attr.Menu в допустимый описатель меню. TMDIWindow.Init устанавливает для вас AttrMenu.

    Типичный метод InitMainWindow для приложения MDI может выглядеть следующим образом:

     procedure TMDIApplication.InitMainWindow;
     begin
       MainWindow := New(PMyFrame, Init('Заголовок рамки',
                         LoadMenu(HInstance, 'MenuName'));

    Если предположить, что TMyFrame - это потомок TMDIWindow, при этом будет создаваться окно-рамка MDI с заголовком "Заголовок рамки" и строкой меню, заданной ресурсом "MenuName".

Создание меню дочерних окон


    Меню окна-рамки должно включать в себя меню дочернего окна в стиле MDI. Открытие дочернего окна MDI добавляет его заголовок к меню дочернего окна, а закрытие дочернего окна удаляет его из списка. Это позволяет пользователю активизировать любое дочернее окно, даже если оно не является видимым.

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

     constructor TMyMDIWindow.Init(ATitle: PChar; AMenu: HMenu);
     begin
       inherited Init(ATitle, AMenu);
       ChildMenuPos := 1;
     end;

    TMDIWindow.Init также вызывает InitClientWindow для конструирования объекта TMDIClient, который будет служит его окном клиента MDI. TMDIWindow.SetupWindow создает окно клиента MDI.

Создание дочерних окон MDI


    TMDIWindow определяет автоматический метод реакции CreateChild, который вызывается при выборе из меню варианта, результатом которого будет команда с идентификатором Create_Child. Обычно этот вариант меню называется New или Create. Как это определено в TMDIWindow, CreateChild конструирует и создает дочернее окно MDI типа TWindow вызовом TMDIWindow.InitChild. Для задания корректного типа дочернего окна (производного от TWindow), переопределим InitChild для вашего типа окна-рамки MDI:

     function MyMDIWindow.InitChild: PWindowsObject;
     begin
      InitChild:=New(PMyChild, Init(@Self,
                     'Новое дочернее окно'));
     end;

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


    Может потребоваться, чтобы ваша окно-рамка воспроизводило только одно дочернее окно MDI при своем первом появлении. Для этого первого дочернего окна вы можете явно задать его размер. В отличие от других дочерних окон, дочерние окна MDI должны быть сконструированы и созданы в методе SetupWindow окна-рамки MDI, а не в Init. Вы также должны явно создать экранный элемент дочернего окна с помощью вызова MakeWindow:

     procedure MyMDIWindow.SetupWindow;
     var
        ARect: TRect;
        NewChild: PMyChild;
     begin
        TMDIWindow.SetupWindow;
        NewChild:=PMyChild(InitChild);
        GetClientRect(HWindow, ARect);
      with NewChild^.Attr, ARect do
      begin
         W:=(right*4) div 5;
         H:=(bottom*3) div 5;
         Title:='Child #1';
      end;
        Application^.MakeWindow(NewChild);
     end;

    В некоторых приложениях вам может потребоваться создать дочернее окно MDI в ответ на более чем один выбор в меню. Например, пункты меню New и Open в редакторе файла могут приводить к возникновению нового дочернего окна с заголовком в виде имени файла. В этом случае определите для построения дочернего окна методы автоматической реакции. ObjectWindows определяет команды cm_MDIFileOpen и cm_MDIFileNew, что облегчает дифференциацию от стандартных cm_FileOpen и cm_FileNew.

Управление дочерним окном MDI


    Тип окна MDI в ObjectWindows содержит методы манипулирования дочерними окнами MDI приложения MDI. Хотя большая часть скрытой работы делается в TMDIClient, доступ к данным и функциям происходит через метод TMDIWindow.

    TMDIWindow определяет методы реакции на сообщения Windows, которые автоматически реагируют на выбор команды стандартного меню MDI: Title, Cascade, Arrange Icon и Close All. Эти методы ожидают основанных на командах сообщений с заранее определенными константами идентификаторов меню. Обязательно используйте эти идентификаторы при построении ресурса меню дочернего окна:

Стандартные методы, команды и действия MDI. Таблица 14.1
ДействиеКонстанта ID менюМетод TMDIWindow
Tile cm_TileChildren CM_TileChildren
Cascade cm_CascadeChildren CM_CascadeChildren
Arrange Iconscm_ArrangeChildIcons CM_ArrangeChildIcons
Close All cm_CloseChildren CM_CloseChildren

    Методы реакции TMDIWindows, подобные CMTileChildren, вызывают другие методы TMDIWindows, такие как CMChildren. Эти методы вызывают методы TMDIClient с тем же именем, например, TMDIClient^.TileChildren. Для переопределения такого автоматического поведения нужно переопределить TMDIWindow.TileChildren или другой метод TMDIWindow. Для дочерних окон MDI не подходит реагирование на основанные на командах сообщения, генерируемые меню дочернего окна.

Настройка активизации дочернего окна


    Пользователь приложения MDI может свободно активизировать любое открытое или минимизировать дочернее окно MDI. Однако, вам может потребоваться предпринять некоторые действия, когда пользователь дезактивирует одно дочернее окно активизацией другого. Например, меню окна-рамки может отражать текущее состояние активного дочернего окна, выделяя его цветом. Каждый раз, когда дочернее окно становится активным или неактивным, оно получает сообщение Windows wm_MDIActivate. Определив метод реакции на это с общение для дочернего окна, вы можете отслеживать, какое дочернее окно активно и соответственно реагировать.

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


    Как и для обычных порождающих и дочерних окон, основанные на командах и дочерних идентификаторах сообщения Windows сначала поступают в дочерние окна для их восприятия и обработки. Затем сообщения поступают в порождающее окно. Однако, в случае приложения MDI сообщения поступают к текущему дочернему окну MDI, затем к окну клиента MDI, и, наконец, к окну-рамке MDI (которое является порождающим окном для всех дочерних окон MDI). Следовательно, меню окна-рамки можно использовать для управления работой в текущем активном дочернем окне MDI. Затем шанс отреагировать получают окно клиента и окно-рамка.

Пример приложения MDI


    Программа MDITest создает приложение MDI, показанное на Рис. 14.1. Полный текст файла MDITEST.PAS содержится на ваших дистрибутивных дискетах.