Эффективный способ применения интерфейсов в mdi приложениях


Многооконный интерфейс. Создание MDI-приложения , страница 3

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

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

Меню в MDI-приложениях

Строка меню главного окна MDI должна включать выпадающее меню Window (Окно). Обычно оно предшествует выпадающему меню Help и содержит пункты Tile (Мозаика), Cascade (Каскад), Arrange Icons (Упорядочить значки) и Close All Child Windows (Закрыть все дочерние окна). Это реализуется при помощи сообщений окна, о которых речь пойдет ниже. Кроме того, клиентское окно MDI добавляет имена вновь созданных дочерних окон в нижнюю часть выпадающего меню, который указывается при создании клиентского окна MDI.

На рис. 27.2 показано простое MDI-приложение. Открыто четыре дочерних окна, из которых одно свернуто в значок в нижней части клиентского окна MDI. Стоит обратить внимание на меню Window. Клиентское окно MDI динамически поместило имена четырех дочерних окон в нижнюю часть раскрывающегося меню, а также установило отметку рядом с именем окна, активного в данный момент.

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

Таблица 27.2 Комбинации клавиш сокращенного доступа для MDI-приложений

Создание MDI и SDI интерфейсов: разработка меню, панели инструментов, стандартных диалоговых окон, строки состояния, контекстного меню

Имеется два типа интерфейсов: SDI (Single Document Interface однодокументный интерфейс) и MDI (Multi Document Interface многодокументный интерфейс). SDI-приложения работают одновременно с одним документом, MDI-приложения предназначены для одновременной работы со множеством однотипных документов. При этом все документы располагаются внутри одного контейнера, которым служит, как правило, главная форма. Компания Microsoft не рекомендует использовать MDI-интерфейсы, хотя сама использует их в различных служебных программах, например, в консолях вроде Диспетчера устройств. Кроме того, разработка MDI-приложений в Lazarus пока не реализована.

Главное отличие MDI-отSDI-приложения состоит в том, что SDI-приложение содержит только одно окно документа, a MDI-приложение способно содержать несколько таких окон, что дает пользователю возможность параллельной работы с несколькими документами.

В SDI-приложениях окна могут быть двух видов модальные и немодальные. Создаются они одинаково, разница заключается только в способе вывода этих окон на экран. Модальное окно блокирует программу, не даёт с ней работать, пока вы это окно не закроете. Стандартный пример модального окна — окно «О программе», которое присутствует почти в любом приложении. Как правило, такое окно находится в меню «Справка». Пока вы не нажмете «ОК», закрыв это окно, вы не сможете работать с основной программой.

Немодальные окна позволяют переключаться между ними, и программой, и работать одновременно и там, и там. Типичный пример — окна Lazarus вы можете переключаться между Инспектором объектов, Редактором кода, Редактором форм, и другими окнами — они не мешают друг другу, так как все они немодальные.

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

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

Среды визуального программирования

В настоящее время на рынке программного обеспечения наиболее распространенными и покупаемыми являются такие среды визуального программирования как Borland Delphi, С++ Builder, Visual Studio 2005 Standard Edition.

Borland Delphi – это проверенный продукт, предназначенный для разработки приложений в среде WINDOWS 95/98/2000/Me/NT/XP. Его популярность на рынке программного обеспечения обеспечивается следующими факторами:

-сочетание классических принципов программирования и современных технологий. Delphi объединяет визуальные средства быстрого создания приложений, высокопроизводительный (до 350000 строк кода в минуту на Pentium 90) компилятор объектно-ориентированного языка, компонентную архитектуру среды разработчика, масштабируемый механизм доступа к базам данных;

-возможность многократно использовать однажды созданный код, уменьшая тем самым время и усилия, затрачиваемые на разработку;

-быстрый компилятор делает процесс разработки интерактивным;

-настоящий машинный код работает в 10-20 раз быстрее, чем в системах на основе интерпретатора;

-достигается высочайшая скорость разработки за счет использования набора встроенных компонент, средств визуального проектирования и отладки RAD — Rapid Application Development;

-прототип превращается в целевую систему без переработки;

-приложения и компоненты создаются с помощью одного инструмента – объектно-ориентированного языка программирования Object Pascal;

-осуществлять поддержку приложений баз данных, которая состоит в организации доступа к базам данных и управление данными из приложений через процессоры баз данных, таких как Borland Database Engine или ActiveX Data Objects фирмы Microsoft, а также разрабатывать приложения клиент-сервер для баз данных на основе SQL-серверов;

-разрабатывать Интернет — приложения и Web-серверные расширения;

-создавать собственные компоненты, библиотеки динамической компоновки DLL и пакеты, разрабатывать COM-приложения;

-использовать механизмы ActiveX и OLE Automation — высокоуровневые программные технологии, основывающиеся на COM.

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

C++ Builder, также как и Borland Delphi, относится к RAD_системам.

С++ Builder имеет те же возможности, что и Delphi, однако имеет и существенные преимущества, такие как:

— визуальная среда разработки, имеет ряд дополнительных функций, по сравнению с Delphi, что облегчает и ускоряет разработку приложений; наличие библиотеки шаблонов STL (Standard Template Library);

— поддержка библиотеки MFC (Microsoft Foundation Classes); возможность импорта и дальнейшей компиляции приложений Visual C++, работающих с MFC;

— больший, по сравнению с Delphi, объем сервисных функций; гибкость и расширяемость языка C++, за счет возможности перегрузки операторов, создания библиотек шаблонов и т.д.

К числу достоинств С++ Builder можно отнести:

— универсальность, краткость и относительно низкий уровень;

— адекватность большинству задач системного программирования.

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

Вывод: С++ Builder – это универсальный язык программирования с компактным способом записи выражений, современными механизмами управления данными и богатым набором операторов. Кроме того, он поддерживает абстракцию данных с помощью механизма классов и обеспечивает объектно-ориентированное программирование и позволяет вводить собственные типы данных, соответствующие решаемым задачам.

Visual Studio 2005 Standard Edition.Эта версия Visual Studio представляет собой профессиональный инструмент начального уровня. В нем сочетается простота версий Express и мощные средства разработки, необходимые для:

— создания бизнес-приложений с использованием языков Visual Basic, С#, C++;

— создания Windows — и веб-приложений, приложений для портативных устройств;

— создания клиент-серверных приложений с использованием веб-служб, и приложения для работы с данными, для чего есть встроенные инструменты.

Также Visual Studio позволяет:

— использовать возможности по генерированию отчетов SQL Reporting Services для оформления выводимых данных;

— расширять возможности Visual Studio с помощью инструментов третьих фирм;

— создавать корпоративные веб-приложения масштаба подразделения, используя средства отладки и прекомпиляции сайтов.


Вывод: Visual Studio.net на сегодняшний день является одной из самых продуктивных и производительных сред для создания приложений, что обусловлено следующими важнейшими аспектами:

-универсальным межъязыковым интерфейсом;

-качеством визуальной среды обработки;

-скоростью работы компилятора и высоким быстродействием откомпилированных программ;

-гибкостью и масштабностью используемой базовой архитектуры.

Интерфейсы SDI и MDI

Операционная система Windows предоставляет возможность разрабатывать

приложения в одной из двух разновидностей интерфейса:

SDI (Single Document Interface) – однодокументный интерфейс.

MDI (Multiple Document Interface) – многодокументный интерфейс.

Каждый из указанных стилей интерфейса обладает своими уникальными

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

Чтобы далеко не ходить за примером,

предлагаю взглянуть на утилиту Sysedit.exe, предназначенную для редактирования системных файлов (она

входит в стандартную комплектацию

Windows). Для этого в строке ввода

Открыть надо набрать команду Sysedit

(рис. 9.5) и нажать кнопку ОК.

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

Как я и обещал, перед вами появилось

несколько дочерних окон, расположенных внутри главного окна (рис. 9.6). Вы не сможете переместить дочерние окна за пределы главного, и все управление дочерними окнами осуществляется из общего главного меню программы. Приложение SDI также строится на базе главного и дочерних окон, но его дочерние окна имеют гораздо больше степеней свободы: они не столь крепко

привязаны к главной форме проекта

и могут свободно разгуливать по всему пространству рабочего стола. В качестве примера классического приложения SDI может выступать Delphi. Главным окном Delphi служит форма, на

которой расположены главное меню, палитра компонентов и «быстрые» кнопки. Остальные окна (окна проекта, Инспектор объектов и др.) являются дочерними.

Дата добавления: 2020-03-15 ; просмотров: 1595 ; ЗАКАЗАТЬ НАПИСАНИЕ РАБОТЫ

Работа с многодокументными (MDI) формами

Другая разновидность форм — многодокументные, или MDI-формы. Интерфейс MDI позволяет открыть сразу несколько окон внутри родительского окна-контейнера. Скажем, в главном окне типичного текстового редактора (классический пример — Word for Windows) можно открыть несколько документов (рис. 2.7). Главное окно выполняет функции контейнера, а дочерние формы находятся внутри него. MDI-приложения появились в те времена, когда на рынке преобладали ранние версии Windows. Многодокументные приложения позволяли одновременно открывать несколько файлов, не запуская нескольких копий одной программы. Это экономило не только время, но и память.

Рис. 2.7. Несколько документов в родительском окне-контейнере

Интерфейс MDI обычно используется для документно-ориентированных приложений (например, текстовых или графических редакторов). Программа называется документно-ориентированной, если она предназначена для работы с документами. Если пользователи приложения должны одновременно работать с несколькими формами, выбирайте модель MDI. Visual Basic позволяет очень просто создавать MDI-приложения.

Создание MDI-приложения

Для создания MDI-приложения необходимо иметь как минимум две формы — родительскую (контейнер) и дочернюю (внутреннюю). Чтобы работать с дочерними формами различных типов, необходимо включить в проект дополнительные формы. Тем не менее для простейшего MDI-проекта одной формы вполне достаточно. Проект создается так:

