MFC - Камышинский технологический институт

advertisement
О. О. Привалов
И. В. Степанченко
ЛАБОРАТОРНЫЙ ПРАКТИКУМ
ПО MFC
ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ
ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ
ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ
ВОЛГОГРАДСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ
КАМЫШИНСКИЙ ТЕХНОЛОГИЧЕСКИЙ ИНСТИТУТ (ФИЛИАЛ)
ВОЛГОГРАДСКОГО ГОСУДАРСТВЕННОГО ТЕХНИЧЕСКОГО УНИВЕРСИТЕТА
О. О. Привалов, И. В. Степанченко
ЛАБОРАТОРНЫЙ ПРАКТИКУМ ПО MFC
Учебное пособие
РПК «Политехник»
Волгоград
2007
1
УДК 004.3 (075.3)
П 75
Рецензенты: коллектив кафедры «Информатика и вычислительная
техника» Волгоградского государственного архитектурно-строительного
университета; преподаватель высшей категории ФГОУ СПО «Камышинский технический колледж» В. В. Яковенко
Привалов О. О., Степанченко И. В. ЛАБОРАТОРНЫЙ ПРАКТИКУМ
ПО MFC: Учеб. пособие / ВолгГТУ, Волгоград, 2007. – 80 с.
ISBN 5-230-05096-9
Рассматривается библиотека классов MFC. Излагаются задачи создания программного кода пользовательского интерфейса от окна приложения до диалоговых окон.
Предназначено для студентов специальности 2202 «Автоматизированные системы обработки информации и управления (по отраслям)»
(код ОКСО 230103.51) очной и очно-заочной форм обучения.
Ил. 17. Табл. 12. Библиогр.: 3 назв.
Печатается по решению редакционно-издательского совета
Волгоградского государственного технического университета
ISBN 5-230-05096-9
©
2
Волгоградский
государственный
технический
университет, 2007
ВВЕДЕНИЕ
Одна из главных целей операционной системы Microsoft Windows –
дать человеку, который хотя бы немного знаком с системой, возможность
сесть за компьютер и запустить любое приложение без предварительной
подготовки (изучения интерфейса, команд и т. п.). Для этого Windows
предоставляет дружественный интерфейс пользователя, такой, чтобы
пользователь думал над тем, что программа делает, а не над тем, как ею
пользоваться. Ведь, фактически, значительная часть кода Windowsприложения предназначена именно для организации интерфейса с пользователем (но не самая важная в приложении).
Хотя создание удобного интерфейса «под Windows» является постоянной задачей при написании любой Windows-программы, такой интерфейс не создается автоматически.
Объем исходного кода операционной системы Microsoft Windows,
начиная с версии Windows 95, составляет более миллиона строк. Часть
его программисты-разработчики оформили в виде функций интерфейса
прикладной программы Win32 API, некоторые из которых вошли в состав библиотеки MFC. Эти функции программисты могут использовать в
своих программах для создания интерфейса.
В цикле лабораторных работ Вы познакомитесь с некоторыми классами библиотеки MFC, которые позволят Вам создавать интерфейс пользователя в программах. Кроме того, в ходе изучения лабораторных работ
Вы постепенно освоите основные приемы, необходимые для создания
приложений под Windows. Полученные знания позволят Вам самостоятельно совершенствоваться и изучать другие библиотеки классов.
3
1. ОСНОВЫ ПРОГРАММИРОВАНИЯ ПОД WINDOWS
1.1. Что такое MFC?
Функций API в операционной системе Microsoft Windows довольно
много, и чтобы прикладному программисту было легче с ними работать,
они были сгруппированы в классы, которые составляют библиотеку MFC.
MFC (Microsoft Foundation Classes) – это базовый набор классов
(библиотека), написанных на языке C++ и предназначенных для
упрощения и ускорения процесса программирования под Windows.
В библиотеку входит более 200 классов. Они дают возможность создавать Windows-приложения на базе объектно-ориентированного подхода.
Одним из основных преимуществ работы с MFC является возможность
многократного использования одного и того же кода. Так как библиотека содержит много элементов, общих для всех Windows-приложе-ний, нет необходимости каждый раз писать их заново. Вместо этого их можно просто
наследовать. Кроме того, интерфейс, обеспечиваемый библиотекой, практически независим от конкретных деталей, его реализующих.
Небольшое число классов, определенных в библиотеке, не связано
непосредственно с программированием под Windows. Это, в частности,
классы, предназначенные для создания строк управления файлами и обработки особых ситуаций. Иногда называемые классами общего назначения, они могут использоваться как Windows-, так и нe Windows-приложениями.
В операционной системе Windows реализован интерфейс вызова
функций. Благодаря данному интерфейсу, доступ к системным ресурсам
операционной системы осуществляется через целый ряд системных
функций. Совокупность функций называется прикладным программным
интерфейсом, или API (Application Programming Interface). Этот механизм достаточно удобен: так, программисту не надо каждый раз заново
писать программный код для прорисовки меню, кнопок, панелей инструментов, окон и т. п. Он может воспользоваться уже готовыми и встроенными в операционную систему Windows элементами интерфейса и функциями обработки действий пользователя.
Поскольку API состоит из большого числа функций, может сложиться впечатление, что при компиляции каждой программы, написанной для
Windows, к ней подключается код довольно значительного объема. В
действительности это не так. Функции API содержатся в библиотеках
динамической загрузки (Dynamic Link Libraries, или DLL), которые загружаются в память только в тот момент, когда к ним происходит обращение, т. е. при выполнении программы. Хотя возможно скомпилировать
4
программу и с использованием статических библиотек, но тогда ее объем
значительно возрастет.
1.2. Многозадачность в Windows
Как известно, все версии операционной системы Windows поддерживают многозадачность. Начиная с версии Windows 95/NT, поддерживаются два типа многозадачности: основанный на процессах и основанный на потоках. Поскольку это очень важный момент, рассмотрим его
подробнее.
Процесс – это программа, которая выполняется. При многозадачности такого типа две или более программы могут выполняться параллельно. Конечно, они по очереди используют ресурсы центрального процессора и, с технической точки зрения, выполняются не одновременно. Но
благодаря высокой скорости работы компьютера это практически незаметно. Если в компьютере имеются два центральных процессора или два
ядра в одном процессоре, то выполнение программ вообще может происходить независимо.
Поток – это отдельная часть исполняемого кода. Название произошло от понятия «направление протекания процесса». В многозадачности
данного типа отдельные потоки внутри одного процесса также могут выполняться одновременно. Все процессы имеют, по крайней мере, один
поток, но их может быть несколько.
Отсюда можно сделать вывод, что в Windows допускается существование процессов, состоящих из двух или нескольких частей, причем части и процессы могут выполняться одновременно. Оказывается, такое
предположение верно. Следовательно, при работе возможно параллельное выполнение как программ, так и отдельных частей самих программ.
В Windows используется приоритетная многозадачность. Каждому
активному потоку предоставляется определенный промежуток времени
работы процессора. По истечении данного промежутка управление автоматически передается следующему потоку. Это не дает возможность программам полностью захватывать ресурсы процессора.
1.3. Взаимодействие программ и Windows
Windows и программы взаимодействуют следующим образом: программа ожидает получения сообщения от Windows. Когда это происходит, то выполняется некоторое действие. После его завершения программа ожидает следующего сообщения. Важно понимать, что на действия
пользователя или других программ прежде всего реагирует операционная
система, а затем она может послать сообщение Вашей программе. Ваша
программа может обработать это сообщение или проигнорировать.
Напрямую взаимодействия программы с пользователем или другими
программами, минуя посредничество Windows, обычно не происходит.
Операционная система реагирует на все внешние воздействия и посылает
5
сообщения всем программам, а будет ли программа реагировать на эти
сообщения, зависит от программиста.
Windows может посылать программе сообщения различных типов.
Например, каждый раз при щелчке мышью в окне активной программы посылается соответствующее сообщение. Другой тип сообщений посылается,
когда необходимо обновить содержимое активного окна. Сообщения посылаются также при нажатии клавиши. Необходимо запомнить одно: по
отношению к программе сообщения появляются случайным образом.
1.4. Основы программирования под Windows
Поскольку архитектура Windows-программ основана на принципе
сообщений, все эти программы содержат некоторые общие компоненты.
Обычно их приходится в явном виде включать в исходный код. Но, к счастью, при использовании библиотеки MFC это происходит автоматически; нет необходимости тратить время и усилия на их написание. Тем не
менее, чтобы до конца разобраться, как работает Windows-программа,
написанная с использованием MFC, и почему она работает именно так,
необходимо в общих чертах понять назначение этих компонентов. Кроме
того, на них ссылаются практически во всех книгах, посвященных программированию под Windows.
Все Windows-программы должны содержать специальную функцию,
которая не используется в самой программе, но вызывается операционной системой. Эту функцию обычно называют функцией окна или процедурой окна. Она вызывается Windows, когда системе необходимо передать сообщение в программу. Именно через нее осуществляется взаимодействие между программой и системой. Функция окна передает сообщение в своих аргументах. Согласно терминологии Windows, функции,
вызываемые системой, называются функциями обратного вызова. Таким
образом, функция окна является функцией обратного вызова.
Как объяснялось выше, Windows взаимодействует с программой, посылая ей сообщения. Все приложения Windows должны организовывать
так называемый цикл сообщений. В этом цикле каждое необработанное
сообщение должно быть извлечено из очереди сообщений данного приложения и передано назад в Windows, которая затем вызывает функцию
окна программы с данным сообщением в качестве аргумента.
Как будет показано дальше, каждое окно в Windows-приложении характеризуется определенными атрибутами, называемыми классом окна. В
традиционной программе класс окна должен быть определен и зарегистрирован прежде, чем будет создано окно. При регистрации необходимо сообщить Windows, какой вид должно иметь окно и какую функцию оно выполняет. В то же время регистрация класса окна еще не означает создания
самого окна. Для этого требуется выполнить дополнительные действия.
6
1.5. Типы данных в Windows
Как Вы вскоре убедитесь, в Windows-программах вообще (и в использующих библиотеку MFC в частности) не слишком широко применяются
стандартные типы данных из С или C++, такие как int или char*. Вместо
них используются типы данных, определенные в различных библиотечных
(заголовочных) файлах. Наиболее часто используемыми типами являются
HANDLE, HWND, BYTE, WORD, DWORD, UINT, LONG BOOL, LPSTR и
LPCSTR. Тип HANDLE обозначает 32-разрядное целое число, используемое в качестве дескриптора. Есть несколько похожих типов данных, но все
они имеют ту же длину, что и HANDLE, и начинаются с литеры Н.
Дескриптор – это число, определяющее некоторый ресурс (важнее не
само число, а ресурс, который определяется дескриптором). Дескриптор является уникальным числом и однозначно указывает на ресурс.
Дескрипторы необходимы, чтобы Windows могла обратиться к любому ресурсу, элементу интерфейса. Понятно, что эти числа не должны
повторяться. Например, тип HWND обозначает 32-разрядное целое – дескриптор окна.
Тип BYTE обозначает 8-разрядное беззнаковое символьное значение, тип WORD – 16-разрядное беззнаковое короткое целое, тип DWORD
– беззнаковое длинное целое, тип UINT – беззнаковое 32-разрядное целое. Тип LONG эквивалентен типу long. Тип BOOL обозначает целое и
используется, когда значение может быть либо истинным, либо ложным
(значения True и False). Тип LPSTR определяет указатель на строку, а
LPCSTR – константный (const) указатель на строку.
1.6. Соглашение об использовании имен
Программный код библиотеки MFC написан с соблюдением соглашения об использовании имен, принятом фирмой «Microsoft». Например,
имя функции должно начинаться с глагола, за которым следует существительное. Первые символы глагола и существительного должны быть
написаны с заглавной буквы. В большинстве случаев имена функций в
лабораторных работах соответствуют данному правилу.
Для имен переменных «Microsoft» решила применять довольно
сложную систему включения ссылки на используемый тип данных. С
этой целью в начало имени переменной добавляется префикс, который
соответствует определенному типу данных и записывается строчными
буквами. Само имя начинается с заглавной буквы. Префиксы типов приведены в табл. 1. Откровенно говоря, использование этих префиксов считается спорным и не всегда соблюдается. Многие программисты придерживаются этого правила, а многие нет. В лабораторных работах префик7
сы используются только тогда, когда это кажется достаточно разумным.
Вы можете поступать так, как привыкли (до соответствующего материала
и указаний преподавателя).
Таблица 1
Символы префиксов типов переменных
Префикс
b
c
C
dw
f
fn
h
l
lp
n
p
pt
w
sz
lpsz
rgb
Тип данных
булевый (один байт; Boolean)
символ (один байт; Char)
класс (Class)
длинное беззнаковое целое (Double Word)
16-разрядное битовое поле (флаги; Flags)
функция (Function)
дескриптор (Handle)
длинное целое (Long)
длинный указатель (Long Pointer)
короткое целое (Short Int)
указатель (Pointer)
длинное целое, хранящее экранные координаты (Point)
короткое беззнаковое целое (Word)
указатель на строку, завершающуюся символом «\0» (String by Zero)
длинный указатель на строку, завершающуюся символом «\0» (Long
Pointer String by Zero)
длинное целое, хранящее значение цвета в формате RGB (Red, Green, Blue)
1.7. Иерархия классов MFC
Структура иерархии классов MFC довольно большая. В ее вершине
находится класс CObject. Он содержит различные функции, используемые во время выполнения программы и предназначенные, в частности,
для предоставления информации о текущем типе данных. Класс CObject
является предком большинства других классов MFC, его свойства наследуются во всех порожденных классах.
Некоторые классы порождаются непосредственно от CObject. Наиболее широко используемыми среди них являются CCmdTarget, CFile, CDC,
CGdiObject, CException и CMenu. Класс CCmdTarget предназначен для обработки сообщений. Если помните, в первой главе говорилось о том, что
Windows взаимодействует с программой через механизм сообщений, который и реализован в CCmdTarget. Класс CFile предназначен для работы с
файлами, в частности с файлами на диске. Класс CDC обеспечивает поддержку контекстной связи с устройством (под устройством здесь понимается преимущественно экран). Класс CException предназначен для обработки особых ситуаций, а CMenu – для создания меню. Помимо вышеперечисленных, есть еще несколько классов, порождаемых непосредственно
от CObject и предназначенных для синхронизации потоков, а также для работы с базами данных, массивами, списками и преобразователями типов.
8
От класса CCmdTarget порождается очень важный класс: CWnd. Он
является базовым для создания всех типов окон, а также различных элементов управления окна. Наиболее широко используемым классом, порождаемым от CWnd, является CFrameWnd. Он обеспечивает поддержку
так называемых SDI-окон (SDI – Single Document Interface – однодокументный интерфейс). В классах, порождаемых от CFrameWnd, будет
инициализироваться очередь сообщений программы.
Другим классом, порождаемым от CCmdTarget, является CWinThread.
С его помощью создается поток выполнения приложения и поддерживается механизм потоковой многозадачности.
От CWinThread порождается, наверное, единственный из наиболее
важных классов, обращение к которому в MFC-программах происходит
напрямую: CWinApp. Хотя он расположен лишь на четвертом уровне
иерархии в MFC, тем не менее, он является одним из фундаментальных
классов, поскольку предназначен для создания самого приложения. В
частности, он позволяет управлять загрузкой, инициализацией, выполнением и завершением приложения. В каждой программе имеется один (и
только один) объект класса CWinApp. Как только он будет создан, приложение начнет выполняться.
1.8. Глобальные функции и функции-члены в MFC
Большинство функций, вызываемых в MFC-программе, являются членами одного из классов, определенных в библиотеке. Вот несколько примеров:
 функция, позволяющая отобразить окно, называется ShowWindow и
является членом класса CWnd;
 для вывода текстовой строки в окно используется функция TextOut –
член класса CDC.
Большинство функций API доступны через функции-члены MFC.
Именно по этой причине последние используются так часто. Всегда
можно обратиться непосредственно к самим API-функциям.
В библиотеке есть и ряд глобальных функций, которые также можно
свободно использовать. Все они начинаются с префикса afх. Например,
одной из наиболее широко используемых функций является AfxMessageBox. Далее в лабораторных работах будет показано, как с ее помощью
отобразить на экране окно сообщения. Поскольку этот тип окон является
заранее определенным, такое окно можно отображать независимо от других окон приложения.
1.9. Файл AFXWIN.H
Все MFC-программы должны включать файл AFXWIN.H. В нем (а
также в различных вспомогательных файлах) содержатся описания клас9
сов библиотеки MFC. Он автоматически подключает большинство других библиотечных файлов, относящихся к MFC, в том числе файл WINDOWS.H. Включая файл AFXWIN.H в свою программу, Вы тем самым
подготавливаете все необходимое для создания Windows-приложения.
1.10. Контрольные вопросы
1. Что такое MFC?
2. Какой файл обязательно необходимо подключать в MFC-программы?
3. Что такое дескриптор?
4. Как взаимодействует Windows и программа?
5. Что такое очередь сообщений?
6. Что такое сообщение?
7. С помощью какого механизма взаимодействуют Windows и приложения?
8. Что такое цикл сообщений?
9. Какие виды многозадачности есть в Windows?
10. Что такое процесс?
11. Что такое поток?
12. Перечислите наиболее важные классы в MFC?
13. За что отвечает класс CWnd?
14. Для чего предназначен класс CWinApp?
15. Приведите пример глобальной функции и функции-члена в MFC?
16. Какой класс стоит в вершине иерархии классов MFC?
17. Какой заголовочный файл должны включать все программы на MFC?
10
2. ЛАБОРАТОРНАЯ РАБОТА № 1.
СОЗДАНИЕ ПРОСТЫХ ПРИЛОЖЕНИЙ WINDOWS
Время выполнения лабораторной работы – 4 часа.
Цель работы – изучить каркас простых приложений Windows.
Оборудование и программное обеспечение, необходимое для выполнения лабораторной работы: компьютер с процессором не хуже Pentium II с тактовой частотой 266 МГц, оперативной памятью не менее 64
Мб, с операционной системой Microsoft Windows 98/NT/2000/XP, средой
программирования Microsoft Visual C++ 6.0.
2.1. Каркас MFC-программы
В данной лабораторной работе мы напишем простую программу, которая послужит основой для написания всех остальных программ в других лабораторных работах.
В простейшем случае программа, написанная с использованием
MFC, содержит два класса, порождаемые от классов иерархии библиотеки: класс, предназначенный для создания приложения, и класс, предназначенный для создания окна. Другими словами, для создания минимальной программы необходимо породить один класс, определяющий
непосредственно приложение, и второй класс, определяющий главное
окно приложения. Первый порождается от CWinApp, второй – от
CFrameWnd. Хотя в программе могут при необходимости наследоваться
любые классы, но эти два обязательны для любой программы.
Кроме создания вышеупомянутых классов, в программе также должна
быть организована обработка всех сообщений, поступающих от Windows.
Поскольку в данном примере не производится ничего, кроме отображения
окна, нет и необходимости отвечать на какое-либо сообщение. Однако механизм обработки сообщений все же должен присутствовать.
Для создания MFC-программы необходимо выполнить следующие
действия:
1. От CWinApp породить класс, определяющий приложение.
2. От CFrameWnd породить класс, определяющий окно.
3. Создать очередь сообщений.
4. Переопределить функцию InitInstance класса CWinApp.
5. Создать экземпляр класса, определяющего приложение.
Прежде чем продолжить чтение, протестируйте пример программы,
приведенной в листинге № 1. В нем создается стандартное окно, содержащее заголовок, системное меню, а также кнопки минимизации, максимизации и закрытия.
Результат выполнения программы показан на рис. 1.
11
Листинг № 1. Минимальная MFC-программа
// Минимальная MFC-программа
#include <afxwin.h>
// Порождение основных классов
// Это класс, предназначенный для создания главного окна
class CMainWin:public CFrameWnd
{
public:
CMainWin();
// Конструктор класса
DECLARE_MESSAGE_MAP() // Макрос, определяющий очередь
// сообщений
};
// Определение конструктора класса CMainWin
CMainWin::CMainWin()
{
// Создание окна
Create(NULL, "Скелет MFC-приложения");
};
// Это класс, предназначенный для создания приложения
class CApp:public CWinApp
{
public:
BOOL InitInstance(); // Функция инициализации приложения
};
// Инициализация приложения
BOOL CApp::InitInstance()
{
// Объявление указателя и создание экземпляра CMainWin через
указатель
m_pMainWnd = new CMainWin;
// Отображение окна
m_pMainWnd -> ShowWindow(m_nCmdShow);
// Обновление окна
m_pMainWnd -> UpdateWindow();
// Возврат успешного завершения инициализации приложения
return TRUE;
};
// Это пустая очередь сообщений приложения
// Не обрабатывается ни одного сообщения
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
END_MESSAGE_MAP()
CApp App;
// создание экземпляра приложения
Конец листинга № 1
12
Рис. 1. Результат работы программы, приведенной в листинге № 1
Работает данная простейшая программа следующим образом:
 создается экземпляр класса CApp (самая последняя строка в листинге № 1), для чего вызывается конструктор этого класса, но так как он
