Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP_Lab_16_17.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
14.46 Mб
Скачать

Сценарий создания приложения

Мы не станем возиться с каким-нибудь простеньким примером, а встроим в наше диалоговое окно почти все возможные элементы управления. Работа эта несложная, потому что нам поможет редактор диалоговых окон MVS-2010. Законченный вид диалогового окна представлен на рис. 1.

Рис. 1. Законченное диалоговое окно в полной красе

Как видите, это диалоговое окно предназначено для учета кадров. Это пример довольно скучного делового приложения — неплохо бы сделать с ним что-нибудь такое, что было невозможно во времена перфокарт (вы хоть знаете, что это такое?). Программу слегка оживляют ползунки вредность и лицемерие — классический пример прямого ввода и наглядного отображения данных. Еще интереснее были бы здесь элементы управления ActiveX, но использовать их мы пока не будем, так как пока не научились. Впрочем, вы можете себе позволить добавить в диалоговое окно все, что угодно.

Шаг 1. Генерация каркаса приложения.

Выполните команду File→New Project... и в появившемся окне (рис.2) в списке Project types (тип проекта) выберите поддержку MFC в ветви Visual C++ и MFC Application в окне Templates и задайте имя приложения в поле Name, предположим, DiaWin. Проследите, чтобы в поле Location было выбрано корректное имя каталога, в котором будет размещен каталог проекта. Рекомендуется не устанавливать переключатель Create directory for solution для получения более структуры каталогов файлов проекта.

После нажатия кнопки ОК будет показано окно (рис. 3), с помощью которого Вы сможете задать свойства проекта, отличные от заданных по умолчанию. Если в этом окне Вы сделаете щелчок ЛКМ по полю Application Type, то в правой части основного окна Вы сможете выбрать параметры приложения в соответствии с рис. 3: выберите, обязательно, тип приложения Dialog Based, язык ресурса – Руский и включите переключатель Use Unicode Libraries. Не устанавливайте переключатель Use HTML dialog, так как это опасно для жизни! Последовательно нажимая на кнопку Next, просмотрите, но не изменяйте параметры приложения для User Interface Features, Advanced Features и Generated Classes. Альтернативно, можно было бы выбирать соответствующие поля – User Interface Features, Advanced Features и Generated Classes – и устанавливать свойства приложения. Если Вы уверены, что не хотите изменять свойства приложения по умолчанию, можете сразу щелкать по кнопке Finish. Таким образом, не приходя в сознание, ИС создаст вам проект, диалоговое окно которого основано на базовом классе CDialogEX.

Рис. 2. Параметры приложения DiaWin

Рис. 3. Окно выбора свойств проекта

После нажатия на эту самую кнопку Finish спец ИС создаст каркас приложения, основу которого составляют два файла: DiaWin.cpp и DiaWinDlg.cpp.

Шаг 2. Изучаем текст программы.

В файле DiaWin.cpp находится реализация класса приложения CDiaWinApp с единственной, но важной, функцией BOOL CDiaWinApp::InitInstance() и конструктором класса CDiaWinApp::CDiaWinApp(). Кроме того, в этом же файле глобально объявлен объект приложение:

CDiaWinApp theApp;

Ознакомьтесь, пожалуйста, с текстом функции CDiaWinApp::InitInstance(), в частности, с комментариями. Строку

SetRegistryKey(_T("Local AppWizard-Generated Applications"));

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

Вот следующие строки функции, с моими комментариями:

CDiaWinDlg dlg; /* Определяется экземпляр класса диалога. Само по

себе диалоговое окно на экране, естественно, пока не

появляется*/

m_pMainWnd = &dlg; /* Адрес объекта диалоговое окно запоминается

в член-данном m_pMainWnd класса CDiaWinApp */

INT_PTR nResponse = dlg.DoModal();/* Вызываем на выполнение функцию

CDialog::DoModal(), вследствие чего на экране появляется

диалоговое окно. Возврат из этой функции произойдет при

нажатии пользователем клавиши ОК или Cancel, что в следующем

условном операторе и проверяется*/

if (nResponse == IDOK)

{

/* Доделайте: поместите сюда операторы, которые должны быть

выполнены при завершении диалога пользователем с помощью

клавиши OK*/

}

else if (nResponse == IDCANCEL)

{

/* Доделайте: поместите сюда операторы, которые должны быть

выполнены при завершении диалога пользователем с помощью

клавиши Cancel*/

}

/* Так как диалог завершен, то функция CDiaWinApp::InitInstance()

возвращает значение false, указывая на то, что приложение

должно завершиться, а не входить в цикл обработки сообщений*/

Теперь посмотрим на файл DiaWinDlg.cpp. В нем, очевидно из соображений экономии, мастером размещена реализация сразу двух классов: CAboutDlg и CDiaWinDlg. Оба этих класса являются наследниками MFC-класса CDialogEX. Назначение класса CAboutDlg достаточно ясно из его названия (отображает окно с информацией «О программе») и мы его обсуждать не будем, а вот класс CDiaWinDlg собственно и представляет всю «визуальную» часть нашего приложения (если не считать окна About).

В конструкторе класса