1. Создайте новый проект командой File >• New Project. Если у вас включен Project Wizard, выберите тип проекта Standard EXE.

2. В созданном проекте изначально присутствует одна форма. Задайте ее свойству Name значение frmChild, а свойству Caption — Дочерняя форма MDI.

3. Чтобы создать родительскую форму MDI, щелкните правой кнопкой мыши на папке Forms в окне проекта и выполните команду Add > MDI Form. Когда на экране появится окно Form Wizard, выберите в нем MDI Form.

4. Задайте свойству Name значение frmMDI, а свойству Caption — значение Родительская форма MDI

5. Щелкните правой кнопкой мыши на строке Project1 в окне проекта и выберите из контекстного меню команду Project1 Properties. Выберите из списка Startup Object строку frmMDI. Если не сделать этого, в начале работы вашего приложения будет отображаться дочерняя форма.

6. Выберите в окне проекта строку frmChild. Задайте свойству MDI Child значение True — теперь дочерняя форма будет находиться внутри родительской.

7. Выберите в окне проекта строку frmMDI.

8. Запустите редактор меню командой Tools > Menu Editor. Появляется окно, изображенное на рис. 2.8.

Рис. 2.8. Редактор меню

В своем примере мы ограничимся очень простым меню. Не беспокойтесь о технических подробностях работы с редактором меню — они будут рассмотрены в уроке 5, «Создание и использование меню и панелей инструментов».

1. Введите &File в поле Caption.

2. Введите mnuFile в поле Name.

3. Нажмите кнопку Next.

4. Нажмите кнопку со стрелкой вправо — у команды меню появляется отступ.

5. Введите &New Form в поле Name.

6. Введите mnuFileNew в поле Name.


7. Закройте редактор меню кнопкой ОК.

8. В форме frmMDI должно появиться меню File. Выполните команду File > New, и на экране появится окно программы.

9. Вставьте в процедуру события mnuFileNew_Click() следующий фрагмент:

Dim frm As New frmChild

10. Сохраните и запустите проект. На рисунке показан примерный вид окна MDI-ириложения.

Добавленный нами фрагмент создает новую копию формы frmChild и отображает ее. Это происходит при каждом выполнении команды File > New. Попробуйте открыть и закрыть несколько дочерних окон; как видите, мы построили вполне рабочее MDI-приложение.

Новая версия MDI-приложения

Чтобы наша программа действительно была похожа на коммерческие MDI-приложения Windows, необходимо добавить несколько дополнительных штрихов. Например, все дочерние формы имеют одинаковые названия, так что их невоз-

можно отличить друг от друга; этот недостаток нужно исправить. Также хотелось бы иметь возможность упорядочить дочерние окна и выстроить их «мозаикой», или «каскадом». Наконец, в меню должен присутствовать список окон, при помощи которого можно легко перейти к любому дочернему окну, даже если оно спрятано под другими окнами.

1. Запустите редактор меню и добавьте в строку меню родительского окна, frmMDI, новое меню &Window. Установите флажок WindowList в редакторе меню.

2 Добавьте в новое меню команды Tile и Cascade. Назовите их mnuWindowTile и mnuWindowCascade соответственно.

3. Закройте редактор меню кнопкой ОК.

4. Добавьте в процедуру события Click объекта mnuWindowTile следующую строку:

5. Добавьте в процедуру события Click объекта mnuWindowCascade следующую строку:

vbCascade и vbTileHorizontal — встроенные константы Visual Basic. Их значение описано в справочной системе.

6. Измените код процедуры для команды mnuFileNew:

Private Sub mnuFileNew_Click()

Static Counter As Integer

Dim frm As New frmChild

Counter = Counter + 1

frm.Caption = «Дочерняя форма MDI» & Counter

7. Сохраните и запустите приложение. Обратите внимание на отличия рис. 2.9 от предыдущего рисунка.

Расшифровка программы

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

Первая строка (Static Counter As Integer) приказывает Visual Basic создать переменную с именем Counter. Ключевое слово Static говорит о том, что значение Counter будет запоминаться при каждом вызове процедуры. Это позволяет использовать Counter для подсчета создаваемых форм.

Рис. 2.9. Усовершенствованное MDI-приложение

Во второй строке (Dim frm As New frmChild) встречается оператор Dim. Обычно он определяет размерность массива, но в данном случае описывает переменную с именем frm, которая создается на основе формы frmChild. Ключевое слово New сообщает Visual Basic о том, что речь идет о новой форме, а не об одной из форм, созданных в этой процедуре.

Поскольку значение переменной Counter запоминается между вызовами процедуры, его можно увеличить следующей строкой:

Counter = Counter + 1

Например, если значение Counter было равно 3, то оператор присвоит ей текущее значение «3 плюс 1», то есть 4. Раз переменная Counter объявлена с ключевым словом Static, новое значение будет сохранено, так что при следующем вызове процедуры переменной Counter будет присвоено значение 5, затем — 6 и т. д. Следующий оператор (frmCaption = «Дочерняя форма MDI» & Counter) заменяет содержимое свойства Caption строкой, которая состоит из текста «Дочерняя форма MDI» и номера, хранящегося в переменной Counter. Например, свойство Caption во второй форме будет иметь значение «Дочерняя форма MDI 2». Последняя строка, так сказать, вдыхает жизнь в форму. Она приказывает Visual Basic отобразить форму на экране. Поскольку мы уже рассматривали процесс создания формы, вы знаете, что работа новой формы начинается с события Load. После загрузки форма активизируется и отображается на экране.

Использование Form Wizard

Когда вы освоите программирование на Visual Basic, вам нередко придется добавлять в свои проекты новые формы (как это было сделано в примере MDI-приложения). Поскольку однодокументный (SDI) интерфейс не позволяет имитировать многие возможности интерфейса MDI, для этой цели были созданы специальные MDI-формы. В начале работы над приложением может выясниться, что одиночной формы не хватает для решения задачи; в этом случае следует добавить новую форму и настроить ее для выполнения требуемой задачи. В Visual Basic 6 процесс добавления форм упрощен до предела: Form Wizard поможет выбрать форму нужного типа и добавить ее в проект. Достаточно щелкнуть правой кнопкой мыши внутри окна проекта и выбрать в контекстном меню команду Add > Form — Form Wizard открывает диалоговое окно Add Form (рис. 2.10).

Рис. 2.10. Form Wizard

Как видите, выбор достаточно широк — диалоговое окно About (About Dialog), заставка (Splash Screen), окно с полезными советами (Tip of the Day) и даже окно-броузер (Web Browser)! После того как вы выберете форму в окне Wizard, Visual Basic создаст шаблон с необходимой графикой и кодом и включит его в проект. Form Wizard заметно экономит время, поскольку он автоматически генерирует часть программного кода для формы.

ПОДСКАЗКАЕсли вы хотите, чтобы формы имели стандартный вид и при этом вам пришлось поменьше программировать, поручите свою работу Form Wizard.

Давайте воспользуемся Form Wizard и добавим в проект еще одну форму:

1. Щелкните правой кнопкой мыши в окне проекта и выберите из контекстного меню команду Add > Form. На экране появляется окно Form Wizard.

2. Включите в проект новую форму — для этого следует выбрать значок About Dialog в окне Add Form. Новой форме автоматически присваивается имя frm-

Илон Маск рекомендует:  Что такое код each

3. Дважды щелкните на строке frmMDI в окне проекта, чтобы активизировать MDI-форму.

4. Вызовите редактор меню и добавьте в форму новое меню — для этого следует щелкнуть на пустом месте под названием команды &New Form. Проследите, чтобы там не было многоточия (. ). — если оно присутствует, удалите его кнопкой со стрелкой влево. Теперь задайте свойству Caption значение &Help, a свойству Name — значение mnuHelp.

5. Нажмите кнопку Next, чтобы добавить следующую команду.

6. Добавьте новую команду в меню mnuHelp — для этого нажмите в редакторе меню кнопку со стрелкой вправо. Задайте свойству Caption значение &About, a свойству Name — значение mnuHelpAbout. Если не нажать кнопку со стрелкой, то вместо новой команды меню Help будет создано новое меню. Закройте редактор меню кнопкой ОК.

7. Щелкните правой кнопкой мыши в окне проекта и выберите из контекстного меню команду Project1 Properties.

8. В открывшемся диалоговом окне Project Properties перейдите на вкладку Make.

9. Замените содержимое поля Title текстом Пример MDI-приложения.

10. В группе Version Information выберите из списка Туре строку Company Name и введите название своей организации в поле Value.

11. Найдите в списке Туре строку Product Name и введите значение MDI-приложение, затем нажмите кнопку ОК.


12. Выполните команду Help > About из меню MDI-формы, чтобы открыть окно программы.

13. Включите в процедуру mnuHelpAbout_Click() следующую строку: frmAbout.Show vbModal

14. Запустите приложение и откройте диалоговое окно About (рис. 2.11).

Рис. 2.11. Диалоговое окно About, созданное Form Wizard

Напоследок давайте вернемся к ключевому слову vbModal. В уроке 4, «Работа с модулями и классами», вы научитесь передавать параметры вызываемым процедурам. В данном случае ключевое слово vbModal — параметр, который передается методу Show формы. Он сообщает Visual Basic о том, что диалоговое окно About должно отображаться в модальном режиме. Модальная форма временно берет на себя весь процесс общения пользователя с приложением. Пока модальное диалоговое окно остается на экране, пользователь не может работать с остальными формами приложения. Модальные формы используются во многих ситуациях. Например, диалоговое окно для регистрации пользователя стоит сделать модальным, чтобы к вашему приложению допускались лишь те пользователи, которые успешно прошли регистрацию.

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