не описан, то выполняется конструктор родительского класса
CWinApp;
 в конструкторе родительского класса вызывается функция инициализации приложения InitInstance, которая переопределена в классе
CApp;
 в функции инициализации приложения создается экземпляр класса
CMainWin, являющийся дочерним от класса CFrameWnd, для чего
вызывается конструктор класса CMainWin;
 в конструкторе класса CMainWin создается окно с помощью функции Create;
 далее в функции инициализации InitInstance приложения окно отображается на экране функцией ShowWindow и обновляется его содержимое функцией UpdateWindow;
 функция инициализации возвращает значение TRUE в конструктор
класса CWinApp.
Как видите, почти весь код в примере предназначен для порождения
двух классов от базовых классов, определенных в MFC – CFrameWnd и
CWinApp. Первый класс предназначен для создания главного окна приложения, а второй – для создания самого приложения. Приложение начинает выполняться, когда создается экземпляр класса второго типа.
Для создания стандартного окна в приложении должен наследоваться класс от класса CFrameWnd. В примере порождаемый класс называется CMainWin. Он содержит два члена: конструктор CMainWin и макрос
DECLARE_MESSAGE_MAP. В макросе объявляется очередь сообщений
для класса CMainWin, что позволяет организовать обработку сообщений.
Для любого окна, в котором обрабатываются сообщения, необходимо
применять этот макрос. Кроме того, он должен быть последним членом,
объявляемым в классе (в противном случае следующий член должен
быть объявлен с помощью оператора доступа – «::»).
13
Само окно создастся в конструкторе CMainWin с помощью вызова
функции-члена Create. Класс CFrameWnd является потомком класса
CWnd, который, в свою очередь, порождается от CCmdTarget, а тот – от
CObject. Это означает, что любой класс, порождаемый от CFrameWnd,
имеет доступ ко всем функциям-членам и переменным, объявленным в
пределах этой иерархии. Функция Create используется почти во всех
приложениях. Она предназначена для создания окна и является членом
класса CFrameWnd. Если быть абсолютно точным, то объект «окно» создается конструктором CMainWin, но этот объект не является видимым
окном, функция Create создает как раз видимый объект.
Класс САрр, предназначенный для создания приложения, порождается от CWinApp. Он является потомком класса CWinThread, который, в
свою очередь, порождается от CCmdTarget. CWinApp содержит ряд переменных и функций-членов, из которых только одна, InitInstance, используется в программе. Ее прототип таков:
virtual BOOL CWinApp::InitInstance();
InitInstance – это виртуальная функция, которая должна переопределяться в классе, ответственном за создание приложения. Она вызывается
каждый раз при запуске программы. Внутри InitInstance выполняются все
действия, связанные с инициализацией приложения. Функция должна
возвращать ненулевое значение при успешном завершении и ноль в противном случае (в листинге № 1 всегда возвращается значение TRUE, хотя
возможна ситуация аварийного завершения).
Внутри InitInstance в листинге № 1 содержатся три команды. Сначала создается объект типа CMainWin и указатель на него запоминается в
переменной m_pMainWnd. Эта переменная является членом класса
CWinThread, неявно наследуемым в САрр. Она имеет тип CWnd* и используется почти во всех MFC-программах, потому что содержит указатель на объект главного окна программы. Через нее будут вызываться
различные функции-члены, связанные с главным окном, как это показано
в следующих двух строках примера.
Когда главное окно создано, через m_pMainWnd вызываются две
функции, являющиеся членами класса CWnd. Первая из них называется
ShowWindow. Ее прототип выглядит следующим образом:
BOOL CWnd::ShowWindow(int How);
Функция ShowWindow определяет, каким образом будет отображено
на экране главное окно приложения. Режим отображения задается параметром How. При первом вызове в качестве параметра необходимо передать переменную m_nCmdShow. Она является членом класса CWinApp и
определяет, как будет выглядеть главное окно при запуске приложения.
14
При последующих вызовах функции можно задать другие режимы либо
закрыть окно. Некоторые значения, которые может принимать параметр
How, приведены в табл. 2.
Функция ShowWindow возвращает признак предыдущего состояния
окна. Если оно было на экране, то результат ненулевой, в противном случае значением функции будет ноль.
Следующая функция, вызываемая в InitInstance, называется UpdateWindow. Она также является членом класса CWnd. Хотя, с технической точки зрения, для такой простой программы нет необходимости в
вызове этой функции, вызов все же присутствует, потому что данная
функция используется в большинстве приложений Windows. Фактически,
она указывает Windows на необходимость послать программе сообщение
о том, что содержимое главного окна нужно обновить (это сообщение
будет рассмотрено далее).
Таблица 2
Режимы отображения
Константа отображения
Действие
Окно закрывается, и активность передается другому окну (приложение при этом остается в памяти, можно
SW_HIDE
увидеть на вкладке «Процессы» в диспетчере задач)
Окно раскрывается на весь экран
SW_MAXIMIZE
Окно сворачивается в пиктограмму, и активизируется
SW_MINIMIZE
окно верхнего уровня в системном списке
Окно активизируется и отображается в текущем полоSW_SHOW
жении и текущего размера
Окно активизируется и приводится к нормальному размеру и положению (указываются при создании окна),
SW_RESTORE
если оно было минимизировано или максимизировано
Окно активизируется и отображается как максимизироSW_SHOWMAXIMIZED
ванное
Окно активизируется и отображается как пиктограмма
SW_SHOWMINIMIZED
Окно отображается на новой позиции новым размером,
SW_SHOWNOACTIVE
окно, которое было активным, остается активным
Окно отображается в виде пиктограммы, окно, которое
SW_SHOWMINNOACTIVE
было активным, остается активным
Окно отображается в его текущем состоянии, окно, коSW_SHOWNA
торое было активным, остается активным
Окно активизируется и отображается в нормальном положении и нормального размера (задаются при создании
SW_SHOWNORMAL
окна)
В конце программы помещены довольно странные команды:
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
END_MESSAGE_MAP()
Это так называемая очередь сообщений главного окна. В пределах
данной очереди должны содержаться вызовы функций, ответственных за
15
обработку поступающих сообщений. В нашем примере ни один из них в
очередь не помещен, так как обработка сообщений не ведется. Однако
именно она является важнейшей (если не основной) частью любого приложения Windows.
До сих пор был определен лишь вид главного окна и заложена основа приложения, но сами они не существуют до тех пор, пока не создан экземпляр объекта класса, производного от CWinApp. Это выполняется
следующей инструкцией:
СAрр Арр;
Как уже упоминалось, в программе может быть лишь один объект
класса CWinApp. Приложение начинает выполняться только после создания данного объекта.
Задание № 1. Запустите программу, приведенную в листинге № 1
(при этом необходимо установить требуемые опции компилятора и
линковщика). Можно создать с помощью мастера (Wizard) готовое
однодокументное приложение MFC, удалить все файлы из проекта и
добавить данный файл. Исследуйте каждую строку программы, установите различные режимы отображения окна (требуется изменить
параметр функции ShowWindow).
2.2. Подробнее о создании масштабируемых окон
В листинге № 1 было создано стандартное масштабируемое окно.
Для простых программ этого может быть вполне достаточно, но, как правило, в главном окне должны находиться различные элементы управления. К счастью, их легко можно получить с помощью функции Create. Ее
прототип таков:
BOOL CFrameWnd::Create(LPCSTR ClassName, // имя класса окна
LPCSTR Title,
// заголовок окна
DWORD Style = WS_OVERLAPPEDWINDOW,
// стиль
const RECT &XYsize = rectDefault,
// позиция и размер
CWnd *Parent = NULL,
// окно-предок
LPCSTR MenuName = NULL,
// название главного меню
DWORD ExStyle = 0,
// спецификатор расширенного стиля
CCreateContext *Context = NULL); // дополнительная структура контекста
Как видите, все параметры, кроме первых двух, содержат значения
по умолчанию. Поэтому в примере мы могли использовать очень простую форму функции Create.
Первый параметр, ClassName, определяет указатель на имя класса
окна. В данном контексте термин «класс» не связан с понятием класса в
C++, а означает скорее стиль. Когда этот параметр равен NULL (а так,
чаще всего, и есть), для окна используется стандартный класс, опреде16
ленный в библиотеке MFC. Можно, однако, зарегистрировать собственное имя класса окна с помощью функции AfxRegisterWndClass.
Заголовок окна – это строка, на которую указывает параметр Title.
Это может быть и пустая строка, но, как правило, окно все же имеет заголовок.
Стиль (или тип) создаваемого окна задается значением параметра
Style. Константа WS_OVERLAPPEDWINDOW определяет стандартное
окно, в котором имеются системное меню, рамка, кнопки минимизации,
максимизации и закрытия. Таков стиль по умолчанию, если параметр
Style не задан. Хотя этот стиль является наиболее общим, можно определить другой стиль по собственным критериям. Для этого достаточно с
помощью операции побитового ИЛИ объединить несколько констант.
Используемые константы приведены в таблице 3.
Таблица 3
Стили масштабируемых окон
Константа стиля
WS_BORDER
WS_CAPTION
WS_CHILD
WS_CLIPCHILDREN
WS_CLIPSIBLINGS
WS_DISABLED
WS_DLGFRAME
WS_GROUP
WS_HSCROLL
WS_VSCROLL
WS_MAXIMIZE
WS_MAXIMIZEBOX
Стиль окна
Окно имеет рамку без заголовка
Окно имеет заголовок и рамку; как правило, этот стиль
используется для перекрывающихся окон и не может
применяться совместно со стилем WS_DLGFRAME
Дочернее окно; не может использоваться совместно со
стилем WS_POPUP
Исключает область, занятую дочерним окном, из области
рисования родительского окна; используется только для
родительских окон
Исключает все другие дочерние окна из своей области
рисования; другими словами, если дочерние окна перекрываются, а этот стиль не указан, то при изменении рабочей области одного из окон могут быть испорчены рабочие области других дочерних окон; этот стиль используется только вместе со стилем WS_CHILD
Неактивное окно, т. е. сразу после его создания оно недоступно для ввода с клавиатуры или с помощью мыши
Окно имеет двойную рамку и не имеет заголовка
Определяет первый элемент управления группы окон, к
которым пользователь может переходить при помощи
клавиш со стрелками; все элементы управления, определенные с этим стилем, заканчивают текущую и начинают
новую группу (одна группа заканчивается там, где начинается другая)
Окно имеет горизонтальную полосу прокрутки
Окно имеет вертикальную полосу прокрутки
Окно будет отображено в развернутом виде
Окно имеет кнопку «Развернуть»; если окно является дочерним окном элемента управления, то этот флаг стиля
используется под другим именем – WS_TABSTOP
17
Окончание табл. 3
Константа стиля
Стиль окна
Окно имеет кнопку «Развернуть»; если окно является дочерним окном элемента управления, то этот флаг стиля
WS_MAXIMIZEBOX
используется под другим именем – WS_TABSTOP
Окно будет отображено в виде значка; используется
WS_MINIMIZE
только со стилем WS_OVERLAPPED
Окно имеет кнопку «Свернуть»; если окно является дочерним окном элемента управления, то этот флаг стиля
WS_MINIMIZEBOX
используется под другим именем – WS_GROUP
Окно является перекрывающимся; обычно имеет заголоWS_OVERLAPPED
вок и рамку
Всплывающее окно; не может использоваться совместно со
WS_POPUP
стилем WS_CHILD
WS_SYSMENU
Окно имеет значок системного меню в полосе заголовка
Назначается одному или нескольким элементам управлеWS_TABSTOP
ния для того, чтобы между ними можно было перемещаться с помощью клавиши табуляции Таb
Окно имеет утолщенную рамку, при помощи которой
WS_THICKFRAME
можно изменять размер окна
Окно становится видимым сразу после создания; этот
WS_VISIBLE
стиль в основном используется для окон диалога
Комбинация
стилей
WS_OVERLAPPED,
WS_OVERLAPPEDWINDOW WS_CAPTION, WS_SYSMENU, WS_THICKFRAME,
WS_MINIMIZEBOX и WS_MAXIMIZEBOX
Комбинация стилей WS_POPUP, WS_BORDER и
WS_POPUPWINDOW
WS_SYSMENU
Примечание. Префикс WS_ означает windows style (стиль окна).
Начальное расположение и размер окна определяются параметром
XYsize. Если он не указан (в большинстве случаев), то оба значения будут
выбраны операционной системой Windows. Однако их можно задавать и
явно, передавая в качестве параметра ссылку на структуру типа RECT.
Тип RECT объявлен следующим образом:
typedef struct tagRECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT;
Здесь необходимо определить координаты верхнего левого угла окна
в переменных left и top, а также координаты нижнего правого угла в переменных right и bottom. Однако нужно помнить, что данные параметры
задаются в абсолютных единицах (в данном случае в пикселях). Это
означает, что координаты заданы по отношению к экрану.
Параметр Parent содержит указатель на окно, являющееся предком
окна, создаваемого функцией Create. Так как главное окно находится на
18
верхнем уровне иерархии окон программы, то в данном случае параметр
должен иметь значение NULL.
Хотя в примере не создается главное меню, в большинстве реальных
программ оно есть. В этом случае название меню необходимо указывать
в параметре MenuName. Параметр является идентификатором соответствующего ресурса.
Атрибуты расширенного стиля окна задаются в параметре ExStyle.
Если таковых нет, то указывается NULL.
Последний параметр в функции Create – Context, обычно равен
NULL. Это указатель на структуру типа CCreateContext. Создание
структур контекста является специальной задачей программирования,
поэтому используется редко.
Функция Create возвращает ненулевое значение, если окно создано,
и ноль, если создать окно невозможно.
Чтобы проверить, как используются дополнительные параметры в
функции, можно заменить в примере конструктор CMainWin следующим
кодом:
CMainWin :: CMainWin()
{
RECT r;
r.top = 10; r.left = 10;
r.bottom = r.right = 200;
Create(NULL, "Маленькое окно", WS_OVERLAPPEDWINDOW, r);
}
В этом случае будет создано стандартное окно, верхний левый угол
которого имеет координаты (10,10), а правый нижний – (200, 200).
Задание № 2. В программе (листинг № 1) испытайте различные стили отображения окна, изучите каждый параметр функции Create. Постройте не менее 7 различных приложений, отличающихся видом окна (или в одном приложении создайте 7 различных видов окон).
2.3. Содержание отчета
Отчет по лабораторной работе представляет собой распечатку созданного программного кода с комментариями. Также в отчет рекомендуется включить необходимые скриншоты созданных окон. Отчет должен быть подготовлен с помощью Microsoft Word и содержать титульный
лист установленного образца.
1.
2.
2.4. Контрольные вопросы
Назовите два класса, на базе которых строится простейшая программа (листинг № 1).
Какие необходимо выполнить действия программисту, чтобы создать
минимальную программу?
19
Как работает простейшая программа?
Назовите несколько параметров функции Create и опишите их назначение.
5. Какие стили окна существуют?
6. Какие режимы отображения окна существуют?
7. Что делает функция ShowWindow?
8. Для чего нужна функция InitInstance?
9. Какие параметры необходимо выбрать в Wizard, чтобы создать пустой проект в Microsoft Visual C++?
10. Какие параметры необходимо задать в установках проекта, чтобы
проект запускался в Microsoft Visual C++?
11. Как изменить название окна, которое видит пользователь?
12. Как создать окно без системной кнопки «Свернуть»?
3.
4.
1.
2.
3.
2.5. Задачи на самостоятельную работу
Создайте приложение – круглое окно (задача простой сложности –
программа описана в литературе).
Создайте приложение – прозрачное окно в Windows XP (задача простой сложности – программа описана в литературе).
Создайте приложение, которое не видно в диспетчере задач и в процессах (задача повышенной сложности – части программы опубликованы в Интернете).
2.6. Методика проведения лабораторной работы № 1
Лабораторная работа № 1 проводится на двух занятиях. Перед её
началом студенты должны ознакомиться с текстом лабораторной работы
(пп. 2.1 и 2.2).
Занятие № 1 (2 аудиторных часа). Изучение теоретического материала по пп. 2.1, 2.2. Подготовка ответов на контрольные вопросы. Запуск
программы в среде Microsoft Visual C++ 6.0. Установка требуемых параметров по заданию № 1.
Занятие № 2 (2 аудиторных часа). Выполнение задания № 2 и подготовка отчета по п. 2.3. Защита отчета по лабораторной работе.
Повышение рейтинговой оценки за лабораторную работу возможно
при выполнении одного задания из п. 2.5 во внеаудиторное время.
20
3. ЛАБОРАТОРНАЯ РАБОТА № 2.
ОБРАБОТКА СООБЩЕНИЙ
Время выполнения лабораторной работы – 6 часов.
Цель работы – изучить принцип взаимодействия программ и Windows.
Оборудование и программное обеспечение, необходимое для выполнения лабораторной работы: компьютер с процессором не хуже Pentium II с тактовой частотой 266 МГц, оперативной памятью не менее 64
Мб, с операционной системой Microsoft Windows 98/NT/2000/XP, средой
программирования Microsoft Visual C++ 6.0.
3.1. Что такое сообщения?
В операционной системе Windows имеется огромное число сообщений. Каждое из них представляется уникальным целым значением. В
библиотечном файле WINUSER.Н для всех сообщений определены стандартные имена. Вот наиболее распространенные сообщения:
WM_CHAR
WM_PAINT
WM_MOVE
WM_CLOSE
WM_LBUTTONUP
WM_SIZE
WM_LBUTTONDOWN
WM_COMMAND
Пример определения имени сообщения:
#define WM_COMMAND 0x0lll
или
#define WM_PAINT 0x000F
Часто сообщения сопровождаются различными числовыми параметрами, несущими дополнительную информацию. К ним относятся, например, координаты текстового курсора или курсора мыши, код нажатой клавиши, а также некоторые системные значения, такие как размер символа.
Windows взаимодействует с программой через механизм сообщений.
В частности, каждый раз, когда происходит определенное событие, на
которое должна отреагировать программа, ей посылается сообщение,
определяющее данное событие. Важно понимать, что нет необходимости
явно отвечать на все сообщения. Фактически, из-за огромного их числа
программа не будет реагировать на большинство из них. Обрабатывать
нужно только те сообщения, которые относятся непосредственно к программе. Для остальных в библиотеке MFC содержатся стандартные обработчики.
Сообщения классифицируются следующим образом:
 аппаратные сообщения;
 сообщения обслуживания окна;
 сообщения организации интерфейса пользователя;
 сообщения о завершении;
 частные сообщения;
 информационные сообщения системных ресурсов;
