Стоит Windows 8 Pro, Visual Studio 2010 Express Edition . |
Однодокументный интерфейс MFC
Изменение меню
Чтобы полностью завершить решаемую задачу, введем возможность установки цвета и толщины линии в меню. Для этого необходимо
- Добавить категорию для выбора цвета
- Добавить категорию для выбора толщины линии
- Добавить обработчики для каждого нового пункта меню
- Добавить обработчики, которые бы проверяли, какая позиция меню представляет текущий цвет и текущую толщину линии
Изменение ресурса меню
- Через вкладку Resource View вызовите ресурс меню на редактирование
- Отредактируйте ресурс меню в соответствии с рисунками, которые получены для режима Edit IDs оболочки
Мастер создал макроопределения. Чтобы их увидеть, в панели Solution Explorer необходимо файл приложения Resource.h. Файл должен содержать, примерно, следующий код
Файл приложения Resource.h //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by DrawSDI.rc // #define IDD_ABOUTBOX 100 #define IDP_OLE_INIT_FAILED 100 #define IDR_MAINFRAME 128 #define IDR_DrawSDITYPE 129 #define ID_COLOR_BLACK 32771 #define ID_COLOR_BLUE 32772 #define ID_COLOR_GREEN 32773 #define ID_COLOR_CYAN 32774 #define ID_COLOR_RED 32775 #define ID_COLOR_MAGENTA 32776 #define ID_COLOR_YELLOW 32777 #define ID_COLOR_WHITE 32778 #define ID_WIDTH_WIDTH 32779 #define ID_WIDTH_1 32780 #define ID_WIDTH_2 32781 #define ID_WIDTH_3 32782 #define ID_WIDTH_4 32783 #define ID_WIDTH_5 32784 #define ID_WIDTH_6 32785 #define ID_WIDTH_7 32786 #define ID_WIDTH_8 32787 ...............................................
Добавление обработчиков меню
Сообщения о нажатиях пунктов меню должен принимать и обрабатывать класс документа. Поэтому именно для него мы добавим соответствующие обработчики. Обработчики сообщений о выборе пунктов меню можно добавить через оболочку двумя способами:
Первым способом является вызов в редакторе меню через команду Add Event Handler контекстного меню соответствующего пункта
В появившемся диалоговом окне мастера добавления обработчика нужно выбрать класс, который будет обрабатывать сообщение, а также нужно выбрать тип (из двух) сообщения.
Вторым способом является выделение в панели Class View нужного класса, который будет принимать и обрабатывать сообщения от пунктов меню. Затем в панели Properties необходимо установить режим Events и для соответствующих идентификаторов пунктов меню создать обработчики, раскрыв узлы дерева событий
Первый тип сообщения ( COMMAND ) Windows генерирует каждый раз, когда пользователь выбирает соответствующий пункт меню. При этом сообщение передается со значением ID идентификатора пункта в качестве параметра сообщения. Сообщение типа UPDATE_COMMAND_UI не является чистым сообщением Windows, это запрос MFC на обновление внешнего вида отдельных пунктов меню перед их показом пользователю. Мы создадим обработчики для обеих сообщений каждого пункта, добавленного нами в меню.
- Откройте вкладку Class View и выберите класс CDrawSDIDoc
- В панели Properties установите режим Events и найдите идентификатор ID_COLOR_BLACK
-
Раскройте узел событий для пункта меню как объекта и для каждого из сообщений COMMAND и UPDATE_COMMAND_UI создайте обработчики, которые заполните так
Обработчики меню для выбора цвета Black void CDrawSDIDoc::OnColorBlack() { m_iColor = 0; } void CDrawSDIDoc::OnUpdateColorBlack(CCmdUI *pCmdUI) { // Отобразить пункт меню (по умолчанию TRUE) как доступный pCmdUI->Enable(); // Отобразить пункт меню (если TRUE) с флажком pCmdUI->SetCheck(m_iColor == 0 ? TRUE : FALSE); }
-
Аналогичным образом создайте обработчики для выбора всех других цветов
Обработчики меню для выбора остальных цветов void CDrawSDIDoc::OnColorBlue() { m_iColor = 1; } void CDrawSDIDoc::OnUpdateColorBlue(CCmdUI *pCmdUI) { pCmdUI->Enable(); pCmdUI->SetCheck(m_iColor == 1 ? TRUE : FALSE); } ......................................................... void CDrawSDIDoc::OnColorWhite() { m_iColor = 7; } void CDrawSDIDoc::OnUpdateColorWhite(CCmdUI *pCmdUI) { pCmdUI->Enable(); pCmdUI->SetCheck(m_iColor == 7 ? TRUE : FALSE); }
-
Подобным образом создайте обработчики для выбора через меню толщины линий рисования
Обработчики меню для выбора толщины линий void CDrawSDIDoc::OnWidth1() { m_iWidth = 1; } void CDrawSDIDoc::OnUpdateWidth1(CCmdUI *pCmdUI) { pCmdUI->Enable(); pCmdUI->SetCheck(m_iWidth == 1 ? TRUE : FALSE); } ..................................................... void CDrawSDIDoc::OnWidth8() { m_iWidth = 8; } void CDrawSDIDoc::OnUpdateWidth8(CCmdUI *pCmdUI) { pCmdUI->Enable(); pCmdUI->SetCheck(m_iWidth == 8 ? TRUE : FALSE); }
Построение комбинированных обработчиков
Мы видим, что количество обработчиков пунктов меню очень большое. Имеется возможность скомбинировать обработчики меню каждой категории, существенно уменьшив количество обработчиков и сделав код более изящным. Для этого применяют групповые сообщения Windows с параметрами.
Карта сообщений для индивидуальных обработчиков
Если посмотреть карту сообщений в файле DrawSDIDoc.cpp, то можно увидеть, что обработчики и идентификаторы пунктов меню связаны так
Карта сообщений в файле CDrawSDIDoc.cpp BEGIN_MESSAGE_MAP(CDrawSDIDoc, CDocument) ON_COMMAND(ID_COLOR_BLACK, OnColorBlack) ON_UPDATE_COMMAND_UI(ID_COLOR_BLACK, OnUpdateColorBlack) ON_COMMAND(ID_COLOR_BLUE, OnColorBlue) ON_UPDATE_COMMAND_UI(ID_COLOR_BLUE, OnUpdateColorBlue) ON_COMMAND(ID_COLOR_GREEN, OnColorGreen) ON_UPDATE_COMMAND_UI(ID_COLOR_GREEN, OnUpdateColorGreen) ON_COMMAND(ID_COLOR_CYAN, OnColorCyan) ON_UPDATE_COMMAND_UI(ID_COLOR_CYAN, OnUpdateColorCyan) ON_COMMAND(ID_COLOR_RED, OnColorRed) ON_UPDATE_COMMAND_UI(ID_COLOR_RED, OnUpdateColorRed) ON_COMMAND(ID_COLOR_MAGENTA, OnColorMagenta) ON_UPDATE_COMMAND_UI(ID_COLOR_MAGENTA, OnUpdateColorMagenta) ON_COMMAND(ID_COLOR_YELLOW, OnColorYellow) ON_UPDATE_COMMAND_UI(ID_COLOR_YELLOW, OnUpdateColorYellow) ON_COMMAND(ID_COLOR_WHITE, OnColorWhite) ON_UPDATE_COMMAND_UI(ID_COLOR_WHITE, OnUpdateColorWhite) ON_COMMAND(ID_WIDTH_1, OnWidth1) ON_UPDATE_COMMAND_UI(ID_WIDTH_1, OnUpdateWidth1) ON_COMMAND(ID_WIDTH_2, OnWidth2) ON_UPDATE_COMMAND_UI(ID_WIDTH_2, OnUpdateWidth2) ON_COMMAND(ID_WIDTH_3, OnWidth3) ON_UPDATE_COMMAND_UI(ID_WIDTH_3, OnUpdateWidth3) ON_COMMAND(ID_WIDTH_4, OnWidth4) ON_UPDATE_COMMAND_UI(ID_WIDTH_4, OnUpdateWidth4) ON_COMMAND(ID_WIDTH_5, OnWidth5) ON_UPDATE_COMMAND_UI(ID_WIDTH_5, OnUpdateWidth5) ON_COMMAND(ID_WIDTH_6, OnWidth6) ON_UPDATE_COMMAND_UI(ID_WIDTH_6, OnUpdateWidth6) ON_COMMAND(ID_WIDTH_7, OnWidth7) ON_UPDATE_COMMAND_UI(ID_WIDTH_7, OnUpdateWidth7) ON_COMMAND(ID_WIDTH_8, OnWidth8) ON_UPDATE_COMMAND_UI(ID_WIDTH_8, OnUpdateWidth8) END_MESSAGE_MAP()
Обратите внимание, что каждое макроопределение карты сообщений, сгенерированное мастером Add Event Handler, имеет в заголовке только два параметра. Но существуют макроопределения, которые мастером не генерируются и которые нужно вводить вручную, но зато они имеют три параметра и способны регистрировать на один обработчик диапазон идентификаторов. Применительно к нашей задаче они могут выглядеть так
Возможная таблица сообщений с диапазоном в файле CDrawSDIDoc.cpp BEGIN_MESSAGE_MAP(CDrawSDIDoc, CDocument) ON_COMMAND_RANGE(ID_COLOR_BLACK, ID_COLOR_WHITE, OnColorCommand) ON_UPDATE_COMMAND_UI_RANGE(ID_COLOR_BLACK, ID_COLOR_WHITE, OnUpdateColorUI) ON_COMMAND_RANGE(ID_WIDTH_1, ID_WIDTH_8, OnWidthCommand) ON_UPDATE_COMMAND_UI_RANGE(ID_WIDTH_1, ID_WIDTH_8, OnUpdateWidthUI) END_MESSAGE_MAP()
Обработчикам передаются значения ID пунктов меню
Написанные нами ранее в приложении обработчики, на примере выбора одного цвета, имеют вид
Обработчики сообщений файла CDrawSDIDoc.cpp void CDrawSDIDoc::OnColorBlack() { m_iColor = 0; } void CDrawSDIDoc::OnUpdateColorBlack(CCmdUI *pCmdUI) { // Отобразить пункт меню (по умолчанию TRUE) как доступный pCmdUI->Enable(); // Отобразить пункт меню (если TRUE) с флажком pCmdUI->SetCheck(m_iColor == 0 ? TRUE : FALSE); } ....................................................
Мастер при создании первого обработчика типа OnColorBlack() не предусмотрел в нем передачу аргументов, однако этот обработчик может принимать аргумент в виде значения идентификатора нажатого пункта меню. Во второй обработчик типа OnUpdateColorBlack(CCmdUI *pCmdUI) в качестве аргумента передается указатель на объект, в свойстве pCmdUI->m_nID которого содержится значение идентификатора соответствующего пункта меню. Учитывая это, мы можем создать комбинированные обработчики для каждой категории меню.
Вначале расширим категорию меню Width добавлением новых элементов, позволяющих задавать существенную толщину линий.
Дополнительные пункты меню категории Width | |
---|---|
Название пункта | Толщина пера |
Medium (Средняя) | 16 |
Thick (Толстое) | 24 |
Very Thick (Очень толстое) | 32 |
- Через панель Resource View вызовите редактор меню
- Добавьте в конец категории Width три новых пункта так, чтобы вначале названия пунктов были введены цифрами 16, 24, 32. Это нужно для того, чтобы редактор автоматически сформировал имена идентификаторов похожими на предыдущие пункты
- Измените названия уже сгенерированных пунктов на слова, приведенные в таблице выше
Должно получиться что-то подобное в режиме Edit IDs редактора меню
Новые комбинированные обработчики
Теперь займемся сокращением количества обработчиков пунктов меню. Вспомним, что интересующие нас обработчики прописаны в трех местах приложения:
- Прототипы расположены объявлении класса CDrawSDIDoc в файле DrawSDIDoc.h
- Определения размещены в конце файла DrawSDIDoc.cpp
- Связь идентификаторов пунктов меню и имен соответствующих обработчиков прописана в карте сообщений класса CDrawSDIDoc в начале файла DrawSDIDoc.cpp
Для того, чтобы нам не организовывать в комбинированном обработчике условия проверки значений передаваемых идентификаторов, необходимо учесть, чтобы значения идентификаторов в каждой из категорий шли в порядке возрастания с шагом единица. Для этого:
-
Откройте через панель Solution Explorer файл Resource.h
Он должен иметь примерно такой вид
Идетификаторы пунктов меню в файле Resource.h ............................................... #define ID_COLOR_BLACK 32771 #define ID_COLOR_BLUE 32772 #define ID_COLOR_GREEN 32773 #define ID_COLOR_CYAN 32774 #define ID_COLOR_RED 32775 #define ID_COLOR_MAGENTA 32776 #define ID_COLOR_YELLOW 32777 #define ID_COLOR_WHITE 32778 #define ID_WIDTH_WIDTH 32779 #define ID_WIDTH_1 32780 #define ID_WIDTH_2 32781 #define ID_WIDTH_3 32782 #define ID_WIDTH_4 32783 #define ID_WIDTH_5 32784 #define ID_WIDTH_6 32785 #define ID_WIDTH_7 32786 #define ID_WIDTH_8 32787 #define ID_WIDTH_16 32788 #define ID_WIDTH_24 32789 #define ID_WIDTH_32 32790 ...............................................
-
В файле DrawSDIDoc.h уберите прототипы всех созданных ранее обработчиков для категорий меню Color и Width и введите прототипы комбинированных обработчиков отдельно для каждой категории в конец класса
Код должен выглядеть так
Прототипы комбинированных обработчиков в файле DrawSDIDoc.h class CDrawSDIDoc : public CDocument { ................................................ public: // Объявление таблицы цветов static const COLORREF m_Colors[8]; // Комбинированные обработчики Color и Width afx_msg void OnColorCommand(UINT nID); afx_msg void OnUpdateColorUI(CCmdUI *pCmdUI); afx_msg void OnWidthCommand(UINT nID); afx_msg void OnUpdateWidthUI(CCmdUI *pCmdUI); };
-
В файле DrawSDIDoc.cpp карта сообщений после редактирования должна выглядеть так
Карта сообщений в файле DrawSDIDoc.cpp BEGIN_MESSAGE_MAP(CDrawSDIDoc, CDocument) ON_COMMAND_RANGE(ID_COLOR_BLACK, ID_COLOR_WHITE, OnColorCommand) ON_UPDATE_COMMAND_UI_RANGE(ID_COLOR_BLACK, ID_COLOR_WHITE, OnUpdateColorUI) ON_COMMAND_RANGE(ID_WIDTH_1, ID_WIDTH_32, OnWidthCommand) ON_UPDATE_COMMAND_UI_RANGE(ID_WIDTH_1, ID_WIDTH_32, OnUpdateWidthUI) END_MESSAGE_MAP()
-
Уберите в файле DrawSDIDoc.cpp код всех прежних обработчиков для категорий Color и Width и введите новый код обобщенных обработчиков так
Новый код обработчиков сообщений в файле DrawSDIDoc.cpp // Срабатывает при нажатии на любой пункт меню Color void CDrawSDIDoc::OnColorCommand(UINT nID) { m_iColor = nID - ID_COLOR_BLACK; } // Срабатывает для каждого пункта перед раскрытием меню Color void CDrawSDIDoc::OnUpdateColorUI(CCmdUI *pCmdUI) { // Отобразить пункт меню (по умолчанию TRUE) как доступный pCmdUI->Enable(); bool check = pCmdUI->m_nID == m_iColor + ID_COLOR_BLACK; // Какой цвет установлен, такой пункт // меню и отобразить с флажком pCmdUI->SetCheck(check ? TRUE : FALSE); } // Срабатывает при нажатии на любой пункт меню Width void CDrawSDIDoc::OnWidthCommand(UINT nID) { switch(nID){ case ID_WIDTH_16: m_iWidth = 16; break; case ID_WIDTH_24: m_iWidth = 24; break; case ID_WIDTH_32: m_iWidth = 32; break; default: m_iWidth = nID - ID_WIDTH_1 + 1; } } // Срабатывает для каждого пункта перед раскрытием меню Width void CDrawSDIDoc::OnUpdateWidthUI(CCmdUI *pCmdUI) { // Отобразить пункт меню (по умолчанию TRUE) как доступный pCmdUI->Enable(); bool check; int pos = 0; if(m_iWidth <= 8) pos = m_iWidth - 1; //0-7 else if(m_iWidth == 16) pos = m_iWidth - 8; //8 else if(m_iWidth == 24) pos = m_iWidth - 15;//9 else if(m_iWidth == 32) pos = m_iWidth - 22;//10 // Какая толщина установлена, такой пункт // меню и отобразить с флажком check = pCmdUI->m_nID == pos + ID_WIDTH_1; pCmdUI->SetCheck(check ? TRUE : FALSE); }
- Постройте приложение и проверьте его работоспособность
- Оформите только About, но ни в коем случае не оформляйте строку заголовка окна и не русифицируйте ресурс String Table. Может смениться кодировка и придется тексты интерфейсов переколачивать заново вручную