CDiaWinDlg::CDiaWinDlg(CWnd* pParent /*=NULL*/)

: CDialogEx(CDiaWinDlg::IDD, pParent)

{

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

}

используется очень полезная MFC-функция AfxGetApp(), которая возвращает указатель на объект theApp, объявленный в другом файле (DiaWin.cpp) и, следовательно, «невидимый» в файле DiaWinDlg.cpp. Этот самый указатель на объект theApp частенько бывает нужен в самых разных местах (файлах, классах) программы и, поэтому, постарайтесь запомнить функцию AfxGetApp(). В данном конструкторе она используется для тривиальной цели: загрузки изображения иконки приложения.

Назначение функции CDiaWinDlg::DoDataExchange(), сгенерированной мастером, станет, возможно, понятным позднее. Пока что она, можно сказать, ничего не делает.

Следующий фрагмент файла представляет собой так называемую карту сообщений (message map):

BEGIN_MESSAGE_MAP(CDiaWinDlg, CDialogEx)

ON_WM_SYSCOMMAND()

ON_WM_PAINT()

ON_WM_QUERYDRAGICON()

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

В этой самой карте сообщений указаны макросы, которые генерируют заголовки обработчиков сообщений Windows, присутствующие в данном классе. В данном случае это макросы сообщений ON_WM_SYSCOMMAND(), ON_WM_PAINT() и ON_WM_QUERYDRAGICON(), смысл которых Вам может быть понятен (но скорее всего не может) из их названий. Если Вы посмотрите на содержимое заголовочного файла DiaWinDlg.h, то обнаружите там такие объявления (сопоставьте их с приведенной выше картой сообщений):

// Generated message map functions

virtual BOOL OnInitDialog();

afx_msg void OnSysCommand(UINT nID, LPARAM lParam);

afx_msg void OnPaint();

afx_msg HCURSOR OnQueryDragIcon();

DECLARE_MESSAGE_MAP()

Важное замечание. В принципе Вы вообще (українською взагалі) можете не морочить себе голову с этими картами сообщений, классами, функциями и т.п. ерундой, если не собираетесь получать представление и навыки по разработке приложений Windows в MVS-2010☻.

Как видно из этих строк, объявления функций-обработчиков сообщений Windows, сгенерированные мастером, начинаются с макроса afx_msg. Когда в дальнейшем Вы будете создавать свои обработчики сообщений Windows, ИС MVS-2010 будет добавлять их в карты сообщений подобно тому, как это уже сделано в данном каркасе приложения.

Важно. Пока Вы не наберетесь опыта в разработке программ, не редактируйте вручную приведенные фрагменты программы, касающиеся обработчиков сообщений. Делайте это с помощью «Окна свойств» (команда ViewProperties Window). Этот инструмент позволяет добавлять обработчики сообщений и, что не менее важно, удалять их

Функция CDiaWinDlg::OnInitDialog(), как следует из ее названия, вызывается при инициализации диалогового окна, но до его появления на экране. В частности, в этой функции к системному меню окна добавляется команда вызова окна About и к приложению «привязываются» иконки. Самое важное, что именно в эту функцию надо будет добавлять программный код, обеспечивающий, в частности, засылку начальных значений в визуальные управляющие элементы диалогового окна.

Функция CDiaWinDlg::OnSysCommand() предназначена для выполнения команд системного меню.

Функция CDiaWinDlg::OnPaint() вызывается при необходимости прорисовки содержимого диалогового окна, например, при первом отображении окна, а также когда окно разворачивается или перекрывается другим окном. Необходимость добавления своего кода в эту функцию возникает редко, например, при добавлении в диалоговое окно какой-либо «картинки». Собственно, всю прорисовку окна выполняет функция CDialogEx::OnPaint() базового класса.

Совет. Выполнять трассировку таких функций, как OnPaint(), практически невозможно из-за того, что при остановке в этой функции она вызывается повторно для прорисовки затертого отладчиком изображения окна и, таким образом, мы попадаем как бы в «вечный» цикл. Выйти из этой ситуации можно очень просто: добавить в функцию вызов макроса TRACE для вывода сообщений в окно Debug ИС MVS-2010

Шаг 3. Сборка и выполнение приложения.

С помощью макросов TRACE удобно отслеживать порядок вызова функций приложения и, при необходимости, выводить значения переменных в окно Debug. Добавьте такие макросы в функции этого приложения, выполните компиляцию и сборку приложения, запустите его на выполнение в режиме отладки (с помощью команды F5), проследите за содержимым окна Debug. Обратите внимание, что в окне уже имеются кнопки ОК и Cancel. При закрытии диалогового окна любым способом завершается и выполнение приложения.

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

Размещение в окне управляющих элементов

Для размещения в окне своих управляющих элементов необходимо перейти к вкладке Resource View в окне решений, раскрыть в ней ветвь Dialog и сделать двойной щелчок ЛКМ по имени ресурса диалогового окна – в нашем случае это идентификатор IDD_DIAWIN_DIALOG (рис. 4). Любое окно, в том числе и Resource View, можно вызвать с помощью соответствующей команды меню View.

Рис. 4. Вкладка Resource View окна решений