21


сообщения о совместном использовании данных;
внутрисистемные сообщения.
Информацию о типе сообщения содержит префикс. Префикс «WM_»
говорит о том, что речь идет об «оконном сообщении» (от Window Message), «ЕМ_» (Edit Message) используется для сообщений элемента
управления Edit, «BM_» (Button Message) – для сообщений элемента
управления Button, «LM_» (Listbox Message) – для сообщений элемента
управления Listbox и т. д.
3.2. Обработка сообщений в MFC
Для организации обработки сообщения необходимо выполнить следующие действия:
1) в очередь сообщений программы должна быть включена макрокоманда соответствующего сообщения;
2) прототип функции-обработчика должен быть включен в описание
класса, ответственного за обработку данного сообщения;
3) в программу должна быть включена реализация функцииобработчика.
Теперь давайте рассмотрим этот механизм подробнее.
3.3. Включение макрокоманд в очередь сообщений
В программе очередей сообщения может быть несколько. Каждое
окно может обрабатывать сообщения. К какому из окон относится данная
очередь, определяется в параметрах макроса BEGIN_MESSAGE_MAP.
Его общий вид приведен ниже:
BEGIN_MESSAGE_MAP(Владелец, Базовый)
// здесь помещаются макрокоманды сообщений
END_MESSAGE_MAP()
Название класса, к которому относится очередь, определяется параметром Владелец. Параметр Базовый определяет название базового класса, от которого был порожден класс-владелец. Например, в очереди
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
// здесь помещаются макрокоманды сообщений
END_MESSAGE_MAP()
классом-владельцем является CMainWin, который порождается от стандартного класса CFrameWnd, предназначенного для создания масштабируемых окон.
Чтобы программа могла ответить на сообщение, в очередь сообщений должна быть помещена связанная с ним макрокоманда. Названия
макрокоманд в MFC соответствуют именам стандартных сообщений
Windows, но дополнительно имеют префикс ON_ и заканчиваются парой
22
круглых скобок. Однако из этого правила есть одно исключение: сообщению WM_COMMAND соответствует макрокоманда ON_COMMAND().
Причина заключается в том, что WM_COMMAND – это особое сообщение, которое специальным образом обрабатывается в MFC. Ниже приведены названия макрокоманд MFC, соответствующих перечисленным
выше сообщениям:
ON_WM_CHAR()
ON_WM_PAINT()
ON_WM_MOVE()
ON_WM_CLOSE()
ON_WM_LBUTTONUP() ON_WM_LBUTTONDOWN()
ON_COMMAND()
ON_WM_S1ZE()
Чтобы включить макрокоманду в очередь сообщений, нужно просто
поместить
ее
между
макросами
BEGIN_MESSAGE_MAP
и
END_MESSAGE_MAP. Например, если необходимо, чтобы в программе
обрабатывалось сообщение WM_CHAR, то очередь сообщений должна
выглядеть следующим образом:
BEGIN MESSAGE_MAP(CMainWin, CFrameWnd)
ON_WM_CHAR()
END_MESSAGE_MAP()
Конечно, в очереди может находиться более одной макрокоманды.
Например, в следующем примере производится обработка сообщений
WM_CHAR и WM_LBUTTONDOWN.
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
ON_WM_CHAR()
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
Как будет показано дальше, сообщение WM_CHAR генерируется при
нажатии клавиши на клавиатуре, а сообщение WM_LBUTTONDOWN –
при нажатии левой кнопки мыши.
3.4. Включение обработчиков сообщений в описание класса
Каждое сообщение, обрабатываемое в программе, должно быть связано с одним из обработчиков. Все обработчики сообщений являются
членами класса CWnd и могут быть переопределены в программе. Как
правило, имя функции-обработчика состоит из названия сообщения (без
WM_), которому предшествует префикс On. Например, обработчик для
WM_CHAR называется OnChar, для WM_LBUTTONDOWN – OnLButtonDown, для WM_PAINT – OnPaint.
Обработчик сообщения должен быть помещен в описание класса,
ответственного за создание окна. Это выполняется включением прототипа функции в объявление класса. В следующем примере обработчик
OnChar включается в класс CMainWin:
23
class CMainWin:public CFrameWnd
{ public:
CMainWin();
afx_msg void OnChar(UINT ch, UINT count, UINT flags);
DECLARE_MESSAGE_MAP()
};
Как видите, для OnChar использован спецификатор типа afx_msg. С
помощью него должны быть объявлены все прототипы функцийобработчиков. Теперь, когда OnChar является частью класса CMainWin,
этот класс сможет принимать сообщение WM_CHAR.
3.5. Обработка сообщений от клавиатуры
Одно из наиболее распространенных в Windows сообщений генерируется при нажатии клавиши. Оно называется WM_CHAR, и для него используется макрокоманда ON_WM_CHAR. Приложение никогда не получает данные непосредственно от клавиатуры. Вместо этого каждый раз
при нажатии клавиши активному окну посылается сообщение
WM_CHAR. Чтобы продемонстрировать это, давайте расширим пример
из лабораторной работы № 1 (листинг № 1) таким образом, чтобы в нем
обрабатывались сообщения от клавиатуры.
Обработчик сообщения WM_CHAR называется OnChar. Его прототип таков:
afx_msg void OnChar(UINT Ch, UINT Count, UINT Flags);
Этот обработчик вызывается всякий раз, когда поступает сообщение
WM_CHAR. ASCII-код нажатой клавиши передается в параметре Ch. Количество повторяющихся символов, сгенерированных в результате удерживания клавиши нажатой, передается в параметре Count. Параметр
Flags кодируется следующим образом (табл. 4).
Таблица 4
Содержимое параметра Flags
Бит
15
14
13
12 – 11
10 – 9
8
7–0
Значение
Установлен, если клавиша отпущена; в противном случае сброшен
Установлен, если клавиша была нажата до того, как послано сообщение;
сброшен, если клавиша не была нажата
Установлен, если также нажата клавиша ALT, иначе сброшен
Используется Windows
В настоящий момент не используется
Установлен, если нажатая клавиша является функциональной или находится
на расширенной клавиатуре; сброшен в противном случае
Код клавиши, зависящий от производителя (т. е. скэн-код)
Пока для нас важным является лишь параметр Ch, так как он определяет код нажатой клавиши.
Для обработки сообщения WM_CHAR необходимо поместить его
макрокоманду в очередь сообщений, добавить прототип соответствую24
щего обработчика в объявление класса, предназначенного для создания
главного окна, и включить в программу реализацию функции OnChar.
Вот, например, версия OnChar, в которой поступающие символы отображаются на экране:
char str[80]; // строка для вывода
// Обработка сообщения WM_CHAR
afx_msg void CMainWin::OnChar (UINT ch, UINT count, UINT
flags)
{
CClientDC dc(this);
dc.TextOut (1, 1, " ", 3); // стирает предыдущий символ
wsprintf(str, "%c", ch);
dc.TextOut(1, 1, str, strlen (str));
}
Смысл кода внутри OnChar весьма прост: введенный символ дублируется на экране (так называемый режим «эха»). Должно быть, вы удивлены, что для решения столь тривиальной задачи потребовалось так много строк. Причина заключается в том, что Windows должна организовать
связь между программой и экраном. Это называется контекстом
устройства, или просто DC (Device Context), и достигается путем объявления объекта типа CClientDC, порождаемого от CDC. Класс CClientDC
позволяет предоставить контекст устройства для рабочей области окна.
Более точное определение контекста устройства мы рассмотрим далее.
Для начала достаточно знать, что, получив контекст устройства, программа может начать выводить текст на экран.
Функция, предназначенная для отображения символа, называется
TextOut и является членом класса CDC. Есть два варианта этой функции.
Их прототипы таковы:
virtual BOOL CDC::TextOut(int X, int Y, LPCSTR lpszStr,
int Length);
BOOL CDC::TextOut(int X, int Y, const CString &StrOb);
В первом случае текстовая строка, адресуемая указателем lpszStr,
выводится в окно в точке с координатами, которые содержатся в параметрах Х и Y (по умолчанию координаты приводятся в пикселях). Длина
строки задается параметром Length. Во втором случае выводится строка,
содержащаяся в переменной StrOb. CString – это один из специальных
выделенных классов в MFC. Он предназначен для работы со строками.
Мы будем работать с первым вариантом TextOut. В обоих случаях функция TextOut возвращает либо ненулевое значение при успешном завершении, либо ноль.
Каждый раз при вызове OnChar символ, введенный пользователем,
записывается с помощью функции wsprintf в строку единичной длины, а
затем с помощью TextOut отображается в окне в позиции с координатами
25
(1, 1). Переменная str объявлена глобальной, потому что должна сохранять свое значение между вызовами функции в последующих примерах.
Верхний левый угол рабочей области окна имеет координаты (0, 0).
Координаты точки окна всегда относятся к самому окну, а не к экрану,
поэтому вводимый символ будет отображаться в верхнем левом углу
независимо от действительного расположения окна на экране.
Цель первого вызова функции TextOut – стереть любой ранее отображенный символ. Поскольку Windows – это графическая система, символы в ней могут иметь разный размер, и замена одного символа другим
не обязательно приведет к полному удалению первого. Например, если
символ «w» заменяется символом «i», то часть «w» все равно останется
на экране, если только не будет удалена специально.
Сначала вам может показаться, что использование TextOut для вывода одного символа не совсем рационально. Дело в том, что в MFC (и вообще в Windows) нет функции, предназначенной просто для вывода символа. Большую часть диалога с пользователем Windows осуществляет через диалоговые окна, меню, панели инструментов и т. д. Поэтому функций для вывода текста в рабочую область окна совсем немного.
Ниже приведен пример программы обработки строк, начинающийся
с библиотечного файла MESSAGE1.H, содержащего объявления порождаемых классов.
Задание № 1. Исследуйте программу (листинг № 2). Изучите каждую
строку. Создайте обработчик сообщения от клавиатуры, который будет выполнять другие действия, чем в листинге № 2.
Листинг № 2. Обработка сообщения WM_CHAR
// Файл MESSAGE1.H – Обработка сообщения
// Это класс, предназначенный для создания главного окна
class CMainWin:public CFrameWnd
{ public:
CMainWin();
afx_msg void OnChar(UINT ch, UINT count, UINT flags);
DECLARE_MESSAGE_MAP()
};
// Это класс, предназначенный для создания приложения
class CApp:public CWinApp
{ public:
BOOL InitInstance(); };
// Файл MESSAGE1.CPP - Программа, обрабатывающая сообщение WM_CHAR
#include <afxwin.h>
#include <string.h>
#include "message1.h"
char str[80];
// строка для вывода
26
// Создание окна
CMainWin::CMainWin()
{
Create(NULL, "Обработка сообщения WM_CHAR"); };
// Инициализация приложения
BOOL CApp::InitInstance()
{
m_pMainWnd = new CMainWin;
m_pMainWnd -> ShowWindow(m_nCmdShow);
m_pMainWnd -> UpdateWindow();
return TRUE;
};
// Это очередь сообщений приложения
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
ON_WM_CHAR()
END_MESSAGE_MAP()
// Обработка сообщения WM_CHAR
afx_msg void CMainWin::OnChar(UINT ch, UINT count, UINT flags)
{
CClientDC dc(this);
dc.TextOut(1, 1, "
", 3); // стирает предыдущий символ
wsprintf(str, "%c", ch);
dc.TextOut(1, 1, str, strlen(str));
}
CApp App;
// создание экземпляра приложения
Конец листинга № 2
3.6. Контекст устройства
Программа в листинге № 2 для вывода символов на экран должна
получать контекст устройства.
В широком смысле, контекст устройства представляет собой
путь, по которому приложение Windows, используя соответствующий драйвер устройства, выводит информацию в рабочую область
окна. Контекст устройства является структурой, полностью определяющей состояние этого драйвера и способ вывода информации.
Перед тем как приложение начнет выводить информацию в рабочую
область окна, оно должно получить контекст устройства. Пока это не
сделано, нет связи между программой и окном, предназначенным для вывода. А поскольку контекст устройства требуется для работы TextOut и
других функций вывода, то это правило становится обязательным.
Поскольку Windows может предоставить лишь небольшое число
контекстов, важно, чтобы программа освободила контекст по окончании
работы с ним. К счастью, в MFC есть классы, способные управлять этим
процессом. Например, при создании экземпляра объекта типа CClientDC
программе выделяется контекст устройства. Если этот объект необходимо удалить, то вызывается функция ReleaseDC и контекст устройства ав27
томатически освобождается. В этом одно из преимуществ использования
библиотеки MFC.
При создании объекта типа CClientDC программе предоставляется
контекст устройства, относящийся к рабочей области окна. Конструктор
класса CClientDC таков:
CClientDC(CWnd *Window);
Параметр Window является указателем на окно, которому выделяется
контекст устройства. Для активного окна можно указать ключевое слово
this. Если контекст устройства не может быть предоставлен, то конструктор CClientDC обращается к классу CResourceException.
Таким образом, всякий раз, когда необходимо вывести информацию
в рабочую область окна, необходимо получить контекст устройства с помощью CClientDC. Однако существует особый случай, когда контекст
устройства должен быть получен по-другому. Это происходит при обработке сообщения WM_PAINT, рассматриваемого далее.
3.7. Обработка сообщения WM_PAINT
Задание № 2. Запустите программу из листинга № 2 и введите
символ, например «а». А затем минимизируйте окно и верните его в
прежнее состояние.
В результате выполнения задания № 2 вы обнаружите, что последний введенный символ не будет отображен. Аналогично, если окно закрыть другим окном, а затем вновь активизировать, символ не будет заново отображен. Причина этого проста: в общем случае Windows не сохраняет содержимое окна. Чтобы этого избежать, каждый раз, когда содержимое окна должно быть обновлено, программе посылается сообщение WM_PAINT. И каждый раз, когда программа получает это сообщение, она должна обновить содержимое окна.
Обработчик сообщения WM_PAINT называется OnPaint. Его прототип таков:
afx_msg void OnPaint();
Макрокоманда для этого сообщения называется ON_WM_PAINT.
Для обработки сообщения WM_PAINT необходимо добавить прототип
его обработчика в объявление класса CMainWin, включить соответствующую макрокоманду в очередь сообщений и определить тело обработчика. Например, приводимый ниже обработчик позволяет вывести на экран
текущее содержимое строки str, т. е. последний введенный символ.
// Обработка сообщения WM_PAINT
afx_msg void CMainWin::OnPaint ()
{
28
CPaintDC dc (this);
// следующая строка предназначена для отображения
// последнего введенного символа
dc.TextOut(1, 1, str, strlen (str));
}
Прежде всего обратите внимание, что контекст устройства получается путем создания объекта типа CPaintDC, а не CClientDC. По различным
причинам при обработке сообщения WM_PAINT контекст устройства
должен быть получен именно таким образом. Конструктор класса
CPaintDC имеет следующий прототип:
CPaintDC(CWnd *Window);
В общем он идентичен конструктору CClientDC. Но класс CPaintDC
включает одну важную переменную, называемую m_ps, которая содержит структуру типа PAINTSTRUCT. Когда создается объект типа
CPaintDC, то в этой структуре содержится информация, относящаяся к
текущему состоянию контекста устройства. Структура PAINTSTRUCT
определена следующим образом:
typedef struct tagPAINTSTRUCT {
HDC hdc;
// дескриптор контекста устройства
BOOL fErase;
// TRUE, если фон окна нужно стереть
RECT rcPaint;
// координаты области окна, которую необходимо перерисовать
BOOL fRestore;
// зарезервировано
BOOL fIncUpdate;
// зарезервировано
BYTE rgbReserved[16];// зарезервировано
}PAINTSTRUCT;
Здесь hdc – это дескриптор для контекста устройства. В традиционных Windows-программах с контекстами устройств работают именно через дескрипторы. Однако в MFC используются объекты, поэтому потребность в дескрипторах возникает редко. Если фон окна должен быть стерт,
то переменная fErase будет содержать ненулевое значение. В переменной
rcPaint содержатся координаты области окна, которая должна быть перерисована. Тип RECT был описан в первой лабораторной работе. Использовать содержимое rcPaint нам не придется, поскольку мы предполагаем,
что перерисовать нужно все окно. Значения fRestore, fIncUpdate, rgbReserved зарезервированы и используются внутри Windows.
Ниже приведен пример программы (листинг № 3), обрабатывающей
сообщение WM_PAINT. Первым показан файл MESSAGE2.H, содержащий объявления порождаемых классов.
Следует подчеркнуть, что в большинстве реальных проектов обработка WM_PAINT значительно сложнее, так как обычно окна содержат
гораздо больше информации, которую необходимо обновлять.
29
Реализация восстановления содержимого окна возможна тремя способами. Во-первых, можно просто вычислять координаты выводимых
символов. Это легче всего осуществлять, если не предполагается ввод
данных пользователем. Во-вторых, иногда можно хранить запись последовательности событий, произошедших в окне, и воспроизводить их, когда необходимо обновить содержимое окна. И, наконец, в программе может поддерживаться так называемое виртуальное окно, содержимое которого просто копируется в реальное окно каждый раз при его обновлении. Это наиболее общий метод. Какой из способов лучше, зависит от
приложения.
Задание № 3. Наберите, откомпилируйте и запустите программу
(листинг № 3 ). Найдите отличия в работе программы по сравнению с
программой в листинге № 2.
Листинг № 3. Обработка сообщений WM_CHAR и WM_PAINT
// Файл MESSAGE2.H - Обработка сообщений WM_CHAR и WM_PAINT
class CMainWin:public CFrameWnd
{ public:
CMainWin();
afx_msg void OnChar(UINT ch, UINT count, UINT flags);
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP() };
class CApp:public CWinApp
{ public:
BOOL InitInstance(); };
// Файл MESSAGE2.CPP
// Программа, обрабатывающая сообщения WM_CHAR и WM_PAINT
#include <afxwin.h>
#include <string.h>
#include "message2.h"
char str[80] = "Пример"; // строка для вывода
// Создание окна
CMainWin::CMainWin()
{
Create(NULL, "Обработка сообщения WM_PAINT"); };
BOOL CApp::InitInstance()
{
m_pMainWnd = new CMainWin;
m_pMainWnd -> ShowWindow(m_nCmdShow);
m_pMainWnd -> UpdateWindow();
return TRUE; };
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
ON_WM_CHAR()
ON_WM_PAINT()
END_MESSAGE_MAP()
30
// Обработка сообщения WM_CHAR
afx_msg void CMainWin::OnChar(UINT ch, UINT count, UINT flags)
{
CClientDC dc(this);
dc.TextOut(1, 1, "
", 3); // стирает предыдущий символ
wsprintf(str, "%c", ch);
dc.TextOut(1, 1, str, strlen(str));
}
// Обработка сообщения WM_PAINT
afx_msg void CMainWin::OnPaint()
{
CPaintDC dc(this);
// отображение последнего введенного символа
dc.TextOut(1, 1, str, strlen(str));
}
CApp App; // создание экземпляра приложения
Конец листинга № 3
3.8. Обработка сообщений мыши
Поскольку Windows является операционной системой, в значительной степени ориентированной на работу с мышью, все программы для
Windows должны определенным образом реагировать на действия мыши.
Учитывая важность этого момента, в Windows имеется несколько типов
сообщений мыши. В данном пункте рассматриваются два наиболее общих из них, WM_LBUTTONDOWN и WM_RBUTTONDOWN, генерируемые при нажатии левой и правой кнопок мыши, соответственно. Обработчиками этих сообщений являются функции OnLButtonDown и
OnRButtonDown. Их прототипы таковы:
afx_msg void OnLButtonDown(UINT Flags, CPoint Loc);
afx_msg void OnRButtonDown(UINT Flags, CPoint Loc);
Значение параметра Flags указывает на то, была ли при генерировании
сообщения нажата клавиша [CTRL], [SHIFT] или другая кнопка мыши. Параметр может содержать любую комбинацию из следующих значений:
MK_CONTROL
MK_SHIFT
MK_MBUTTON
MK_RBUTTON
MK_LBUTTON
Если одновременно с кнопкой мыши была нажата клавиша [CTRL],
то параметр Flags будет содержать MK_CONTROL. Если была нажата
клавиша [SHIFT], то значением Flags будет MK_SHIFT. Если при нажатии левой кнопки мыши удерживалась и правая кнопка, значением будет
MK_RBUTTON. И, наоборот, если при нажатии правой кнопки удерживалась левая, значением будет MK_LBUTTON. Если же при нажатии одной из этих кнопок удерживалась также средняя кнопка (при ее наличии
и поддержки драйверами мыши), то параметр Flags будет содержать
MK_MBUTTON. Одновременно может быть установлено несколько таких значений.
31
Параметр Loc, являющийся объектом типа CPoint, содержит координаты курсора мыши в момент нажатия кнопки. Класс CPoint порождается
от структуры POINT, определенной следующим образом:
typedef struct tagPOINT{
LONG x;
LONG у;
} POINT;
Структура POINT содержит координаты (X,Y) курсора мыши. Таким
образом, координата Х находится в переменной Loc.x, а координата Y – в
переменной Loc.y.
В следующем примере (листинг № 4) в нашу программу вносятся
необходимые элементы, позволяющие ей обрабатывать сообщения мыши. Каждый раз при нажатии левой или правой кнопки мыши выводится
строка сообщения, которая отображается в позиции курсора мыши.
Листинг № 4. Обработка сообщений мыши
// Файл MESSAGE3.H - Обработка сообщений мыши
class CMainWin:public CFrameWnd
{ public:
CMainWin();
afx_msg void OnChar(UINT ch, UINT count, UINT flags);
afx_msg void OnPaint();
afx_msg void OnLButtonDown(UINT flags, CPoint loc);
afx_msg void OnRButtonDown(UINT flags, CPoint loc);
DECLARE_MESSAGE_MAP() };
class CApp:public CWinApp
{ public:
BOOL InitInstance(); };
// Файл MESSAGE3.CPP - Программа, обрабатывающая сообщения от мыши
#include <afxwin.h>
#include <string.h>
#include "message3.h"
char str[80] = "Пример"; // строка для вывода
CMainWin::CMainWin()
{
Create(NULL, "Обработка сообщений от мыши"); };
BOOL CApp::InitInstance()
{
m_pMainWnd = new CMainWin;
m_pMainWnd -> ShowWindow(m_nCmdShow);
m_pMainWnd -> UpdateWindow();
return TRUE; };
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
ON_WM_CHAR()
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_RBUTTONDOWN()
32
END_MESSAGE_MAP()
afx_msg void CMainWin::OnChar(UINT ch, UINT count, UINT flags)
{
CClientDC dc(this);
dc.TextOut(1, 1, "
", 3); // стирает предыдущий символ
wsprintf(str, "%c", ch);
dc.TextOut(1, 1, str, strlen(str));
}
afx_msg void CMainWin::OnPaint()
{
CPaintDC dc(this);
dc.TextOut(1, 1, str, strlen(str)); }
// Обработка нажатия левой кнопки мыши
afx_msg void CMainWin::OnLButtonDown(UINT flags,CPoint loc)
{
CClientDC dc(this);
wsprintf(str, "Нажата левая кнопка мыши");
dc.TextOut(loc.x, loc.y, str, strlen(str)); }
// Обработка нажатия правой кнопки мыши
afx_msg void CMainWin::OnRButtonDown(UINT flags,CPoint loc)
{
CClientDC dc(this);
wsprintf(str, "Нажата правая кнопка мыши");
dc.TextOut(loc.x, loc.y, str, strlen(str)); }
CApp App;
// создание экземпляра приложения
Конец листинга № 4
Рис. 2. Пример работы программы обработки сообщений мыши (листинг № 4)
Задание № 4. Запустите программу (листинг № 4), изучите ее
работу. Напишите собственный обработчик сообщений мыши, который использует информацию сообщения от мыши.
3.9. Генерация сообщения WM_PAINT
Программа сама может вызвать генерацию сообщения WM_PAINT.
Это необходимо, так как процесс перерисовки окна может занимать до33
статочно много времени, ведь Windows – это многозадачная система и в
ней одновременно могут выполняться несколько программ, каждой из
которых также требуются ресурсы процессора. Поэтому программе нужно просто сообщить Windows о необходимости вывода информации, но
предоставить системе самой решить, когда это лучше всего выполнить.
Такой подход позволяет Windows эффективнее распределять ресурсы
процессора между всеми выполняющимися задачами. В этом случае программа приостанавливает вывод на экран до тех пор, пока не будет получено сообщение WM_PAINT.
В предыдущих примерах сообщение WM_PAINT принималось только тогда когда окно не было перекрыто. Но если вывод на экран задерживается до принятия WM_PAINT, то для организации интерактивного ввода-вывода необходимо найти способ сообщить Windows о том, что она
должна послать сообщение WM_PAINT в окно, если вывод не завершен.
Чтобы побудить Windows послать сообщение WM_PAINT, программа должна вызвать функцию InvalidateRect, являющуюся членом класса
CWnd. Прототип функции таков:
void CWnd::InvalidateRect(LPCRECT IpRegion, BOOL Erase = TRUE);
Здесь параметр IpRegion является указателем на объект типа CRect,
определяющий область внутри окна, которая должна быть перерисована.
Класс CRect порождается от структуры типа RECT, описанной выше. Координаты обновляемой области определятся полями структуры RECT.
Однако если значение параметра равно NULL, то обновить необходимо
содержимое всего окна. Если параметр Erase не равен нулю, то нужно
стереть фон окна. Если значение параметра равно нулю, то фон будет
оставлен без изменений.
При вызове функции InvalidateRect системе сообщается о том, что
содержимое окна некорректно и должно быть обновлено. Это, в свою
очередь, приводит к тому, что Windows посылает программе сообщение
WM_PAINT.
Ниже приводится переработанный вариант предыдущей программы
(листинг № 5), в котором весь процесс вывода направляется с помощью
обработчика сообщения WM_PAINT. Каждый раз, когда необходимо
осуществить вывод, посылается сообщение WM_PAINT. Другие обработчики просто подготавливают предназначенную для вывода информацию, а затем вызывают функцию InvalidateRect.
Листинг № 5. Генерация сообщения WM_PAINT
// Файл MESSAGE4.H - генерация сообщения WM_PAINT
class CMainWin:public CFrameWnd
{
public:
34
CMainWin();
afx_msg void OnChar(UINT ch, UINT count, UINT flags);
afx_msg void OnPaint();
afx_msg void OnLButtonDown(UINT flags, CPoint loc);
afx_msg void OnRButtonDown(UINT flags, CPoint loc);
DECLARE_MESSAGE_MAP() };
class CApp:public CWinApp
{
public: BOOL InitInstance(); };
// Файл MESSAGE4.CPP - программа, генерирующая сообщение WM_PAINT
#include <afxwin.h>
#include <string.h>
#include "message4.h"
char str[80] = "Пример"; // строка для вывода
int X = 1, Y = 1;
// текущая позиция вывода
CMainWin::CMainWin()
{
Create(NULL, "Генерация сообщения WM_PAINT"); };
BOOL CApp::InitInstance()
{
m_pMainWnd = new CMainWin;
m_pMainWnd -> ShowWindow(m_nCmdShow);
m_pMainWnd -> UpdateWindow();
return TRUE; };
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
ON_WM_CHAR()
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_RBUTTONDOWN()
END_MESSAGE_MAP()
// Обработка сообщения WM_CHAR
afx_msg void CMainWin::OnChar(UINT ch, UINT count, UINT flags)
{
X = Y = 1;
wsprintf(str, "%c", ch);
InvalidateRect(NULL); }
// Обработка сообщения WM_PAINT
afx_msg void CMainWin::OnPaint()
{
CPaintDC dc(this);
dc.TextOut(X, Y, str, strlen(str)); }
// Обработка нажатия левой кнопки мыши
afx_msg void CMainWin::OnLButtonDown(UINT flags,CPoint loc)
{
wsprintf(str, "Нажата левая кнопка мыши");
X = loc.x;
Y = loc.y;
InvalidateRect(NULL); }
// Обработка нажатия правой кнопки мыши
afx_msg void CMainWin::OnRButtonDown(UINT flags,CPoint loc)
{
wsprintf(str, "Нажата правая кнопка мыши");
35
X = loc.x;
Y = loc.y;
InvalidateRect(NULL); }
CApp App;
// создание экземпляра приложения
Конец листинга № 5
Задание № 5. Изучите программу (листинг № 5). Испытайте ее
для различных ситуаций. Последовательно удалите в обработчиках
сообщений вызов функции InvalidateRect и изучите поведение программы.
Заметьте, что в программе добавились две новые глобальные переменные, Х и Y, содержащие координаты позиции окна, в которой будет
выведен текст при получении сообщения WM_PAINT.
Как видите, организация вывода на основе сообщения WM_PAINT
позволила немного сократить программу и сделать ее, в определенном
смысле, более понятной.
3.10. Обработка сообщения WM_DESTROY
Есть одно специальное сообщение, на которое программе также требуется отвечать: WM_DESTROY. Оно посылается окну, когда последнее
должно быть закрыто. Если такое сообщение получает главное окно приложения, это означает, что программа завершается. В этом случае приложение
должно выполнить все необходимые операции по выгрузке. Обработчик сообщения WM_DESTROY называется OnDestroy, а его прототип таков:
afx_msg void OnDestroy();
До сего момента не было необходимости явно обрабатывать это сообщение, потому что ни в одной программе не требовалось выполнять
специальных действий при ее завершении. Однако на практике, чаще
всего, бывает наоборот. Действия, которые необходимо совершить в конце, выполняются внутри функции OnDestroy. Программа на листинге № 6
из п. 3.11 содержит пример выполнения таких действий.
3.11. Обработка сообщений таймера
С помощью Windows можно управлять работой таймера, который
периодически прерывает работу программы. Каждый раз, когда происходит прерывание таймера, программе посылается сообщение WM_TIMER.
Это хороший способ периодически «будить» программу. Он особенно
полезен, когда программа выполняется в фоновом режиме.
Для запуска таймера используется функция SetTimer, являющаяся
членом класса CWnd. Ее прототип таков:
36
UINT CWnd::SetTimer(UINT ID, UINT Length,
void(CALLBACK EXPORT *TFunc) (HWND,
DWORD));
UINT,
UINT,
Здесь параметр ID задает числовой идентификатор, который будет
ассоциироваться с данным таймером (может быть несколько таймеров).
Значение Length определяет длину интервала работы таймера, т. е. промежуток времени, проходящий между двумя прерываниями. Функция, на
которую указывает параметр TFunc, является функцией таймера и вызывается в момент прерывания. Это должна быть функция обратного вызова, которая возвращает VOID CALLBACK и принимает параметры тех
же типов, что и традиционная функция окна. Если же значение TFunc
равно NULL, как это обычно бывает, то сообщения таймера будут посылаться главному окну программы. В таком случае при каждом прерывании таймера сообщение WM_TIMER помещается в очередь сообщений
программы и обрабатывается так же, как и все другие сообщения. Этот
принцип использован в следующем примере. В случае успеха функция
возвращает значение ID. Если таймер недоступен, возвращается ноль.
Когда таймер запущен, он продолжает периодически прерывать выполнение программы до тех пор, пока не будет вызвана функция
CWnd::KillTimer, прототип которой приведен ниже:
BOOL CWnd::KillTimer(int ID);
Здесь параметр ID является идентификатором соответствующего
таймера. Функция KillTimer возвращает ненулевое значение при успешном завершении и ноль – в противном случае.
Сообщения таймера обрабатываются функцией OnTimer, которая
имеет такой прототип:
afx_msg void OnTimer(UINT ID);
Каждый раз при вызове функции OnTimer параметр ID содержит
идентификатор того таймера, который инициировал прерывание. Если в
программе используются несколько таймеров, то необходимо проверять
значение параметра ID, чтобы определить, какой из таймеров послал сообщение.
Использование таймера демонстрируется в следующей программе,
где с его помощью будут созданы «часы». Текущее системное время и
дата могут быть получены и отображены с помощью функций класса
CTime. При каждом прерывании таймера, происходящем примерно раз в
секунду, значение времени обновляется. Таким образом, время отображается с точностью до секунды. Поскольку все таймеры по окончании использования должны быть деактивированы, то при получении сообщения
WM_DESTROY программа «останавливает» таймер.
37
Листинг № 6. Программа «Часы», обрабатывает сообщение WM_DESTROY
// Файл MESSAGE5.H - Программа "Часы" - обработка сообщения WM_DESTROY
class CMainWin:public CFrameWnd
{ public:
CMainWin();
afx_msg void OnPaint();
afx_msg void OnTimer(UINT ID);
afx_msg void OnDestroy();
DECLARE_MESSAGE_MAP() };
class CApp:public CWinApp
{ public:
BOOL InitInstance(); };
// Файл MESSAGE5.CPP - Программа "Часы"
#include <afxwin.h>
#include <string.h>
#include <time.h>
#include "message5.h"
char str[80] = "";
// строка для вывода
CMainWin::CMainWin()
{
RECT r;
r.left = r.top = 10;
r.right = 200;
r.bottom = 60;
Create(NULL, "Часы", WS_OVERLAPPEDWINDOW, r);
};
// Инициализация приложения
BOOL CApp::InitInstance()
{
m_pMainWnd = new CMainWin;
// Запуск таймера
if (m_pMainWnd -> SetTimer(1, 1000, NULL) != 1) return FALSE;
m_pMainWnd -> ShowWindow(m_nCmdShow);
m_pMainWnd -> UpdateWindow();
return TRUE; };
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
ON_WM_PAINT()
ON_WM_TIMER()
ON_WM_DESTROY()
END_MESSAGE_MAP()
// Обработка сообщения WM_PAINT
afx_msg void CMainWin::OnPaint()
{
CPaintDC dc(this);
// следующая строка предназначена для отображения
// даты и времени
dc.TextOut(1, 1, str, strlen(str)); }
38
// Обработка сообщения WM_TIMER
afx_msg void CMainWin::OnTimer(UINT ID)
{
CTime curtime = CTime::GetCurrentTime();
struct tm *newtime;
newtime = curtime.GetLocalTm();
wsprintf(str, asctime(newtime));
str[strlen(str)-1] = '\0'; // удаляет символы конца строки
InvalidateRect(NULL, 0);
}
// Завершение приложения – обработка сообщения WM_DESTROY
afx_msg void CMainWin::OnDestroy()
{
KillTimer(1);
}
CApp App;
// создание экземпляра приложения
Конец листинга № 6
Рис. 3. Пример работы программы «Часы»
Задание № 6. Изучите программу (листинг № 6). Создайте еще
один таймер в программе и напишите код для обработки его сообщения. Не забудьте удалить его при завершении работы приложения.
3.12. Основные типы сообщений Windows
Аппаратные сообщения. Оконная программа получает сообщения,
инициируемые тремя частями аппаратного обеспечения: клавиатурой,
мышью и системным таймером. Все они генерируют аппаратные прерывания. Поскольку диспетчеризация Windows не действует в режиме прерываний, аппаратные события должны быть буферизованы. Это обеспечивает обработку событий в порядке их поступления. Например, когда
вы нажимаете клавишу [К], прерывание уведомляет систему о том, что
приготовлены данные от клавиатуры. Драйвер клавиатуры отыскивает
эти данные и создает элемент в первичной очереди, откуда система их
передает в очередь приложения, а точнее, конкретного потока. Когда
наступает его очередь, сообщения, содержащие информацию о клавише
[К], направляются в соответствующую оконную процедуру. Сообщения
от мыши и системного таймера обрабатываются аналогично. В табл. 5
представлены основные сообщения, которые формируются в ответ на работу аппаратного обеспечения.
39
Таблица 5
Аппаратные сообщения
Название сообщения
Описание
Сообщения от мыши в клиентской области окна
WM_LBUTTONDBLCLK
Двойное нажатие левой кнопки
WM_LBUTTONDOWN
Нажатие левой кнопки
WM_LBUTTONUP
Отпускание левой кнопки
WM_MBUTTONDBLCLK
Двойное нажатие средней кнопки
WM_MBUTTONDOWN
Нажатие средней кнопки
WM_MBUTTONUP
Отпускание средней кнопки
WM_MOUSEMOVE
Перемещение мыши
WM_RBUTTONDBLCLK
Двойное нажатие правой кнопки
WM_RBUTTONDOWN
Нажатие правой кнопки
WM_RBUTTONUP
Отпускание правой кнопки
Сообщения от мыши в неклиентской области окна
WM_NCLBUTTONDBLCLK
Двойное нажатие левой кнопки
WM_NCLBUTTONDOWN
Нажатие левой кнопки
WM_NCLBUTTONUP
Отпускание левой кнопки
WM_NCLBUTTONUP
Двойное нажатие средней кнопки
WM_NCMBUTTONDBLCLK
Нажатие средней кнопки
WM_NCMBUTTONDOWN
Отпускание средней кнопки
WM_NCMBUTTONUP
Перемещение мыши
WM_NCRBUTTONDBLCLK
Двойное нажатие правой кнопки
WM_NCRBUTTONDOWN
Нажатие правой кнопки
WM_NCRBUTTONUP
Отпускание правой кнопки
Сообщения от клавиатуры
WM_CHAR
Ввод символа
WM_DEADCHAR
Диакритический знак (умляут, ударение и т. д.)
WM_KEYDOWN
Была нажата клавиша
WM_KEYUP
Была отпущена клавиша
WM_SYSCHAR
Ввод специального символа
WM_SYSDEADCHAR
Специальный диакритический знак
WM_SYSKEYDOWN
Была нажата специальная клавиша
WM_SYSKEYUP
Была отпущена специальная клавиша
Сообщения от таймера
WM_TIMER
Истекло заданное время
Сообщение WM_MOUSEMOVE заслуживает особого упоминания, так
как обрабатывается особым образом, чтобы избежать переполнения очереди аппаратных событий. Дело в том, что при быстром перемещении
мыши могут произойти сотни прерываний. Во избежание переполнения
очереди система Windows хранит только одно сообщение о перемещении
мыши в каждый момент времени. Когда приходит новое сообщение,
Windows проверяет, есть ли уже подобное. Если оно есть, то в нем обновляется информация о положении. Новое сообщение помещается в
очередь сообщений только в том случае, если сообщения о перемещении
мыши еще не было.
40
Сообщения от клавиатуры делятся на два основных типа: обычные и
системные. Обычно системные сообщения от клавиатуры (начинающиеся на
WM_SYS...) игнорируются, так как оконная процедура, устанавливаемая по
умолчанию, превращает их в соответствующую системную команду.
Сообщения обслуживания окна. Здесь собраны наиболее часто используемые сообщения. К сожалению, имена не окажут большой помощи.
Таблица 6
Сообщения обслуживания окна
Название сообщения
Описание
Оконные сообщения: уведомление
WM_ACTIVATE
Окно активно
WM_ACTIVATEAPP
Приложение активно
WM_DREATE
Окно было создано
WM_DESTROY
Окно было уничтожено
WM_ENABLE
Был разрешен ввод в окно
WM_KILLFOCUS
Окно потеряло фокус ввода от клавиатуры
WM_MOUSEACTIVATE
Окно станет активным после нажатия кнопки мыши
WM_MOVE
Окно было перемещено
WM_SETFOCUS
Окно получило фокус ввода от клавиатуры
WM_S1ZE
Окно изменило размер
Оконные сообщения: требование действия
WM_CLOSE
Закрыть (уничтожить) окно
WM_ERASEBKGND
Удалить фон
WM_CONERASEBKGND
Удалить фон окна, показываемого в виде пиктограммы
WM_NCACTIVATE
Изменить заголовок окна для показа активного состояния
WM_NCCREATE
Создать неклиентскую область окна
WM_NCDESTROY
Уничтожить неклиентскую область окна
WM_NCPAINT
Перерисовать неклиентскую область
WM_PAINT
Перерисовать клиентскую область
Перерисовать клиентскую область окна, показываемого
WM_PAINTICON
в виде пиктограммы
WM_SETREDRAW
Запретить перерисовку окна
WM_SETTEXT
Изменить текст окна
WM_SHOWWINDOW
Изменить режим отображения окна
Оконные сообщения: запросы
Запрос информации о минимальном/максимальном разWM_GETMINMAXINFO
мере окна
Запрос текста заголовка окна, а для элементов, не имеWM_GETTEXT
ющих заголовка (например, для кнопок) – текста, размещенного в окне
WM_GETTEXTLINGTH
Запрос длины текста окна
WM_NCCALCSIZE
Запрос размеров клиентской области
WM_QUERYNEWPALETTE Запрос существования новой палитры
Запрос возможности открытия окна, отображаемого в
WM_QUERYOPEN
виде пиктограммы
Сообщение-уведомление говорит оконной процедуре о том, что состояние окна было изменено. При этом оконная процедура вовсе не
41
должна выполнять действие, обозначенное именем сообщения. Например, сообщение WM_MOVE не является запросом на некоторое перемещение. Наоборот, оно является «сообщением после действия» и напоминает, что окно уже было перемещено. Обратите внимание, что из сообщения не ясно, каким образом произошло перемещение. Это могло быть
результатом действий пользователя, равно как и другой программы. В
любом случае сообщения-уведомления представляют собой одностороннюю связь с оконной процедурой.
Сообщение-требование действия говорит само за себя, так как отсутствие необходимого действия вызывает ошибку в интерфейсе пользователя. Например, сообщение WM_PAINT посылается оконной процедуре,
если окно необходимо перерисовать (обновить) из-за изменения данных,
связанных с окном, или в результате активизации и перевода окна на передний план из-за другого окна. Обычно устанавливаемая по умолчанию
оконная процедура обеспечивает некоторые необходимые минимальные
действия, однако зачастую следует перехватывать и обрабатывать такого
рода сообщения самому, как в случае с сообщением WM_PAINT. При
этом нередко нужно воспроизвести все действия обработчика, устанавливаемого по умолчанию.
Сообщения-запросы требуют ответа. Они используются для двусторонней связи между Windows и Вашей программой. Подобно сообщениям-требованиям действия, оконная процедура, устанавливаемая по умолчанию, даст приемлемый ответ в большинстве случаев. Сообщениязапросы следует перехватывать, если Вы хотите изменить ответ по умолчанию. Например, сообщение WM_QUERYOPEN возникает, когда пользователь попытается открыть минимизированное окно. Если Вы хотите,
чтобы программа выполнялась только тогда, когда окно отображается в
виде пиктограммы, то нужно просто ответить FALSE вместо TRUE,
установленного по умолчанию.
Сообщения об организации интерфейса пользователя. Эта группа
содержит сообщения для других объектов пользовательского интерфейса,
включающих меню прикладной программы, указатель мыши, линейки
прокрутки, блоки диалога и их элементы управления, а также сообщения,
используемые для поддержки многодокументного интерфейса (MDI).
Таблица 7
Сообщения организации интерфейса пользователя
Название сообщения
WM_COMMAND
WM_INITMENU
WM_MENUCHAR
WM_MENUSELECT
Описание
Сообщения от меню
Был выбран пункт меню
Уведомление о возможности изменить меню перед его активизацией
Для выбора меню использована мнемоническая клавиша
Пользователь просматривает меню
42
Окончание табл. 7
Название сообщения
Описание
Системные команды
WM_SYSCOMMAND
Была выбрана системная команда
Сообщения от указателя мыши
WM_NCHITTEST
Передача в окно текущего положения указателя мыши
Уведомление окну при перемещении мыши с возможностью
WM_SETCURSOR
изменить курсор в зависимости от положения указателя
Сообщения линейки прокрутки
WM_HSCROLL
Была нажата горизонтальная линейка прокрутки
WM_VSCROLL
Была нажата вертикальная линейка прокрутки
Сообщения блока диалога и его элементов управления
WM_COMMAND
Управляющая связь с блоком диалога
Посылается родителю с запросом на сравнение двух элеWM_COMPAREITEM
ментов для сортировки
WM_CTLCOLOR
Управляющий запрос на установку цветов
Уведомление, направляемое элементам управления ListBox
WM_DELETEITEM
и ComboBox о том, что удален элемент
Запрос родителю пользовательских элементов управления и
WM_DRAWITEM
меню на рисование
Запрос оконной процедуре, какие типы сообщений будут ею
WM_GETDLGCODE
впоследствии обрабатываться
WM_GETFONT
Запрос дескриптора основного шрифта отображения
WM_INITDIALOG
Запрос на инициализацию диалогового окна
Запрос родителю элемента управления на обеспечение его
WM_MEASUREITEM
размеров при прорисовке
Требование к элементу управления: использовать данный
WM_SETFONT
шрифт
Сообщения многодокументного интерфейса (MDI)
Объявляет порождающему окну об активизации дочернего
WM_CHILDACTIVATE
окна
Объявляет дочернему окну MDI, что оно получает или теряWM_MDIACTIVATE
ет активизацию
Требование оформить дочерние окна MDI в каскадной (стуWM_MDICASCADE
пенчатой) форме
WM_MDICREATE
Требование окну клиента MDI создать дочернее окно
WM_MDIGETACTIVE
Запрос окна клиента MDI о текущем активном дочернем окне
Требование упорядочить расположение дочерних окон MDI,
WM_MDIICONARRANGE
отображаемых в виде пиктограмм
WM_MDIMAXIMIZE
Требование максимизировать дочерние окна MDI
WM_MDINEXT
Требование активизировать следующее дочернее окно MDI
Требование восстановить дочернее окно MDI в его предыWM_MDIRESTORE
дущем состоянии
WM_MDISETMENU
Настраивает меню для текущего дочернего окна MDI
Требование оформить дочерние окна MDI в форме черепичWM_MDITITLE
ного (без перекрытий) расположения
Сообщения о завершении. Эта группа сообщений самая малочисленная, но все они очень важны, так как используются для управления
завершением приложений.
43
Таблица 8
Сообщения о завершении
Название сообщения
WM_QUIT
WM_QUERYENDSESSION
WM_ENDSESSION
Описание
Требование на завершение приложения
Запрос готовности приложения к завершению работы
всей системы
Уведомление о результатах запроса на завершение работы системы
Частные сообщения. Очень просто определить собственные сообщения и использовать их для связи между различными окнами, которые
Вы создаете. При определении частного сообщения необходимо использовать область значений, начинающуюся с WM_USER, следующим образом:
#define WM_USER 0x0400
Например, если мы хотим работать с окнами, которые показывают
числа, то могли бы контролировать появление чисел различных систем
исчисления с помощью следующих частных сообщений (РМ_ – префикс
«частного сообщения» от Private Message):
#define
#define
#define
#define
PM_DECIMAL WM__USER + 0
PM_BINARY WM_USER + 1
PM_HEX WM_USER + 2
PM_OCTAL WMJJSER + 3
Кроме того, есть несколько частных системных сообщений (табл. 9).
Таблица 9
Частные системные сообщения
Название сообщения
WM_CANCELMODE
WM_ENTERIDLE
Описание
Требование системы отменить режим, например, захват мыши
Сообщает, что система находится в режиме ожидания
Информационные сообщения системных ресурсов. Имеется всего
семь сообщений с уведомлением об изменении некоторых системных ресурсов (табл.10). Они посылаются окну верхнего уровня каждой программы при изменении системного ресурса. Например, когда добавляются или удаляются шрифты в системе, распространяется сообщение
WM_FONTCHANGE. А когда пользователь изменяет системные цвета
или системное время, отправляются сообщения WM_SYSCOLORCHANGE
или WM_TIMECHANGE, соответственно. Типичной реакцией на часть
такого рода сообщений является немедленная запись произведенных изменений, т. е. сохранение выполненной работы.
Не всякое изменение представляет интерес для каждой программы.
Например, программа, использующая таймер, вероятно, будет проверять
новое значение времени при получении сообщения WM_TIMECHANGE.
Но если она не работает со шрифтами, то, скорее всего, сообщение
WM_FONTCHANGE будет проигнорировано. Поскольку большинство
программ использует системные цвета, то они будут реагировать на сообщение WM_SYSCOLORCHANGE, организуя перерисовывание новыми цветами.
44
Таблица 10
Сообщения с уведомлением об изменениях системных ресурсов
Название сообщения
WM_COMPACTING
WM_DEVMODECHANGE
WM_FONTCHANGE
WM_PALETTECHANGED
WM_SPOOLERSTATUS
WM_SYSCOLORCHANGE
WM_TIMECHANGE
Описание
Недостаточно системной памяти
Изменилась настройка принтера
Изменились установленные в системе шрифты
Палитра аппаратных цветов изменена
Задание было удалено из очереди буфера печати
Один или более системных цветов изменены
Изменилось системное время
Сообщения о совместном использовании данных. В Windows есть
сообщения, которые посылаются при совместном использовании данных
для поддержки механизмов работы с буфером обмена (clipboard) и динамического обмена данными (DDE), которые определены в файлах
WINUSER.H и DDE.H, соответственно.
Таблица 11
Сообщения о совместном использовании данных
Название сообщения
Описание
Сообщения от буфера обмена
WM_ASKCBFORMATNAME
Запрашивает имя формата буфера обмена
WM_CNANGECBCHAIN
Уведомление об изменении цепочки просмотра
WM_DESTRIYCLIPBOARD
Содержимое буфера обмена уничтожено
WM_DRAWCLIPBOARD
Содержимое буфера обмена изменено
WM_HSCROLLCLIPBOARD
Горизонтальная прокрутка буфера обмена
WM_PAINTCLIPBOARD
Запрашивает рисование буфера обмена
Запрашивает данные для всех ранее объявленных
WM_RENDERALLFORMATS
форматов буфера обмена
Запрашивает данные для ранее объявленного формата
WM_RENDERFORMAT
буфера обмена
WM_SIZECLIPBOARD
Размер буфера обмена изменен
WM_VSCROLLCLIPBOARD
Вертикальная прокрутка буфера обмена
Сообщения DDE
WM_DDE_ACK
Подтверждение приема
Запрос от пользователя DDE об установлении постоWM_DDE_ADVISE
янного канала передачи данных
Направляет элемент данных от DDE-сервера клиенту
WM_DDE_DATA
DDE
WM_DDE_EXECUTE
Дает указание DDE-серверу выполнить серию команд
WM_DDE_INITIATE
Вход в DDE-сервер
Запрос пользователя серверу об обновлении опредеWM_DDE_POKE
ленного элемента данных
WM_DDE_REQUEST
Однократный запрос пользователя на информацию
WM_DDE_TERMINATE
Выход из DDE-сервера
Завершить постоянный канал передачи данных, иниWM_DDE_INADVISE
циализированный сообщением WM_DDE_ADVISE
Внутрисистемные сообщения. Помимо перечисленных, имеется
большая группа сообщений, не описанных ни в какой документации. Это
45
внутренние системные сообщения, которые Windows использует в своих
собственных целях. Эти сообщения передаются оконной процедуре и, если она их не обработает, то направляются обработчику сообщений, устанавливаемому по умолчанию.
В принципе, ничто не мешает вам использовать эти сообщения в
собственных целях, но с определенной осторожностью. Найти их имена
можно в файле WINUSER.H.
Задание № 7. Разработайте программу, обрабатывающую не менее трех типов сообщений из таблиц 5–11.
3.13. Содержание отчета
Отчет по лабораторной работе представляет собой распечатку созданных четырех программ с комментариями. Первая программа – новый
обработчик сообщений от клавиатуры, вторая программа – новый обработчик сообщений от мыши, третья программа – два таймера, четвертая
программа – обработка трех сообщений, не описанных в пп. 3.2–3.11.
Отчет должен содержать таблицу по проведенным экспериментам и
исследованиям. Таблица состоит из двух столбцов. Первый содержит описание выполняемых действий (изменений), второй – результат изменений.
Также в отчет рекомендуется включить необходимые скриншоты созданных программ. Отчет должен быть подготовлен с помощью Microsoft
Word и содержать титульный лист установленного образца.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
3.14. Контрольные вопросы
Что такое сообщение?
Приведите классификацию сообщений.
Приведите не менее пяти примеров из разных типов сообщений.
Для чего служит очередь сообщений?
Что такое обработчик сообщений?
Дайте определение контексту устройства.
Опишите действия, которые должен выполнить программист для обработки сообщений.
Расскажите о процессе обработки сообщений Windows и программой.
В чем заключается проблема перерисовки?
Когда возникает проблема перерисовки?
Какие есть способы решения задачи перерисовки?
Какое сообщение помогает решить проблему перерисовки?
Что делает функция InvalidateRect?
Как запустить таймер в программе?
Как программа определяет таймер, который послал сообщение?
Как остановить таймер в программе?
Что делает функция TextOut?
46
1.
2.
3.
4.
5.
3.15. Задачи на самостоятельную работу
Создайте программу, содержащую таймер, который по двойному
щелчку мыши меняет отсчет времени с обычного на остаток до нового года, т. е. показывает, сколько времени осталось до ближайшего
нового года (задача простой сложности – программа описана в литературе).
Создайте приложение – окно, меняющее форму в зависимости от того, находится ли мышь на окне или вне окна (задача средней сложности – различные части программы описаны в литературе).
Создайте программу – окно с дополнительной кнопкой возле кнопки
«Свернуть окно», выполняющее какое-либо действие (задача средней сложности – различные части программы описаны в литературе).
Создайте программу – музыкальный синтезатор. На 7–15 клавиш
клавиатуры устанавливается обработчик сообщений, который выдает
звук из системного динамика компьютера разной длины и частоты в
зависимости от нажатой клавиши (задача средней сложности – различные части программы описаны в литературе).
Создайте программу, которая обрабатывает нажатие на кнопки
«Свернуть окно», «Развернуть окно» по-другому. На кнопку «Свернуть окно» программа разворачивает окно, на кнопку «Развернуть
окно» программа сворачивает окно (задача повышенной сложности –
различные части программы опубликованы в Интернете).
3.16. Методика проведения лабораторной работы № 2
Лабораторная работа № 2 проводится на трёх занятиях. Перед её
началом студенты должны ознакомиться с текстом лабораторной работы
(пп. 3.1–3.12).
Занятие № 1 (2 аудиторных часа). Изучение теоретического материала по пп. 3.1–3.12. Подготовка ответов на контрольные вопросы. Выполнение заданий №№ 1–3, 5.
Занятие № 2 (2 аудиторных часа). Выполнение заданий № 4 и 6.
Подготовка отчета по п 3.13.
Занятие № 3 (2 аудиторных часа). Выполнение задания № 7 и подготовка отчета по п. 3.13. Защита отчета по лабораторной работе.
Повышение рейтинговой оценки за лабораторную работу возможно
при выполнении одного из задания п. 3.15 во внеаудиторное время.
47
4.
ЛАБОРАТОРНАЯ РАБОТА № 3.
ДИАЛОГОВЫЕ ОКНА
Время выполнения лабораторной работы – 6 часов.
Цель работы – изучить принципы создания диалоговых окон в программе.
Оборудование и программное обеспечение, необходимое для выполнения лабораторной работы: компьютер с процессором не хуже Pentium II с тактовой частотой 266 МГц, оперативной памятью не менее 64
Мб, с операционной системой Microsoft Windows 98/NT/2000/XP, средой
программирования Microsoft Visual C++ 6.0.
4.1. Ресурсы
Практически любая программа под Windows имеет меню, диалоговые окна, пиктограммы и панели инструментов. В Windows эти наиболее
распространенные типы объектов определены как ресурсы.
По сути, ресурс – это объект, определенный вне программы, но
используемый в ней.
Ресурсы создаются отдельно от программы и добавляются в ЕХЕфайл на этапе компиляции. Их описание содержится в так называемом
файле ресурсов, который имеет расширение RC. Обычно имя файла ресурсов совпадает с именем соответствующего ЕХЕ-файла. Например, если программа называется PROG.EXE, то файл ресурсов должен называться PROG.RC. Следует помнить, что с программой может быть связано несколько файлов ресурсов.
В зависимости от типа ресурса некоторые из них могут быть описаны с помощью текстовых определений. Например, стандартное меню является текстовым ресурсом. Текстовые ресурсы обычно полностью определяются в самом файле ресурсов. Другие, такие как пиктограммы, являются графическими ресурсами и создаются с помощью графических
редакторов. Однако они также должны быть указаны в RC-файле программы. Кроме того, можно автоматизировать построение файла ресурсов с помощью редактора ресурсов, который имеется в Microsoft Visual
C++. Вызов редактора ресурса производится командой «Add Resource…»
из меню «Project». В появившемся меню выбирают тип ресурса и нажимают кнопку «New».
4.2. Виды диалоговых окон
Обычно Windows-программы взаимодействуют с пользователем при
помощи диалоговых окон. Диалоговое окно может просто содержать какое-то сообщение и кнопку OK, а может представлять сложную форму с
полями и элементами управления. Диалоговое окно – это настоящее ок48
но. Оно имеет размеры, его можно перемещать, сворачивать, закрывать.
Оно принимает и посылает сообщения и даже отрабатывает сообщения о
перерисовке изображения в клиентской области.
Существует два типа диалоговых окон. Базовый класс CDialog поддерживает как модальные, так и немодальные диалоговые окна. Пока
открыто модальное диалоговое окно (Modal Dialog) (например, Open File
– открытие файла), пользователь не может работать ни с какой другой
частью программы. Если же открыто немодальное диалоговое окно
(Modeless Dialog), работать с другим окном программы можно. Например, диалоговое окно «Найти и заменить» Microsoft Word'а совершенно
не мешает редактировать текст. Выбор типа окна зависит от характера
приложения.
Диалоговое окно отличается от обычного окна Windows тем, что почти всегда связано с каким-нибудь ресурсом Windows, идентифицирующим элементы и определяющим структуру окна. Поскольку диалоговое
окно можно редактировать в редакторе диалоговых окон, то они создаются быстро и наглядно.
Диалоговое окно содержит набор элементов управления (Controls).
Windows управляет этими элементами, используя специальную логику
группирования и обхода. На элементы управления ссылаются либо по
указателю CWnd, так как элементы сами являются окнами, либо по идентификаторам определенных #define. Реагируя на действия пользователя,
элементы передают сообщения родительскому диалоговому окну.
MFC и ClassWizard, тесно взаимодействуя, облегчают программирование диалоговых окон. ClassWizard генерирует класс, производный от
CDialog, затем соотносит переменные и сообщения с элементами окна.
4.3. Создание диалогового окна в редакторе ресурсов
После добавления ресурса диалоговое окно появляется следующее
окно (рис. 4).
Рис. 4. Заготовка для диалогового окна
Добавление элементов на диалоговое окно происходит аналогично с
рисованием в векторном редакторе (выбираются соответствующие элементы на панели инструментов «Controls» и рисуются на форме). С по49
мощью контекстно-зависимого меню, вызываемого правой кнопкой мыши, можно установить параметры элемента управления (пункт «Properties», рис. 5).
Рис. 5. Контекстно-зависимое меню и окно параметров
После того, как окно создано, нужно зарегистрировать его класс и
связать с приложением. Для этого используется MFC ClassWizard (меню
«View», команда «ClassWizard»). В нем указывается название класса окна, базовый класс, определяются переменные-члены класса и вставляются обработчики сообщений.
Задание № 1. С помощью панели «Controls» (рис. 6) создайте
диалоговое окно, изображенное на рис. 6.
Рис. 6. Панель инструментов «Controls» и диалоговое окно
50
4.4. Модальные диалоговые окна
Общая схема создания и вызова модального окна такова:
1) с помощью редактора диалоговых окон создается окно;
2) менеджером классов (ClassWizard) определяется новый, производный от CDialog, класс для созданного окна;
3) определяется событие, после которого окно должно появиться на
экране (выбор пункта меню, нажатие кнопкой мыши);
4) с помощью MFC создается обработчик этого события;
5) в обработчике создается экземпляр класса окна, вызывается базовая
функция CDialog::DoModal();
6) если необходимо, обрабатываются возвращенные данные.
Рассмотрим это на конкретном примере.
Выбираем из меню «File» команду «New». На вкладке «Projects» выбираем из списка пункт «MFC AppWizard». В поле «Project name» вводим
название проекта, например «DWindow».
На первом шаге мастера создания приложения выбираем Single Document. Второй, третий и четвертый проходим без изменений. На пятом
шаге мастера определяются базовые классы. Необходимо, чтобы у класса
CDWindowView базовым классом был CView. Завершаем работу мастера
нажатием на кнопку «Finish».
В проекте переходим на вкладку «Resources», правой кнопкой щелкаем по пункту «Dialog» и выбираем «Insert Dialog». Будет создано диалоговое окно с идентификатором IDD_DIALOG1.
Открываем окно менеджера классов (ClassWizard) с помощью меню
«View» командой «ClassWizard». Менеджер классов определит, что диалоговое окно пока не связано ни с каким классом и предложит две альтернативы: создать новый класс («Create A New Class») или выбрать существующий класс («Select an existing class»). Выбираем создание нового
класса и вводим имя класса, например CDialogBox, базовым классом указываем CDialog. Нажимаем кнопку «ОК».
Пусть вызов диалогового окна будет осуществляться из пункта меню. Для этого переходим в окне ресурсов на пункт «Menu» и выбираем
«IDR_MAINFRAME», где вставляем пункт «Dialog Box» и присваиваем
ему идентификатор IDM_DIALOG.
Затем добавляем обработчик пункта меню. Для этого открываем
файл DWindowView.cpp и выбираем команду «ClassWizard» из меню
«View». Выбираем в списке «Object ID» пункт IDM_DIALOG, а в списке
«Messages» – пункт COMMAND и нажимаем кнопку «Add Function». Имя
обработчика оставляем без изменений. Нажимаем кнопку «OK».
После переходим к редактированию, где вместо кода
// TODO: Add your command handler code here
необходимо ввести следующий код:
51
// Создание экземпляра класса CDialogBox с именем dlg
CDialogBox dlg;
// Вызов окна
// процесс ждет возврата из окна
dlg.DoModal();
Также необходимо вставить в заголовок файла DWindowView.cpp
после строк
#include "D_BoxDoc.h"
#include "D_BoxView.h"
строку
#include "DialogBox.h"
Возвращаемые данные пока не обрабатываем. Если нужно будет вызвать диалоговое окно щелчком мыши, в окне «ClassWizard» в списке
«Object ID» указываем «CDWindowView» (т. е. само окно), в списке «Messages» указываем WM_LBUTTONDOWN и аналогично вносим изменения в программный код.
Задание № 2. Сделайте необходимые изменения в проекте, который был создан в задании № 1, чтобы вызывалось диалоговое окно
(любым способом) по методике, приведенной выше.
4.5. Обмен данными с диалоговым окном
Как Вы могли заметить, элементы управления, располагающиеся на
диалоговом окне, никак не реагируют на действия (кроме полей ввода и
флажков). Поэтому необходимо рассмотреть общие принципы передачи
данных между диалоговым окном и остальной частью программы.
Менеджер классов ClassWizard создает две стандартные функции
DoDataExchange (DDX) и DoDataVerify (DDV). Первая передает данные
диалоговому окну, а вторая проверяет их перед возвратом родительскому
окну. Нужно сразу заметить, что DDX и DDV хорошо справляются со
стандартными типами данных (int, double, char, CString и т. д.), но если
использовать собственный тип данных, то придется писать собственную
реализацию функций DDV и DDX.
Чтобы DDX и DDV начали передавать данные, надо связать с каждым элементом управления (есть исключения) свою переменную, определить для них тип данных и присвоить им значения до вызова функции
DoModal. Например, с текстовым полем, статическим полем связывается
CStirng, с флажками и переключателями – int или BOOL.
Функции обработки DDX/DDV имеют общую структуру:
DDX(DDV)_элемент_управления
(указатель
на
объект
CDataExchange, куда/откуда передавать, переменная для передачи/записи);
Например,
DDX_Scroll(pDx, IDC_SCROLL1, m_Scroll);
52
передаст данные из переменной-члена m_Scroll в элемент управления панель прокрутки (Scroll) с идентификатором IDC_SCROLL1,
DDV_CbString(pDx, IDC_COMBO1, m_ComboStr);
проверит и передаст выбранную строку из комбинированного списка с
идентификатором IDC_COMBO1 в переменную m_ComboStr.
Напрямую функции DDV и DDX используются редко, обо всем заботится каркас приложения при вызове и возврате из функции DoModal.
Взаимодействие с окном для обмена данными строится также на основе
механизма обработки сообщений, изученных в лабораторной работе № 2.
Так, например, сообщения BN_CLICKED, BN_DBLCLICK посылаются при нажатии на кнопки в окне, EM_UPDATE, EM_SETTEXT – обрабатываются полями ввода, LB_SELCAHNGE – при смене выбора в
списке и т. д.
Действия, которые выполняет программист для создания обработчика событий, состоят в следующем:
 открывается окно «ClassWizard» и выбирается класс диалогового окна (рис. 7);
 выбирается нужный элемент управления по его идентификатору;
 находится и выбирается нужное сообщение;
 нажимается кнопка «Add Function» (если эта кнопка недоступна, то
это значит, что обработчик уже создан, и тогда он просто редактируется) и затем кнопка «Edit Code»;
 создается код обработчика.
