ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП |
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы. |
Часть 27 Обработка событий ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД После того, как вы определили команду и установили способ управления ее генерацией, например, с помощью эле- мента меню или командной кнопки окна диалога, вы должны обучить отображаемый элемент реагировать на эту команду. Каждый отображаемый элемент наследует метод HandleEvent, который способен реагировать на большую часть входных данных пользователя. Если вы хотите, чтобы отобра- жаемый элемент выполнил какую-либо операцию для вашей прик- ладной программы, то вам следует переопределить его метод HandleEvent и обучить новый метод выполнению двух операций - реагировать на определенные вами новые команды и реагиро- вать на события, связанные с "мышью" и клавиатурой, нужным вам образом. Метод HandleEvent определяет поведение отображаемого элемента. Два отображаемых элемента с идентичными методами HandleEvent будут одинаково реагировать на события. Когда вы порождаете новый тип отображаемого элемента, вы хотели бы, чтобы его поведение в целом более-менее повторяло пове- дение предшествующего отображаемого элемента, с некоторыми изменениями. Наиболее простым способом здесь является вызов метода HandleEvent предшествующего отображаемого элемента в качестве составной части метода HandleEvent нового объекта. Общая схема метода HandleEvent порожденного отображае- мого элемента выглядит следующим образом: procedure NewDescendant.HandleEvent(var Event: TEvent); begin { код, изменяющий или ограничивающий унаследованное поведение } Parent.HandleEvent(Event); { код, выполняющий дополнительные функции } end; Другими словами, если вы хотите, чтобы новый объект обрабатывал определенные события иначе, чем его предок (или совсем их не обрабатывал!), вы должны прервать эти события до передачи события методу HandleEvent предшествующего отображаемого элемента. Если вы хотите, чтобы поведение но- вого объекта было аналогично поведению его предшественника, но чтобы он имел и ряд дополнительных функций, вы должны добавить соответствующий код после вызова процедуры HandleEvent предка. Запись события ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД До сих пор в данной главе события рассматривались в теоретическом плане. Описывались различные виды событий (связанные с "мышью", клавиатурой, сообщениями и "холосты- ми" действиями), определяемыми полем события What. Было также кратко упомянуто использование поля Command для собы- тий, связанных с командами. Теперь рассмотрим, что представляет собой запись собы- тия. Модуль DRIVERS.TPU Turbo Vision определяет в качестве вариантной записи тип TEvent: TEvent = record What: Word; case Word of evNothing: (); evMouse: ( Buttons: Byte; Double: Boolean; Where: TPoint); evKeyDown: ( case Integer of 0: (KeyCode: Word); 1: (CharCode: Char; ScanCode: Byte)); evMessage: ( Command: Word; case Word of 0: (InfoPtr: Pointer); 1: (InfoLong: Longint); 2: (InfoWord: Word); 3: (InfoInt: Integer); 4: (InfoByte: Byte); 5: (InfoChar: Char)); end; TEvent является вариантной записью. Ее содержание вы можете узнать из поля What. Так, например, если TEvent.What является событием вида evMouseDown, то TEvent будет содер- жать: Buttons: Byte; Double: Boolean; Where: TPoint; Если TEvent.What является событием типа evKeyDown, то компилятор разрешит вам доступ к данным либо как: KeyCode: Word; либо как: CharCode: Char; ScanCode: Byte; Заключительное поле признака в записи события - это значение Pointer, LongInt, Word, Integer, Byte или Char. Это поле в Turbo Vision используется различным образом. Отображаемые элементы могут самостоятельно генерировать со- бытия и передавать их другим отображаемым элементам, и при этом они часто используют поле InfoPtr. Примечание: Вопросы связи между отображаемыми элементами и полем InfoPtr рассматриваются в разделе "Связь между отображаемыми элементами" в данной гла- ве. Очистка событий ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Метод отображаемого элемента HandleEvent завершает процесс обработки события вызовом метода ClearEvent. Этот метод устанавливает поле Event.What равное событию evNothing и поле Event.InfoPtr, равное @Self, которые слу- жат универсальными сигналами того, что событие обработано. Если событие передается затем другому объекту, то этот объ- ект проигнорирует это событие "холостого действия". Несостоявшиеся события ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Обычно какой-либо отображаемый элемент выполняет обра- ботку события в вашей прикладной программе. Если такой отображаемый элемент не обнаруживается, то режимный отобра- жаемый элемент вызывает метод EventError. EventError вызы- вает метод EventError владельца отображаемого элемента и так далее вверх по дереву отображаемых элементов до вызова метода TApplication.EventError. По умолчанию метод TApplication.EventError не выполня- ет никаких действий. Он может использоваться в процессе разработки программы для переопределения метода EventError, чтобы привести в действие окно диалога ошибок или подать звуковой сигнал. Так как конечный пользователь вашего при- ложения не несет ответственность за его неспособность обра- ботать событие, такое окно диалога ошибок в поставляемой версии будет, вероятно, вызывать раздражение. Метод ClearEvent также способствует осуществлению свя- зи между отображаемыми элементами. Не следует забывать, что обработка события не может быть закончена без вызова ClearEvent. Модификация механизма обработки событий ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Основным в текущем режимном отображаемом элементе яв- ляется следующий цикл: var E: TEvent; begin E.What := evNothing; repeat if E.What <> evNothing then EventError(E); GetEvent(E); HandleEvent(E); until EndState <> Continue; end; Централизованный сбор событий ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Одним из важнейших преимуществ объектно-ориентирован- ного программирования является то, что вашей программе мо- жет быть неизвестен источник событий. Объекту окна, напри- мер, требуется лишь знать, что когда он получает в событии команду cmClose, окно должно закрыться. Не имеет значения, получена ли команда от щелчка кнопкой "мыши" или пиктограм- мы закрытия окна, выбора элемента меню, оперативной клавиши или сообщения от другого объекта в программе. Ему даже не требуется определять, для него ли предназначена эта коман- да. Ему необходимо лишь знать, что ему передано событие для обработки, и он его будет обрабатывать в соответствии с его осведомленностью в этой области. Ключом к подобным событиям из "черного ящика" служит метод GetEvent приложения. Метод GetEvent является единс- твенной частью вашей программы, которой требуется знать ис- точник событий. Объекты в вашей прикладной программе просто вызывают GetEvent и полностью на него полагаются при полу- чении событий, связанных с "мышью", клавиатурой или отло- женных событий, генерируемых другими объектами. Если вы хотите создать новые виды событий (например, чтение символов из последовательного порта), то нужно прос- то переопределить метод TApplication.GetEvent в объекте приложения. Как вы можете видеть из фрагмента программы TProgram.GetEvent в примере программы APP.PAS, цикл GetEvent считывает информацию от "мыши" и с клавиатуры и затем вызывает метод Idle. Для того, чтобы ввести новый ис- точник событий, вы должны либо переопределить Idle для счи- тывания символов из последовательного порта и генерировать события на их основе, либо переопределить сам метод GetEvent, чтобы добавить в цикл вызов GetComEvent(Event), где метод GetComEvent возвращает запись события, если име- ется символ в предназначенном ему последовательном порте. Переопределение метода GetEvent ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Метод GetEvent текущего режимного отображаемого эле- мента вызывает метод GetEvent его владельца и так далее на всем протяжении вверх по дереву отображаемых элементов до метода TApplication.GetEvent, являющегося обычным местом выбора следующего события. Поскольку Turbo Vision всегда использует метод TApplication.GetEvent для выполнения фактического выбора событий, вы можете модифицировать события для всей вашей прикладной программы с помощью переопределения этого мето- да. Например, для реализации клавиатурных макрокоманд вы можете просматривать события, возвращаемые методом GetEvent, перехватывать определенные нажатия клавиш и раз- вертывать их в макрокоманды. Далее для прикладной программы будет известно, что поток событий будет идти прямо от поль- зователя. procedure TMyApp.GetEvent(var Event: TEvent); begin inherited GetEvent(Event); { вызвать метод TApplication } . { специальная обработка } . . end; Использование времени простоя ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Центральная роль TApplication.GetEvent имеет еще и то преимущество, что он вызывает метод TApplication.Idle, даже если события еще не поступали. TApplication.Idle - это пус- той метод, который вы можете переопределять, чтобы выпол- нять обработку отображаемого элемента, параллельно обработ- ке данного отображаемого элемента. Предположим, например, что вы определяете отображаемый элемент по имени THeapView, который использует метод UpDate для отображения имеющейся динамически распределяемой облас- ти памяти. Если вы переопределите метод TApplication.Idle следующим методом, то пользователь получит возможность неп- рерывно просматривать имеющуюся динамическую память незави- симо от того, в каком месте программы он находится. Примечание: Пример средства просмотра динамичес- кой памяти имеется в составе примеров программ на ва- ших дистрибутивных дисках. procedure TMyApp.Idle; begin inherited Idle; HeapViewer.Update; end; Связь между отображаемыми элементами ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Программа Turbo Vision является инкапсулированной (скрытой) в теле объектов, таким образом вы разрабатываете фрагмент программы только в терминах объектов. Предположим, что объекту требуется обменяться информацией с другим объ- ектом в вашей программе - в традиционной программе, которая представляет собой простое копирование информации из одной структуры данных в другую, или в объектно-ориентированной программе, которая может быть не такой простой, так как объекты могут не знать, как обнаружить друг друга. Связь между отображаемыми элементами - это не просто передача данных между эквивалентными частями прикладной программы на языке Паскаль. (Хотя две части традиционной прикладной программы на Паскале значительно уступают по своим функциональным возможностям двум отображаемым элемен- там Turbo Vision.) Если вам требуется осуществить связь между отображае- мыми элементами, то первым возникает вопрос о том, насколь- ко правильно вы распределили задачи между отображаемыми элементами. Здесь могут проявиться недостатки в разработке программы. Возможно, следует объединить два отображаемых элемента в один, или переместить часть одного отображаемого элемента в другой. Промежуточные отображаемые элементы ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Если разработка программы неплохая, но, тем не менее, требует осуществления связи между отображаемыми элементами, то правильным решением будет создать промежуточный отобра- жаемый элемент. Предположим, например, что у вас есть объект в виде электронной таблицы и объект в виде текстового процессора, и вы хотите вставить какой-либо фрагмент из электронной таблицы в текстовый процессор, и наоборот. В прикладной программе Turbo Vision вы можете это сделать с помощью пря- мой связи между отображаемыми элементами. Предположим, что позднее вам потребуется добавить к этой группе объектов, к примеру, базу данных и включать данные в эту базу и считы- вать из нее. Для этого вам потребуется распространить уста- новленную вами связь между первыми двумя объектами на тре- тий объект. Лучшим решением здесь будет установка промежуточного отображаемого элемента - в данном случае, буфера вырезанно- го изображения. Объекту потребуется лишь знать, как зано- сить в него запись или изымать запись. Вне зависимости от того, сколько новых объектов вы добавите к группе, ваша ра- бота от этого не усложнится. Сообщения между отображаемыми элементами ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Если вы внимательно анализировали ситуацию и уверены в разработке вашей программы и вам не требуется создание про- межуточного вида, то можете реализовать простую связь между двумя отображаемыми элементами. Прежде, чем будет установлена связь между отображаемы- ми элементами, отображаемый элемент должен обнаружить дру- гой отображаемый элемент и даже удостовериться, существует ли он вообще. Приведем наглядный пример. Модуль Stddlg содержит окно диалога TFileDialog (это отображаемый элемент, который отк- рывается в интегрированной среде, когда вы хотите загрузить новый файл). TFileDialog имеет объект TFileList, показываю- щий дисковый каталог и выше его - FilеInputLine, демонстри- рующий файл, выбранный для загрузки в данным момент. Всякий раз, когда пользователь выбирает в списке файлов FileList другой файл, данный список должен требовать от FileInputLine вывода имени нового файла. В данном случае FileList может быть уверен, что FileInputLine существует, так как оба они существуют в од- ном и том же объекте FileDialog. Каким же образом FileList сообщает FileInputLine, что пользователь выбрал новое имя? FileList создает и передает сообщение. В данном случае имеется метод TFileList.FocusItem, который передает событие и метод (объекта FileInputLine) HandleEvent, который его принимает: procedure TFileList.FocusItem(Item: Integer); var Event: TEvent; begin inherited FocusItem(Item); { вначале вызывается наследуемый метод } Message(TopView, evBroadcast, cmFileFocused, List^.At(Item)); { TopView указывает текущий режимный отображаемый элемент } end; procedure TFileInputLine.HandleEvent(var Event:TEvent); var Name: NameStr; begin inherited HandleEvent(Event); if (Event.What = evBroadcast) and (Event.Command = cmFileFocused) and (State and sfSelected = 0) then begin if PSearchRec(Event.InfoPtr)^.Attr and Directory <> 0 then Data^ := PSearchRec(Event.InfoPtr)^.Name + '\' + PFileDialog(Owner)^.WildCard else Data^ := PSearchRec(Event.InfoPtr)^.Name; DrawView; end; end; Примечание: TopView указывает на текущий режим- ный отображаемый элемент. Message - это функция, которая генерирует событие, связанное с сообщением, и возвращает указатель на объект (если таковой имеется), который выполняет обработку собы- тия. Имейте в виду, что TFileList.FocusItem для применения функции Message в качестве процедуры, использует расширен- ный синтаксис Турбо Паскаля (директиву компилятора $X+), так как здесь не имеют значения результаты, получаемые от функции Message. Кто управляет оповещением? ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Предположим, прежде чем вы приступите к выполнению ка- кого-либо действия, вам требуется узнать, имеется ли в опе- ративной области открытое окно. Каким же образом вы сможете это сделать? Для этого ваш фрагмент программы должен пере- дать событие, связанное с оповещением, на которое окна смо- гут отреагировать. "Подпись", оставленная объектом, обраба- тывающим событие, укажет вам его обработчик. Все ли в порядке? ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Приведем конкретный пример. В интегрированной среде разработки программ Турбо Паскаля IDE, если пользователю требуется открыть окно просмотра, то выполняющей это прог- рамме необходимо сделать проверку на открытие такого окна ранее. Если такое окно не открыто, то программа его откро- ет; если не открыто, то программа переместит его наверх. Выполнить передачу оповещающего сообщения достаточно просто: AreYouThere := Message(DeskTop, evBroadcast, cmFindWindow, nil); В программе для метода HandleEvent окна просмотра име- ется проверка на реагирование на cmFindWindow посредством "очистки" события: case Event.Command of . . . cmFindWindow: ClearEvent(Event); . . . end; Не следует забывать, что ClearEvent не только устанав- ливает поле What записи события на evNothing, но также и устанавливает поле InfoPtr на @Self. Функция Message выпол- няет чтение из этих полей, и, если событие было обработано, возвращает указатель на объект, который обрабатывал связан- ное с сообщением событие. В данном случае этим объектом бу- дет окно просмотра. Таким образом, далее за строкой, кото- рая посылает сообщение, мы включаем следующее: if AreYouThere = nil then CreateWatchWindow { если отсутствует, создать } else AreYouThere^.Select; { иначе вывести на экран } Пока окно просмотра будет единственным объектом, кото- рый способен реагировать на передачу cmFindWindow, то по завершении программы поверх отображаемых элементов в опера- тивной области будет наверняка находиться только одно окно просмотра. Какое окно верхнее? ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Используя изложенный ранее способ вы можете также оп- ределить, например, какое окно является самым верхним отоб- ражаемым элементом этого типа в рабочей области. Так как событие, связанное с передачей сообщения, передается каждо- му из отображаемых подэлементов режимного отображаемого элемента в Z-последовательности (порядок обратной вставки), то отображаемый элементом, включенный позднее других, будет "верхним" отображаемый элементом оперативной области. Рассмотрим кратко ситуацию в интегрированной интерак- тивной среде, при которой пользователь имеет открытое окно просмотра наверху в оперативной области при прохождении программы через окно редактирования. Окно просмотра может быть активным окном (с двойной рамкой, вершиной стека), од- нако строка выполнения в окне программы должна отслеживать выполняющую программу. Если в вашей оперативной области открыто несколько окон редактирования, они не должны накла- дываться, а интегрированная среда должна знать, в каком из окон редактирования она будет выполнять отслеживание выпол- нения программы. Этим окном должно быть переднее или самое верхнее окно редактирования, которое определяется как последнее встав- ленное окно. Для того, чтобы указать, какое окно находится "сверху", интегрированная интерактивная среда передает со- общение, на которое смогут отреагировать только окна редак- тирования. Первым окном редактирования, которое получит со- общение, будет последнее включенное окно; оно будет обраба- тывать событие посредством "очистки", и интегрированная среда поэтому будет знать, какое окно использовать для отс- леживания программы с помощью чтения возвращаемого функцией Message результата. Вызов метода HandleEvent ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Вы можете также создать или модифицировать событие, а затем вызвать непосредственно метод HandleEvent. У вас име- ется в распоряжении три вида вызова: 1. Отображаемый элемент вызывает непосредственно метод HandleEvent братского отображаемого элемента. Собы- тие не будет распространяться на другие отображае- мые элементы. Оно перейдет непосредственно к друго- му методу HandleEvent, который управляет возвратом к пользователю. Примечание: "Братские" отображаемые элементы - это отображаемые элементы, имеющие одного вла- дельца. 2. Вы можете вызвать метод HandleEvent владельца. Со- бытие будет распространяться на другие отображаемые элементы вниз по цепочке отображаемых элементов. (Если вы вызываете метод HandleEvent из вашего собственного метода HandleEvent, то ваш метод HandleEvent будет вызываться рекурсивно.) После об- работки события управление возвращается к пользова- телю. 3. Вы можете вызвать метод HandleEvent вида из другой цепочки отображаемых элементов. Событие будет спус- каться вниз по этой цепочке отображаемых элементов. После обработки события управление возвращается к пользователю. ГЛАВА 10. Объекты приложения ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Сердцевиной любой программы Turbo Vision является объ- ект приложения. В данной главе подробно описываются различ- ные виды объектов приложений, что они могут делать, и как вы можете их настраивать. Она охватывает следующие темы: * Организация объектов приложения. * Построение объекта приложения. * Настройка оперативной области. * Выход в командный процессор DOS. * Настройка строки состояния. * Настройка строки меню. * Использование времени простоя. * Создание контекстно-зависимого справочника. Организация объектов приложения ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Объект приложения играет в вашем приложении Turbo Vision две решающих роли. Это отображаемый элемент, который управляет всем экраном и является управляемым событиями ме- ханизмом, взаимодействующим с "мышью", клавиатурой и други- ми частями компьютера. Эти две роли взаимосвязаны, но вы можете рассматривать их отдельно. В данном разделе эти воп- росы рассматриваются в плане следующих вопросов: - роль приложения как отображаемого элемента; - роль приложения как группы; - три важнейших метода: Init, Run и Done. Приложение как отображаемый элемент ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Сначала может показаться странным рассматривать прило- жение как отображаемый элемент. Кроме того, отображаемый элемент - это что-то видимое, в то время как приложения представляет собой просто некую концепцию. Однако основным принципом отображаемого элемента является то, что он зани- мает прямоугольную область экрана, а приложение отвечает за весь экран. В действительности объект приложение делает гораздо больше, чем простое управление экраном, но это одна из его важнейших обязанностей, и в этом смысле в определенные мо- менты очень важно помнить, что объект приложения - это отображаемый элемент. Приложение как группа ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Приложение является не только отображаемым элементом, но и группой. Групповые отображаемые элементы имеют два специальных свойства: возможность владеть отображаемыми по- дэлементами и способность быть режимными. Объекты приложе- ния используют преимущества и того, и другого свойства. Приложение как владелец подэлементов ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Границы отображаемого элемента приложения охватывают весь экран, но само приложение невидимо. Оно подразделяет экран на три различных области и назначает для каждой из них отображаемый подэлемент. По умолчанию приложение прис- ваивает объекту строки меню верхнюю строку экрана, строке состояния - нижнюю строку экрана, а объекту приложения - все строки между ними. Три отображаемых подэлемента приложения легко запом- нить, поскольку вы видите их при работе с программой, но иногда легко забыть, что владельцем их всех является объект приложения. Другие разделы данной главы посвящены конкретно строка меню, строкам состояния и оперативным областям, од- нако важно помнить, что за всеми ими стоит объект приложе- ния. Вы можете рассматривать отображаемый элемент приложе- ния как конечного владельца всех отображаемых элементов программы. Если вы будете следовать по цепочке владения от любого заданного отображаемого элемента, это приведет вас обратно к объекту приложения. Режимность приложения ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Большую часть времени во время работы программы Turbo Vision объект приложения является режимным отображаемым элементом. Единственным исключением является время, когда вы выполняете другой отображаемый элемент (обычно диалого- вое окно), который становится текущим отображаемым элемен- том, пока не вызывается метод EndModal и приложение снова не становится режимным. Методы Init, Run и Done ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Основной блок приложения Turbo Vision всегда состоит из трех операторов, вызывающих три основных метода объекта приложения: Init, Run и Done: var AnyApp: TApplication; begin AnyApp.Init; AnyApp.Run; AnyApp.Done; end; Никакие другие операторы помещать в основной блок вам не потребуется. Специфическое для приложения поведение сле- дует задавать в конструкторе Init и завершаться в деструк- торе Done. Конструктор Init ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Поскольку объект приложения (как все отображаемые эле- менты) содержит виртуальные методы, перед использованием объекта вы должны вызвать конструктор. По умолчанию все объекты Turbo Vision имеют конструктор с именем Init. Конс- труктор приложения задает отображаемые элементы приложения и инициализирует подсистемы приложения, включая драйверы "мыши" и видеодрайверы, администратор памяти и обработчик ошибок. Если вы переопределяете Init, чтобы добавить к ва- шему приложению специальные элементы, убедитесь, что вы вы- зываете конструктор Init, наследуемые от TApplication. Метод Run ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Run - это простой метод, но он очень важен. После того как Init устанавливает объект приложения, метод Run выпол- няет объект приложения, делая его режимным и запуская рабо- ту приложения. Основная деятельность метода Run состоит в простом цикле repeat..until, который имеет примерно следую- щий вид: Repeat получить событие; обработать событие; until Quit; Это не является фактическим кодом, а просто показывает принципы. Метод Run получает отложенные события от "мыши" или клавиатуры или других частей приложения, затем обраба- тывает события непосредственно или выполняя их маршрутиза- цию соответствующим отображаемым элементам. В итоге некото- рое событие генерирует команду "выхода", цикл завершает ра- боту и приложение завершается. Деструктор Done ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД После завершения метода Done деструктор Done уничтожа- ет любые принадлежащие приложению объекты: строку меню, строку состояния, оперативную область и любые добавленные вами объекты, а затем останавливает обработчик ошибок Turbo Vision и драйверы. В общем случае деструктор Done вашего приложения дол- жен отменять все, что установлено конструктором Init, затем вызывать деструктор Done, наследуемый из TApplication, ко- торый управляет уничтожением стандартных отображаемых подэ- лементов приложения и остановом подсистем приложения. Если вы переопределяете метод Init приложения, то, возможно, вам потребуется переопределить также метод Done. Построение объекта приложения ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Конструктор приложения в общем случае достаточно прост и не требует параметров, но выполняет ряд важных функций. Когда вы определяете конструктор приложения, то существует относительно немного вещей, которые вы обязаны сделать, и одним из них является вызов конструктора Init, наследуемого от TApplication. Конструктор TApplication выполняет две важных функции, которые вы должны понимать: - вызов наследуемого конструктора; - инициализация подсистем. Вызов наследуемого из TApplication конструктора Init полностью берет на себя заботу об этом. Если ваше приложе- ние является производным от TProgram, а не TApplication, то убедитесь, что ваш конструктор вызывает наследуемый конс- труктор и устанавливает все подсистемы, которые вы хотите использовать. Вызов наследуемого конструктора ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД В большинстве случаев, когда вы в своем объекте прило- жения переопределяете конструктор Init, то включаете вызов наследуемого конструктора, а затем добавляете код. Большую часть настроек вы можете получить путем переопределения виртуальных методов, поэтому вам редко требуется заменять наследуемый конструктор. В следующем разделе описывается все поведение, наследуемое из TProgram, которое вам нужно заменить, если вы не вызываете наследуемый конструктор. Конструктор TProgram ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Конструктор TProgram выполняет несколько важных функ- ций: - устанавливает переменную Application таким образом, чтобы она указывала на ваш объект приложения; - вызывает виртуальный метод InitScreen для установки переменных режима экрана; - вызывает конструктор, наследуемый из TGroup; - устанавливает флаги State и Options; - устанавливает видеобуфер; - вызывает виртуальные методы InitDesktop, InitStatusLine и InitMenuBar. Обратите внимание на инициализацию переменных режима экрана, объекта оперативной области, объекта строки состоя- ния и объекта строки меню, которая происходит через вызовы виртуальных методов, так что вы можете переопределить соот- ветствующие методы в своем объекте приложения, и наследуе- мый конструктор будет вызывать ваши переопределенные мето- ды. Примечание: Эти виртуальные методы описываются в разделах данной главы, посвященных оперативной облас- ти, строке состояния и строке меню. Порядок вызовов ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД При переопределении конструктора приложения очень ва- жен порядок, в котором вы вызываете наследуемый конструк- тор. В качестве общего правила вам следует вызывать насле- дуемый конструктор Init первым, а затем определять все спе- цифические моменты своего приложения: constructor TNewApplication.Init; begin inherites Init; { вызов TApplication.Init } . . { ваш код инициализации } . end; Помните, что объекты приложения представляют собой отображаемые элементы, и конечный объект-предок TObject очищает все поля в объекте, присваивая им нули или значения nil. Поскольку вызов TApplication.Init дает в результате вызов TObject.Ini, любые изменения, которые вы вносите в объект приложения пере вызовом наследуемого конструктора Init, будут потеряны. В общей ситуации единственным случаем, когда вы должны что-то делать перед вызовом наследуемого конструктора при- ложения является использование объектов редакторов файлов. Перед построением объекта приложения вы должны выделить бу- феры файлового редактора (как описывается в Главе 15). Инициализация подсистем ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Основной разницей между TApplication и его предшеству- ющим типом TProgram является то, что TApplication переопре- деляет конструктор объекта и его деструктор для инициализа- ции, а затем завершает работу основных подсистем. Этими пятью подсистемами являются: - администратор памяти; - видеоадминистратор; - администратор событий; - обработчик системных ошибок; - администратор протокола. Turbo Vision устанавливает каждую подсистему, вызывая процедуру в модуле App. Конструктор TApplication вызывает каждую из них перед вызовом конструктора Init, наследуемого из TProgram: constructor TApplication.Init; begin InitMemory; { установить администратор памяти } InitVideo; { установить видеоадминистратор } InitEvents; { установить администратор событий } InitSysError; { установить обработчик системной ошибки } InitHistory; { установить администратор протокола } inherited Init; { вызвать TProgram.Init } end; Хотя можно создать рабочее приложение, производное не- посредственно от TProgram, вам все равно следует использо- вать по крайней мере некоторые из стандартных подсистем приложения. Например, чтобы создать тип приложения, не ис- пользующий систему ведения протокола, вы можете аналогично TApplication создать из TProgram новый производный тип, но без вызова в конструктореv InitHistory. Администратор памяти ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Администратор памяти делает для Turbo Vision три важ- ных вещи: - устанавливает буфер безопасности; - управляет отменяемыми буферами отображения; - управляет перемещаемыми буферами файлового редактора. Буфер безопасности - это составная часть Turbo Vision. Когда вы выделяете память для объекта Turbo Vision, адми- нистратор памяти выполняет проверку, чтобы убедиться, что распределение не затронуло буфера безопасности в конце па- мяти. Эта защита предохраняет ваше приложение от ситуации нехватки памяти, и дает вашему приложению возможность осво- бодить память и выполнить восстановление. При наличии свободной памяти над стеком групповые объ- екты распределяют в этой области отменяемые буфера отобра- жения. Сохраняя копию образа экрана, группа может сохранить время при вызове для самоотображения. Если эта память пот- ребуется при другой операции выделения памяти, группа отме- няет буфер и в следующий раз полностью отображается заново. Если вы используете в своих приложения файловые редак- торы, вам нужно зарезервировать, для перемещаемых буферов, память выше динамически распределяемой области (как поясня- ется в Главе 15 "Редактор и текстовые отображаемые элемен- ты"). Подсистема администратора памяти управляет за вас данными буферами. Видеоадминистратор ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Процедура InitVideo устанавливает видеоадминистратор Turbo Vision. Видеоадминистратор отслеживает режим экрана при запуске приложения, так что может восстановить экран при завершении приложения. InitVideo также устанавливает значения внутренних видеопеременных Turbo Vision: ScreenHeight, ScreenWidth, ScreenBuffer, CheckSnow, CursorLines и HiResScreen. Соответствующая процедура DoneVideo восстанавливает экран в то состояние, в каком он был при запуске, очищает экран и восстанавливает курсор. Администратор событий ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Процедура InitEvents проверяет, установлена ли в сис- теме "мышь", и, если "мышь" присутствует, устанавливает пе- ременную MouseEvents в значение True, разрешая обработчик прерываний "мыши" и выводя на экран курсор "мыши". Если Turbo Vision не обнаруживает при запуске "мышь", админист- ратор событий полностью игнорирует "мышь". Соответствующая процедура DoneEvent останавливает ад- министратор событий, запрещая обработчик прерывания "мыши и скрывая курсор "мыши. Обработчик системной ошибки ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Обработчик системной ошибки выполняет в вашем приложе- нии две функции: - перехватывает критические ошибки DOS; - перехватывает нажатия клавиш Ctrl+Break. По умолчанию обработчик критической ошибки перехваты- вает критические ошибки DOS и выводит на экран в строке состояния приложения Turbo Vision предупреждающее сообще- ние, предоставляя пользователю возможность восстановления. Обработчик ошибок также перехватывает клавиши Ctrl+Break, позволяя вашей программе реагировать некоторым отличным от завершения способом. Если вы не вызываете для установки обработчика ошибок Turbo Vision InitSysError, ваше приложение будет обрабаты- вать критические ошибки и клавиши Ctrl+Break как любое дру- гое приложение Паскаля: критические ошибки будут давать ошибки этапа выполнения, а Ctrl+Break будет обрабатываться в соответствии с системной установкой. Примечание: Об обработке ошибок по умолчанию рассказывается в Главе 20 "Руководства по языку". Администратор протокола ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Процедура InitHistory выделяет блок памяти для списков протокола строк ввода. Переменная HistorySize определяет объем выделяемой памяти (по умолчанию это 1 килобайт). Если вы хотите выделить другой объем памяти, то перед вызовом InitHistory (то есть перед вызовом TApplication.Init) нужно изменить значение HistorySize. Если выделение памяти выполняется успешно, то InitHistory устанавливает переменную HistoryBlock таким об- разом, что она указывает на выделенную память. Если выделе- ние памяти завершается неудачно, то HistoryBlock содержит значение nil, и все попытки добавить данные в протокол или считать их из него будут игнорироваться. Соответствующая процедура DoneHistory освобождает блок памяти, выделенный для HistoryBlock. Процедура DoneHistory использует HistorySize для определения объема освобождаемой памяти, поэтому важно, чтобы вы не изменяли значение пере- менной HistorySize после вызова InitHistory. Изменение режимов экрана ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Turbo Vision отслеживает текущий режим экрана в бито- вой переменной с именем ScreenMode. ScreenMode содержит комбинацию констант режима экрана smMono, smBW80, smCO80 и smFont8x8. По умолчанию приложение Turbo Vision подразуме- вает режим экрана, который ваша операционная среда DOS ис- пользует при запуске приложения. Если вы находились в цвет- ном 25-строчном режиме, то его будут использовать приложе- ния Turbo Vision. Если вы находились в 50-строчном тексто- вом режиме VGA, приложение Turbo Vision также запускается в этом режиме. В большинстве случаев вам не нужно переключаться между монохромным, черно-белым и цветным режимами, поскольку они обычно зависят от аппаратуры пользователя. Чаще вы будете переключаться между 25-строчным обычным режимом и 43-/50-строчным режимом высокого разрешения. Для этого пе- реключите бит smFont8x8 в ScreenMode, вызвав для этого ме- тод SetScreenMode. В следующем примере показана часть мето- да приложения HandleEvent, который отвечает на команду cmVideo переключением бита режима шрифта 8х8 элементов изображения: procedure TSomeApplication.HandleEvent(var Event: TEvent); begin inherited HandleEvent(Event); if Event.What = evCommand then case Event.Command of cmVideo: SetScreenMode(ScreenMode xor smFont8x8); . . . end; end; Настройка оперативной области ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Объект оперативной области изменять требуется редко. Используемый по умолчанию объект оперативной области охва- тывает весь экран, кроме верхней и нижней его строки, и знает управлять включенными в нее окнами и диалоговыми ок- нами. Однако, вам может потребоваться изменить ее размер или позицию, либо изменить используемый по умолчанию обра- зец фона. В данном разделе мы коснемся следующих тем: - построение объекта оперативной области; - включение и выполнение окон; - упорядочивание окон; - изменение образца фона. Построение объекта оперативной области ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Объекты приложения вызывают для построения объекта оперативной области и присваивание его глобальной перемен- ной Desktop виртуальный метод InitDesktop. По умолчанию InitDesktop получает ограниченный прямоугольник приложения и строит объект оперативной области типа TDesktop, который покрывает отображаемый элемент приложения кроме первой и последней строки. Чтобы построить объект приложения, охватывающий другую область, вам нужно переопределить InitDesktop. Например, если ваше приложение не имеет строки состояния, вам нужно обеспечить охват объектом оперативной области той строки, которая обычно принадлежит строке состояния. Вы можете сде- лать это двумя способами: путем вызова наследуемого метода и модификации результата, либо путем полной замены наследу- емого метода. Использование наследуемого метода ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Так как вы знаете, что делает наследуемый метод InitDesktop, то можете вызвать этот метод, а затем модифи- цировать полученный в результате объект, изменив, как пока- зано в следующем примере, границы оперативной области: procedure TMyApplication.InitDesktop; var R: TRect; begin inherited InitDesktop; { конструктор используемой по умолчанию оперативной области } Desktop^.GetExtent(R); { получить его границы } Inc(R.B.Y); { переместить нижнюю на строку вниз } Desktop^.Locate(R); { установить для границ новый размер } end; Замена наследуемого метода ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Вместо того, чтобы использовать наследуемый метод, вы можете также создать отдельный объект оперативной области. В показанном ниже примере приведен метод InitDesktop, кото- рый строит объект оперативной области, покрывающий ту же область, что и оперативная область в предыдущем примере. procedure TMyApplication.InitDesktop; var R: TRect; begin GetExtent(R); { получить границы приложения } Int(R.A.Y); { переместить верхнюю строку, что даст место для строки меню } New(Desktop, Init(R)); { построить оперативную область с этими границами } end; Преимущество данного подхода в том, что он несколько быстрее, но он основан на знании наследуемого метода. То есть, использование наследуемого метода гарантирует, что все выполняемые наследуемым методом действия будут выпол- няться. Если вы заменяете метод, то нужно обеспечить дубли- рование всех действий заменяемого метода. Включение и выполнение окон ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Почти во всех случаях объект оперативной области вла- деет всеми окнами и диалоговыми окнами в приложении. Так как оперативная область - это группа, для включения режим- ных и безрежимных отображаемых элементов вы можете исполь- зовать обычные методы Insert и Execute. Однако для включе- ния и выполнения приложение предлагает более надежный спо- соб. Включение безрежимных окон ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Объект приложения наследует метод InsertWindow, восп- ринимающий в качестве своего параметра оконный объект и обеспечивающий допустимость окна перед включением его в оперативную область. Использование InsertWindow вместо включения окон непосредственно в оперативную область обес- печивает, что любые окна в оперативной области проходят две проверки на допустимость, что позволит вам избежать проб- лем. InsertWindow выполняет с оконным объектом две провер- ки: * Вызывает метод ValidView, чтобы убедиться, что пост- роение окна не приведет при распределении памяти к использованию памяти из буфера безопасности. * Вызывает метод Valid окон, передавая ему параметр cmValid. Этот метод возвращает значение True только если окно и все его отображаемые подэлементы постро- ены корректно. Если оба метода (Valid и ValidView) указывают, что ок- но является допустимым, то InsertWindow вызывает метод Insert объекта оперативной области для включения окна. Если окно не проходит одну из проверок, то InsertWindow не вклю- чает окно, уничтожает его и возвращает False. Выполнение режимных отображаемых элементов ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Метод ExecuteDialog приложения во многом аналогичен методу InsertWindow. Разница между ними в том, что при оп- ределении допустимости оконного объекта ExecuteDialog вызы- вает метод Execute оперативной области, чтобы сделать окно режимным, а не включает его. Как и подразумевает его назва- ние, метод ExecuteDialog сконструирован с учетом диалоговых окон, но вы можете передать ему любой оконный объект, кото- рый хотите сделать режимным. ExecuteDialog воспринимает также второй параметр - указатель на буфер данных для использования GetData и SetData. Если указатель имеет значение nil, то ExecuteDialog пропускает обработку GetData/SetData. Если значение указателя отлично от nil, то ExecuteDialog перед выполнением окна вызывает SetData, а если пользователь не отменяет диалогового окна, то вызывает метод GetData. Примечание: Методы GetData и SetData описываются в Главе 12 "Объекты управляющих элементов". Упорядочивание окон ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Объектам оперативной области известны два способа упо- рядочивания принадлежащих им окон - вывод с перекрытием и без перекрытия. Вывод без перекрытия предусматривает такое упорядочивание и изменение размеров окон, что на экране они не перекрываются. Вывод с перекрытием означает упорядочива- ние окон по убыванию размера, начиная с верхнего левого уг- ла оперативной области. Первое окно покрывает всю оператив- ную область, следующее смещается вниз и вправо на один про- бел и так далее. Результатом будет стек окон, спускающихся вниз по оперативной области с видимой строкой меню и левой стороной каждого окна. Вывод без перекрытия или с перекрытием выполняется ме- тодами объекта TApplication Tile и Cascade соответственно. По умолчанию метод HandleEvent объекта TApplication связы- вает Tile и Cascade с командами cmTile и cmCascade. Эти ко- манды поступают от пунктов Tile и Cascade стандартного меню Window. Чтобы автоматически выводить окна с перекрытием или без перекрытия, для всех окон нужно установить бит ofTileable. По умолчанию для оконных объектов бит ofTileable установлен, а для диалоговых окон - нет. Если вы собираетесь использовать безрежимные диалоговые окна, кото- рые хотите выводить с перекрытием или без, не забудьте ус- тановить в конструкторе объекта бит ofTileable. Задание области упорядочивания ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД По умолчанию для вывода окон с перекрытием и без пе- рекрытия используется вся оперативная область. Если вы хо- тите изменить эту область, ваш объект приложения должен пе- реопределять виртуальный метод GetTileRect. Например, если у вас есть неперекрывающиеся окна сооб- щений, которые все охватывают последние четыре строки опе- ративной области, вы можете упорядочить окна для покрытия области выше окна сообщений: procedure TTileApp.GetTileRect(var R: TRect): begin Desktop^.GetExtent(R); { получить размеры оперативной области } R.B.Y := R.B.Y - 4; { исключить последние четыре строки } end; Задание направления ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Оперативная область позволяет вам управлять направле- нием вывода перекрывающихся окон по вертикали или горизон- тали. По умолчанию окна упорядочиваются по вертикали. Это означает, что если у вас есть два окна, и они выводятся с перекрытием, то одно выводится выше другого. Если вы уста- новите поле TileColumnFirst объекта оперативной области в значение True, то оперативная область будет при перекрытии использовать упорядочивание в горизонтальном направлении. При установке TileColumnFirst в True и выводе двух окон с перекрытием они будут располагаться рядом. Изменение фона ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Объект оперативной области даже перед включением ка- ких-либо окон по умолчанию владеть еще одним отображаемым элементом. Это отображаемый элемент фона. Фоновый отобража- емый элемент - это очень простой отображаемый элемент, ко- торый ничего не делает, а только отображается в неперекры- той части оперативной области. В Z-последовательности фон располагается за всеми другими отображаемыми элементами, и, поскольку он не является выбираемым, то всегда там остает- ся. Оперативная область сохраняет указатель на свой фоновый отображаемый элемент в поле Background. Вероятно, единственное, что вы захотите сделать с фо- ном - это изменение образца фона. По умолчанию объект опе- ративной области выводит единственный символ, повторяя его по всех области. Изменить этот единственный символ очень просто. Изменение фона для отображения более одного символа несколько более сложно. Изменение символа образца ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Простейший способ изменения символа образца фона сос- тоит в том, чтобы подождать, пока оперативная область соз- даст свой используемый по умолчанию фон. Затем вы можете изменить поле Pattern объекта фона, которое содержит этот повторяющийся символ. В следующем примере используемый по умолчанию символ фона заменяется буквой C: procedure TMyApplication.InitDesktop; begin inherited InitDesktop; { построить оперативную область по умолчанию } Desktop^.Background^.Pattern := 'C'; { изменить символ образца } end; Начальное значение символа образца фона передается в качестве параметра конструктору фона отображаемого элемен- та, который вызывается виртуальным методом InitBackground объекта оперативной области. Если вы создаете свой произ- водный объект оперативной области, то можете переопределить InitBackground для передачи при построении фона нужного символа, а не более позднего его изменения. Однако, пос- кольку единственной причиной определения вами нового объек- та оперативной области является создание более сложного фо- на, вам следует просто подставить новое значение в поле Pattern в InitDesktop. Изображение сложного фона ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Изображение сложного фона с образцом, состоящим из бо- лее чем одного символа, требует создания двух новых произ- водных объектов - фонового объекта, который отображается нужным вам образом, и объекта оперативной области, который использует вместо стандартного фона TBackground ваш специа- лизированный фон. Программа следующего примера реализует фоновый объект, который повторяет заданную строку по всей оперативной об- ласти. program NewBack; uses Objects, Drivers, Views, App; type PMyBackground = ^TMyBackground; TMyBackground = object(TBackground) Text: TTitleStr; constructor Init(var Bounds: TRect; AText: TTitleStr); procedure Draw; virtual; end; PMyDesktop = ^TMyDesktop; TMyDesktop = object(TDesktop) procedure InitBackground; virtual; end; TMyApplication = object(TApplication) procedure InitDesktop; virtual; end; constructor TMyBackground.Init(var Bounds: TRect; AText: TTitleStr); begin inherited Init(Bounds, ' '); { построить новый отображаемый элемент } Text := AText; { получить текст } while Length(Text) < SizeOf(TTitleStr) - 1 do Text := Text + AText; { заполнить всю строку } end; procedure TMyBackground.Draw; var DrawBuffwer: TDrawBuffer; begin MoveStr(DrawBuffer, Text, GetColor(1)); { поместить строку в буфер } WriteLine(0, 0, Size.Y, DrawBuffer); { записать текст } end; procedure TMyDesktop.InitBackground; var R: TRect; begin GetExtrent(R); { получить прямоугольник оперативной области } Background := New(PMyBackground, Init(R,'Turbo Vision')); end; procedure TMyApplicartion.InitDesktop; var R: TRect; begin GetExtent(R); { получить прямоугольник приложения } R.Grow(0, -1); { обеспечить место для строки меню и строки состояния } Desktop := New(PMyDesktop, Init(R)); { построение специальной оперативной области } end; var MyApp: TMyApplication; begin MyApp.Init; MyApp.Run; MyApp.Done; end. Ключевым в фоновом объекте является метод Draw. Если вы с ним поработаете, то можете получить весьма интересные эффекты. Однако имейте в виду, что обычной целью фона явля- ется обеспечение нейтрального фона для работы пользовате- лей, поэтому он не должен быть слишком отвлекающим. Выход в командный процессор DOS ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД TApplication обеспечивает простой способ, который поз- воляет пользователям вашего приложения работать с командным процессором DOS. В ответ на команду cmDosShell стандартного меню File, TApplication вызывает его метод DosShell. DosShell перед фактическим запуском командного процес- сора останавливает некоторые из подсистем приложения, а за- тем вновь запускает их при выходе пользователя командный процессор. Используемый командным процессором интерпретатор команд задается переменной операционной среды COMSPEC. Настройка сообщения командного процессора ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Перед выходом в интерпретатор команд DosShell вызывает виртуальный метод WriteShellMsg для вывода на экран следую- щего сообщения: Type EXIT to return... Переопределив WriteShellMsg, вы можете настроить сооб- щение для вывода другого текста. Однако, чтобы избежать компоновки ненужного кода, вместо процедуры WriteLn следует использовать PrintStr. Следующий фрагмент программы выводит другое сообщение: procedure TShellApp.WriteShellMsg; begin PrintStr('Выход из Turbo Vision для DOS.'); PrintStr('Для возврата нажмите Exit.'); end; Настройка строки состояния ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Используемый по умолчанию конструктор объекта приложе- ния для инициализации и построения объекта строки состояния вызывает виртуальный метод InitStatusLine. Чтобы создать специализированную строку состояния, вам нужно переопреде- лить InitStatusLine для построения нового объекта строки состояния и присвоить его глобальной переменной StatusLine. Строка состояния выполняет в приложении три важных функции: * Показывает команды, на которых пользователь может щелкать "мышью". * Связывает с командами оперативные клавиши. * Обеспечивает для пользователя контекстно-зависимую справку. Первые две функции задаются, когда вы строите объект строки состояния. С другой стороны, контекстно-зависимая справка управляется методом Hint объекта. Конструктор объекта строки состояния воспринимает два параметра: прямоугольник ее границ и указатель на связанный список определений состояния. Определение состояния - это запись, которая содержит диапазон контекстно-зависимой справки и список клавиш состояния, которые строка состояния выводит при попадании в этот диапазон справочного контекста приложения. Клавиши состояния - это записи, которые содер- жат команды и текстовые строки и оперативные клавиши, кото- рые генерируют эти команды. Построение строки состояния состоит из трех шагов: * Установки границ отображаемого элемента. * Задания определений состояния. * Определения клавиш состояния. Определение границ строки состояния ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Строка состояния почти всегда выводится в нижней стро- ке приложения, но вы можете поместить ее там, где хотите. Вы можете даже сделать строку состояния невидимой, так что пользователь не будет ее видеть и не сможет щелкать на ней кнопкой "мыши", но оперативные клавиши все равно будут свя- заны с командами. Простейший способ размещения строки состояния в нижней строке экрана состоит в привязке ее расположения к ограни- чивающему прямоугольнику объекта приложения. Это показано в следующем примере: procedure TYourApplication.InitStatusLine; var R: TRect; begin GetExtent(R); { получить границы приложения } R.A.T := R.B.Y - 1; { задать верх на одну строку выше нижней границы } . . . { использовать R как ограничивающий прямоугольник строки состояния } end; Использование невидимых строк состояния ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Чтобы использовать невидимый объект строки состояния, вы можете либо назначить прямоугольник границ вне экрана (например, на одну строку ниже нижней границы приложения) или пустой прямоугольник. Например, если вы измените в пре- дыдущем примере присваивание на следующее: R.A.Y := R.B.Y; то строка состояния не будет иметь высоты, и, следователь- но, не появится на экране. Убедитесь, что границы объекта оперативной области охватывают ту область, которую обычно занимает строка состояния. Создание определений состояния ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Записи определений состояния создаются обычно с по- мощью функции NewStatusDef, которая облегчает создание свя- занного списка записей путем вложенных вызовов NewStatusDef. NewStatusDef воспринимает четыре параметра: - нижнюю границу диапазона справочного контекста; - верхнюю границу диапазона справочного контекста; - указатель на связанный список клавиш состояния; - указатель на следующую запись определения состояния (если она имеется). Используемый по умолчанию объект строки состояния, создаваемый методом InitStatusLine объекта TProgram, очень прост. Он содержит только одно определение состояния, кото- рое получает свой список клавиш состояния от функции StandardStatusKeys: procedure TProgram.InitStatusLine; var R: TRect; begin GetExtent(R); { получить границы приложения } R.A.Y := R.B.T - 1; { использовать только нижнюю строку} New(StatusLine, Init(R, { построить StatusLine с помощью R } NewStatusLineDef(0, $FFFF, { охватить все возможные справочные контексты} NewStatusKey('~Alt+X~ Exit', kbAltX, cmQuit, { вывести Alt+X } StdStatusKeys(nil)), nil))); { включить стандартные клавиши } end; Для простых приложений вероятно достаточно единствен- ной строки состояния для всех справочных контекстов. Если ваше приложение содержит другие отображаемые элементы, ко- торые могут потребовать доступности в строке состояния дру- гих команд, вы можете обеспечить их, задав для этих отобра- жаемых элементов другой справочный контекст и создав для каждого из них соответствующие определения состояния. Простая программа в следующем примере (которую вы мо- жете найти на дистрибутивных дисках в файле TWOSTAT.PAS) показывает, как можно изменить строки состояния, изменяя справочный контекст: program TwoStat; uses Object, Drivers, Views, App, Menus; type TStatApp = object(TApplication); constructor Init; procedure InitStatusLine; virtual; end; constructor TStatApp.Init; var R: TRect; Window: PWindow; begin inherited Init; Desktop^.GetExtent(R); R.B.X := R.B.X div 2; Window := New(PWindow, Init(R, 'Окно A', 1)); InsertWindow(Window); Desktop^.GetExtent(R); R.A.X := R.B.X div 2; Window := New(PWindow, Init(R, 'Окно B', 2)); Window^.HelpCtx := $8000; InsertWindow(Window); end; procedure TStatusApp.InitStatusLine; var R: TRect; begin GetExtent(R); R.A.Y := R.B.Y - 1; New(StatusLine, Init(R, NewStatusDef(0, $7FFF, NewStatusKey('~F6~ Go to B', kbF6, cmNext, StdStatusKeys(nil)), NewStatusDef(0, $FFFF, NewStatusKey('~F6~ Go to A', kbF6, cmNext, StdStatusKeys(nil)), end; var StatApp: TStatApp; begin StatApp.Init: StatApp.Run; StatApp.Done; end. Создание клавиш состояния ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД После задания определений для каждого из них требуется список клавиш состояния. Запись клавиши состояния состоит из четырех полей: - текстовой строки, которая выводится в строке состоя- ния; - кода опроса клавиатуры для оперативной клавиши; - генерируемой команды; - указателя на следующую запись клавиши состояния (ес- ли она имеется). Использование функции NewStatusKey ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Простейший способ создания списка клавиш состояния состоит в том, чтобы сделать вызовы функции NewStatusKey вложенными. Создание простого одноэлементного списка клавиш состояния требует одного вызова вида: NewStatusKey('~Alt+Q~ Quit', bkAltQ, cmQuit, nil); Чтобы создать более длинный список, замените nil дру- гим вызовом NewStatusKey: NewStatusKey('~Alt+Q~ Quit', bkAltQ, cmQuit NewStatusKey('~F10~ Menu', bkF10, cmMenu, nil); Использование функций клавиш состояния ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Если вы в нескольких различных определения состояния или в нескольких приложения используете один и тот же набор клавиш состояния, то вы, вероятно, захотите сгруппировать их вместе в функцию. Модуль App предусматривает одну такую функцию с именем StdStatusKeys для общих наиболее часто ис- пользуемых команд. В следующем примере показано описание StdStatusKeys: function StdStatusKeys(Next: PStatusItem): PStatusItem; begin StdStatusKeys := NewStatusKey('', kbAltX, cmQuit, NewStatusKey('', kbF10, cmMenu, NewStatusKey('', kbAltF3, cmClose, NewStatusKey('', kbF5, cmZoom, NewStatusKey('', kbCtrlF5, cmResize, NewStatusKey('', kbF6, cmNext, Next)))))); end; Заметим, что предусматривая указатель на следующий элемент, вы можете использовать такую функцию как StdStatusKey в середине списка клавиш, а не в конце. Добавление справочной информации в строку состояния ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Объект строк состояния предусматривает виртуальный ме- тод Hint, который вы можете переопределить для обеспечения в строке состояния контекстно-зависимой информации справа от выводимых клавиш состояния. Hint воспринимает в качестве единственного параметра номер справочного контекста и возв- ращает по этому номеру строку. По умолчанию наследуемый из TStatusLine метод Hint возвращает для любого ввода нулевую строку, поэтому для получения осмысленных сообщений вам нужно переопределить Hint. program Hinter; uses Objects, Drivers, Menus, Views, App; const hcFile = 1001; hcFileNew = 1002; hcFileOpen = 1003; hcFileExit = 1004; hcTest = 1005; hcWindow = 1100; cmFileNew = 98; cmFileOpen = 99; type PHintStatusLine = ^THintStatusLine; THintStatusLine = object(TStatusLine) function Hint(AHelpCtx: Word): String; virtual; end; THintpp = object(TApplication) constructor Init; procedure InitMenuBar; virtual; procedure InitStatusLine; virtual; end; function THintStatusLine.Hint(AHelpCtx: Word): String; begin case AHelpCtx of hcFile: Hint := 'Это меню File'; hcFileNew: Hint := 'Создание нового файла'; hcFileOpen: Hint := 'Открытие существующего файла'; hcFileExit: Hint := 'Завершение приложения'; hcText: Hint = 'Это тест, всего лишь тест.'; hcWindow: Hint := 'Это окно'; else Hint := ''; end; end; constructor THintApp.Init; var R: TRect; Windows: PWindow; begin inherited Init; Desktop^.GetExtent(R); Window := New(PWindow, Init(R, 'Окно', wnNoNumber)); Window^.HelpCtx := hcWindow; InsertWindow(Window); end; procedure THintApp.InitMenuBar; var R: TRect; begin GetExtent(R); R.B.Y := R.A.Y + 1; MenuBar := New(PMenuBar, Init(R, NewMenu( NewSubMenu('~F~ile', hcFile, NewMenu( NewItem('~N~ew', '', kbNoKey,cmFileNew,hcFileNew, NewItem('~O~pen...', 'F3', kbF3, cmFileOpen, hcFileOpen, NewLine( NewItem('E~x~it', 'Alt+X', kbAltX, cmQuit, hcFileExit, nil))))), NewItem('~T~est', '', kbNoKey, cmMenu, hcTest, nil))))); end; procedure THintApp.InitStatusLine; var R: TRect; begin GetExtent(R); R.A.Y := R.B.Y - 1; StatusLine := New(PHintStatusLine, Init(R, NewStatusDef(0, $FFFF, StandardStatusKeys(nil), nil)))); end; var HintApp: THintApp; begin HintApp.Init; HintApp.Run; HintApp.Done; end. В сложном приложении, которое выводит множество раз- личных справок, для задания строк вместо длинных операторов case в Hint следует использовать списки строковых ресурсов. Обновление строки состояния ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Вам никогда не следует изменять строку состояния вруч- ную. Метод Idle объекта приложения вызывает метод Update объекта строки состояния, поэтому клавиши оперативной стро- ки и справки никогда не будут "устаревшими". Настройка меню ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Меню в Turbo Vision состоит из двух частей - списка меню, который содержит описание пунктов меню и генерируемые ими команды, и отображаемый элемент меню, который выводит эти элементы на экран. Turbo Vision определяет два вида отображаемых элемен- тов меню: строки (полосы) меню и блоки меню. Оба отображае- мых элемента используют в своей основе один и тот же список пунктов меню. Фактически, одни и те же элементы меню можно выводить в виде полосы меню, либо в виде блока. Основное отличие в том, что полоса меню может представлять собой только меню верхнего уровня, обычно фиксированно располо- женное по верхней строке экрана приложения. Блок меню может быть основным меню (обычно всплывающим или локальным меню) или более часто подменю, выводимое по пункту полосы меню или другого блока меню. Конструктор Init приложения для построения строки меню и присваивания ее переменной ManuBar вызывает виртуальный метод с именем MenuBar. Чтобы определить свою собственную строку меню, вам нужно переопределить метод InitMenuBar для создания специальной полосы меню и присвоить ее MenuBar. Создание меню предусматривает два шага: - задание границ полосы меню; - определение пунктов меню. Задание границ полосы меню ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Полоса меню почти всегда занимает верхнюю строку экра- на приложения. Наилучший способ размещение вашего меню в верхней строке состоит в установке его границ на основе границ приложения. Это показано в следующем примере: procedureTYourApplication.InitMenuBar; var R: TRect; begin GetExtent(R); { получить границы приложения } R.B.Y := R.A.Y + 1; { установить нижнюю строку } . . . end; В отличие от полос меню блоки настраивают свои границы в соответствии с их содержимым, поэтому вам не нужно беспо- коиться об установке размеров каждого подменю. Вам следует просто задать границы полосы меню, а объекты меню позабо- тятся об остальном. Определение пунктов меню ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Для определения структуры меню система меню использует два различных вида записей. Каждый из типов записей пред- назначен для использования в связанном списке и имеет ука- затель на следующую запись. * TMenu определяет список пунктов меню и отслеживает используемый по умолчанию (или выбранный) пункт. Каждое основное меню и подменю содержат одну запись TMenu. Список пунктов меню - это связанный список записей TMenuItem. * TMenuItem определяет текст, оперативную клавишу, ко- манду и справочный контекст пункта меню. Каждый пункт меню (команда или подменю) имеет собственную запись TMenuRecord. Когда меню выводится в виде строки, строка оперативных клавиш является скрытой, хотя оперативные клавиши действу- ют. Использование функции NewItem ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Обычный способ распределения и инициализации записи пунктов меню состоит в использовании функции NewItem. Путем вложенных вызовов MenuItem вы легко можете создать список пунктов. Использование функции NewSubMenu ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Подменю - это элемент меню, который вместо генерации команды выводит другое меню. Обычно меню создаются путем вызова вместо NewItem функции NewSubMenu. На самом деле NewSubMenu и NewItem имеют только два отличия: * Подменю не имеет связанной с ним команды, поэтому NewSubMenu устанавливает поле Command в ноль, а опе- ративная клавиша не присваивается и не описывается. * Кроме указания на следующий элемент меню, подменю указывает на запись TMenu, которая содержит список пунктов в подменю. Использование времени простоя ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД В цикле сообщения объекта приложения при отсутствии ожидающих событий в очереди событий вызывается виртуальный метод с именем Idle. Это означает, что при отсутствии ввода от пользователя вы можете использовать Turbo Vision для вы- полнения фоновых процессов. Чтобы создать фоновый процесс, вам нужно просто пере- определить метод Idle и сделать так, чтобы он выполнял не- которые задачи, которую вы хотите выполнять в фоновом режи- ме. Не забудьте, однако, вызвать наследуемый метод Idle, поскольку по умолчанию Idle выполняет такие функции, как обновление строки состояния и уведомление отображаемых эле- ментов о запрещении или разрешении команд. Нужно обеспечить, чтобы любая фоновая обработка, поме- щаемая в методе Idle, не занимает слишком много времени, в противном случае прикладная программа будет слишком медлен- но реагировать на действия пользователя. Программа TVDemo на ваших дистрибутивных дисках ис- пользует два отображаемых элемента из модуля Gadgets. Один из них - это отображаемый элемент часов, который обновляет время с помощью метода приложения Idle. Другой представляет собой указание объема доступной динамически распределяемой памяти, также обновляемое методом Idle. Контекстно-зависимый справочник ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД В Turbo Vision имеются встроенные средства, которые способствуют реализации контекстной подсказки в вашей прик- ладной программе. Вы можете присвоить отображаемому элемен- ту номер контекстной подсказки, и Turbo Vision гарантирует, что как только отображаемый элемент будет выделен, то номер его контекстной подсказки станет номером текущей контекс- тной подсказки программы. Для создания глобальной системы контекстной подсказки вы можете реализовать объект HelpView, которому известны определенные вами номера контекстных подсказок. При активи- зации HelpView (обычно при нажатии пользователем клавиши F1 или другой оперативной клавиши), он попросит у владельца текущую контекстную подсказку с помощью вызова метода GetHelpCtx. После этого HelpView сможет считать и выдать на экран правильный текст подсказки. Пример HelpView имеется на ваших дистрибутивных дисках Турбо Паскаля. Контекстная подсказка - это одна из последних функций, которую вы захотите реализовать в вашей программе, поэтому объекты Turbo Vision инициализируются с заданным по умолча- нию контекстом hcNoContext, который является предопределен- ным контекстом, не изменяющим текущий контекст. В свое вре- мя вы сможете разработать систему номеров подсказок, затем вставить правильный номер в соответствующий отображаемый элемент с помощью вызова SetHelpCtx сразу после создания отображаемого элемента. Контексты подсказки используются также в строке состо- яния для определения отображаемых элементов для вывода на экран. Помните, что при создании строки состояния вы вызы- ваете метод NewStatusDef, который определяет набор элемен- тов состояния для заданного диапазона значений контекста подсказки. Когда новый отображаемый элемент становится вы- деленным, то контекст подсказки для этого элемента опреде- ляет, какая строка состояния изображается на экране. ГЛАВА 11. Объекты окон и диалоговых окон ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Объекты окон представляют собой специализированные групповые отображаемые элементы, обеспечивающие ограничен- ные рамкой перекрывающиеся окна с заголовками, которые при- ложения Turbo Vision выводят в оперативной области. Диало- говые окна - это специализированные окна, поэтому все опи- санное в данной главе и специфицирующее окна в равной сте- пени относится и к диалоговым окнам. В этой главе описыва- ются также свойства, уникальные для объектов диалоговых окон. В данной главе описываются следующие темы: - принципы организации окон и диалоговых окон; - работа с окнами; - работа с диалоговыми окнами; - использование управляющих элементов с диалоговыми окнами; - использование стандартных диалоговых окон. Принципы построения окон ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Окна в Turbo Vision отличаются от окон в других систе- мах, с которыми вы, возможно, работали. Вместо того, чтобы быть подмножеством экрана, с которым вы можете выполнять операции чтения и записи, окно Turbo Vision представляет собой групповой отображаемый элемент. Это различие поясня- ется, если рассмотреть простого потомка окна - диалоговое окно. На Рис. 11.1 показано типичное диалоговое окно, кото- рое содержит различные управляющие элементы. Если посмот- реть на это диалоговое окно, то станет совершенно ясно, что пользователь взаимодействует с ним путем набора в строке ввода, щелчка кнопкой "мыши" на командных кнопках и т.д. Пользователь не предполагает возможности набора данных в фоновых областях. ЙН[REMPRN]ННННННННННННННННННННННННDirectoriesНННННННННННННН є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є є±EXE & TPU directories±ЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫ±є є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є є±Include directories±±±ЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫ±є є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є є±Unit directories±±±±±±Ыc:\tp;c\tp\tvision;c:\tp\tvdemoЫ±є є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є є±Object directories±±±±Ыc:\tp;c:tp\tvisionЫЫЫЫЫЫЫЫЫЫЫЫЫЫ±є є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є є±±±±±±±±±±±±±±±ЫЫЫЫOKЫЫЫЫ±±±±±ЫЫCancelЫЫ±±±±ЫЫЫHelpЫЫЫЫ±±є є±±±±±±±±±±±±±±±±ІІІІІІІІІІІ±±±±ІІІІІІІІІІІ±±±ІІІІІІІІІІІ±є є±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±є ИНННННННННННННННННННННННННННННННННННННННННННННННННННННННННј Рис. 11.1 Типичное диалоговое окно. В этом отношении диалоговое окно не отличается от дру- гого окна Turbo Vision. Это не только область, куда вы за- писывается данные, но и то, что содержит другие отображае- мые элементы. Если вы хотите вывести в окне текст, то вклю- чаете в окно текстовый отображаемый элемент. Чем отличаются окна и диалоговые окна ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД В большинстве случаев объекты окон и диалоговых окон взаимозаменяемы. Однако диалоговые окна имеют несколько до- полнительных обновлений, которые делают их особенно полез- ными при использовании в качестве режимных отображаемых элементов. Если вы помните, режимным может быть любой груп- повой отображаемый элемент, включая окна, диалоговые окна и приложения. Однако, диалоговые окна включают в себя некото- рые элементы, которые пользователи ожидают от режимных отображаемых элементов. Объекты диалоговых окон обрабатывают события несколько отлично от диалоговых окон. Они делают следующее: - преобразуют нажатия клавиши Esc в команды cmCancel; - преобразуют нажатия клавиши Enter в оповещения cmDefault; - в ответ на стандартные команды cmOK, cmCancel, cmYes и cmNo закрывают диалоговое окно (завершая режимное состояния). Работа с окнами ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД В данном разделе описывают различные задачи, которые вы можете выполнять над всеми объектами окон, включая диа- логовые окна: - построение оконных объектов; - включение окон в оперативную область; - работа с режимными окнами; - изменение используемых по умолчанию параметров окон; - управление размером окон; - создание полос прокрутки окна. Построение оконных объектов ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Оконные объекты обеспечивают определенную гибкость, позволяющую вам настроить их поведение без необходимости создания новых производных оконных типов. Данных раздел освещает следующие темы: * Построение используемого по умолчанию объекта окна. * Изменение флагов окна. Построение используемых по умолчанию окон ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Конструктор используемого по умолчанию оконного объек- та воспринимает три параметра: ограничивающий прямоуголь- ник, строку заголовка и номер окна. Используемое по умолча- нию окно создает групповой отображаемый элемент с заданными границами, устанавливает его поле заголовка для указания на копию строки заголовка, сохраняет номер окна и устанавлива- ет состояние флагов параметров, чтобы задать тень окна или сделать его выбираемым. После вызова конструктора окна вы можете модифициро- вать любое из его полей, как и в случае любого другого объ- екта. Например, чтобы окно центрировалось при включении его в оперативную область, установите в его поле Options флаг ofCentered: . . Window := New(PWindow, Init(R, 'Заголовок окна', wmNoNumber)); Window^.Options := Window^.Options or ofCentered; Application^.InsertWindow(Window); . . Изменение флагов окна ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Кроме флагов параметров стандартного отображаемого элемента оконные объекты имеют битовое поле Flags, которое управляет отдельными видами поведения при перемещении и из- менении размера. Биты в поле флагов окна идентифицируются константами, начинающимися с wf. Назначение каждого из этих флагов описывается в следующей таблице: Действие флагов окна Таблица 11.1 ЪДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї іФлаг і Смысл і ГДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ іwfMove і Пользователь может перемещать окно путем букси-і і і ровки его заголовка. і ГДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ іwfGrow і Пользователь может изменять размер окна путемі і і буксировки нижнего правого угла. і ГДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ іwfClose і Пользователь может закрывать окно, щелкая кноп-і і і кой "мыши" в верхнем левом углу. і ГДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ іwfZoom і Пользователь может распахивать окно или возвра-і і і щать его к прежнему размеру, щелкая кнопкой ві і і правом верхнем углу. і АДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ По умолчанию для окон устанавливаются все четыре флага. Включение окон в оперативную область ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Окна обычно включаются в группу оперативной области приложения, так как вы обычно хотите, чтобы окно выводилось в области между строкой меню и строкой состояния без их пе- рекрытия. Включение в оперативную область обеспечивает от- сечение окон на границе оперативной области. Лучшим способов включение окна в оперативную область является вызов метода InsertWindow объекта приложения. Ме- тод InsertWindow выполняет над объектом окна перед его включением две проверки допустимости. Этим обеспечивается, что при включении окна пользователь сможет его использо- вать. Примечание: О проверке надежности, выполняемой InsertWindow, рассказывается в Главе 10 "Объекты при- ложения". InsertWindow - это функция. Она возвращает указатель на окно, передаваемый в качестве параметра, если окно было допустимым, или nil, если окно допустимым не было. Если ок- но не было допустимым, InsertWindow уничтожает его, так что вам не требуется снова обращаться к указателю. Фактически, во многих случаях вам, вероятно, даже не потребуется ре- зультат функции. Поскольку InsertWindow полностью берет на себя проверку допустимости окон, вы можете использовать преимущества расширенного синтаксиса (директиву компилятора $X+) для интерпретации InsertWindow как процедуры. Следующая программа показывает типичное использование InsertWindow как процедуры. Эту же программу содержит файл INSWIN.PAS на дистрибутивных дисках. procedure InsWin; uses Objects, App, Drivers, Views, Menus; const cmNewWin = 2000; type TInsApp = object(TApplication) WinCount: Integer; procedure HandleEvent(var Event: TEvent); virtual; procedure InitMenuBar; virtual; end; procedure TInsApp.HandleEvent(var Event: TEvent); var R: TRect; begin inherited HandleEvent(Event); if Event.What = evCommand then begin if Event.Command = cmNewWin then begin Inc(WinCount); Desktop^.GetExtent(R); InsertWindow(New(PWindow, Init(R, 'Test window', WinCount))); end; end; end; procedure TInsApp.InitMenuBar; var R: TRect; begin GetExtent(R); R.B.Y := R.A.Y + 1; MenuBar := New(PMenuBar, Init(R, NewMenu( NewItem('~A~dd window', 'F3', kbF3, cmNewWin, hcNoContext, nil))))); end; var InsApp: TInsApp; begin InsApp.Init; InsApp.Run; InsApp.Done; end. Выполнение режимных окон ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Выполнение режимного окна аналогично включению окна в оперативную область. Существуют два исключение: это то, что окно становится текущим режимным окном приложения и то, что вы передаете для инициализации его управляющих элементов запись данных. Использование режимных окон требует от вас понимание следующих трех задач: - перевода окна в режимное состояние; - завершение режимного состояния; - обработка записи данных. Перевод окна в режимное состояние ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Выполнить режимное окно очень просто. После построения оконного объекта вы передаете его методу объекта приложения ExecuteDialog. Как и подразумевает его название, метод ExecuteDialog используется обычно с диалоговыми окнами, но вы можете выполнять любой оконный объект. Метод ExecuteDialog воспринимает два параметра - ука- затель на объект окна и указатель на запись данных для ини- циализации управляющих элементов окна (как описывается в следующем разделе). Приведем простой пример использования ExecuteDialog: MyWindow := New(PWindow, Init(R, 'Будет режимным', wmNoNumber)); ExecuteDialog(MyWindow, nil); Передача значения nil в качестве указателя записи дан- ных позволяет обойти автоматическую установку и считывание значений управляющих элементов. Завершение режимного состояния ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД На самом деле единственная тонкость при работе с диа- логовыми окнами заключается в том, чтобы убедиться, что вы обеспечили способ завершения режимного состояния. Все окон- ные объекты наследуют ют от TGroup метод EndModal, но вы должны убедиться, что ваши объекты в ответ на некоторые со- бытия или событие вызывают метод EndModal. Объекты диалого- вых окон обладают этой возможностью построения методов HandleEvent по умолчанию, но если вы хотите выполнять дру- гие диалоговые объекты, то должны добавить это самостоя- тельно. Обработка записей данных ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД ExecuteDialog автоматически поддерживает установку и считывание управляющих элементов окна. Второй передаваемый ExecuteDialog параметр указывает на запись данных для уп- равляющих элементов окна. Запись данных поясняется ниже в разделе "Работа с управляющими элементами". После выполнения окна ExecuteDialog вызывает метод ок- на SetData, передавая запись данных, на которую указывает второй параметр. Когда пользователь завершает режимное сос- тояние окна без отмены (другими словами, вызывает EndModal с командой, отличной от cmCancel), ExecuteDialog вызывает GetData для считывания значений управляющих элементов об- ратно в запись данных. Изменение параметров окна, заданных по умолчанию ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД После построения объектов окна существует несколько аспектов его внешнего вида и поведения, которое вы можете изменить. В этом разделе поясняются следующие вопросы: - использование стандартных палитр окна; - изменение заголовка окна; - изменение рамки окна; - использование номеров окон. Использование стандартных палитр окна ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Turbo Vision использует для оконных объектов три стан- дартных цветовых схемы. Используемой по умолчанию схемой цветов является голубое окно с белой рамкой, желтым текс- том, зелеными пиктограммами рамки и бирюзовыми полосами прокрутки. Альтернативные схемы цветов используются для се- рых окон (по умолчанию используется диалоговыми окнами) и бирюзовых окон (которые в IDE используются для сообщений и окон просмотра). Схема цветов для данного окна управляется полем Palette оконного объекта. По умолчанию конструктор объекта окна устанавливает Palette в wpBlueWindow. Чтобы изменить одну из других палитр, установите Palette в wpCyanWindow или wpGrayWindow. Метод GetColor объекта окна использует значение Palette для определения отображения цветов в па- литру объекта приложения. Примечание: Полностью об отображении цветов рассказывается в Главе 14 "Палитры и выбор цветов". Конструктор в приведенном ниже примере создает окно, которое использует бирюзовую палитру окна: constructor TCyanWindow.Init(var Bounds: TRect; ATitle: TTitleStr; ANumber: Integer); begin inherited Init(Bounds, ATitle, ANumber); { используемое по умолчанию окно} Palette := wpCyanWindow; { изменение палитры окна} end; Изменение заголовка окна ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Объект окна сохраняет строку заголовка, переданную его конструктору, в поле Title. Однако в общем случае вам сле- дует обращаться к строке заголовка с помощью метода GetTitle объекта окна, который предоставляет возможность ограничить длину строки заголовка. В общем случае единс- твенной частью программы, где требуется доступ к заголовку окна, является рамка объекта окна, которая вызывает метод GetTitle при своем отображении. GetTitle воспринимает единственный параметр целого ти- па, который вы можете использовать для ограничения длины возвращаемой строки. По умолчанию GetTitle игнорирует пара- метр длины и возвращает всю строку Title, которую в случае превышения заданной длины рамка затем усекает. Во многих случаях не важно, какая часть строки усекается. Однако, ес- ли вы хотите сохранить определенную информацию, то можете переопределить метод GetTitle для возврата строки соответс- твующей длины, которая содержит важную информацию. Вы можете также использовать метод GetTitle для возв- рата различных заголовков в зависимости от обстоятельств. Например, тип TEditWindow обычно выводит на экран полное имя маршрута файла в редакторе. Если файл еще не имеет име- ни, GetTitle возвращает вместо этого строку 'Untitled'. Изменение рамки окна ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД По умолчанию объект окна строит экземпляр типа TFrame, используя его в качестве его рамки. Объекты рамки очень просты, и у вас редко возникает необходимость изменять их. Однако, Turbo Vision облегчает изменение рамки окна, если вы этого хотите. По умолчанию конструктор объекта окна Init вызывает для построения объекта рамки виртуальный метод InitFrame и присваивает этот объект полю Frame объекта. После вызова InitFrame конструктор Init проверяет, что поле Frame отлич- но от nil, и, если может, включает его. Чтобы построить другую рамку, переопределите InitFrame для построения экземпляра некоторого типа, производного от TFrame, и присваивает этот объект Frame. Затем Init будет включать вашу производную рамку в окно. Использование номеров окон ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Последний параметр, передаваемый используемому по умолчанию конструктору окна, это номер, который окно запи- сывает в поле Number. Если число находится между 1 и 9, то число выводится в рамке окна справа от заголовка рядом с пиктограммой распахивания. По умолчанию нажатие клавиш Alt+1 по Alt+9 выбирает (активизирует и выводит на передний план) окна с соответствующим номером. Turbo Vision предусматривает механизм для отслеживания того, какие номера вы присвоили и какие из них доступны. Если вы хотите использовать преимущества работы с номерами окон, ваше приложение само должно поддерживать номера. Turbo Vision только обрабатывает присваивание передаваемых номеров полю Number и выбирает окна, выделенные по клавише Alt. Turbo Vision также предусматривает мнемонические конс- танты wmNoNumbner, которые вы можете передать конструктору окна для указания того, какое окно имеет конкретный номер. Управление размером окна ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД По умолчанию пользователи могут изменять размеры окон путем буксировки нижнего правого угла в нужную позицию и распахивания окна для заполнения оперативной области путем щелчка кнопкой "мыши" на пиктограмме окна. Tubo Vision дает вам средство управления обоими этими аспектами поведения, что позволяет вам ограничивать размер окон и задавать для окон "нераспахиваемый" размер. Ограничение размера окна ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Как и для всех отображаемых элементов, минимальный и максимальный размеры окна определяются виртуальным методом SizeLimits. Однако TWindow вносит в SizeLimits одно важное изменение. По умолчанию минимальный размер отображаемого элемента равен 0. TWindow переопределяет это и устанавлива- ет минимальный размер окна в значение, записанное в пере- менной MinWinSize. По умолчанию MinWinSize ограничивает окно минимум 16 столбцами (ширина) и 6 строками (высота), что обеспечивает вывод угла изменения размера, пиктограммы закрытия, пиктог- раммы распахивания, плюс некоторая часть заголовка. Вы мо- жете переопределить SizeLimits для специальных типов окон, например, обеспечить вывод в них полос прокрутки в рамке. Распахивание окон ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Каждый объект окна имеет виртуальный метод с именем Zoom, который переключает размер окна между полным заполне- нием оперативной области (распахивание) и возвратом к преж- нему размеру, заданному полем объекта окна ZoomRect. ZoomRect первоначально содержит границы окна при его пост- роении. Когда вы распахиваете окно для заполнения оператив- ной области, в ZoomRect записывается новый размер окна, ко- торый оно имело до распахивания. Если вы хотите изменить для конкретного типа окна по- ведение при распахивании (например, чтобы при отмене распа- хивания всегда устанавливать конкретный размер), то можете переопределить Zoom. Вероятно, вы не захотите вызывать ме- тод Zoom, наследуемый из TWindow в методах потомках, так как если окно не заполняет оперативную область, TWindow.Zoom устанавливает значение ZommRect в новый размер окна. Создание полос прокрутки окна ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Объектный тип TWindow предоставляет функцию для гене- рации полос прокрутки окна. Если у вас есть окна, в которых требуется прокручивать все содержимое, вызов метода StandardScrollBar строит, включает объект полосы прокрутки в рамке окна и возвращает указатель на него. StandardScrollBar воспринимает единственный параметр, задающий нужный вам вид полосы прокрутки. Если вы передаете sbVertical, то метод возвращает вертикальную полосу прок- рутки с левой стороны рамки окна. Передача sbHorizontal да- ет горизонтальную полосу прокрутки в нижней части рамки ок- на. Чтобы полученная в результате полоса прокрутки могла кроме нажатия кнопки "мыши" реагировать на клавиши стрелок и перевода страницы, вы может комбинировать sbHandleKeyboard с sbVertical или sbHorizontal (с помощью операции or). В следующем примере конструктор окна использует StandardScrollBar для создания полос прокрутки для прокрут- ки внутренней области, заполняющей окно. Заметим, что вам не нужно включать полосы прокрутки окна, как это обычно де- лается. constructor TScrollWindow.Init(Var Bounds: TRect; ATitle: TTitleStr; ANumber: Integer); var R: TRect; Interior: PScroller; begin inherited Init(Bounds, ATitle, ANumbner); { построение окна } GetExtent(R); { получить границы окна } R.Grow(-1, -1); { сжать прямоугольник } Interior := New(PScroller, Init(R), { построить в R элемент прокрутки } StandardScrollBar(sbHorizontal or sbHandleKeyboard), StandardScrollBar(sbVertical or sbHandleKeyboard)); Insert(Interior); end; Работа с диалоговыми окнами ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Диалоговые окна могут делать все то, что делает любой другой объекта окна. Основное отличие между объектами диа- логового окна и объектом окна состоит в том, что объект ди- алогового окна имеет различные используемые по умолчанию атрибуты, встроенную поддержку для режимных операций и приспособлены для работы с диалоговыми объектами. В данном разделе описываются атрибуты диалогового окна и режимные операции. Использование управляющих элементов описывается ниже в соответствующих разделах. Используемые по умолчанию атрибуты диалогового окна ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Назначенные по умолчанию свойства объекта диалогового окна только слегка отличаются от других оконных объектов. Конструктор диалогового окна воспринимают вместо трех толь- ко два параметра, поскольку диалоговые окна по умолчанию не имеют номера окна. Между используемыми по умолчанию объектами диалоговых окон и оконными объектами существуют следующие отличия: - Серая схема цвета (Palette равно wpGrayWindow). - Отсутствие номера окна. - Фиксированный размер, поэтому GrowMode равно нулю, а Flags исключает wfGrow и wfZoom. Эти отличия влияют на диалоговые окна, когда вы ис- пользуете их как режимные или безрежимные окна или выполня- ете их как режимные диалоговые окна. Поведение режимного диалогового окна ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Объекты диалоговых окон имеют два метода, которые уп- рощают их использование как режимных отображаемых элемен- тов. Это методы HandleEvent и Valid. Обработка событий диалогового окна ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Объекты диалоговых окон обрабатывают большинство собы- тий аналогично другим оконным объектам, но вносите два из- менения, которые вы заметите при использовании диалогового окна в качестве режимного отображаемого элемента: * Клавиши Enter и Esc обрабатываются особым образом. По клавише Enter рассылается оповещающее сообщение cmDefault, что приводит в действие командную кнопку (как при ее "нажатии"). Esc транслируется в команду cmCancel. * Отдельные команды автоматически завершают режимное состояние. Команды cmOk, cmCancel, cmYew и cmNo приводят к вы- зову EndModal (команда передается в качестве пара- метра). Использование в диалоговом окне управляющих элементов ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Обычно диалоговые окна используются как место для включения управляющих элементов. Управляющие элементы представляют собой специализированные отображаемые элемен- ты, которые позволяют реализовать такие виды взаимодействия с пользователем как командные кнопки, блоки списка и полосы прокрутки. Хотя вы можете включать управляющие элементы в объект окна, диалоговые окна специально приспособлены для работы с ними. Добавление в диалоговое окно управляющих элементов ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Добавление в окно управляющих элементов аналогично до- бавлению любых других отображаемых подэлементов. Обычно эта операция является частью конструктора окна. После вызова наследуемого конструктора окна вы можете построить и вклю- чить другие управляющие объекты. Это показано в следующем примере: constructor TCtlWindow.Init(var Bounds: TRect; ATitle: TTitleStr; ANumber: Integer); var R: TRect; begin inherited Init(Bounds, ATitle, ANumber); { построение окна } R.Assign(5, 5, 20, 7); Insert(New(PInputLine, Init(R, 15))); { включение управляющего элемента } R.Assign(10, 8, 20, 10); Insert(New(PButton, Init(R, 'O~k~', cmOk, bfDefault))); { командная кнопка } end; Вы должны представлять тот порядок, в котором включае- те управляющие элементы. Порядок включения (вставки) уста- навливает Z-последовательность отображаемых элементов, ко- торые, в свою очередь, определяют порядок табуляции для уп- равляющих элементов. Порядок табуляции - это порядок, в ко- тором управляющие элементы получают фокус (становятся выде- ленными) при нажатии пользователем клавиши Tab. Порядок табуляции имеет важное значение, поскольку он определяет: - порядок взаимодействия с пользователем; - порядок инициализации управляющих элементов. Как пользователи наблюдают порядок табуляции ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Хорошим примером того, как порядок табуляции представ- ляется пользователям, является формуляр ввода данных. Когда пользователь заканчивает ввод в одном поле и нажимает кла- вишу Tab для перехода к следующему полю, фокус должен пере- мещаться на следующий логический управляющий элемент. Если программист неаккуратно задал порядок полей ввода данных, это раздражает пользователей и делает их работу менее про- дуктивной. Общепринятых правил, управляющих порядком управляющих элементов в диалоговом окне, нет, но в общем неплохо иметь такой порядок. Когда установлен порядок сверху-вниз или слева-направо, то следует придерживаться этого шаблона. Как программист наблюдает порядок табуляции ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Как уже отмечалось ранее, порядок включения управляющих эле- ментов в окно определяет порядок табуляции, так что когда вы за- писываете для окна код инициализации, учитывайте порядок, в кото- ром вы создаете и включаете управляющие элементы. Важно принимать во внимание не только фактический код, в ко- тором создаются и включаются управляющие элементы, но также и код, в котором устанавливаются и считываются значения управляющих элементов (как описывается в следующем разделе). Работа с управляющими элементами ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД В любой момент после построения объекта окна с управ- ляющими элементами вы можете установить или считать значе- ния всех управляющих элементов с помощью методов SetData и GetData. Эти методы отличаются от соответствующих методов в управляющих и других отображаемых элементах. Все группы, включая окна и диалоговые окна, наследуют методы GetData и SetData, которые выполняют итерацию по их подэлементам в Z-последовательности, вызывая методы GetData и SetData отображаемых подэлементов. Примечание: О методах GetData и SetData и от- дельных управляющих элементах рассказывается в Главе 12 "Объекты управляющих элементов". В том случае, когда окно содержит управляющие элемен- ты, вызов его метода SetData приводит к вызову по порядку метода SetData каждого управляющего элемента, так что вмес- то того, чтобы вручную инициализировать каждый управляющий элемент, вы можете переложить эти обязанности на окно. Пе- редаваемый SetData параметр - это запись, которая содержит поле для каждого управляющего элемента в окне. Определение записей данных окна ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Чтобы определить запись данных для окна или диалогово- го окна, сделайте следующее: - перечислите каждый управляющий элемент в Z-последо- вательности; - определите для каждого управляющего элемента запись данных; - создайте для каждого управляющего элемента запись с полем. Установка значений управляющих элементов ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД Метод SetData объекта окна вызывает в Z-последователь- ности методы SetData каждого его отображаемого подэлемента. Передаваемая каждому отображаемому подэлементу запись дан- ных является подмножеством записи, переданной методу SetData окна. Первый управляющий элемент в Z-последователь- ности получает весь отображаемый элемент. Если он считывает из записи несколько байт (что сообщается методом DataSize), то SetData передает следующему отображаемому подэлементу только оставшуюся часть записи. Поэтому если первый управ- ляющий считывает 4 байта, метод SetData объекта окна пере- дает второму отображаемому подэлементу запись, начинающуюся на 4 байта от начала исходной записи. |