После появления диалогового окна для размещения в нем управляющих элементов надо вызвать окно ToolBox (команда меню ViewToolBox) и просто перетаскивать в окно необходимые управляющие элементы либо выбирать их в окне ToolBox и затем размещать в диалоговом окне путем щелчка ЛКМ (рис. 5). Окно Properties свойств диалогового окна и размещенных в нем управляющих элементов вызывается с помощью команды меню ViewProperties Window.

Краткая справка по окну свойств Properties

Это окно используется на этапе разработки приложения (design-time) для просмотра и редактирования свойств и событий выбранных объектов, размещенных в визуальном редакторе. Это же окно можно использовать для просмотра и редактирования свойств решения (solution), проекта, файлов и классов. Свойства, отображаемые серым цветом, являются свойствами только для чтения.

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

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

Таблица 1.

Назначение кнопок, размещенных на инструментальной панели окна свойств Properties

Categorized. Все свойства выбранного объекта отображаются разбитыми на категории, причем категории можно разворачивать и сворачивать.

Alphabetical. Все свойства выбранного объекта отображаются отсортированными в лексикографическом порядке.

Properties. Отображение свойств объекта, а не событий.

Control Events. Отображение событий объекта, а не свойств.

Property Pages. Эта кнопка используется в том случае, когда в качестве инспектируемого объекта выбрано решение (Solution) или проект.

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

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

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

Управление размерами и положением управляющих элементов в окне

При размещении элемента управления в диалоговом окне его размеры и положение можно задавать как с помощью мыши, так с помощью клавиш управления курсором. В последнем случае для точного задания размеров управляющего элемента нужно удерживать нажатой клавишу Shift и использовать клавиши управления курсором. Размеры элемента, как и его положение в окне, отображаются в строке статуса главного окна ИС. В окне свойств размеры и координаты элемента не отображаются. Единица измерения – так называемые «диалоговые единицы» (DLU – dialog unit). Фактическая величина DLU базируется на размере шрифта диалогового окна, обычно это MS Sans Serif 8. Горизонтальная DLU – это среднее значение ширины символов шрифта, деленное на 4. Вертикальная DLU – это среднее значение высоты символов шрифта, деленное на 8.

Рис. 5. Окна ToolBox и Properties ИС MVS-2010

Шаг 4. Размещение элементов управления в диалоговом окне.

Перетащите мышью нужные элементы из окна ToolBox в создаваемое диалоговое окно, а затем разместите и отмасштабируйте их в соответствии с рис. 1. Предварительно удалите из окна статический текст, размещенный мастером.

Теперь коротко рассмотрим элементы управления нашего диалогового окна.

Статический текст (Static Text) для поля Фамилия. Этот элемент просто выводит на экран данные и собственно к диалогу с пользователем прямого отношения не имеет. Расположив ограничивающий прямоугольник, введите текст в поле Caption окна Properties (размеры прямоугольника можно при необходимости изменить). Это единственный элемент управления «статический текст», который здесь упоминается, но в нашем диалоговом окне есть и другие подобные элементы: создавайте их по аналогии. Все они имеют один и тот же идентификатор IDC_STATIC, равный -1, но это не важно, поскольку доступ к ним программе не нужен. Кстати, значением идентификатора в файле ресурсов является просто целое число.

Поле ввода (Edit Control) Name, расположенное правее текста Фамилия. Поле ввода — основное средство ввода текста в диалоговых окнах. Смените его идентификатор с IDC_EDIT1 на IDC_NAME. Остальные свойства оставьте такими, какие предлагаются по умолчанию. Заметьте: по умолчанию устанавливается Auto HScroll==true, т.е. текст по мере заполнения текстового поля прокручивается справа налево. Начальное значение для этого поля в окне свойств задать невозможно – мы это будем делать позже. Обратите внимание на свойство Number: если выбрать для него значение true, то в поле можно будет вводить только числа.

Поле ввода Биография. Это такое же поле ввода, как и предыдущее, но с установленным свойством Multiline=True (многострочное) и AutoHScroll=False. Присвойте ему идентификатор IDC_BIO, бог с ним.

Группирующая рамка (Group Box) Вид оплаты. Этот элемент нужен только для того, чтобы сгруппировать два переключателя. Введите его название (caption) Вид оплаты.

Переключатели (Radio Button) Сдельная оплата и Ставка. Разместите эти переключатели, которые часто называют радио-кнопками, в группирующей рамке Вид оплаты. Кнопке Сдельная оплата присвойте идентификатор IDC_CAT, установите Group=True и TabStop=true. Идентификатор для кнопки Ставка оставьте без изменений (IDC_RADIO2), установите Group=False и TabStop=true. Убедитесь в том, что у обеих кнопок установлено свойство Auto=true (пo умолчанию) и что только у кнопки Сдельная установлено свойство Group. При правильном задании этих свойств Windows гарантирует, что пользователь сможет одновременно выбрать только одну из кнопок. Группирующая рамка Вид оплаты никак не сказывается на их функционировании.

Группирующая рамка Страховка. В этом элементе управления размещаются три флажка. Разместите в окне рамку и введите ее название — Страховка.