Имя класса
Элементы
Сообщения
Рис. 7. Менеджер классов – окно «MFC ClassWizard»
Существуют особенности обработки нажатий клавиш [Enter] и [Escape]. Обе клавиши закрывают окно, но делают это по-разному.
53
При нажатии клавиши [Enter] Windows ищет кнопку, на которой
установлен фокус ввода (кнопка с пунктирной рамкой, рис. 8б). Если такой кнопки нет, то ищется кнопка по умолчанию (у такой кнопки более
толстые границы, рис. 8в). Если и такой кнопки нет, то остается последний вариант – вызывается базовая функция CDialog::OnOK, которая вызывает функцию DDV и закрывает окно.
Обычная кнопка
а)
Кнопка с фокусом
ввода
б)
Кнопка по
умолчанию
в)
Рис. 8. Виды кнопок
Для управления процессом установки фокуса выполняются следующие действия:
1) с помощью менеджера классов создается обработчик BN_CLICKED
для кнопки IDOK;
2) удаляется код CDialog::OnOK();
3) снимается с кнопки «OK» стиль «Default Button» и изменяется ее
идентификатор на ID_OK;
4) изменяется порядок обхода кнопок в окне, то есть указывается, в каком порядке кнопки будут становиться активными при нажатии клавиши [Tab]. Изменение делается через меню «Layout» командой «Tab
Order», при выборе команды на экране появятся числа – это и есть
порядок обхода (рис. 9). Нажав на объекты в правильном порядке,
можно задать необходимый порядок обхода.
Рис. 9. Порядок обхода элементов управления
Конечно, для изменения порядка обхода в окне должно быть не менее трех элементов. Аналогично можно обработать и клавишу [Escape].
Задание № 3. Сделайте необходимые изменения в проекте, который был создан в задании № 1, чтобы обход элементов осуществлялся слева направо и сверху вниз.
4.6. Элементы диалогового окна и их свойства
Находясь в редакторе диалоговых окон, щелкните правой кнопкой по
диалоговому окну в свободном от элементов управления месте и выберите
54
команду «Properties». Откроется окно «Dialog Properties» (рис. 10). В этом
окне можно установить стили диалогового окна и его свойства.
Рис. 10. Окно «Dialog Properties»



















