Приложение MFMenu
Приложение MFMenu
Приложения, которые мы рассматривали до сих пор, фактически никак не могли взаимодействовать с пользователем. Они не имели ни меню, ни панели управления. И самое главное – они не содержали обработчиков сообщений.
Сейчас мы рассмотрим приложение MFMenu, которое имеет меню и содержит обработчики сообщений, передаваемых приложению, когда пользователь открывает меню и выбирает из него строки. Для создания приложения MFMenu мы также не станем пользоваться средствами автоматизированной разработки MFC AppWizard, и наберем текст приложения вручную. Этот позволит вам лучше разобраться с механизмом сообщений.
Создайте новый проект под названием MFMenu. В качестве типа приложения выберите из списка Type строку Application (рис. 4.1 из главы “Приложение с главной диалоговой панелью”). Наберите в редакторе исходный текст приложения и сохраните его в файле MFMenu.cpp (листинг 2.7). Чтобы быстрее набрать текст приложения, вы можете получить его, изменив исходный текст приложения MFStart. Затем включите этот файл в проект.
Листинг 2.7. Файл MFMenu.cpp
// Включаемый файл для MFC
#include <afxwin.h>
#include "MFMenuRes.h"
//=====================================================
// Класс CMFMenuApp – главный класс приложения
//=====================================================
class CMFMenuApp : public CWinApp {
public:
// Мы будем переопределять метод InitInstance,
// предназначенный для инициализации приложения
virtual BOOL InitInstance();
};
// Создаем объект приложение класса CMFMenuApp
CMFMenuApp MFMenuApp;
//=====================================================
// Класс CMFMenuWindow – представляет главное окно
//=====================================================
class CMFMenuWindow : public CFrameWnd {
public:
// Объявляем конструктор класса CMFMenuWindow
CMFMenuWindow();
// Объявляем методы для обработки команд меню
afx_msg void MenuCommand();
afx_msg void ExitApp();
// Макрокоманда необходима, так как класс
// CMFMenuWindow обрабатывает сообщения
DECLARE_MESSAGE_MAP()
};
//=====================================================
// Метод MenuCommand
// Обрабатывает команду ID_TEST_BEEP
//=====================================================
void CMFMenuWindow::MenuCommand() {
MessageBeep(0);
}
//=====================================================
// Метод ExitApp
//=====================================================
void CMFMenuWindow::ExitApp() {
DestroyWindow();
}
//=====================================================
// Таблица сообщений класса CMFMenuWindow
//=====================================================
BEGIN_MESSAGE_MAP(CMFMenuWindow, CFrameWnd)
ON_COMMAND(ID_TEST_BEEP, MenuCommand)
ON_COMMAND(ID_TEST_EXIT, ExitApp)
END_MESSAGE_MAP()
//=====================================================
// Метод InitInstance класса CMFMenuApp
//=====================================================
BOOL CMFMenuApp::InitInstance() {
// Создаем объект класса CMFMenuWindow
m_pMainWnd = new CMFMenuWindow();
// Отображаем окно на экране
m_pMainWnd–>ShowWindow(m_nCmdShow);
// Обновляем содержимое окна
m_pMainWnd–>UpdateWindow();
return TRUE;
}
//=====================================================
// Конструктор класса CMFMenuWindow
//=====================================================
CMFMenuWindow::CMFMenuWindow() {
// Создаем окно приложения, соответствующее
// данному объекту класса CMFMenuWindow
Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));
}
Ресурсы приложения
Добавить в проект новый ресурс очень просто. Вы можете для этого воспользоваться кнопками панели управления Progect или установить указатель мыши на название типа ресурса и нажать правую кнопку мыши. На экране появится временное меню свойств данного ресурса.
Если у вас уже есть ресурсы, которые разработаны ранее и записаны в отдельных файлах на диске, вы можете подключить их к своему проекту. Для этого надо выбрать из временного меню свойств ресурса строку Import. На экране появится диалоговая панель Import Resource с приглашением ввести имя файла подключаемого вами ресурса. Новый ресурс проекта будет записан в подкаталог RES, расположенный в каталоге проекта.
Сам по себе новый ресурс, подключенный к приложению, не принесет никакой пользы. Ваше приложение должно обращаться к нему – загружать в память, обрабатывать сообщения, связанные с этим ресурсом
Среда Visual C++ версии 4.0 не только позволяет просматривать и редактировать ресурсы приложения, она позволяет сразу после создания ресурса вызвать ClassWizard и подключить к ресурсу управляющий программный код. Пока мы не будем использовать ClassWizard, а код, управляющий ресурсами приложения, создадим вручную.
Создание меню для приложения MFMenu
Так как наше приложение будет содержать меню, мы приступим к созданию ресурсов приложения. Для этого создайте новый файл ресурсов. Выберите из меню File строку New, а затем из открывшейся диалоговой панели выберите строку Resource Script и нажмите кнопку OK.
Теперь надо создать меню. Выберите из меню Insert строку Resource. На экране появится диалоговая панель Insert Resource. Выберите из нее строку Menu и нажмите кнопку OK. Вы сможете в диалоговом режиме разработать меню. Чтобы быстрее перейти к редактированию меню, вы можете нажать кнопку New Menu (
Создайте меню, содержащее единственную строку Test, при выборе которой открывается меню, содержащее три строки – Beep и Exit. Внешний вид меню во время разработки представлен на рисунке 2.22.
Рис. 2.22. Разработка меню приложения
Когда вы создаете новое меню верхнего уровня или определяете строки, входящие в это меню, на экране появляется диалоговое окно Menu Item Properties (рис. 2.23). В нем полностью описывается выбранный элемент меню. В поле Caption задается название меню или строки меню. В названии вы можете вставить символ &. Он будет означать, что символ, следующий за ним, можно будет использовать для быстрого выбора данного элемента меню. Например, если перед названием строки меню Beep поставить символ &, то во время работы приложения символ B будет отображаться с подчеркиванием, и строку B eep можно будет выбрать, нажав комбинацию клавиш <Alt+B>.
Рис. 2.23. Диалоговая панель Menu Item Properties
Как вы знаете, каждый элемент меню должен иметь уникальный идентификатор, однозначно его определяющий. Вы можете задать идентификатор в поле ID или выбрать его из списка предопределенных идентификаторов.
Редактор ресурсов сам предлагает вам название идентификатора, создавая его из названия главного меню и строки меню. Так например строке Beep меню Test по умолчанию будет присвоен идентификатор ID_TEST_BEEP.
В поле Prompt можно в нескольких словах описать данный пункт меню. Введенная вами строка будет записана в строковый ресурс. Ей будет присвоен тот же самый идентификатор, что и пункту меню. Приложение может использовать строку описания, чтобы предоставить пользователю краткую справку о меню.
Если вы разрабатываете приложение с помощью средств MFC AppWizard, то при выборе пользователем строк меню их описание будет появляться в панели состояния StatusBar. Что интересно, для этого вам не потребуется написать ни одной строки текста программы. Это будет сделано автоматически благодаря возможностям библиотеки классов MFC и MFC AppWizard.
Остальные переключатели диалоговой панели Menu Item Properties описаны в следующей таблице.
Переключатель Описание Break Этот переключатель может принимать три различных значения – None, Column и Bar. Он позволяет задать структуру меню. По умолчанию выбрано значение None оно соответствует нормальному виду меню без деления на колонки. Если выбрать значение Column, тогда пункт меню будет размещен в новом столбце. Значение Bar соответствует Column, за исключением меню верхнего уровня. Если указать Bar для меню верхнего уровня, то новая колонка будет отделена от старой вертикальной линией Checked Если установить переключатель, то строка меню будет выделена символом ?. Потом, обращаясь к специальным методам, вы сможете удалять или отображать этот символ Grayed Если включить переключатель Grayed, тогда пункт меню будет отображаться серым цветом и будет недоступен для выбора пользователем. Такая возможность удобна, если вам надо сделать недоступным какие-либо возможности приложения. Впоследствии приложение может сделать пункт меню доступным. Для этого надо вызвать соответствующий метод Help Если установить переключатель Help, тогда для него будет установлено выравнивание по правому краю. Обычно этот переключатель устанавливают для меню верхнего уровня, которое управляет справочной системной приложения Inactive Если включен переключатель Grayed, тогда переключатель недоступен. В противном случае вы можете установить переключатель Inactive. В этом случае пункт меню будет неактивен Pop-up Вы можете создавать многоуровневые меню. Если вы включите переключатель Pop-up, то данный пункт меню будет являться меню верхнего уровня, которое можно открыть. По умолчанию, все пункты главного меню имеют установленный переключатель Pop-up. Так как меню верхнего уровня служит только для объединения других пунктов меню, то оно не имеет идентификатора Separator Если переключатель установлен, тогда в меню вставляется разделитель. Для разделителя все остальные поля и переключатели диалоговой панели не используютсяСохраните файл ресурсов в файле с именем MFMenu.rc. Редактор ресурсов создает кроме самого файла ресурсов еще включаемый файл, в котором определяются константы, используемые в файле ресурсов. В нашем случае в нем определяются идентификаторы меню приложения. По умолчанию этот файл сохраняется под именем resource.h . Вы можете изменить его, выбрав из меню View строку Resource Includes. Для нашего приложения мы изменили имя включаемого файла для ресурсов на MFMenuRes.h. Содержимое этого файла представлено листингом 2.8.
Листинг 2.8. Файл MFMenuRes.h
//{{NO_DEPENDENCIES}}
// Включаемый файл, созданный Microsoft Developer Studio
// Используется в файле ресурсов MFMenu.rc
//
#define IDR_MENU 101
#define ID_TEST_BEEP 40001
#define ID_TEST_EXIT 40002
// Следующие значения идентификаторов используются по
// умолчанию для новых объектов
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40003
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
В листинге 2.9 мы привели файл ресурсов MFMenuRes.rc приложения. Этот файл был подготовлен редактором ресурсов Visual C++. Одна из первых строк файла содержит директиву #include которой подключается файл MFMenuRes.h, содержащий описание идентификаторов ресурсов (листинг 2.8).
Среди прочих служебных строк, необходимых редактору ресурсов и компилятору Visual C++, вы можете обнаружить описание меню приложения IDR_MENU. Для первого приложения, использующего ресурсы мы привели файл ресурсов полностью. Впоследствии мы ограничимся словесным описанием ресурсов и будем приводить только выдержки из файла ресурсов.
Листинг 2.9. Файл MFMenuRes.rc
// Файл описания ресурсов приложения, созданный
// Microsoft Developer Studio
#include "MFMenuRes.h"
#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
// Включаем файл afxres.h, содержащий определения стандартных
// идентификаторов
#include "afxres.h"
//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
// Руссификацированные ресурсы
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
#ifdef _WIN32
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
#pragma code_page(1251)
#endif //_WIN32
////////////////////////////////////////////////////////////
// Меню
//
IDR_MENU MENU DISCARDABLE
BEGIN
POPUP "Test"
BEGIN
MENUITEM "Beep", ID_TEST_BEEP
MENUITEM SEPARATOR
MENUITEM "Exit", ID_TEST_EXIT
END
END
#ifdef APSTUDIO_INVOKED
////////////////////////////////////////////////////////
// Ресурсы TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"MFMenuRes.h"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h"" "
""
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
" "
""
END
#endif // APSTUDIO_INVOKED
#endif // Руссификацированные ресурсы
//////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
#endif
Когда вы создадите ресурсы приложения и включите файл ресурсов в проект обратите внимание на окно Project Workspace. В нем появится еще одна, четвертая страница ResourceView (рис. 2.24). Эта страница показывает все ресурсы, входящие в проект. В приложении MFMenu определен только один ресурс – меню, имеющее идентификатор IDR_MENU.
Вы можете быстро перейти к редактированию меню, если выберите его идентификатор и щелкните два раза левой кнопкой мыши.
Рис. 2.24. Страница ResourceView окна Project Workspace
Теперь проект готов. Вы можете построить его и запустить полученное приложение MFMenu. Внешний вид приложения представлен на рисунке 2.25. Как видите окно приложения имеет меню Test, состоящее из двух строк – Beep и Exit.
Если вы выберите строку Beep из меню Test, то услышите на внутреннем динамике компьютера звуковой сигнал. В случае если звуковой сигнал не слышен, проверьте подключен ли внутренний динамик, а если в компьютере установлена звуковая плата, правильно установите громкость сигнала.
Когда вы завершите работу с приложением, его можно закрыть. Для этого воспользуйтесь системным меню приложения или выберите из меню Test строку Exit.
Рис. 2.25. Приложение MFMenu
Чтобы объекты класса могли обрабатывать сообщения, в определении этого класса необходимо поместить макрокоманду DECLARE_MESSAGE_MAP. По принятым соглашениям эта макрокоманда должна записываться в конце определения класса в секции public.
//=====================================================
// Класс CMFMenuWindow – представляет главное окно
//=====================================================
class CMFMenuWindow : public CFrameWnd {
public:
// Объявляем конструктор класса CMFMenuWindow
CMFMenuWindow();
// Объявляем методы для обработки команд меню
afx_msg void MenuCommand();
afx_msg void ExitApp();
// Макрокоманда необходима, так как класс
// CMFMenuWindow обрабатывает сообщения
DECLARE_MESSAGE_MAP()
};
Однако это еще не все. Необходимо также определить таблицу сообщений. Таблица начинается макрокомандой BEGIN_MESSAGE_MAP и заканчивается макрокомандой END_MESSAGE_MAP. Между этими макрокомандами расположены строки таблицы сообщений, определяющие сообщения, подлежащие обработке данным классом и методы, которые выполняют такую обработку.
Приложение может содержать несколько классов, обладающих собственными таблицами сообщений. В следующем разделе мы приведем пример такого приложения. Чтобы однозначно определить класс, к которому относится таблица сообщений, имя этого класса записывается в первый параметр макрокоманды BEGIN_MESSAGE_MAP.
Приложение MFMenu обрабатывает только две команды от меню приложения. Первая команда имеет идентификатор ID_TEST_BEEP и передается, когда пользователь выбирает из меню Test строку Beep. Для обработки этой команды вызывается метод MenuCommand. Вторая команда с идентификатором ID_TEST_EXIT передается приложению, когда пользователь выбирает из меню Test строку Exit. Обработка этого сообщения выполняется методом ExitApp.
//=====================================================
// Таблица сообщений класса CMFMenuWindow
//=====================================================
BEGIN_MESSAGE_MAP(CMFMenuWindow, CFrameWnd)
ON_COMMAND(ID_TEST_BEEP, MenuCommand)
ON_COMMAND(ID_TEST_EXIT, ExitApp)
END_MESSAGE_MAP()
Конечно, приложению MFMenu может поступать гораздо больше сообщений и команд, чем указано в таблице сообщений класса CMFMenuWindow. Необработанные сообщения передаются для обработки базовому классу CMFMenuWindow – классу CFrameWnd . Класс, который будет обрабатывать сообщения, не указанные в таблице сообщений, указывается во втором параметре макрокоманды BEGIN_MESSAGE_MAP.