Примечание. Позже, установив порядок обхода элементов в диалоговом окне, Вы добьетесь того, чтобы группирующая рамка Страховка следовала за последней кнопкой рамки Вид оплаты. Только не забудьте установить у элемента управления Страховка свойство Group=True, чтобы «завершить» предыдущую группу. Если этого не сделать, особой беды не будет, но, запустив программу под отладчиком, Вы получите предупреждающее сообщение.

Флажки (Check Box) Жизни, По инвалидности и Медицинская.

Разместите эти элементы управления в группирующей рамке Страховка. Примите свойства, предлагаемые по умолчанию, но замените идентификаторы на IDC_LIFE (жизнь), IDC_DIS (по инвалидности) и IDC_MED (медицинская).

В отличие от переключателей (радио-кнопок), флажки ведут себя независимо — пользователь сможет устанавливать любую их комбинацию.

Комбинированный список (Combo Box) Профессиональные навыки. Это первый из трех типов комбинированных списков. Измените его идентификатор по умолчанию на IDC_SKILL, а затем задайте Туре=Simple. В поле значения свойства Data введите наименования трех профессий, отделяя их друг от друга символом «точка с запятой».

Не забудьте «растянуть» по вертикали список Профессиональные навыки в окне редактора, иначе на этапе выполнения Вы не увидите введенных строк (названий профессий).

Замечание. Чтобы выровнять два или более элемента управления, щелкните сначала первый, а затем, удерживая клавишу Shift, все прочие. Далее вызовите меню FormatAlign и выберите подходящий способ выравнивания. То же самое можно сделать с помощью кнопок панели инструментов Dialog Editor. Любую панель инструментов можно вызвать, если щелкнуть ПКМ в свободной области панели инструментов ИС и выбрать из контекстного меню нужную панель

Этот тип комбинированного списка называется простым (Simple). В поле ввода, располагающемся вверху, пользователь может вводить любые данные и/или при помощи мыши выделять элемент в окне списка. Заметьте, что у списка есть свойство Sort, управляющее сортировкой элементов списка.

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

Комбинированный список (Combo Box) Образование. Замените идентификатор IDC_COMBO2 на IDC_EDUC, а прочие параметры оставьте такими, какими они предлагаются по умолчанию. Введите в поле Data три уровня образования: Детясли, Детсад, Верховна Рада. В этом комбинированном списке (Type==Dropdown) пользователь сможет набрать в поле ввода все, что ему заблагорассудится или, щелкнув стрелку, раскрыть прикрепленное окно списка и выбрать любой элемент (мышью или клавишами-стрелками Up/Down).

Список (List Box) Отдел. Замените идентификатор IDC_LIST1 на IDC_DEPT, а прочие параметры оставьте такими, какими они предлагаются по умолчанию. В этом списке пользователь сможет выбрать лишь один элемент мышью, клавишами Up/Down или введя первый символ нужной строки. Строки этого списка мы будем вводить уже в программном коде, так как он не имеет соответствующего свойства, доступного из графического редактора. Как это сделать, Вы узнаете чуть попозже.

Комбинированный список (Combo Box) Язык. Замените идентификатор IDC_COMBO4 на IDC_LANG и присвойте свойству Туре значение Drop List. Введите в поле Data названия трех языков: Говяжий, Длинный и Суржик. Как и прежде, разделяйте названия языков точкой с запятой, о чем есть подсказка в нижней части окна Properties.

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

Полосы прокрутки (Horizontal Scroll Bar) Вредность и Лицемерие. Прежде всего отметим, что эти полосы прокрутки не имеют ничего общего с полосами, используемыми для скроллинга окна. В данном случае мы их будем использовать для наглядного ввода дискретного значения соответствующей величины, трактуя ее единицу измерения как процент. Разместите два ползунка, отмасштабируйте их и присвойте им идентификаторы IDC_HARM и IDC_HYPO. Значения остальных свойств оставьте без изменения.

Кнопки (Button) OK и Cancel. Эти кнопки уже есть и имеют идентификаторы IDOK и IDCANCEL. Мы не будем изменять значения свойств этих кнопок. Чуть позже Вы узнаете об особом предназначении идентификаторов по умолчанию IDOK и IDCANCEL.

Произвольный значок (Picture Control). В диалоговом окне при помощи элемента управления Picture Control можно отобразить любой значок или растровое изображение, если они определены в описании ресурсов. Воспользуемся уже имеющимся в ресурсе MFC-значком программы, имеющем идентификатор IDR_MAINFRAME. Установите свойство Туре=Icon, a Image=IDR_MAINFRAME. Идентификатор IDC_STATIC не изменяйте, так как он Вам не нужен. Можно добавить в диалоговое окно свою «картинку», но для этого надо сначала добавить bmp-файл в ресурс, где ему будет присвоен идентификатор, и указать этот идентификатор в свойстве Image.

Итак, все элементы управления размещены. Можно проверить вид диалогового окна с помощью команды тестирования Ctrl+T. Окно должно выглядеть примерно так, как показано на рис. 6. Обратите внимание, что в списке Отдел присутствуют случайные данные.