Рассмотрим основные элементы, меняющие свойства окна:
Font – выбор размера и гарнитуры шрифта диалогового окна;
XPos, YPos – начальное положение диалогового окна при старте
приложения относительно верхнего левого угла экрана (смотри стиль
Absolute Align), но если установлен пункт Center, то поля игнорируются;
Caption – заголовок окна;
Menu – подключаемое меню;
Border – вид рамки окна;
Title Bar – отображается или нет строка заголовка;
System Menu, Minimize / Maximize Box, Context Help – кнопки системного меню;
Horizontal / Vertical Scroll – полосы прокрутки;
System Modal – системное модальное окно;
Absolute Align – указывает, относительно чего задаются XPos / YPos:
всего экрана или родительского окна;
Disabled – создается окно, которое по умолчанию недоступно;
Center – центрирует окно на экране;
Center Mouse – центрирует окно по положению мыши;
Tool Window – создает окно с более узкой строкой заголовка (как на
панели инструментов);
Client Edge – окно с «утопленной» рабочей областью;
Transparent – прозрачное окно (действует только в Windows
2000/XP);
Accept Files – позволяет окошку реагировать на перетаскивание в него файлов;
Control Parent – позволяет переключаться между диалоговым и родительским окном с помощью клавиши [Tab];
Right-to-left reading order – центровка текста.
55
С помощью панели инстурментов «Controls» (см. также рис. 6 на
стр. 50) можно добавить на диалоговом окне:
 рисунок