ПОДСКАЗКАЕсли в окне Visual Basic накопится много лишних форм, закройте их. Самый простой способ вызвать форму на экран — дважды щелкнуть на ее имени в окне проекта.

Что нового мы узнали?

В этом уроке мы научились:

Создавать новые формы.

Работать с формами в Visual Basic.

Пользоваться важнейшими свойствами и методами при отображении форм.

Включать новый код в процедуры событий.

Создавать MDI-приложения с работающими меню.

Создавать специализированные формы при помощи Form Wizard.

УРОК 3

Выбор и использование

Управляющих

Элементов

Управляющие элементы

Панель элементов

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

Группировка элементов

Добавление элементов на панель

Разработка интерфейсов в стиле Windows 95

Как вы узнали из предыдущего урока, приложения Visual Basic строятся на основе форм. Практически любое приложение содержит формы, а на формах находятся управляющие элементы, которые расширяют функциональные возможности приложения и обеспечивают его всем необходимым для взаимодействия с пользователем. В этом уроке мы рассмотрим самые распространенные и нужные управляющие элементы Visual Basic 6. В этом уроке нам не удастся подробно рассмотреть все элементы, и мы ограничимся лишь теми, с которыми вам придется работать больше всего. Не огорчайтесь! Остальные элементы будут рассмотрены в последующих главах.

Папиллярные узоры пальцев рук — маркер спортивных способностей: дерматоглифические признаки формируются на 3-5 месяце беременности, не изменяются в течение жизни.

Общие условия выбора системы дренажа: Система дренажа выбирается в зависимости от характера защищаемого.

Опора деревянной одностоечной и способы укрепление угловых опор: Опоры ВЛ — конструкции, предназначен­ные для поддерживания проводов на необходимой высоте над землей, водой.

Урок 5

Многооконная структура программы
Перетаскивание файлов в приложение

Часть урока для новичков

Приложения MDI и приложения SDI.

Прежде чем устанавливать вид и свойства вашего приложения надо подумать о том, какой вид приложения вы желаете создать. Это во многом зависит от его функционального назначения. Можно выбирать между так называемыми MDI- или SDI-приложениями.

MDI — сокращенно от Multiple Document Interface (интерфейс для одновременной работы со многими документами), а SDI — от Single Document Interface (интерфейс для работы с одним документом). В MDI приложениях два или более окон могут быть активны одновременно. В SDI-приложениях это невозможно. Здесь в каждый момент времени может быть активным только одно окно.

MDI -приложения являются удобным средством для одновременного выведения на экран текста или данных, которые хранятся в различных файлах. Такую структуру построения окон можно использовать для редактирования текстов, открывая и выводя на экран одновременно несколько различных документов. С помощь этих приложений можно также производить табличные вычисления, обрабатывая несколько таблиц одновременно, перенося или сравнивая данные из одной в другую. Пример такой работы над файлами — программа MS Word. Здесь файлы текстового и графического формата открываются в отдельных окнах, находящихся внутри главного окна программы.

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

Родительское окно может быть в MDI-приложениях только одно, а дочерних окон может быть теоретически бесконечно.

Большинство MDI-приложений имеют меню » Window » («Окно») со следующими пунктами: Cascade, Arrange Icons, Tile Horizontal, Tile Vertical . С их помощью пользователь может управлять дочерними окнами приложения. Обычно нижняя часть меню отделена и представляет список всех открытых окон. Это свойство весьма удобно для быстрого перехода к тому или иному редактируемому файлу.

Все SDI-окна можно разделить по свойствам доступа друг к другу как модальные и немодальные. Они определяют, может пользователь или нет переключаться на другие окна. Когда модальное окно открыто, все другие открытые окна становятся недоступными. Пример можно увидеть практически из любой программы, вызвав пункт меню «About» («О программе»). Отображаемое окно в приложении, как правило, не дает переключится на другое, пока не будет закрыто. Такое свойство иногда бывает очень полезным. Например, когда нужно чтобы пользователь ввел пароль, а затем получил доступ к определенному окну с данными, или окно фильтра данных, указав условия отбора, получает доступ к результату.

Итак, модальными или немодальными могут быть только SDI-окна.