Рис. 6. Вид диалогового окна

Шаг 5. Корректировка порядка перехода между элементами управления.

Проверьте порядок перехода между элементами управления. Выполните команду FormatTab Order. Укажите мышью порядок переключения между элементами управления, который будет соблюдаться при нажатии клавиши Tab. Для этого щелкните мышью каждый из элементов управления в требуемом порядке, как показано на рис. 7, а затем нажмите Enter.

Рис. 7. Порядок перехода между элементами управления диалогового окна

Если в процессе указания последовательности выбора элементов управления с помощью клавиши Tab Вы ошибетесь – начните сначала с помощью двойного щечка ЛКМ по первому элементу управления.

Замечание. В текст заголовка элемента управления «статический текст» (например, Фамилия) можно вставить знак &. В период выполнения символ, за которым стоит этот знак, будет подчеркнут, как это часто выглядит в меню. Это позволяет переходить к нужным элементам управления нажатием Alt+«подчеркнутый символ». Ясно, что такие символы должны быть уникальны в пределах диалогового окна.

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

Шаг 6. Добавление член-данных в класс CDiaWinDlg.

Итак, на этом этапе мы имеем ресурс «диалоговое окно», т.е. собственно окно с расположенными в нем элементами управления, для которых мы задавали значения (некоторых) свойств. Где зафиксированы выполненные нами действия, т.е. все эти идентификаторы элементов управления, их размеры, расположение и др. свойства? Вот именно – в сущности, называемой ресурсом. В данном случае это файл DiaWin.rc. В текстовом виде этот файл можно посмотреть (и только посмотреть – не редактируйте его, пока не наберетесь опыта), если на вкладке Solution Explorer окна решений выбрать файл DiaWin.rc, с помощью ПКМ вызвать для него контекстное меню и выбрать в нем команду View Code. Рекомендую посмотреть на его текст и закрыть файл, не модифицируя!

Кроме этого ресурса у нас имеется еще и класс CDiaWinDlg, который уже имеет некоторые сгенерированные мастером ИС методы и в который мы можем (и будем!) добавлять свои методы и член-данные для того, чтобы «добраться» до данных, которые пользователь будет вводить или выбирать с помощью созданных нами элементов управления, ну и решать какую-либо уже прикладную задачу. (Кстати, пока Вы «возились» с размещением элементов управления в диалоговом окне, никаких изменений в объявлении и реализации класса CDiaWinDlg, т.е. в файлах DiaWinDlg.h и DiaWinDlg.cpp, не появилось.)

Рассмотрим конкретную задачу. Например, как сделать так, чтобы при запуске приложения в поле фамилия (Edit Control) уже была отображена какая-нибудь конкретная фамилия, а когда пользователь введет новую фамилию, то как ее узнать, т.е. поместить в какую-нибудь свою переменную? Для решения подобного рода задач MFC предлагает сопоставить с каждым элементом управления специальную переменную, которая будет член-данным класса диалога CDiaWinDlg. Если до показа диалогового окна этой переменной мы присвоим некоторое значение, то оно будет (автоматически) отображено в соответствующем поле, а после закрытия диалогового окна в этой переменной будет храниться значение, введенное пользователем. Естественно, эта «автоматика» обеспечивается некоторым уже встроенным в класс программным кодом. Тип переменной, которую мы будем сопоставлять с конкретным управляющим элементом, не может быть совершенно произвольным. Например, для случая Edit Control этот тип не может быть булевским или указателем, а для Radio Button, как нетрудно догадаться, тип связанной переменной может быть булевским, но может быть и целым.

Сделаем что-нибудь конкретное. Для добавления связанной переменной к управляющему элементу Фамилия (Edit Control) нужно выделить в диалоговом окне поле ввода фамилии (идентификатор IDC_NAME), с помощью ПКМ вызвать контекстное меню и выбрать в нем команду Add Variable, в результате чего появится окно определения переменной (рис. 8). Это же окно можно вызвать и с помощью команды ProjectAdd Variable.

Обсудим назначение элементов этого окна. Прежде всего отметим, что значения полей взаимосвязаны. Когда мы заполним все обязательные поля и нажмем кнопку Finish, в заголовочный файл DiaWinDlg.h класса диалога будет добавлено объявление этой переменной, а в реализацию конструктора класса (файл DiaWinDlg.cpp) мастер добавит инициализацию этой переменной нулевым значением.

Рис. 8. Окно добавления переменной, связанной с полем ввода фамилии IDC_NAME

Назначение полей окна мастера добавления переменной приведено в табл.1.

Таблица 1

Назначение элементов окна Add Member Variable Wizard

Элемент

Назначение

Access

Статус доступа к переменной (public, protected, private). Значение по умолчанию – public – подойдет в большинстве случаев

Variable type

При установленном флаге Control variable и выбранном в поле Category значении Value, тип переменной – это тип значения, обрабатываемого управляющим элементом. Если же Category=Control, то тип переменной – это тип класса управляющего элемента. Например, если управляющий элемент Edit Control, то типом такой переменной будет CEdit и с помощью этой переменной, представляющей объект класса, мы получаем доступ к член-данным и член-функциям класса, т.е. получаем полный контроль над управляющим элементом. Следовательно, одному и тому же управляющему элементу можно сопоставить две переменных: одну для Category=Control и другую для Category=Value