(Picture);
 статический текст
(Static Text);
 поле ввода
(Edit Box);
 рамку
(Group Box);
 кнопку
(Button);
 флажок
(Checkbox);
 переключатель
(Radio Button);
 поле со списком
(Combobox);
 список
(Listbox);
 горизонтальную полосу прокрутки
(Horizontal Scrollbar);
 вертикальную полосу прокрутки
(Vertical Scrollbar);
 счетчик
(Spin);
 индикатор продвижения
(Progress);
 ползунок
(Slider);
 горячую клавишу
(Hot Key);
 графический список
(List Control);
 древовидный список
(Tree Control);
 вкладки
(Tab Control);
 анимацию
(Animate);
 поле ввода с форматированием
(Rich Edit);
 выбор даты времени
(Date Time Picker);
 календарь
(Month Calendar);
 IP адрес
(IP Adress);
 пользовательский элемент
(Custom Control);
 расширенный комбинированный список
(Extended Combobox).
Некоторые из этих элементов являются статическими. Они несут
лишь декоративный или вспомогательный смысл. Если эти элементы не
изменяются во время выполнения программы, то им можно оставить
идентификатор по умолчанию, если же необходимо их менять или управлять ими, то можно сменить идентификаторы.
Рамка. Ограничивает общие по смыслу элементы. Рассмотрим основные свойства рамки:
 Horizontal Alignment – указывает расположение заголовка рамки;
 Icon / Bitmap – устанавливает вместо текстового заголовка пиктограмму или рисунок в формате BMP;
 Flat, Client Edge, Static Edge, Modal Frame – указывает глубину / высоту положения рамки;
56

Accept Files – позволяет окну реагировать на перетаскивание в него
файлов;
 Right-to-left reading order – центровка текста вправо.
Разберем на примере, как вставить вместо заголовка рисунок или
пиктограмму:
1) в редакторе диалоговых окон добавляется рамка и назначается ей
идентификатор IDC_GROUP;
2) создается обработчик функции WM_INITDIALOG для диалогого окна;
3) в менеджере классов выбираются класс CDialogBox и сообщение
WM_INITDIALOG, нажимается кнопка «Add Fnction», а затем «Edit
Code»;
4) пишется следущий код:
// Получаем указатель на элемент управления
// по идентификатору
CButton* GroupBox=(CButton*)GetDlgItem(IDC_GROUP);
// Назначаем стандартную иконку вопросительного
// знака (IDI_QUESTION)
GroupBox->SetIcon(::LoadIcon(NULL, IDI_QUESTION));
Аналогично можно вставлять и рисунки через SetBitmap(HBITMAP)
и ::LoadBitmap(HINSTANCE, int).
Статический текст. Используется в качестве подписи к полям.
Обычно текст сразу вписывается, но можно задать текст и во время исполнения программы. Для этого выполняются следующие действия:
1) открывается редактор диалоговых окон, и вставляется в него текст с
назначением идентификатора IDC_STATICTEXT;
2) добавляется обработчик для нового элемента;
3) в менеджере классов на вкладке «Member Variables» в списке «Control ID’s» выбирается IDC_STATICTEXT и нажимается кнопка «Add
Variable». Вводится имя переменной m_Text и выбирается для нее
класс CString.
4) в обработчике OnDialog (в менеджере классов выбирается класс
CView, идентификатор IDM_DIALOG, сообщение COMMAND и
нажимается кнопка «Edit Code») добавляется следущий код (до
функции DoModal):
dlg.m_Text="Новый текст";
Данный прием «переменая – инициализация» является основой теории модальных окон.
Основные свойства статического текста:
 Align Text – форматирование текста (справа / слева);
 Center Vertically – центровка по вертикали;
 No prefix – запрещает использование символа «&» как знака для горячей клавиши и просто отображает его в тексте;
 No wrap, simple – сжатый текст без разрывов;
57

Sinken, Border, Client Edge, Static Edge, Modal Frame – указывает глубину / высоту положения рамки;
 Transperent – прозрачное поле.
Другие основные свойства данного элемента совпадают со свойствами элемента «Рамка».
Рисунок. Отображает в себе рисунок, пиктограмму или пустую рамку в зависимости от свойств. Для инициализации элемента необходимо
задать один из типов (Type):
 Frame (рамка);
 Rectangle (закрашенная рамка);
 Icon (пиктограмма);
 Bitmap или Enhanced Metafile (*.emf).
Затем для Frame и Rectangle задают цвет заливки / закраски, а для
Bitmap / Icon – ресурс с пиктограммой или рисунком (в поле Image). Enhanced Metafile загружается программно.
Свойства элемента «Рисунок»: Sinken, Border, Client Edge, Static
Edge, Modal Frame – указывают глубину / высоту положения рамки.
Списки и поля ввода. Эти элементы в основном используются для
заполнения форм. Они предоставляют пользователю возможность ввода
текста и выбора предопределенных значений.
Элементы «Поле ввода» и «Поле ввода с форматированием» используются для ввода текста. Эти два поля внешне одинаковы, но обычное
поле ввода имеет ограничение на длину текста в 64 Кбайт и поддерживает лишь один тип шрифта, а поле с форматированием поддерживает
больший объем текста и позволяет использовать шрифтовые выделения,
но использование полей с форматированием в данной лабораторной работе не рассматривается.
Все свойства полей определяются следующими стилями:
 Align Text – форматирование текста (справа / слева);
 Multiline – многострочное поле;
 Number – вводятся только числа;
 Auto HScroll / Auto VScroll – текст автоматически прокручивается
вправо / вверх, когда курсор достигает края поля ввода;
 Horizontal / Vertical Scroll – линейки прокрутки для поля;
 Password – вводимые символы заменяются на знаки (применяется для
ввода пароля);
 Uppercase – автоматический перевод вводимых символов в верхний /
нижний регистр;
 No Hide Selection – выделенный текст остается выделенным, когда