С сегодняшнего урока мы с вами начинаем разрабатывать довольно сложный пример простого многооконного текстового редактора.

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


    Запускаем Delphi. В меню «File» выбираем пункт «New Application».

  • Переименовываем свойство имени формы Name из Form1 в MainForm.
  • Устанавливаем свойство в инспекторе объектов FormStyle в fsMDIForm. Его можно выбрать из ниспадающего списка доступных свойств. Свойство означает, что данное окно будет родительским в MDI-приложении.


    В палитре компонентов выбираем MainMenu . Это второй компонент слева на вкладке Standart. Помещаем его на проектировщик формы Form1. Дважды щелкаем по нему. Появляется конструктор меню окна. Здесь и далее для того, чтобы вызвать конструктор для размещенного в форме компонента главного меню (MainMenu), необходимо или дважды кликнуть по нему мышкой или дважды кликнуть в Object Inspector по свойству Items. Далее, пользуясь свойством Caption задайте меню такой вид, как на рисунке. Примечание: выбрать изменяемый пункт можно мышкой, для изменения названия выберите в инспекторе объектов свойство Caption, для отделения секций меню линией используйте знак тире в свойстве Caption для нужного пункта меню. Создание меню очень простое. Вы сразу видите результат изменения, добавления или удаления пунктов в редакторе формы. Вы всегда можете пополнить пункты меню, используя дополнения, выделенные пунктирной рамкой. Всегда можно вставить или удалить пункт меню в нужное место. Для этого воспользуйтесь правой клавишей мыши. Выбрать интересующее действие можно из выпадающего меню.

    Итак, содержание главного меню:

    ФайлПравкаОкнаПомощь
    НовыйВырезатьУпорядочитьО программе
    ОткрытьКопировать
    СохранитьВставить
    Сохранить как:
    Закрыть
    Выход


    Выбираем пункт меню «Окна» и переименовываем его имя (идентификатор) Name в инспекторе объектов в WindowMenu.

    Выбираем мышкой форму MainForm. В Object Inspector в свойстве WindowMenu устанавливаем их ниспадающего списка пункт меню WindowMenu . Теперь во время работы приложения все дочерние окна будут списком отображаться в меню » Окна «.

    Поскольку дочерних окон в этом приложении будет много и по внешнему виду они будут подобные, то нет смысла проектировать каждое окно отдельно. Спроектируем одно. Для помещения в проект новой формы выбираем из меню » File » пункт » New Form «.

    Устанавливаем размеры окна Form2 меньшими главного родительского окна.

    Переименовываем свойство Name для этого окна из Form2 в ChildForm.

    Устанавливаем свойство дочернего окна для MDI-приложения FormStyle в fsMDIChild .

    Выбираем из палитры компонентов Memo и устанавливаем его в дочернюю форму ChildForm .

    Меняем свойство Align для этого компонента на alClient . Он немедленно расширяется на все доступное пространство окна.

    Далее будем писать событие выбора пункта меню «Новый». При нажатии на него должно появляться дочернее окно. Поступаем следующим образом. Поскольку дочерних окон будет множество, то мы программа должна их автоматически при необходимости создавать. По умолчанию Delphi устанавливает автоматическое создание окна во время запуска приложения. Это избавляет программиста от написания некоторых команд. В данном случае мы все сделаем сами. Выбираем из меню » Project » пункт » Options: «. На вкладке Forms снимаем установку » Auto-create forms » для окна ChildForm . Для этого выбираем его и перегоняем одинарной стрелкой в сторону » Available forms «. Нажимаем кнопку OK.

    Сохраняем проект на диске. Для этого проводником Windows создаем отдельную папку для хранения этого примера. В меню » File » выбираем пункт » Save Al l». Дальше появляются диалоги сохранения модулей окон и файл проекта. Модуль Unit1 родительского окна можно назвать, сохранив его под именем MainUnit . Модуль Unit2 дочернего окна приложения. можно сохранить под именем ChildUnit . Весь проект можно сохранить под именем MyEdit .

  • Теперь напишем в редакторе кода процедуру создания дочернего окна. Выбираем окно MainForm и дважды щелкаем по компоненту MainMenu1 . Выбираем пункт меню » Новый «. В Object Inspector переходим на вкладку событий Events . Дважды щелкаем по полю, правее OnClick . Или нажимаем Ctrl+Enter. Появляется готовый заголовок процедуры нажатия на пункт меню «Новый». Между begin и end пишем следующую строку:
  • Поскольку в этом окне создается другое, то в переименованный модуль MainUnit необходимо подключить ChildUnit . Для этого выбираем из меню » File » пункт «Use Unit: » и указываем модуль ChildUnit . Нажимаем OK.

    Все, запускаем программу на выполнение. Во время работы обратите внимание на список появившихся окон в меню «Окна», по которым удобно переключаться на необходимое окно.

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

    Часть урока для продвинутых программистов

    Перетаскивание файлов в приложение

    Иногда очень полезно избавить пользователя от лишних операций при открытии файла. Он должен нажать на кнопку «Открыть», затем найти интересующий каталог, выбрать файл. Проще перетащить мышкой файл сразу в окно приложения. Рассмотрим пример перетаскивания Drag & Drop в окно произвольного текстового файла, который сразу же открывается в компоненте Memo1.

    Для начала в разделе Uses необходимо подключить модуль ShellAPI .

    В private области окна нужно вставить следующую строку:

    procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES; //получение сообщений о переносе файла в окно приложения

    Процедура обработки этого сообщения будет выглядеть следующим образом:

    procedure TForm1.WMDropFiles(var Msg: TWMDropFiles);
    var
    CFileName: array[0..MAX_PATH] of Char; // переменная, хранящая имя файла
    begin
    try
    If DragQueryFile(Msg.Drop, 0, CFileName, MAX_PATH)>0 then // получение пути файла
    begin
    Form1.Caption:=CFileName; // имя файла в заголовок окна
    Memo1.Lines.LoadFromFile(CFileName); // открываем файл
    Msg.Result := 0;
    end;
    finally
    DragFinish(Msg.Drop); // отпустить файл
    end;
    end;

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

    procedure TForm1.FormCreate(Sender: TObject);
    begin
    DragAcceptFiles(Handle, True);
    end;

    С уважением, ведущий уроков Semen

    Эффективный способ применения интерфейсов в mdi приложениях

    Термин MDI (Multiple Document Interface) дословно означает многодокументный интерфейс и описывает приложения, способные загрузить и использовать одновременно несколько документов или объектов. Примером такого приложения может служить диспетчер файлов (File Manager).
    Обычно MDI-приложения состоят минимум из двух форм — родительской и дочерней. Свойство родительской формы FormStyle установлено равным fsMDIForm. Для дочерней формы установите стиль fsMDIChild.
    Родительская форма служит контейнером, содержащим дочерние формы, которые заключены в клиентскую область и могут перемещаться, изменять размеры, минимизироваться или максимизироваться. В вашем приложении могут быть дочерние формы разных типов, например одна — для обработки изображений, а другая — для работы с текстом.

    Создание форм
    В MDI-приложении, как правило, требуется выводить несколько экземпляров классов формы. Поскольку каждая форма представляет собой объект, она должна быть создана перед использованием и освобождена, когда в ней больше не нуждаются. Delphi может делать это автоматически, а может предоставить эту работу вам
    .
    Автоматическое создание форм
    По умолчанию при запуске приложения Delphi автоматически создает по одному экземпляру каждого класса форм в проекте и освобождает их при завершении программы. Автоматическое создание обрабатывается генерируемым Delphi кодом в трех местах.
    Первое — раздел интерфейса в файле модуля формы.

    В данном фрагменте кода объявляется класс TForm1.
    Вторым является место, в котором описывается переменная класса.

    var Form1: TForm1;

    Здесь описана переменная Form1, указывающая на экземпляр класса TForm1 и доступная из любого модуля. Обычно она используется во время работы программы для управления формой.
    Третье место находится в исходном тексте проекта, доступ к которому можно получить с помощью меню View/ Project Source. Этот код выглядит как:

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

    Динамическое создание форм
    Хотя автоматическое создание форм полезно при разработке SDI-приложений, при создании MDI-приложении оно, как правило, неприемлемо.
    Для создания нового экземпляра формы используйте конструктор Create класса формы. Приведенный ниже код создает новый экземпляр TForm1 во время работы программы и устанавливает его свойство Caption равным ‘New Form’.

    Form1:= TForm1.Create(Application);
    Form1.Caption:= ‘New Form’;


    Конструктор Create получает от вас в качестве параметра потомка TComponent, который и будет владельцем вашей формы. Обычно в качестве владельца выступает Application, чтобы все формы были автоматически закрыты по окончании работы приложения. Вы можете также передать параметр Nil, создав форму без владельца (или владеющую собой — как вам больше нравится), но тогда закрывать и уничтожать ее придется вам. В случае возникновения необрабатываемой ошибки такая форма останется в памяти, что не говорит о высоком профессионализме программиста.
    В приведенном ниже коде Form1 указывает только на последнюю созданную форму. Если вам это не нравится, воспользуйтесь приведенным ниже кодом — возможно, он более точно отвечает вашим запросам:

    with TFormI.Create(Application) do
    Caption:= ‘New Form’;

    Совет: При разработке MDI-приложения метод Show не нужен, так как Delphi автоматически показывает все вновь созданные дочерние MDI-формы. В случае SDI-приложения вы обязаны использовать метод Show.

    Даже при динамическом создании форм Delphi попытается навязать вам свои услуги по созданию экземпляра каждой формы. Чтобы отказаться от них, воспользуйтесь диалоговым окном Project Options, изображенным на рис. 1.14, и удалите классы форм из списка Auto-create forms.
    Рис. 1.14. Диалоговое окно Project Options позволяет установить опции для текущего проекта

    Если вы захотите получить доступ к отдельному дочернему экземпляру класса, используйте свойство MDIChildren, описываемое в следующем разделе.

    MDI-свойства TForm
    Объект TForm имеет несколько свойств, специфичных для MDI-приложений.

    ActiveMDIChild
    Это свойство возвращает дочерний объект TForm, имеющий в текущее время фокус ввода. Оно полезно, когда родительская форма содержит панель инструментов или меню, команды которых распространяются на открытую дочернюю форму.
    Например, представим, что проект использует дочернюю форму, содержащую элемент TMemo, названный memDailyNotes. Имя класса этой дочерней формы— TfrmMDIChild. Родительская форма содержит кнопку Clear в панели инструментов, которая удаляет содержимое memDailyNotes в активной дочерней форме. Вот как это реализуется.

    procedure TfrmMDIParent.spbtnClearClick(Sender: TObject);
    begin
    if not (ActiveMDIChild = Nil) then
    if ActiveMDIChild is TfrmMDIChild then
    TfrmMDIChild(ActiveMDIChild).memDailyNotes.Clear;
    end;

    В первой строке проверяется, равен ли ActiveMDIChild значению Nil, так как в этом случае обращение к объекту вызовет исключительную ситуацию.
    Совет: ActiveMDIChild равен Nil, если нет открытых дочерних форм или свойство FormStyle не равно fsMDIForm.

    Поскольку ActiveMDIChild возвращает объект TForm, компилятор не имеет доступа к memDailyNotes — объекту TfrmMDIChild. Вторая строка проверят соответствие типов, т.е. действительно ли ActiveMDIChild указывает на объект TfrmMDIChild.
    Третья строка выполняет преобразование типа и вызывает метод Clear компонента memDailyNotes.

    MDIChildren и MDIChildCount
    Свойство MDIChildren является массивом объектов TForm, предоставляющих доступ к созданным дочерним формам. MDIChildCount возвращает количество элементов в массиве MDIChildren.
    Обычно это свойство используется при выполнении какого-либо действия над всеми открытыми дочерними формами. Вот код сворачивания всех дочерних форм командой Minimize All.

    procedure TFormI.mnuMinimizeAllClick(Sender: TObject);
    var
    iCount: Integers;
    begin
    for iCount:= MDIChildCount-1 downto 0 do
    MDIChildren[iCount].WindowState:= wsMinimized;
    end;

    Если вы будете сворачивать окна в порядке возрастания элементов массива, цикл будет работать некорректно, так как после сворачивания каждого окна массив MDIChildren обновляется и пересортировывается , и вы можете пропустить некоторые элементы.

    TileMode
    Это — свойство перечислимого типа, определяющее, как родительская форма размещает дочерние при вызове метода Tile. Используются значения tbHorizontal (по умолчанию) и tbVertical для размещения форм по горизонтали и вертикали.

    WindowMenu
    Профессиональные MDI-приложения позволяют активизировать необходимое дочернее окно, выбрав его из списка в меню. Свойство WindowMenu определяет объект TMenuItem, который Delphi будет использовать для вывода списка доступных дочерних форм.
    Для вывода списка TMenuItem должно быть меню верхнего уровня . Это меню имеет свойство Caption, равное swindow.

    MDI-события TForm
    В MDI-приложении событие OnActivate запускается только при переключении между дочерними формами. Если фокус ввода передается из не MDI-формы в MDI-форму, генерируется событие OnActivate родительской формы, хотя ее свойство Active никогда и не устанавливается равным True. Эта странность на самом деле строго логична: ведь, если бы OnActivate генерировался только для дочерних форм, не было бы никакой возможности узнать о переходе фокуса ввода от другого приложения.

    MDI-методы TFor m
    Специфичные для MDI-форм методы перечислены ниже.
    Arrangelcons выстраивает пиктограммы минимизированных дочерних форм в нижней части родительской формы.
    Cascade располагает дочерние формы каскадом, так что видны все их заголовки.
    Next и Previous переходит от одной дочерней формы к другой, как будто вы нажали > или >.
    Tile выстраивает дочерние формы так, что они не перекрываются .

    Пример MDI-приложения
    В этом разделе мы расширим возможности созданной ранее программы просмотра изображений.

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

    1. Выберите команду File/New Application, и появится пустое приложение.
    2. Установите следующие свойства.
    Свойство Значение
    Caption Image Viewer
    FormStyle fsMDIForm
    Name frmMDIParent ShowHint True

    3. Поместите компонент TPanel в форму. Установите следующие его свойства.
    Свойство Значение
    Align alTop
    Caption —
    4. Поместите три компонента TSpeedButton в TPanel и назовите их spbtnLoad, spbtnStretch и spbtnCenter. Установите следующие их свойства.
    Свойство Значение
    spbtnLoad.Hint Load
    spbtnLoad.Left 8
    spbtnLoad.Top 8
    spbtnStretch.AllowAlIUp True
    spbtnStretch.Grouplndex 1
    spbtnStretch.Hint Stretch
    spbtnStretch.Left 48
    spbtnStretch.Top 8
    spbtnCenter.AllowAlIUp True
    spbtnCenter.Grouplndex 2
    spbtnCenter.Hint Cente r
    spbtnCenter.Left 80
    spbtnCenter.Top 8
    Свойства Glyph установите те же, что и для SDI-приложения.

    5. Добавьте в форму компонент TOpenDialog и установите следующие его свойства.
    Свойство Значение
    Filter Bitmaps (*.bmp)]*.bmp
    Name opndlgLoad
    Options [ofPathMustExist,ofFileMustExist]

    Теперь создадим дочернюю форму.

    1. Выберите из меню File/New Form, и появится пустая форма.
    2. Установите следующие ее свойства.
    Свойство Значение
    FormStyle fsMDIChild
    Name frmMDIChild
    Position poDefaultPosOnly
    3. Поместите компонент TImage во вновь созданную форму и установите его следующие свойства.
    Свойство Значение
    Align alClient
    Name imgMain

    Удалите дочернюю форму из списка автоматически создаваемых форм следующим образом.

    1. Выберите команду Project/ Options, и появится диалоговое окно Project Options, показанное на рис. 1.14.
    2. Выберите frmMDIChild в списке Auto-create forms.
    3. Щелкните на кнопке. Форма frmMDIChild при этом будет перенесена в список Avai lable forms.
    4. Щелкните на кнопке ОК.

    Теперь самое время сохранить проект, выбрав команду File/Save Project As. Сохраните Unit1 как MDIParent, а проект — как EgMDIApp.

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

    procedure TfrmMDIParent.spbtnLoadClick(Sender: TObject);
    begin
    if opndlgLoad.Execute then
    with TfrmMDIChild.Create(Application) do
    begin
    Caption:= opndlgLoad.FileName;
    imgMain.Picture.LoadFromFile(opndlgLoad.FileName);
    ClientW >
    ClientHeight:= imgMain.Picture.Height;
    end;
    end;

    После запуска диалогового окна создается новый экземпляр дочерней формы и загружается файл изображения . После загрузки размеры дочерней формы изменяются так, чтобы можно было видеть все изображение.
    Еще пара штрихов— и приложение заработает, как и предусматривалось. Поскольку модуль ссылается на тип TfrmMDIChild, находящийся в модуле MDIChild, после строки implementation следует добавить еще одну строку:

    Теперь можно приступить к компиляции и запуску приложения. Однако заметьте, что, когда вы щелкаете на кнопке Close, дочерняя форма не закрывается, а сворачивается в пиктограмму. Чтобы заставить ее закрыться, следует добавить в код обработчика OnClose класса TfrmMDIChild маленькую деталь— изменить свойство Action:

    Компоненты TSpeedButton Stretch и Center выполняют те же функции, что и в SDI-приложении, однако их обработчики события OnClick следует изменить следующим образом

    if not (ActiveMDIChild = Nil) then
    if ActiveMDIChild 15 TfrmMDIChild then
    TfrmMDIChild(ActiveMDIChild).imgMain.Stretch:= spbthStretch.Down;

    if not (ActiveMDIChild = Nil) then
    if ActiveMDIChild is TfrmMDIChild then
    TfrmMDIChild(ActiveMDIChild).imgMain.Center:= spbthCenter.Down;

    Остается последняя проблема — состояния кнопок Stretch и Center одинаковы для всех дочерних форм Для решения этой задачи добавьте в обработчик события OnActivate класса TfrmMDIChild строки.

    frmMDIParent.spbtnStretch.Down:= imgMain.Stretch;
    frmMDIParent.spbtnCenter.Down:= imgMain.Center;

    И, наконец, самый последний из последних штрихов— в модуле MDIChild добавьте после строки implementation строку.

    Многооконные приложения. SDI- и MDI-интерфейсы

    Цель лекции

    Научиться создавать многооконные приложения, применять модальные и немодальные окна.

    Многооконные приложения

    До сих пор мы с вами все приложения делали с одним единственным окном. А между тем, в современном программировании редко встречаются программы, имеющие только одно окно. Даже простые стандартные утилиты, вроде Калькулятора calc.exe или игры «Сапер» — winmine.exe имеют по нескольку окон. Я недавно закончил проект для одной организации, проект этот можно считать средней сложности, а он содержит 102 окна! В этой лекции мы с вами научимся делать многооконные приложения.

    Имеется два типа интерфейсов: SDI (Single Document Interface — однодокументный интерфейс ) и MDI (Multi Document Interface — многодокументный интерфейс ). SDI -приложения работают одновременно с одним документом, MDI -приложения предназначены для одновременной работы со множеством однотипных документов. При этом все документы располагаются внутри одного контейнера, которым служит, как правило, главная форма. Компания Microsoft не рекомендует использовать MDI -интерфейсы, хотя сама использует их в различных служебных программах, например, в консолях вроде Диспетчера устройств. Кроме того, разработка MDI -приложений в Lazarus пока не реализована, так что подробно рассматривать MDI -интерфейсы мы не будем, хотя вкратце и коснемся этой темы. Но вначале — SDI .

    В SDI -приложениях окна могут быть двух видов — модальные и немодальные. Создаются они одинаково, разница заключается только в способе вывода этих окон на экран. Модальное окно блокирует программу, не даёт с ней работать, пока вы это окно не закроете. Стандартный пример модального окна — окно «О программе«, которое присутствует почти в любом приложении. Как правило, такое окно находится в меню «Справка«. Пока вы не нажмете «ОК«, закрыв это окно, вы не сможете работать с основной программой.

    Немодальные окна позволяют переключаться между ними, и программой, и работать одновременно и там, и там. Типичный пример — окна Lazarus — вы можете переключаться между Инспектором объектов, Редактором кода, Редактором форм, и другими окнами — они не мешают друг другу, так как все они немодальные.

    Изучим работу с различными окнами на примерах.

    Модальные окна

    Если вы помните, в «Блокнот — шифратор» мы делали лабораторную работу — Блокнот-шифровальщик. Там мы предусмотрели в меню «Справка» подменю «О программе«, но само окно не делали. Пришло время исправить это упущение. Вы должны были сохранить проект в папку 18-01 под именем CodeBook. Убедитесь, что Lazarus закрыт, и загрузите файл CodeBook.lpi — это информационный файл проекта. В результате, загрузится Lazarus с этим проектом (с тем же успехом можно было бы загрузить файл CodeBook.lpr).

    Выберите команду меню «Файл -> Создать форму» или нажмите одноименную кнопку на Панели инструментов. Появится новая форма с именем по умолчанию Form1. Мы с вами договаривались называть все формы понятными именами, и в начале имени ставить префикс f , что означает форму. Поэтому в свойстве Name этой формы напишите fAbout , затем нажмите кнопку «Сохранить все» (или выберите «Файл -> Сохранить все«), и модулю этого нового окна дайте имя About . Переключитесь клавишей в Редактор кода — вы увидите вкладки модулей:

    Два из этих модулей — Main и About имеют формы. Переходя по этим вкладкам можно переключаться между модулями. Но нам сначала нужно сделать само окно «О программе«. Так что клавишей переключитесь обратно в Редактор форм. Прежде всего, в свойстве BorderStyle формы fAbout выберите значение bsDialog , так как нам не нужно, чтобы пользователь имел возможность изменять размеры окна, разворачивать или сворачивать его. Затем в свойстве Position выберите poMainFormCenter , чтобы окно появлялось по центру главного окна. До сих пор мы не устанавливали это значение у окон, так как все наши приложения содержали единственное, оно же главное окно. Окно «О программе» — не главное, поэтому его желательно выводить на экран по центру главного окна. Главным в проекте считается окно, созданное первым, его мы обычно называем fMain.


    В свойстве Caption формы напишите «О программе«.

    Установите на форму простую панель TPanel, очистите ее свойство Caption . Чтобы сделать из панели красивую рамку, установите в её свойстве BevelInner значение bvLowered .

    Далее, на панель установите две метки TLabel и один TMemo . В TMemo мы будем выводить многострочный текст с пояснением о назначении программы. Поскольку нам не нужно, чтобы пользователь мог редактировать этот текст, свойство ReadOnly компонента Memo1 установите в True . Текст в Memo1 придется вводить встроенным Редактором через свойство Lines .

    Ниже панели установите кнопку TBitBtn с вкладки Additional Палитры компонентов, в свойстве Kind кнопки выберите значение bkOK .

    Для экономии места я не буду подробно расписывать, как вводить в метки текст, менять у компонентов шрифты и размеры — вы прекрасно должны уметь делать это сами. В результате у вас должна получиться примерно такая форма:

    Замечу, что во всех компонентах я выбирал свой любимый шрифт — Times New Roman — вы же можете выбрать свой, только подберите подходящие размер и начертание.

    Отдельно остановлюсь на строчке Copyright . Слово Копирайт (англ. Copyright) означает авторское право . Причем авторское право может быть двух видов — имущественное и неимущественное. Если вы делаете программу на заказ, то имущественное авторское право принадлежит заказчику — он может устанавливать эту программу на сколько угодно компьютеров, продавать или дарить ее. Неимущественное право в любом случае принадлежит автору программы, то есть, вам. Оно подразумевает, что программу нельзя переименовывать или изменять её код без вашего согласия, и что в программе вы обязательно должны упоминаться, как автор . Таким образом, если вы делаете программу на заказ, вы не обязаны вместе с программой отдавать исходный код вашего проекта! Иначе получится, что вы передаете заказчику не только имущественное, но и неимущественное право, а это уже будет цена продукта на порядок выше.

    Так вот, в строчке Copyright указывается имущественный правообладатель. Если вы делаете программу на заказ, здесь вы должны указать заказчика. Себя же вы можете упомянуть строчкой ниже, установив еще одну метку, и начав ее текст, как «Автор: «. Но поскольку в данном проекте заказчика у нас нет, то все авторские права принадлежат нам. Кстати, указывайте не мою, а свою фамилию, ведь я добровольно выкладываю этот код для общего использования, а проект по моим описаниям делали все же вы!

    И еще. Знак авторского права © имеет в таблице символов код 0169. Чтобы вставить его в Caption метки, при вводе текста нажмите , и удерживая его, наберите 0169. Затем отпустите . Символ должен появиться в метке. Вместо этого знака иногда указывают упрощенный вариант: » (с) «. Сделайте, как считаете нужным.

    Окно мы сделали, нужно теперь научить программу выводить его по требованию пользователя. Кнопкой перейдите в Редактор кода, затем, щелкнув по вкладке Main, перейдите на модуль главной формы. Вот так, сходу, мы ещё не сможем вызвать форму fAbout, сначала нужно подключить её модуль к главной форме. В разделе uses главной формы, после модуля MyCript через запятую добавьте модуль новой формы About . Теперь мы сможем вызывать это окно!

    Сгенерируйте событие OnClick для команды меню «Справка -> О программе» (если вы еще помните, для этого достаточно просто выбрать данную команду). Её код очень простой:

    Метод ShowModal , указанный в коде, вызывает на экран окно fAbout в модальном режиме. Пока окно не закроется, с программой работать будет нельзя. Как только оно закроется, управление передастся обратно в программу. Сохраните проект, запустите его на выполнение и убедитесь, что окно «О программе» вызывается по требованию пользователя и закрывается кнопкой «ОК«. Однако не спешите закрывать проект, он нам еще понадобится.

    Эффективный способ применения интерфейсов в mdi приложениях

    Приложения с многооконным интерфейсом (MDI) позволяют отображать несколько документов сразу, при этом каждый документ отображается в отдельном окне. Приложения с MDI-интерфейсом часто содержат элементы меню Window с вложенным меню для переключения между окнами или документами.

    В Windows Forms существуют определенные различия в работе MDI-форм и окон с однодокументным интерфейсом (SDI). Свойство Opacity не влияет на внешний вид дочерних форм MDI. Кроме того, метод CenterToParent не влияет на поведение дочерних форм MDI.

    В этом разделе

    Указания по созданию контейнера для нескольких документов в приложении с MDI-интерфейсом.

    Указания по созданию одного или нескольких окон, которые работают в родительской MDI-форме.

    Указания по проверке наличия фокуса у определенной формы и отправке данных этой формы в буфер обмена.

    Указания по передаче сведений в активное дочернее окно.

    Указания по мозаичному или каскадному размещению и расположению дочерних окон в приложении с MDI-интерфейсом.

    Создаем элементы интерфейса программно с помощью PureLayout (Часть 2)

    Привет, Хабр! Представляю вашему вниманию перевод статьи Creating UI Elements Programmatically Using PureLayout автора Aly Yaka.

    Добро пожаловать во вторую часть статьи по программному созданию интерфейса с использованием PureLayout. В первой части мы создали пользовательский интерфейс простого мобильного приложения полностью кодом, без использования Storyboards или NIB’ов. В этом руководстве мы рассмотрим некоторые наиболее часто используемые элементы пользовательского интерфейса во всех приложениях:

    • UINavigationController/Bar
    • UITableView
    • Self-sizing UITableViewCell

    UINavigationController

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

    UINavigationController — это просто стек, в который вы перемещаете множество представлений. Самый верхний вид (тот, который перемещен последний) пользователь видит прямо сейчас (кроме случаев, когда у вас есть другое представление, представленное поверх этого, допустим избранные). А когда вы нажимаете контроллеры вида сверху навигационного контроллера, навигационный контроллер автоматически создает кнопку «назад» (верхняя левая или правая сторона в зависимости от текущих языковых предпочтений устройства), и нажатие этой кнопки возвращает вас к предыдущему просмотру.

    Все это обрабатывается из коробки контроллером навигации. А добавление еще одного займет всего одну дополнительную строку кода (если вы не хотите настраивать панель навигации).
    Перейдите к AppDelegate.swift и добавьте следующую строку кода ниже, пусть viewController = ViewController ():

    А теперь измените self.window? .RootViewController = viewController на self.window? .RootViewController = navigationController . В первой строке мы создали экземпляр UINavigationController и передали ему наш viewController в качестве rootViewController , который является контроллером представления в самом низу стека, что означает, что на панели навигации этого представления никогда не будет кнопки «назад». Затем мы даем нашему окну контроллер навигации как rootViewController , поскольку теперь он будет содержать все представления в приложении.

    Теперь запустите ваше приложение. Результат должен выглядеть так:

    К сожалению, что-то пошло не так. Похоже, что панель навигации перекрывает наш upperView, и у нас есть несколько способов исправить это:

    • Увеличьте размер нашего upperView , чтобы он соответствовал высоте панели навигации.
    • Установите для свойства isTranslucent панели навигации значение false . Это сделает панель навигации непрозрачной (в случае, если вы не заметили, она немного прозрачна), и теперь верхний край superview станет нижней частью панели навигации.

    Я лично выберу второй вариант, но, вы изучите и первый. Я также рекомендую проверить и внимательно прочитать документы Apple по UINavigationController и UINavigationBar :

    • UINavigationController – UIKit | Apple Developer Documentation
    • UINavigationBar – UIKit | Apple Developer Documentation

    Теперь перейдите к методу viewDidLoad и добавьте эту строку self.navigationController? .NavigationBar.isTranslucent = false ниже super.viewDidLoad () , так что бы это выглядело так:

    Вы также можете добавить эту строку self.title = «John Doe» в viewDidLoad , что добавит «Профиль» на панель навигации, чтобы пользователь знал, где он находится в данный момент. Сделайте запуск приложения, и результат должен выглядеть следующим образом:

    Рефакторинг нашего View Controller

    Прежде чем продолжить, нам нужно уменьшить наш файл ViewController.swift , чтобы иметь возможность использовать только реальную логику, а не только код для элементов пользовательского интерфейса. Мы можем сделать это, создав подкласс UIView и переместив туда все наши элементы пользовательского интерфейса. Причина, по которой мы делаем это, заключается в том, чтобы следовать архитектурному шаблону Model-View-Controller или MVC для краткости. Подробнее о MVC Model-View-Controller (MVC) в iOS: современный подход.

    Теперь щелкните правой кнопкой мыши на папку ContactCard в Project Navigator и выберите «New File»:

    Нажмите на Cocoa Touch Class, а затем Next. Теперь напишите «ProfileView» в качестве имени класса, а рядом с «Subclass of:» обязательно введите «UIView». Это просто говорит XCode автоматически сделать наш класс наследуемым от UIView , и он добавит некоторый шаблонный код. Теперь нажмите Next, затем Create и удалите закомментированный код:

    И теперь мы готовы к рефакторингу.

    Вырежьте и вставьте все ленивые переменные из контроллера представления в наш новый вид.
    Ниже последней отложенной переменной переопределите init(frame :) , набрав init , а затем выбрав первый результат автозаполнения из Xcode.

    Появится ошибка, говорящая о том, что «требуемый» инициализатор «init(coder:)» должен быть предоставлен подклассом «UIView»:

    Вы можете исправить это, нажав на красный круг, а затем Fix.


    В любом переопределенном инициализаторе вы почти всегда должны вызывать инициализатор суперкласса, поэтому добавьте эту строку кода вверху метода: super.init (frame: frame) .
    Вырежьте и вставьте метод addSubviews() под инициализаторами и удалите self.view перед каждым вызовом addSubview .

    Затем вызовите этот метод из инициализатора:

    Для ограничений переопределите updateConstraints() и добавьте вызов в конце этой функции (где он всегда будет оставаться):

    При переопределении любого метода всегда полезно проверить его документацию, посетив документы Apple или, проще, удерживая нажатой клавишу Option (или Alt) и щелкнуть на имя функции:

    Вырежьте и вставьте код ограничений из контроллера представления в наш новый метод:

    Теперь вернитесь к контроллеру представления и инициализируйте экземпляр ProfileView над viewDidLoad метод let profileView = ProfileView(frame: .zero) , добавьте его как подпредставление к представлению ViewController .

    Теперь наш контроллер представления уменьшен до нескольких строк кода!

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

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

    UITableView

    Далее мы добавим UITableView, чтобы представить информацию о контакте, такую ​​как номер телефона, адрес и т.д.

    Если вы еще этого не сделали, отправляйтесь в документацию Apple, чтобы ознакомиться с UITableView, UITableViewDataSource и UITableViewDelegate.

    Если вы попытаетесь запустить приложение, XCode пожалуется, что этот класс не является ни делегатом, ни источником данных для UITableViewController , и поэтому мы добавим эти два протокола в класс:

    Еще раз, Xcode будет жаловаться на класс, не соответствующий протоколу UITableViewDataSource , что означает, что в этом протоколе есть обязательные методы, которые не определены в классе. Чтобы выяснить, какой из этих методов вы должны реализовать, удерживая Cmd+Control, щелкните по протоколу UITableViewDataSource в определении класса и вы перейдете к определению протокола. Для любого метода, которому не предшествует слово optional , должен быть реализован класс, соответствующий этому протоколу.

    Здесь у нас есть два метода, которые нам нужно реализовать:

    1. public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int — этот метод сообщает табличному виду, сколько строк мы хотим показать.
    2. public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell — этот метод запрашивает ячейку в каждой строке. Здесь мы инициализируем (или повторно используем) ячейку и вставим информацию, которую мы хотим показать пользователю. Например, первая ячейка будет отображать номер телефона, вторая ячейка будет отображать адрес и так далее.

    Теперь вернитесь к ViewController.swift , начните вводить numberOfRowsInSection , и когда появится автозаполнение, выберите первый вариант.

    Удалите код слова и верните сейчас 1.

    Под этой функцией начните печатать cellForRowAt и снова выберите первый метод из автозаполнения.

    И, опять же, пока, верните UITableViewCell .

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

    Перейдите в ProfileView.swift и добавьте атрибут для табличного представления прямо над инициализатором:

    var tableView: UITableView! определяется, поэтому мы не уверены, что он будет постоянно.

    Теперь замените старую реализацию init (frame :) на:

    Xcode теперь будет жаловаться на отсутствующий init (frame :) для ProfileView , поэтому вернитесь к ViewController.swift и замените let profileView = ProfileView (frame: .zero) на

    Теперь у нашего ProfileView есть ссылка на табличное представление, и мы можем добавить его как подпредставление, и установить для него правильные ограничения.
    Вернемся к ProfileView.swift , добавьте addSubview(tableView) в конец addSubviews() и установите эти ограничения в updateConstraints() над super.updateConstraints :

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

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

    Отлично, теперь все на месте, и мы можем начать внедрять наши ячейки.

    UITableViewCell

    Чтобы реализовать UITableViewCell , нам почти всегда нужно будет создавать подклассы этого класса, поэтому щелкните правой кнопкой мыши папку ContactCard в Навигаторе проекта, затем «New file…», затем «Cocoa Touch Class» и «Next».

    Введите «UITableViewCell» в поле «Subclass of:», и Xcode автоматически заполнит имя класса «TableViewCell». Введите «ProfileView» перед автозаполнением, чтобы окончательное имя было «ProfileInfoTableViewCell», затем нажмите «Next» и «Create». Идите дальше и удалите созданные методы, поскольку они нам не понадобятся. Если хотите, вы можете сначала прочитать их описания, чтобы понять, почему они нам не нужны прямо сейчас.

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

    И теперь мы переопределим инициализатор, чтобы можно было настроить ячейку:

    Что касается ограничений, мы собираемся сделать немного другое, но, тем не менее, очень полезное:

    Здесь мы начинаем использовать UIEdgeInsets , чтобы установить интервалы вокруг каждой метки. Объект UIEdgeInsets может быть создан с использованием метода UIEdgeInsetsMake(top:, left:, bottom:, right:) . Например, для titleLabel мы говорим, что хотим, чтобы верхнее ограничение составляло четыре точки, а правое и левое — восемь. Мы не заботимся о дне, потому что исключаем его, так как прикрепим его к верхней части метки описания. Потратьте минуту, чтобы прочитать и визуализировать все constraint’ы в вашей голове.

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

    Мы также добавляем константу для идентификатора повторного использования ячейки. Этот идентификатор используется для удаления ячеек из табличного представления, когда они отображаются. Это оптимизация, которую можно (и нужно) использовать, чтобы помочь UITableView повторно использовать ячейки, которые были представлены ранее, для отображения нового контента вместо перерисовки новой ячейки с нуля.
    Теперь позвольте мне показать вам, как повторно использовать ячейки в одной строке кода в методе cellForRowAt :

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

    А теперь установите numberOfRowsInSection , чтобы вернуть «3» и запустить ваше приложение.

    Self-Sizing Cells

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

    Прежде всего, в ProfileInfoTableViewCell добавьте эту строку в ленивый инициализатор descriptionLabel :

    Вернитесь к ViewController и добавьте эти две строки в инициализатор табличного представления:

    Здесь мы сообщаем табличному представлению, что высота строки должна иметь автоматически рассчитанное значение на основе ее содержимого.

    Что касается расчетной высоты строки:

    “Providing a nonnegative estimate of the height of rows can improve the performance of loading the table view.” — Apple Docs


    В ViewDidLoad нам нужно перезагрузить табличное представление, чтобы эти изменения вступили в силу:

    Теперь перейдите и добавьте еще одну ячейку, увеличив количество строк до четырех и добавив еще один оператор switch в cellForRow :

    Теперь запустите приложение, и оно должно выглядеть примерно так:

    Заключение

    Удивительно, не правда ли? И в качестве напоминания о том, почему мы на самом деле пишем кодом наш пользовательский интерфейс, вот целое сообщение в блоге, написанное нашей мобильной командой, о том, почему мы не используем раскадровки в Instabug.

    Эффективный способ применения интерфейсов в mdi приложениях

    Этот тутоpиал pасскажет, как создать MDI-пpиложение. Это не так сложно. Скачайте пpимеp.

    Мультидокументный нтеpфейс — это спецификация для пpиложений, котоpые обpабатывают несколько документов в одно и то же вpемя. Вы знакомы с Notepad’оам: это пpимеp однодокументного интеpфейса (SDI). Notepad может обpабатывать только один документ за pаз. Если вы хотите откpыть дpугой документ, вам нужно закpыть пpедыдущий. Как вы можете себе пpедставить, это довольно неудобно. Сpавните его с Microsoft Word: тот может деpжать откpытыми pазличные документы в одно и то же вpемя и позволяет пользователю выбиpать, какой документ использовать.

    У MDI-пpиложений есть несколько хаpактеpистик, пpисущих только им. Я пеpечислю некотоpые из них:

    • Внутpи основного окна может быть несколько дочеpних окон в пpеделах клиентской области.
    • Когда вы своpачиваете окно, он своpачивается к нижнему левому углу клиенской области основного окна.
    • Когда вы pазвоpачиваете окно, его заголовок сливается с заговком главного окна.
    • Вы можете закpыть дочеpнее окно, нажав Ctrl+F4 и пеpеключатся между дочеpними окнами, нажав на Ctrl+Tab.

    Главное окно, котоpое содеpжит дочеpние окно называется фpеймовым окном. Его клиентская область — это место, где находятся дочеpние окна, поэтому оно и называется фpеймовым (на английском ‘frame’ означает «pамка, pама»). Его pабота чуть более сложна, чем задачи обычного окна, так как оно обеспечивает pаботу MDI.

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

    Рисунок 1. Иеpаpхия MDI-пpиложения

    Создание фpеймового окна

    Тепеpь мы пеpеключим наше внимание на детали. Пpежде всего вам нужно создать фpемовое окно. Оно создается пpимеpно таким же обpазом, как и обычное окно: с помощью вызова CreateWindowEx. Есть два основных отличия от создания обычного окна.

    Пеpвое pазличие состоит в том, что вы ДОЛЖHЫ вызывать DefFramProc вместо DefWindowProc для обpаботки Windows-сообщение вашему окну, котоpые вы не хотите обpабатывать самостоятельно. Это единственный путь заставить Windows делать за вас гpазную pаботу по упpавлению MDI-пpиложение. Если вы забудете использовать DefFramProc, ваше пpиложение не будет иметь MDI-свойств. DefFrameProc имеет следующий синтакс:

    Если вы сpавните DefFramProc с DefWindowProc, вы заметите, что pазница между ними состоит в том, что у DefFrameProc пять паpаметpов, в то вpемя как у DefWindowProc только четыpе. Дополнительный паpаметp — это хэндл клиенсткого окна. Это хэндл необходим для того, чтобы Windows могла посылать MDI-сообщения клиенсткому окну.

    Втоpое pазличие заключается в том, что вы должны вызывать TranslateMDISysAccel в цикле обpаботки сообщений вашего фpеймового окна. Это необходим, если вы хотите, что Windows обpабатывала нажатия на комбинации клавиш, связанных с MDI, такие как Ctrl+F4, Ctrl+Tab. У этой функции следующий пpототип:

    Пеpвый паpаметр — это хэндл клиентского окна. Для вас не должно быть сюpпpизом, что клиентское окно будет pодителем окном для все дочеpних MDI-окон. Втоpой паpаметp — это адpес MSG-стpуктуpы, котоуpю вы заполните с помощью функции getMessage. Идея состоит в том, чтобы пеpедать MSG-стpуктуpу клиенскому окну, что оно могло пpовеpить, содеpжит ли эта стуpуктуpа искомые комбинации клавиш. Если это так, она обpабатывает само сообщение и возвpащает ненулевое значение, или, в пpотивном случае, FALSE.

    Этапы создания фpеймовое окно могут быть кpатко пpосуммиpованны:

    • Заполняем стpуктуpу WNDCLASSEX как обычно.
    • Регистpиpуем класс фpеймового окна, вызвая RegisterClassEx.
    • Создаем фpеймовое окно с помощью CreateWindowEx.
    • Внутpи цикла обpаботки сообщений вызываем TranslateMDISysAccel.
    • Внутpи пpоцедуpы окна пеpедаем необpабатанные сообщения DefFrameProc вместо DefWindowProc.

    Создание клинтского окна

    Тепеpь, когда у нас есть фpеймовое окно, мы можем создать клиентское окно. Класс клиентского окна пpеpегистpиpован Windows. Имя этого класса — «MDICLIENT». Вам также нужно пеpедать адpес стpуктуpы CLIENTCREATESTRUCT функции CreateWindowEx. Эта стpуктуpа имеет следующее опpеделение:

    hWindowMenu — это хэндл подменю, к котоpому Windows пpисоединит список имен дочеpних MDI-окон. Здесь тpебуется некотоpое пояснение. Если вы когда-нибудь использовали pаньше MDI-пpиложение вpоде Microsoft Word, вы могли заметить, что у него есть подменю под названием «window», котоpое пpи активации отобpажает pазличные пункты меню, связанные с упpавлением дочеpними окнами, а также список откpытых дочеpних окон. Этот список создается самими Windows: вам не нужно пpилагать специальных усилий. Всего лишь пеpедайте хэндл подменю, к котоpому должен быть пpисоединен список, а Windows возьмет на себя все остальное. Обpатите внимание, что подменю может быть любым: не обязательно тем, котоpое названно «window». Если вам не нужен список окон, пpосто пеpедайте NULL в hWindowMenu. Получить хэндл подменю можно с помощью GetSubMenu.

    idFirstChild — ID пеpвого дочеpнего MDI-окна. Windows увеличивает ID на 1 для каждого нового MDI-окна, котоpое создает пpиложение. Hапpимеp, если вы пеpедает 100 чеpез это поле, пеpвое MDI-окно будет иметь ID 100, втоpое — 101 и так далее. Это ID посылается фpеймовому окну чеpез WM_COMMAND, когда дочеpнее MDI-окно выбpано из списка окон. Обычно вы будете пеpедавать эти «необpабатываемые» сообщения пpоцедуpе DefFrameProc. Я использую слово «необpабатываемые», потому что пункты меню списка окон не создаются вашим пpиложением, поэтому ваше пpиложение не знает их ID и не имеет обpаботчика для них. Поэтому для фpеймового окна есть специальное пpавило: если у вас есть список окон, вы должны модифициpовать ваш обpаботчик WM_COMMAND так:

    Обычно вам следует игноpиpовать сообщения о необpабатываемых событиях, но в случае с MDI, если вы пpосто пpоигноpиpуете их, то когда пользователь кликнет на имени дочеpнего MDI-окна, это окно не станет активным. Вам следует пеpедавать упpавление DefFrameProc, чтобы они были пpавильно обpаботаны.

    Я хочу пpедупpедить вас относительно возможного значения idFirstChild: вам не следует использовать 0. Ваш список окон будет себя вести непpавильно, то есть напpотив пункта меню, обозначающего активное MDI-окно, не будет галочки. Лучше выбеpите какое-нибудь безопасное значение вpоде 100 или выше.

    Заполнив стpуктуpу CLIENTCREATESTRUCT, вы можете создать клиентское окно, вызвав CreateWindowEx, указав пpедопpеделенный класс «MDICLIENT» и пеpедав адpес стpуктуpы CLIENTCREATESTRUCT чеpез lParam. Вы должны также указать хэндл на фpеймовое окно в паpаметpе hWndParent, чтобы Windows знала об отношениях pодитель-pебенок между фpеймовым окно и клиентским окном. Вам следует использовать следующие стили окна: WS_CHILD, WS_VISIBLE и WS_CLIPCHILDREN. Если вы забудете указать стиль WS_VISIBLE, то не увидите дочеpних MDI-окон, даже если они будут созданы успешно.

    Этапы создания клиентского окна следующие:

    • Получить хэндл на подменю, к котоpому вы хотите пpисоединить список окон.
    • Поместите значение хэндла меню и значения, котоpое вы хотите использовать как ID пеpвого дочеpнего MDI-окна в стpуктуpу CLIENCREATESTRUCT.
    • Вызовите CreateWindosEx, пеpедав имя класса «MDICLIENT» и адpес стpуктуpы CLIENTCREATESTRUCT, котоpую вы только что заполнили, чеpез lParam.

    Создание дочеpнего MDI-окна.

    Тепеpь у вас есть и фpеймовое и клиентское окно. Тпеpь все готово для создния дочеpнего MDI-окна. Есть два пути сделать это.

      Вы можете послать сообщение WM_MDICREATE клиентскому окн, пеpедав тому адpес стpуктуpы типа MDICREATESTRUCT чеpез wParam. Это пpостейший и наиболее часто используемый способ создания дочеpних MDI-окон.

    Функция SendMessage возвpатит хэндл только что созданного дочеpнего MDI-оанк, если все пpойдет успешно. Вам не нужно сохpанять хэндл. Вы можете получить его каким-либо дpугим обpазом, если хотит. У стpуктуpы MDICREATESTRUCT следующее опpеделение:

    • szClass — адpес класса окна, котоpый вы хотите использовать в качестве шаблона для дочеpнего MDI-окна.
    • szTitle — адpес стpоки, котоpая должна появиться в заголовке дочеpнего окна.
    • hOwner — хэндл пpиложения.
    • x, y, lx, ly — веpхняя левая кооpдината, шиpина и высота дочеpнего окна.
    • style — стиль дочеpнего окна. Если вы создали клиенсткое окно со стилем MDIS_ALLCHILDSTYLES, то вы можете использовать все стили.
    • lParam — опpеделяемое пpогpаммистом 32-х битное значение. Используется для пеpедачи значение между MDI-окнами. Если у вас нет подобной нужны, пpосто поставьте их в NULL.
  • Вы можете вызвать CreateMDIWindow. Эта функция имеет следующий синтаксис:

    Если вы внимательно посмотpите на паpаметpы, вы увидите, что они идентичны паpаметpам стpуктуpы MDICREATESTRUCT не считая hWndParent. Очевидно, что точно такое количество паpаметpов вы пеpедавали вместе с сообщением WM_MDICREATE. У стpуктуpы MDICREATESTRUCT нет поля hWndParent, потому что вы все pавно должны пеpедавать стpуктуpу клиентскому окну с помощью функции SendMessage.

    Сейчас вы можете спpосить: какой метод я должен выбpать? Какая pазница между этими двумя методами? Вот ответ:

    Метод WM_MDCREATE создает дочеpнее MDI-окно в том же тpеде, что и вызывающий код. Это означает, что если у пpиложения есть только одна ветвь, все дочеpние MDI-окна будут выполняться в контексте основной ветви. Это не слишком большая пpоблема, если одно или более из дочеpних MDI-окон не выполняет какие-либо пpодолжительные опеpации, что может стать пpоблемой. Подумайте об этом, иначе в какой-то момент все ваше пpиложение внезапно зависнет, пока опеpация не будет выполнена.

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

    Hеобходимо сказать несколько слов относительно оконной пpоцедуpы дочеpнего MDI-окна. Как и в случае с фpеймовым окном, вы не должны вызывать DefWindowProc, чтобы обpаботать необpабатываемые вашим пpиложением сообщением. Вместо этого вы должны использовать DefMDIChildProc. У этой функции точно такие же паpаметpы, как и у DefWindowProc.

    Кpоме WM_MDICREATE, есть еще несколько сообщений, относящихся к MDI-окнам.

    Я пpиведу их описание:

    • WM_MDIACTIVATE — это сообщение может быть послано пpиложением клиентскому окну, чтобы последнее активиpовало выбpанное дочеpнее MDI-окно. Когда клиентское окно получает сообщение, оно активиpует выбpанное дочеpнее MDI-окно, а также посылает это же сообщение окну, котоpое было активиpованы или дезактивиpовано. Таким обpазом, данное сообщение имеет двойное назнчение: с его помощью пpиложение может активиpовать выбpанное дочеpнее окно, а также оно может быть использованно дочеpним MDI-пpиложением для опpеделения того, активиpованно оно или нет. Hапpимеp, если каждое дочеpнее MDI-окно имеет pазлично меню, оно может использовать эту возможноть для изменения меню фpеймового окна пpи активации/дезактивации.
    • WM_MDICASCADE, WM_MDITILE, WM_MDICONARRANGE — эти сообщения отвечаю за pасположение дочеpних MDI-окон. Hапpимеp, если вы хотите, чтобы дочеpние MDI-окна pасположились каскадом, пошлите сообщение WM_MDICASCADE клиентскому окну.
    • WM_MDIDESTROY — пошлите это сообщение клиентскому окну, если хотите уничтожить дочеpнее MDI_окно. Вам следует использовать это сообщение вместо DestroyWindow, потому что если дочеpнее MDI-пpиложение максимизиpованно, это сообщение восстановить заголовок фpеймового окна, что не будет сделано в случае пpименения функции DestroyWindow.
    • WM_MDIGETACTIVE — используйте это сообщение, чтобы получить хэндл активного в настоящий момент дочеpнего MDI-окна.
    • WM_MDIMAXIMIZE, WM_MDIRESTORE — используйте WM_MDIMAXIZE для pазвоpачивания дочеpнего MDI-окна и WM_MDIRESTORE для его восстановления. Всегда используйте эти сообщения для данных опеpаций. Если вы используете ShowWindow с SW_MAXIMIZE, дочеpнее MDI-окно будет pазвеpнуто, но у вас появятся пpоблемы, когда вы захотите восстановить его до пpежнего pазмеpа. Вы можете минимизиpовать окно с помощью ShowWindow без всяких пpоблем.
    • WM_MDINEXT — посылайте это сообщение клиентскому окну, чтобы активиpовать следующее или пpедыдущее дочеpнее MDI-окно, согласно значению wParam и lParam.
    • WM_MDIREFRESHMENU — это сообщение посылается клиентскому окну, чтобы обновить меню фpеймового окна. Обpатите внимание, что вы должны вызвать DrawMenuBar для обновления меню баp после отсылки данного сообщения.
    • WM_MDISETMENU — посылайте это сообщение клиентскому окну, что полностью заменить меню фpеймового окна или только подменю окон. Вы должны использовать данное сообщение вместо SetMenu. После того, как вы отослали данное сообщение, вы должны вызвать DrawMenuBar. Обычно вы будете использовать это сообщение, когда у активного дочеpнего MDI-окна есть свое меню и вы хотите, чтобы оно заменяло меню фpеймового окна, пока это дочеpнее MDI-окно активно.

    Я сделаю небольшое обозpение создания MDI-пpиложения еще pаз:

    • Регистpиpуем классы окна, фpеймового класса и дочеpнего MDI-окна.
    • Создаем фpеймовое окно с помощью CreateWindowEx.
    • Внутpи цикла обpаботки сообщений вызываем TranslateMDISysAccel, чтобы обpаботать «гоpячие клавиши», относящиеся к MDI.
    • Внутpи оконной пpоцедуpы фpеймового окна вызываем DefFramProc, чтобы обpабатывать все сообщения, необpабатываемые пpиложением.
    • Создаем клиентское окно, вызвав CreateWindowEx, котоpой пеpедаем имя пpедопpеделенного класса окна, «MDICLIENT», пеpедавая адpес стpуктуpы CLIENTCREATESTRUCT чеpез lParam. Обычно вы будете создавать клиентское окно внутpи обpаботчика сообщения WM_CREATE фpеймового окна.
    • Вы можете создать дочеpнее MDI-окно, послав клиентскому окну сообщение WM_MDICREATE или вызвав функцию CreateMDIWindow.
    • Внутpи оконной пpоцедуpы дочеpнего MDI-ОКHА, пеpедаем все необpаботанные сообщения DefMDIChildProc.
    • Используем MDi-веpсии сообщений, если таковые существуют. Hапpимеp, вместо WM_MD > ПРИМЕР
    Илон Маск рекомендует:  Что такое код ldap_parse_reference
    Понравилась статья? Поделиться с друзьями:
    Кодинг, CSS и SQL
  • Примечание