Control variable

Если этот флаг установлен, то добавляемая переменная связана с управляющим элементом. Если флаг не установлен, то в класс добавляется «обычная» переменная

Control ID

Идентификатор управляющего элемента

Category

Определяет тип переменной: класс управляющего элемента или тип данных, обрабатываемых этим элементом. См. Variable Type

Control type

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

Max chars

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

Min value

Если тип переменной BOOL, int, UINT, long, DWORD, float, double, BYTE, short, COLECurrency или CTime, то здесь можно задать минимальное значение, которое будет обрабатывать управляющий элемент

Max value

Аналогично Min value

.h file

Имя заголовочного файла для класса-оболочки управляющего элемента ActiveX

.cpp file

Имя файла реализации для класса-оболочки управляющего элемента ActiveX

Comment

Набранный здесь текст будет добавлен в виде комментария к описанию переменной в заголовочном файле

Выберите в диалоговом окне приложения управляющий элемент для ввода фамилии IDC_NAME, с помощью ПКМ вызовите окно для добавления связанной переменной, установите значения элементов диалогового окна в соответствии с рис. 8, придавите Finish. Если Вы все проделали правильно, то в файле DiaWinDlg.h появится объявление переменной:

public:

// Фамилия писателя

CString m_strName;

В файле DiaWinDlg.cpp мастер ИС модифицировал конструктор класса и функцию DoDataExchange() (изменения выделены полужирным шрифтом):

CDiaWinDlg::CDiaWinDlg(CWnd* pParent /*=NULL*/)

: CDialog(CDiaWinDlg::IDD, pParent)

, m_strName(_T(""))

{

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

}

void CDiaWinDlg::DoDataExchange(CDataExchange* pDX)

{

CDialog::DoDataExchange(pDX);

DDX_Text(pDX, IDC_NAME, m_strName);

DDV_MaxChars(pDX, m_strName, 25);

}

Открою большой секрет: функция DoDataExchange() ответственна за обмен данными между управляющим элементом (например, Edit Control) и связанной переменной – в данном случае m_strName.

Добавьте связанные переменные для управляющих элементов в соответствии с табл. 2. Для всех переменных задавайте Category=Value. Убедитесь, что Вы набираете имена переменных-членов и типов именно так, как показано в табл.2, с учетом регистра символов. После ввода переменной для каждого элемента управления тискайте кнопку Finish. Кроме этого, рекомендую почаще сохранять все файлы проекта (FileSave all), так как я наблюдал ошибки ИС, которые приводили к потере выполненной работы по вводу переменных. Не забудьте задать максимальное и минимальное значения для переменных m_nHarm и m_nHypo. Если при определении связанной переменной Вы совершите ошибку, то отредактировать такую переменную с помощью мастера невозможно, по крайней мере я не знаю, как это сделать. Поэтому есть два выхода. С помощью полезной команды меню EditFind and ReplaceFind in Files Ctrl+Shift+F найдите все вхождения имени переменной в файлах проекта и аккуратно их удалите, после чего сделайте вторую попытку по добавлению переменной. Второй выход – отредактируйте все вхождения переменной, если Вы по натуре исследователь и любите долго заниматься отладкой приложения.

Таблица 2

Имена и типы связанных переменных диалогового окна

Идентификатор элемента управления

Имя переменной

Тип переменной

IDC_NAME

m_strName

CString

IDC_BIO

m_strBio

CString

IDC_SKILL

m_strSkill

CString

IDC_DEPT

m_strDept

CString

IDC_CAT

m_bCat

BOOL

IDC_RADIO2

задавать не нужно и невозможно

IDC_LIFE

m_bLife

BOOL

IDC_DIS

m_bDis

BOOL

IDC_MED

m_bMed

BOOL

IDC_EDUC

m_strEduc

CString

IDC_LANG

m_nLang

int

IDC_HARM

m_nHarm

int (Min=0, Max=100)

IDC_HYPO

m_nHypo

int (Min=0, Max=100)

Большинство взаимосвязей между типами элементов управления и типами переменных совершенно прозрачны. Однако уже не столь очевидно, как кнопки-переключатели (радио-кнопки) соотносятся с переменными. Если радио-кнопок в группе всего 2, как в нашем случае, то можно только для первой радио-кнопки ввести связанную переменную булевского типа и проверять ее значение: значение true означает выбор пользователем первой кнопки, а false – второй. Если кнопок несколько, то, проще всего, только с первой из них связать целочисленную переменную. Тогда при выборе первой кнопки значение этой переменной будет равно 0, второй – 1 и т.д. При этом для первой кнопки надо задать Group=True и Tabstop=True, а для всех остальных кнопок группы задать Group=False и Tabstop=True. Альтернативным способом получения состояния кнопок (причем любых кнопок, а не только радио-кнопок) является вызов их функций CButton::GetCheck(), но для этого надо иметь указатель на объект кнопка. Его можно получить с помощью функции CDialog::GetDlgItem(), но только в функциях класса диалога.