поле теряет фокус ввода;
 Want return – для многострочного поля нажатие Enter приводит к новой строке, а не к нажатию кнопки по умолчанию;
58


Border – рамка вокруг поля;
Sinken, Border, Client Edge, Static Edge, Modal Frame – указывает глубину / высоту положения рамки;
 Read Only – текст в поле можно только читать, а редактировать его
нельзя;
 Left Scroll bar – отображает линейку прокрутки у левого края поля
ввода.
Также имеются и другие свойства у элементов «Поле ввода» и «Поле
ввода с форматированием», которые аналогичны свойствам других элементов.
Комбинированный список. Комбинированные списки предлагают
пользователю сделать выбор из ограниченного числа вариантов или добавить новый вариант. Комбинированные списки делятся на три типа:
1) simple (простой) – этот список не содержит кнопки открытия выпадающего списка, он просто расположен ниже поля ввода; в него
можно ввести любые данные или выбрать имеющиеся;
2) dropdown (выпадающий) – можно выбирать существующие элементы
и вводить свои;
3) drop list (выпадающий список) – можно выбирать только существующие элементы.
Заполнить элементы списка можно в редакторе окон и в самой программе. Для первого варианта необходимо на вкладке «Data» в меню
«Properties» заполнить список, причем ввод элемента списка следует завершать клавишами [Ctrl+Enter].
Размер списка в раскрытом состоянии можно изменить, нажав на
кнопку раскрытия списка и отрегулировав нижнюю границу списка.
Основые свойства данного элемента:
 Type – тип списка;
 Sort – сортировка элементов списка по алфавиту;
 Vertical Scroll – вертикальная линейка прокрутки;
 Disable no scroll – сохраняет полосу прокрутки, даже если в списке
мало элементов;
 Auto HScroll – автоматический сдвиг вводимого текста влево (если не
хватает места по ширине списка);
 Uppercase – автоматический перевод вводимых символов в верхний /
нижний регистр;
 Sinken, Border, Client Edge, Static Edge, Modal Frame – указывает глубину / высоту положения рамки.
Другие свойства аналогичны таким же свойствам у других элементов.
Список. Обычный список – это упрощенный вариант комбинированного списка без выпадающей части. Он не поддерживает начальное
59
заполнение и заполняется в коде программы. Общий список обладает рядом приемуществ. Например, всегда видно его содержание и он поддерживает много колонок.
Свойства списка следующие:
 Type – тип списка;
 Border – рамка вокруг списка;
 Sinken, Border, Client Edge, Static Edge, Modal Frame – указывает глубину / высоту положения рамки;
 Sort – сортировка элементов списка по алфавиту;
 Horizontal / Vertical Scroll – полосы прокрутки для списка;
 Selection – сколько пользователю можно выбирать элементов списка:
только один (Single), много (Multiple или Extended), ни одного
(None);
 Use Tabstops – разрешает использовать при вводе клавишу [Tab];
 Multi-column – список с несколькими колонками;
 Disable no scroll – сохраняет линейку прокрутки, даже если в списке
мало элементов.
Флажки и переключатели. Флажки и переключатели используются, когда пользователь должен выбрать из небольшого строго ограниченного числа элементов. Причем флажки используются, когда можно выбирать больше одного элемента, а переключатели используются для выбора одного элемента из нескольких.
Создание флажков простое – расположили их и подписали. А переключатели необходимо сгруппировать. Допустим, нужно создать две
группы переключателей. Например, разрешение экрана и количество
цветов. Для этого выполняют следующие действия:
1) создают все элементы управления строго по порядку, например,
640x480, 800x600, 1024x768, 8 bit , 16 bit, 24 bit (их можно сгруппировать и даже обвести рамочкой, но это никак не повлияет на их работу);
2) каждой первой кнопке в каждой группе устанавливают стиль
«Group» (рис. 11).
Рис. 11. Задание группы элементов
60
Большинство свойств у элементов общие:
Left Text – подпись слева относительно элемента;
Tri-state – элемент имеет три сосояния: выбран, невыбран и неактивен;
Push-like – вместо галочек и точек будет кнопка, которая может быть
утопленной и отжатой;
 Multiline – многострочные подписи;
 Icon / Bitmap – вместо текста подписи будет отображаться рисунок
или пиктограмма;
 Vertical / Horizontal alignment – направление вертикальной / горизонтальной привязки.
Остальные свойства аналогичны таким же свойствам у других элементов.
Теперь рассмотрим создание простого приложения, содержащего
перечисленные выше элементы интерфейса. Для создания приложения
необходимо выполнить следующие действия:
1. В редакторе ресурсов создаем окно (рис. 12) и присваиваем ему
идентификатор IDD_FRAMEDIALOG.
2. Переключатели должны создаваться в последовательном порядке
(«Желтый» – «Красный» – «Синий», «Дерево» – «Металл» – «Пластик»), и для «Желтый» и «Дерево» должен быть указан стиль
«Group».
3. У поля «Пароль» свойство «Password» должно быть установлено, а
у поля «Серийный номер» должно быть установлено поле «Number».
4. Кнопки «OK» и «Cancel» должны иметь идентификаторы IDOK и
IDCANCEL.
5. Остальные идентификаторы необходимо расставить так, как указано
в табл. 12. Все идентификаторы строго произвольны и не несут никакой смысловой нагрузки.
Таблица 12
Названия идентификаторов



Название элемента
интерфейса
Поле ввода «Имя»
Поле ввода «Серийный номер»
Поле ввода «Пароль»
Флажок «Номер 1»
Флажок «Номер 2»
Флажок «Номер 3»
Первый Combobox
Второй Combobox
Третий Combobox
ListBox
Название элемента
интерфейса
Переключатель «Желтый»
Переключатель «Красный»
Переключатель «Синий»
Переключатель «Дерево»
Переключатель «Металл»
Переключатель «Пластик»
Кнопка «Установить»
Идентификатор
IDE_NAME
IDE_SERIAL
IDE_NAME
IDC_CHECK1
IDC_CHECK2
IDC_CHECK3
IDC_COMBO1
IDC_COMBO2
IDC_COMBO3
IDC_LIST
61
Идентификатор
IDR11
IDR12
IDR13
IDR21
IDR22
IDR23
IDB_SET
6.
7.
8.
9.
У первого комбинированного списка необходимо установить тип
«Simple» и ввести данные: «тип А», «тип Б».
У второго комбинированного списка нужно установить тип
«Dropdown» и ввести данные: «Покраска», «Ткань», «Пластик».
Для третьего комбинированного списка нужно установить тип
«Drop List» и ввести данные: «Нет», «Машиной», «Ж/Д».
Затем в менеджере классов выбирают пункт «Create New Class» с
именем CFormDialog, переходят на вкладку «Member Variables» и
добавляют переменные для полей ввода: «Имя» – m_name, «Серийный номер» – m_serial, «Пароль» – m_pword.
Рис. 12. Пример диалогового окна
10. Для IDC_CHECK1, IDC_CHECK2 и IDC_CHECK3 – переменные
m_check1, m_check2 и m_check3 типа BOOL.
11. Для IDC_COMBO1, IDC_COMBO2 и IDC_COMBO3 – переменные
m_frm, m_otd и m_transport типа Cstring.
12. Для поля IDE_NAME – m_name типа CString.
13. Для поля IDE_PASSWORD – m_pword типа Cstring с указанием в
поле «Maximum Characters» значения 16 – это будет максимальная
длина пароля.
14. Для поля IDE_SERIAL – m_serial типа int с указанием минимального
значения – 0, максимального – 9999.
15. Для IDR11 и IDR21 m_color и m_material, соответсвенно, оба типа int.
16. В редакторе меню вставляют пункт меню с названием Form и идентификатором IDM_FORM.
17. С помощью менеджера классов добавляют обработчик нового пункта меню (на самой первой вкладке, в классе CWindowView, выбирают из списка пункт IDM_FORM и сообщение COMMAND, после чего нажимают кнопку «Add Function» и затем «Edit Code»).
18. Вводят следующий код:
62
CFormDialog fDlg; //Создание нового объекта класса
// Начинаем заполнять начальные значения полей,
// положение флажков и других элементов
fDlg.m_check2=TRUE; // Помечаем второй флажок
// Переключатель Цвет ставим в положение Желтый
fDlg.m_color=0;
// Переключатель Материал – в положение Пластик
fDlg.m_material=2;
fDlg.m_name="Иван"; // Заполняем поле имени
fDlg.m_serial=1117; // Заполняем поле серийного номера
// Вводим начальные положения комбинированных списков
fDlg.m_transport=0;
fDlg.m_frm="Тип А";
fDlg.m_otd="Ткань";
//Запускаем окно
fDlg.DoModal();
Пояснения. В коде присваиваются переменным их начальные значения. Особенности присвоения: флажки, обладая возможностью многозначного выбора, инициализируются отдельно переменой типа BOOL, у которой есть 2 значения
TRUE, FALSE (значение по умолчанию FALSE), а вот переключатели имеют отдельную переменную для всей группы (и она уже типа int) и нумерут все значения: 0 соответствует первому значению, 1 – следущему и т. д. Комбинированнные списки, которые допускают ввод произвольного текста, инициализируются
строкой, а те, у которых можно выбирать лишь предопределенные значения, –
целым числом, аналогично и для простого списка.
19. Вставляют в файл DWindowView.cpp строчку
#include "FormDialog.h"
20. В менеджере классов создают обработчик сообщения WM_INITDIALOG
функции класса CFormDialog.
21. Добавляют следующий код:
//Получаем указатель на список
CListBox* pLB = (CListBox*) GetDlgItem(IDC_LIST);
//Вставляем элемент в конец списка
pLB->InsertString(-1, "Шкаф");
pLB->InsertString(-1, "Полка");
pLB->InsertString(-1, "Стол");
Комментарий. Функция GetDlgItem() возвращает указатель на объект по его
идентификатору. А функция InsertString добавляет строку в список (число -1 указывает, что надо вставлять в конец списка, в общем случае это номер элемента,
после которого надо вставить новый элемент). Аналогично добавляются элементы и к комбинированному списку. Удалять строки можно функцией
DeleteString(), а полностью очистить – ResetContent().
22. Добавляют код, чтобы сменить стандартные символы для сокрытия
пароля:
CEdit* pEB = (CEdit*) GetDlgItem(IDE_PASSWORD);
pEB->SetPasswordChar(44);
63
23. Добавляют обработчик нажатия кнопки «Установить». В менеджере
классов добавляют функцию для класса CFormDialog, идентификатора IDB_SET и сообщения COMMAND.
MessageBox("Установлено");
CString outStr, Scolor, Smaterial, Snumber;
if(fDlg.m_check1)Snumber+="1 ";
if(fDlg.m_check2)Snumber+="2 ";
if(fDlg.m_check3)Snumber+="3 ";
switch(fDlg.m_color)
{
case 0:
Scolor="желтый";
break;
case 1:
Scolor="красный";
break;
case 2:
Scolor="синий";
break;
}
switch(fDlg.m_taste)
{
case 0:
Smaterial="дерево";
break;
case 1:
Smaterial="металл";
break;
case 2:
Smaterial="пластик";
break;
}
outStr.Format("Имя - %s\nСерийный номер %d\nПароль
%s\nМатериал %s\nЦвет %s\nДоставка %d\nКаркас %s\n
Оценка %s\nВыбор %s", fDlg.m_name, fDlg.m_serial,
fDlg.m_pword, Smaterial, Scolor, fDlg.m_transport,
fDlg.m_frm, fDlg.m_otd, Snumber);
MessageBox(outStr);
Горизонтальные и вертикальные полосы прокурутки. Это обычные линейки прокрутки окна. Но их также можно использовать внутри
самого окна. При своем движении или при нажатии кнопок со стрелками
посылаются сообщения WM_HSCROLL или WM_VSCROLL, где параметром передается идентификатор элемента, который вызывает сообщение. До начала использования линейки прокрутки необходимо связать с
ней целочисленную переменную и указать пределы ее изменения. Если
пределы будут малы, то бегунок на линейке будет двигаться дискретно
(шагами), а если пределы велики, то движение будет плавным.
64
Стилей у линеек прокрутки нет.
Ползунок. Аналогичен полосам прокрутки, но имеет шкалу и у него
нет кнопок по краям (рис. 13). Способ инициализации аналогичен линейкам прокрутки.
Свойства элемента:
 Orientation – горизонтальное или вертикальное расположение;
 Point – указывает на положение шкалы и заостренного конца;
 Tick Marks, Auto Ticks – определяют внешний вид шкалы ползунка;
 Border, Client Edge, Static Edge, Modal Frame – указывают глубину и
высоту положения рамки.
Рис. 13. Виды ползунков
Индикатор продвижения. Аналогичен линейкам прокрутки и ползунку, у него такие же способы инициализации, но его невозможно двигать вручную.
Свойства элемента:
 Smooth – целое или дискретное поле продвижения;
 Vertical – ориентация;
 Border, Client Edge, Static Edge, Modal Frame – указывают глубину и
высоту положения рамки.
Счетчик. Обычно связан с полем ввода (которое называется buddy –
«приятель») и позволяет увеличивать или уменьшать значения в этом поле,
нажимая на кнопки счетчика. Скорость изменения значения зависит от
времени нажания на кнопку (чем дольше кнопка удерживается, тем быстрее изменяются значения). Со счетчиком необходимо связать переменную,
установить диапазон значений и, если необходимо, задать усокрение набора. Чтобы значения отображались в соответствующем поле ввода, надо обрабатывать сообщение WM_VSCROLL или WM_HSCROLL.
Свойства элемента:
 Orientation – горизонтальный или вертикальный счетчик (соответственно посылает сообщения WM_HSCROLL или WM_VSCROLL);
 Alignment – положение счетчика относительно его поля ввода;
 Auto Buddy – автоматически назначает в «приятели» предшествующее ему поле ввода;
 No thousands – не разделяет тысячи и миллионы запятыми;
 Arrow Keys – позволяет использовать кнопки со стрелками для увеличения значений;
 Client Edge, Static Edge, Modal Frame – указывают глубину и высоту
положения рамки.
IP адрес. Поле для ввода IP адреса автоматически отделяет байты
точками. С ним связывается четырехбайтовая переменная типа
65
CIPAdressCtrl (практически DWORD), а потом из нее вручную извлекаются все байты. Свойств у элемента практически нет.
Элементы, допускающие графические изображения. Следущие
элементы управления позволяют вставлять в себя пиктограммы 16×16.
Для этого сначала нужно подготовить массив этих пиктограммок в специальной переменной класса CImageList. Примерно так:
CImageList myList;
HICON hIcon;
myList.Create(16, 16, ILC_COLOR, 8, 8);
// 16x16 - размер иконок, ILC_COLORn - n-битный цвет,
// если не указано, то битность по умолчанию.
// Первая 8 - начальный размер списка. Вторая 8 –
// на сколько увеличить список, если он переполнится
// Загружаем пиктограмму из ресурса
hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);
myList.Add(hIcon); // Добавляем иконку
Графический список. Этот элемент является продолжением элемента список. Для добавления к нему списка изображений создается и
заполняется список с необходимым количеством изображений, а в функции OnInitDialog вставляется следущий код:
(CListCtrl*) pList=(CListCtrl*)GetDlgItem(IDC_LIST);
pList->SetImageList(&myList, LVSIL_SMALL);
где IDC_LIST – идентификатор элемента управления, myList – список
изображений. LVSIL_NORMAL, LVSIL_SMALL, LVSIL_STATE – размер пиктограмм.
Затем вызывается функция InsertItem (номер вставляемого, текст,
номер пиктограммы из списка). Например, так:
pGL->InsertItem(0,"One",0);









Свойства элемента:
View – режимы отображения: маленькие значки, большие значки,
список, подробно;
Sort – сортировка списка;
Single selection – можно выбирать лишь один элемент из списка;
Edit Labels – позволяет менять подписи к пиктограммам;
No Scroll – убирает Scroll Bar;
No column header – отключает подписи к колонкам;
No sort header – отключает возможность сортировки при нажатии на
заголовок колонки;
Show selection always – показывает выделенный элемент при потере
фокуса ввода;
Transperent – прозрачное поле.
66
Расширенный комбинированный список. Этот элемент не просто
комбинированный список с картинками, он намного сложнее и интереснее, особено при инициализации вручную. Сначала необходимо собрать
список изображений и присоединить его к комбинированному списку:
CComboBoxEx*) pCBox = (CComboBoxEx*) GetDlgItem(IDC_COMBOEX);
pCBox->SetImageList(&myList);
Затем, заполняя поля структуры COMBOBOXITEM, вставляются
элементы и назначаются им рисунки. Стоить заметить, что с каждым
элементом могут сопоставляться три рисунка – в выбранном, невыбранном и перекрытом состоянии. Структура COMBOBOXITEM содержит:
 mask – набор флагов для отображения элементов;
 iItem – номер вставляемого элемента;
 pszText – указатель на вставляемый текст;
 cchTextMax – максимальный размер текста в списке;
 iImage – номер рисунка в списке изображений, используемый для невыбранного состояния;
 iSelectedImage – номер рисунка в списке изображений, используемый
для выбранного состояния;
 iOverlay – номер рисунка в списке изображений, используемый для
перекрытого состояния;
 iIndent – отступ от левого края списка.
Если нужно изменить стили уже вставленных элементов, то заполняется структура, а затем выбирается элемент с помощью функции GetItem
и выполняется для него функция SetItem.
Древовидный список. Все видели древовидный список (например,
на панели Windows Explorer – Проводнике Windows). Его создание немного сложнее, чем создание графического списка. Сначала необходимо
подготовить список изображений, но он не обязательно должен быть
размером с сам древовидный список. Затем необходимо добавлять узлы
(node) и листья (leaf) в список. Пока к элементу не добавлена ветка, он
считается листом, в противном случае можно щелкнуть на крестике рядом с
ним или двойным щелчком на нем самом открыть его ветку. Для вставки
ветви необходимо заполнить структуру TV_INSERTSTRUCT со следущими
полями:
 hParent – указатель на узел, куда вставляем ветвь NULL для корня;
 hInsertAfter – куда вставляем:
1) TVI_FIRST – в начало ветви;
2) TVI_LAST – в конец ветви;
3) TVI_ROOT – в корень;
4) TVI_SORT – сортировка;
 item – структура, где описывается вставляемый элемент:
67
mask – набор флагов для отображения элемента;
hItem – куда ссылаемся;
state – состояние;
stateMask – маска состояния;
pszText – указатель на вставляемый текст;
cchTextMax – размер вставляемого текста;
iImage – номер рисунка в структуре CImageList;
cChildren – количество дочерних элементов (при инициализации указывайте 0, потом это поле можно считывать).
Не обязательно заполнять все поля каждый раз. Можно заполнить
основные поля единственный раз, а потом менять только вставляемый
текст и рисунок.
Свойства элемента:
 Has buttons – отображает кнопки «плюс» и «минус» слева от узла;
 Has lines – проводит пунктирные линии от узлов к листьям;
 Edit Labels – позволяет менять подписи к пиктограммам;
 Show selection always – показывает выделенный элемент при потере