Для связанной переменной комбинированного списка «Язык» можно было бы выбрать тип CString или int. В первом случае переменная содержала бы название выбранного языка, а во втором (выбранном нами) она будет содержать номер выбранного языка в списке, причем значение для первого языка в списке – нулевое.

На этом этапе приложение должно компилироваться и выполняться, причем в диалоговом окне Вы можете вводить фамилию, биографию, выбирать вид оплаты, устанавливать и снимать флаги страховки, выбирать или вводить данные в комбинированный список «Профессиональные навыки», выбирать или вводить уровень образования, выбирать язык. В списке «Отдел» нет никаких данных и Вы их туда ввести не можете. Вы также можете двигать ползунки полос «Вредность» и «Лицемерие», но их положение пока не будет фиксироваться. Кроме того, любые выбираемые или вводимые Вами данные пока нигде не сохраняются. Надо что-то решать, не правда ли?

Кстати, посмотрите на текст конструктора класса

CDiaWinDlg::CDiaWinDlg(CWnd* pParent /*=NULL*/)

в файле CDiaWinDlg.cpp. Заметили, как там инициализируются связанные переменные? Также любопытно посмотреть на функцию CDiaWinDlg::DoDataExchange() в том же файле.

Шаг 7. Добавление переменных для доступа к классам управляющих элементов.

Каждому управляющему элементу, размещенному в диалоговом окне, соответствует свой MFC-класс и, когда при инициализации этот элемент создается, создается объект этого класса. Вместе с тем в программном коде, сгенерированном мастером ИС, этого объекта нет (в отличие от программы, разработанной в Delphi или Builder). Уже упоминаемая ранее функция CDialog::GetDlgItem() позволяет получить ссылку на класс управляющего элемента, но этот указатель временный, как подчеркивается в MSDN, и с его использованием часто возникают проблемы. В связи с изложенным проще всего, и в конечном счете удобнее, добавить в программу переменные, являющиеся именами экземпляров классов (объектов), связанных с управляющими элементами окна.

Для достижения этой благородной цели введем такие связанные переменные по аналогии с предыдущим шагом, но для каждой переменной надо выбирать Category=Control. и флажок Control Variable должен быть установлен. Перечень имен этих переменных приведен в табл. 3. Для добавления переменных-объектов надо, как и на предыдущем шаге, выполнить команду ProjectAdd Variable, в списке Control ID выбрать идентификатор управляющего элемента и в поле Variable Name задать имя переменной. Не забывайте нажимать кнопку Finish после ввода каждой переменной.

Таблица 3

Имена и типы переменных-объектов диалогового окна

Идентификатор элемента управления

Имя переменной

Тип переменной

IDC_NAME

objName

CEdit

IDC_BIO

objBio

CEdit

IDC_SKILL

objSkill

CComboBox

IDC_DEPT

objDept

CListBox

IDC_CAT

objCat

CButton

IDC_RADIO2

задавать не нужно и невозможно

IDC_LIFE

objLife

CButton

IDC_DIS

objDis

CButton

IDC_MED

objMed

CButton

IDC_EDUC

objEduc

CComboBox

IDC_LANG

objLang

CComboBox

IDC_HARM

objHarm

CScrollBar

IDC_HYPO

objHypo

CScrollBar

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

Программа должна компилироваться и выполняться (проверьте), но функциональность ее пока не изменится.

Шаг 8. Инициализация элементов управления диалогового окна в функции CDiaWinDlg::OnlnitDialog().

Мастер ИС сгенерировал для нас виртуальную функцию OnlnitDialog(), которая перекрывает функцию базового класса CDialog::OnlnitDialog(). Заметьте, что функция CDiaWinDlg::OnlnitDialog() должна обязательно содержать вызов функции базового класса CDialog::OnlnitDialog(). Обсуждаемая функция вызывается еще до прорисовки диалогового окна. Если в этой функции мы присваиваем некоторое значение связанной переменной, то это значение отобразится в соответствующем управляющем элементе окна при его показе.

Сгенерированный мастером ИС DDX-код (Dialog Data Exchange – обмен данными в диалоге) в функции CDiaWinDlg::DoDataExchange() не будет инициализировать данные в списках, поэтому нужно добавить соответствующий код вручную. Добавьте в конец функции CDiaWinDlg::OnlnitDialog() следующий код:

Метод InsertString() с первым параметром, равным -1, добавляет строку в конец списка. Им же можно было бы воспользоваться и для комбинированных списков вместо их инициализации в ресурсе – см. подсказку по InsertString(). Занести значение в элемент ввода CEdit можно с помощью функции SetWindowTextW(), как видно из примера. Другим способом инициализации CEdit является присваивание требуемого значения связанной с ним переменной, но это надо проделать до вызова CDialog::OnInitDialog(), т.е. в конструкторе класса диалога или в самом начале метода CDiaWinDlg::OnlnitDialog(). Еще одно место для инициализации – функция CDiaWin::InitInstance().

Занесите, самостоятельно и в полном сознании, начальный текст в поле «Биография». Компилируйте и тестируйте приложение. Теперь Вы должны видеть фамилию, биографию, список отделов. Однако полосы прокрутки по-прежнему должны быть непослушными и мы ими сейчас займемся вплотную.

Шаг 9. Подключение элементов управления «Полосы прокрутки».

Для полос прокрутки надо задать целочисленные константы, соответствующие крайнему левому и крайнему правому положению движка и, кроме того, можно даже задать начальное положение движка. Соответствующие константы удобно (а почему, собственно, как Вы думаете?) описать непосредственно в объявлении класса CDiaWinDlg (файл DiaWinDlg.h) в качестве перечисления:

Далее добавьте в конец функции CDiaWinDlg::OnlnitDialog() следующие операторы:

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

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

  • выделить мышью класс CDiaWinDlg на вкладке Class View окна решений;

  • вызвать с помощью ПКМ контекстное меню, выбрать в нем тему Properties и щелкнуть по ней ЛКМ;

  • в появившемся окне Properties щелкнуть кнопку Messages и в появившемся списке найти сообщение WM_HSCROLL;

  • в столбце справа от имени сообщения с помощью ЛКМ выбрать команду <Add> OnHScroll.

В результате будет сгенерирована заготовка функции:

void CDiaWinDlg::OnHScroll(UINT nSBCode, UINT nPos,

CScrollBar* pScrollBar)

{

// Доделать: Добавьте свой код обработки сообщения и/или

// вызовите обработчик по умолчанию

CDialog::OnHScroll(nSBCode, nPos, pScrollBar);

}

Доработаем ее:

Компилируйте и тестируйте приложение – теперь полосы прокрутки должны подчиняться пользователю.

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

Внимание, задание. В данной реализации обе полосы прокрутки имеют одинаковый диапазон возможных значений, заданный константами nMin и nMax. В жизни, однако, вредность и лицемерие имеют разные диапазоны возможных значений. Поэтому Вам предлагается совершить подвиг: самостоятельно доработать приложение так, чтобы у этих двух сущностей были разные диапазоны возможных значений. Причем для вредности надо выбрать диапазон, включающий и отрицательные значения, чтобы с помощью полосы вредность можно было задавать и полезность. Не исключено, что Вам понадобится функция CWnd::GetDlgCtrlID(), но не обязательно.

Шаг 10. Получение данных, введенных пользователем.

Диалоговое окно, если Вы еще не забыли, создается в функции CDiaWinApp::InitInstance(), которая находится в главном файле приложения – DiaWin.cpp. Там же оно и закрывается. Вот костяк этой функции, с комментариями (несущественные для данного анализа операторы – и только для него! – опущены):

BOOL CDiaWinApp::InitInstance()

{

CDiaWinDlg dlg; // создание экземпляра класса окно

INT_PTR nResponse = dlg.DoModal(); // отображение окна на мониторе

if (nResponse == IDOK)

{

// Сюда надо поместить код, который должен выполняться

// при нажатии пользователем клавиши OK

}

else if (nResponse == IDCANCEL)

{

// Сюда надо поместить код, который должен выполняться

// при нажатии пользователем клавиши Cancel

}

// Так как диалог завершился, очевидно, надо вернуть FALSE

// для завершения приложения

return FALSE;

}

В данном – учебно-тренировочном – приложении мы просто будем выводить введенные пользователем данные в окно Debug с помощью макросов TRACE, естественно, в том случае, когда пользователь завершит работу с окном с помощью кнопки ОК. Удобство макросов TRACE состоит еще и в том, что в окончательной версии программы они не работают и вообще не встраиваются в код.

Как ни странно, но выводить значения строк CString для приложений, скомпонованных с использованием кодировки Unicode (для кодировки ANSI такой проблемы нет), с помощью макросов TRACE невозможно: вместо ожидаемых символов английского языка или кириллицы выводятся «случайные» символы. Имеется функция WideCharToMultiByte(), которая позволяет преобразовать строку символов из кодировки Unicode в кодировку ANSI, в частности. Так как эта функция имеет много параметров, автор данного труда разработал собственную функцию-оболочку, облегчающую использование функции WideCharToMultiByte(). Вот ее текст:

Приведенный код можно поместить непосредственно перед функцией CDiaWinApp::InitInstance() либо в другом подходящем месте. Понятно, что функция не может обрабатывать строки длиной более чем 1024 символов, но мне сдается, что для отладки этого может хватить. С использованием этой чудо-функции CDiaWinApp::InitInstance() для нашего приложения можно записать так:

Тестируйте приложение, проверьте корректность вывода результатов в окно Debug, проанализируйте их. Обратите внимание, что вызов макроса в таком виде

TRACE("Образование = %s Биография = %s\n",ToAnsi(dlg.m_strEduc),

ToAnsi(dlg.m_strBio));

приведет к выводу ошибочных данных, а именно два раза будет выводиться строка dlg.m_strEduc. Другими словами, не указывайте функцию ToAnsi() более одного раза в качестве параметра макроса.

Обратите внимание также на то, что происходит при нажатии клавиши Enter в момент ввода текстовых данных в каком-либо элементе управления — диалоговое немедленно закрывается. А что происходит при нажатии клавиши Escape?.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]