фокуса ввода;
 Check boxes – добавляет флажки для выбора веток;
 Full row select – можно выделять целую ветку;
 Info tip – всплывающая подсказка при наведении мышки;
 Single expand – если открывается какая-либо ветвь, остальные открытые ветви закрываются.
Календарь. Элемент управления календарь позволяет выбирать дату. С ним связывается переменная типа CMonthCalCtrl, из которой можно
читать поля данных и узнавать выбранный день. Элемент посылает сообщение MCN_SELCHANGE при смене даты.
Свойства элемента:
 Day State – позволяет отображать некоторые даты жирным шрифтом;
 Milti select – разрешено выбирать несколько дат;
 No today circle – не обводит текущую дату кружком;
 No taday – включает / выключает отображение текущей даты по
умолчанию;
 Week numbers – также показывает и номера недель.
Выбор даты/времени. Этот элемент управления служит для выбора
текущей даты или текущего времени в зависимости от указанных стилей.
С ним сопоставляется переменная типа CDateTimeCtrl, которая обеспечивает чтение данных из элемента.
Свойства элемента:
 Format – указывает на назначение элемента: ввод даты или ввод времени;
1)
2)
3)
4)
5)
6)
7)
8)
68

Use spin control – способ выбора даты или наборным счетчиком, или
открытием окона с календарем;
 Show none – позволяет не выбирать никакую дату;
 Allow edit – ручное редактирование даты.
Задание № 4. Создайте программу для диалогового окна, показанного на рис. 12.
4.7. Немодальные диалоговые окна
Немодальным диалоговым окнам сопостовляется тот же класс
CDialog, но так как после создания они должны существовать отдельно
от родительского окна, то следить за окном должен программист и от него зависит, когда уничтожить объект. Модальное окно создается командой DoModal() и ждет его завершения. Немодальное окно создается
функцией Create(), которая получает ресурс окна, создает его и сразу же
передает управление в главное окно.
Скорее всего, уничтожить диалоговое окно необходимо будет нажатием кнопок в самом диалоговом окне, но этим процессом должно заниматься главное окно приложения. Но как родитель узнает о том, что происходит в его потомке? Можно, конечно, сделать классы дружественными и просто обращаться друг к другу напрямую, но это создает массу дополнительных проблем. Лучший способ – это послать сообщение из диалогового окна в главное. Но какое сообщение? Никакое из стандартных
сообщений не подходит, поэтому программист должен сам создать свое
сообщение и послать его.
Далее на примере создания простого диалогового окна с полем ввода
будет показана процедура создания немодального диалогового окна
(рис. 14).
Рис. 14. Немодальное диалоговое окно
1.
2.
3.
Создается новый MFC-проект с именем Modeless.
В редакторе создается простое окно, все идентификаторы оставляются без изменений. Обязательно устанавливается свойство «Visible».
В менеджере классов окна сопоставляется класс CModeless и добавляется к полю ввода переменная CString m_text.
69
4.
Чтобы была возможность воспользоваться окнами, надо создать в
каждом из них указатель на другое окно. В Modeless1.h в объявлении
класса вставляются следущие строки:
private:
CView *m_pView;
public:
CModeless(CView *pView);
BOOL Create();
5.
Так создается у диалогового окна указатель на родительское окно. Заодно здесь добавлены прототипы будущих функций для работы
с окном.
Теперь надо определить функции в файле Modeless1.cpp:
CModeless::CModeless(CView* pView)
{
m_pView=pView;
}
BOOL CModeless::Create()
{
return CDialog::Create(CModeless::IDD);
}
6.
Конструктор получит и запомнит указатель на родительское окно, а функция Create() вызывает функцию базового класса с указателем на окно (можно было и не определять эту функцию, но это приведет к проблеме при смене названия окна).
В объявление класса ModelessView в файле ModelessView.h добавляются переменные:
private:
CModeless* m_pDlg;
и перед объявлением класса:
class CModeless;
7.
Теперь в файле ModelessView.cpp заменяются конструктор и деструктор класса CModelessView:
CModelessView::CModelessView()
{
m_pDlg=new CModeless(this);
}
CModelessView::~CModelessView()
{
delete m_pDlg;
}
8.
После этого класс окна знает своего потомка.
Затем добавляется пункт в меню и в его обработчике записываются
следущие строки:
if(m_pDlg->GetSafeHwnd()==0)
m_pDlg->Create();
В этом коде проверяется, есть ли окно на экране. Если его нет, то
оно создается.
70
Если попытаться запустить приложение, то окно не должно открыться.
Причина в том, что при закрытии окна из стандартных функций OnOK
и OnCancel вызывается команда EndDialog, которая подходит лишь
для модальных окон, немодальные должны вызывать DestroyWindow.
10. Вставляем в файл Modeless1.h строку
9.
#define WM_GOODBYE WM_USER+5
Так определяется собственное сообщение. WM_USER – первое
возможное пользовательское сообщение. Некоторые из них использует библиотека MFC, поэтому пропускаются первые пять.
11. Через ClassWizard изменяются обработчики кнопок «OK» и «Cancel»
void CModeless::OnOK()
{
// TODO: Add extra validation here
if (m_pView!=NULL)
m_pView->PostMessage(WM_GOODBYE, IDOK);
else
CDialog::OnCancel();
}
void CModeless::OnCancel()
{
if(m_pView!=NULL)
m_pView->PostMessage(WM_GOODBYE,IDCANCEL);
else
CDialog::OnCancel();
}
Если окно немодальное, то вместо уничтожения посылается сообщение родительскому окну.
12. Теперь в родительском окне необходимо создать обработчик
WM_GOODBYE. С помощью менеджера классов он не создается, его
можно создать только вручную. В файл ModelessView.cpp между
строками AFX_MESSAGE_MAP вставляем
ON_MESSAGE(WM_GOODBYE, OnGoodbye)
Добавляем саму функцию OnGoodBye в конец того же файла:
LRESULT CModelessView::OnGoodbye(UINT wParam, LONG lParam)
{
m_pDlg->DestroyWindow();
if (wParam==IDOK)
MessageBox("Вы нажали OK");
else
MessageBox("Вы нажали Cancel");
return 0L;
}
Наконец, в файле ModelessView.h вставляем прототип функции:
afx_msg LRESULT OnGoodbye(UINT wParam, LONG lParam);
Задание № 5. Создайте программу для своего собственного немодального окна.
71
4.8. Стандартные диалоговые окна
Стандартные диалоговые окна – это окна, предназначенные для часто используемых действий. Все они производные от класса CDialog. Вот
их список:
 CColorDialog – выбор цвета из палитры;
 CFileDialog – открытие / сохранение файла;
 CFindReplaceDialog – поиск / замена текста;
 CFontDialog – выбор шрифта;
 CPageSetupDialog – настройка параметров страницы документа для
печати;
 CPrintDialog – выбор принтера.
Все окна, кроме CFindReplaceDialog, модальные, то есть достаточно
завести соответствующую переменнную класса, а затем вызывать функцию DoModal.
Диалоговое окно выбора цвета создается с помощью конструктора и
функции DoModal:
CColorDialog cDlg(RGB(0,255,100),CC_RGBINIT,this);
cDlg.DoModal();
Первым задается начальный цвет в палитре RGB, затем внешний вид
и третьим параметром – указатель на родительское окно.
Внешний вид может быть следующим:
 CC_FULLOPEN – при открытии окна сразу доступна полная палитра;
 CC_PREVENTFULLOPEN – нет кнопки, открывающей полную палитру;
 CC_RGBINIT – устанавливает начальный цвет;
 CC_SHOWHELP – кнопка помощи – «?» является доступной.
Рис. 15. Два внешних вида диалогового окна «Цвет»
Для получения цвета используется функция GetColor().
Диалоговое окно открытия / сохранения файла (рис. 16) создается
аналогично диалоговому окну выбора цвета:
72
CFileDialog fDlg(FALSE, "*.txt","Document", OFN_HIDEREADONLY |
OFN_OVERWRITEPROMPT,"Text|*.txt|",this);
fDlg.DoModal();
Первый аргумент в функции fDlg() указывает на тип окна (TRUE –
окно открытия файла, FALSE – окно сохранения файла). Следующие два
аргумента – это расширение и имя файла по умолчанию. Затем идет аргумент вида окна. Предпоследний аргумент указывает строку фильтра,
последний является указателем на родительское окно.
Виды окна:
 OFN_ALLOWMULTISELECT – разрешает выбор многих файлов;
 OFN_CREATEPROMPT – подтверждение, если выбран несуществующий файл;
 OFN_DONTADDTORECENT – не добавляет файл в список недавно
используемых документов;
 OFN_EXPLORER – окно в стиле проводника;
 OFN_FILEMUSTEXIST – отображает только существущие файлы;
 OFN_HIDEREADONLY – не показывает файлы с атрибутом «только
для чтения»;
 OFN_NOCHANGEDIR – восстанавливает внешний вид папки, если
пользователь создал папку, а потом нажал кнопку «Отмена»;
 OFN_NONETWORKBUTTON – скрывает кнопку «Сетевое окружение»;
 OFN_SHOWHELP – кнопка помощи – «?» является доступной.
Формат строки фильтра:
Тип файла 1 | Сопоставляемое расширение 1;
Ещё расширение | Тип файла 2 | Сопоставляемое расширение 2
|...Тип файла_n | Сопоставляемое расширение_n ||
Тип файла – это то, что увидит пользователь, а расширение будет
обрабатываться самой программой. Все данные отделяются знаком |. А
расширения (если их несколько) – ; (точка с запятой). В конце стоит знак
– || (две черты).
Рис. 16. Внешний вид диалогового окна «Открыть»
73
С помощью функций GetPathName(), GetFileName(), GetFileExt()
можно узнать путь к файлу, имя файла, расширение файла. Файл открывается с помощью функции CFile::Open().
Окно выбора шрифта (рис. 17) можно создать без всяких параметров:
CFontDialog cFD;
cFD.DoModal();
Но при создании можно установить начальный шрифт, используя
указатель на струкутуру LOGFONT, которая будет содержать все параметры начального шрифта; параметры окна; указатель на контекст принтера; указатель на родительское окно.
Возможные параметры окна:
 CF_APPLY – отображает кнопку «Применить»;
 CF_BOTH – отображает шрифты для экрана и печати;
 CF_TTONLY – отображает только шрифты «True Type»;
 CF_EFFECTS – позволяет изменять начертание шрифта (цвет, жирность, подчеркивание и т. п.);
 CF_NOVECTORFONTS – запрет на векторные шрифты;
 CF_PRINTERFONTS – отображает только шрифты для печати;
 CF_SCALABLEONLY – отображает только масштабируемые шрифты;
 CF_SCREENFONTS – отображает только экранные шрифты;
 CF_SHOWHELP – кнопка помощи – «?» является доступной.
Рис. 17. Внешний вид диалогового окна «Шрифт»
Получить шрифт можно с помощью функции GetCurrentFont (указатель на структуру LOGFONT). Характеристики шрифта получают с помощью функций: GetFaceName(), GetStyleName(), GetSize(), GetColor(),
GetWeight(), IsStrikeOut(), IsUnderline(), IsBold(), IsItalic().
74
Задание № 6. Создайте программу, отображающую три стандартных диалоговых окна.
4.9. Содержание отчета
Отчет по лабораторной работе представляет собой распечатку созданных четырех программ (задания № 2, 4–6) с комментариями. Первая
программа – отображение диалогового окна, вторая программа – диалоговое окно с элементами управления, третья программа – немодальное
диалоговое окно, четвертая программа – отображение стандартных диалоговых окон.
Также в отчет рекомендуется включить необходимые скриншоты созданных программ. Отчет должен быть подготовлен с помощью Microsoft
Word и содержать титульный лист установленного образца.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
1.
4.10. Контрольные вопросы
Дайте определение понятию «ресурс».
Что такое модальное диалоговое окно?
Что такое немодальное диалоговое окно?
Что такое стандартное диалоговое окно?
Как создается пользовательское диалоговое окно?
Перечислите не менее 7 пользовательских элементов управления.
Какие свойства есть у элемента «Список»?
Какие свойства есть у элемента «Рисунок»?
Какие свойства есть у элемента «Кнопка»?
Какие свойства есть у элемента «Флажок»?
Какие свойства есть у элемента «Поле ввода»?
Какие свойства есть у элемента «Переключатель»?
Опишите процедуру создания немодального диалогового окна.
Поясните общий принцип работы со стандартными диалоговыми окнами.
Какие виды кнопок существуют?
Как задать порядок обхода элементов?
Как осуществляется обмен данными с диалоговым окном?
Как создается пользовательское сообщение?
Как осуществляется связь родительского окна с немодальным диалоговым окном?
Перечислите виды стандартных диалоговых окон.
4.11. Задачи на самостоятельную работу
Создайте программу, содержащую два модальных диалоговых окна,
в одном из которых устанавливаются и вводятся параметры (по два
параметра), а во втором выводится результат расчета по любым ма75
2.
3.
4.
тематическим формулам в виде списка (задача простой сложности –
программа описана в литературе).
Создайте программу, содержащую диалоговое окно для работы с базой данных (задача простой сложности – программа описана в литературе).
Создайте программу, демонстрирующую наглядное копирование документов из одного диалогового окна в другое – копирование с помощью мыши (задача средней сложности – различные части программы описаны в литературе).
Создайте программу, которая включает в себя диалоговое окно с
внедренным объектом – Internet Explorer для просмотра HTMLфайлов (задача повышенной сложности – различные части программы опубликованы в Интернете).
4.12. Методика проведения лабораторной работы № 3
Лабораторная работа № 3 проводится на пяти занятиях. Перед её
началом студенты должны ознакомиться с текстом лабораторной работы
(пп. 4.1–4.9).
Занятие № 1 (2 аудиторных часа). Изучение теоретического материала по пп. 4.1–4.5. Подготовка ответов на контрольные вопросы.
Занятие № 2 (2 аудиторных часа). Изучение теоретического материала по п. 4.6. Подготовка ответов на контрольные вопросы. Выполнение
заданий № 1, 2. Подготовка отчета по п. 4.9.
Занятие № 3 (2 аудиторных часа). Изучение теоретического материала по пп. 4.7–4.9. Подготовка ответов на контрольные вопросы. Выполнение заданий № 3, 4. Подготовка отчета по п. 4.9.
Занятие № 4 (2 аудиторных часа). Выполнение задания № 5. Подготовка отчета по п. 4.9.
Занятие № 5 (2 аудиторных часа). Выполнение задания № 6 и подготовка отчета по п. 4.9. Защита отчета по лабораторной работе.
Повышение рейтинговой оценки за лабораторную работу возможно
при выполнении одного из заданий п. 4.11 во внеаудиторное время.
76
СПИСОК РЕКОМЕНДУЕМОЙ ЛИТЕРАТУРЫ
1.
2.
3.
Мешков, А. Visual C++ и MFC [Текст] / А. Мешков, Ю. Тихомиров. – СПб: BHV –
Санкт-Петербург, 1997. – 1040 c.
Олафсен, Ю. MFC и Visual C++ 6 [Текст] / Ю. Олафсен, К. Скрайбнер, К. Д. Уайт. –
СПб: ДиаСофтЮП, 2004. – 980 с.
Шилдт, Г. MFC: основы программирования [Текст]: пер. с англ. и под ред. В. Р. Гинзбурга / Г. Шилдт. – Киев: BHV, 1997. – 556 с.
77
СОДЕРЖАНИЕ
Введение............................................................................................................3
1. Основы программирования под Windows ................................................. 4
1.1. Что такое MFC? ................................................................................ 4
1.2. Многозадачность в Windows ........................................................... 5
1.3. Взаимодействие программ и Windows ........................................... 5
1.4. Основы программирования под Windows ...................................... 6
1.5. Типы данных в Windows .................................................................. 7
1.6. Соглашение об использовании имен .............................................. 7
1.7. Иерархия классов MFC .................................................................... 8
1.8. Глобальные функции и функции-члены в MFC ............................ 9
1.9. Файл AFXWIN.H .............................................................................. 9
1.10. Контрольные вопросы .................................................................. 10
2. Лабораторная работа № 1. Создание простых приложений Windows .. 11
2.1. Каркас MFC-программы ................................................................ 11
2.2. Подробнее о создании масштабируемых окон ............................ 16
2.3. Содержание отчета ......................................................................... 19
2.4. Контрольные вопросы .................................................................... 19
2.5. Задачи на самостоятельную работу .............................................. 20
2.6. Методика проведения лабораторной работы №1 ........................ 20
3. Лабораторная работа № 2. Обработка сообщений .................................. 21
3.1. Что такое сообщения? .................................................................... 21
3.2. Обработка сообщений в MFC ........................................................ 22
3.3. Включение макрокоманд в очередь сообщений .......................... 22
3.4. Включение обработчиков сообщений в описание класса ........... 23
3.5. Обработка сообщений от клавиатуры .......................................... 24
3.6. Контекст устройства ...................................................................... 27
3.7. Обработка сообщения WM_PAINT .............................................. 28
3.8. Обработка сообщений мыши......................................................... 31
3.9. Генерация сообщения WM_PAINT............................................... 33
3.10. Обработка сообщения WM_DESTROY...................................... 36
3.11. Обработка сообщений таймера ................................................... 36
3.12. Основные типы сообщений Windows ......................................... 39
3.13. Содержание отчета ....................................................................... 46
3.14. Контрольные вопросы .................................................................. 46
3.15. Задачи на самостоятельную работу ............................................ 47
3.16. Методика проведения лабораторной работы № 2 ..................... 47
4. Лабораторная работа № 3. Диалоговые окна .......................................... 48
4.1. Ресурсы ............................................................................................ 48
4.2. Виды диалоговых окон .................................................................. 48
4.3. Создание диалогового окна в редакторе ресурсов ...................... 49
78
4.4. Модальные диалоговые окна ......................................................... 51
4.5. Обмен данными с диалоговым окном .......................................... 52
4.6. Элементы диалогового окна и их свойства .................................. 54
4.7. Немодальные диалоговые окна ..................................................... 69
4.8. Стандартные диалоговые окна ...................................................... 72
4.9. Содержание отчета ......................................................................... 75
4.10. Контрольные вопросы .................................................................. 75
4.11. Задачи на самостоятельную работу ............................................ 75
4.12. Методика проведения лабораторной работы № 3 ..................... 76
Список рекомендуемой литературы............................................................. 77
79
Олег Олегович Привалов
Илья Викторович Степанченко
ЛАБОРАТОРНЫЙ ПРАКТИКУМ ПО MFC
Учебное пособие
Редактор Пчелинцева М. А.
Темплан 2007 г., поз. № 31.
Лицензия ИД № 04790 от 18 мая 2001 г.
Подписано в печать 27. 12. 2007 г. Формат 60×84 1/16.
Бумага листовая. Печать офсетная.
Усл. печ. л. 5,0. Усл. авт. л. 4,81.
Тираж 100 экз. Заказ №
Волгоградский государственный технический университет
400131 Волгоград, просп. им. В. И. Ленина, 28.
РПК «Политехник»
Волгоградского государственного технического университета
400131 Волгоград, ул. Советская, 35.
80
Download