|
|
|
||||||||||||
|
|
Собственный, легко управляемый и профессионально разработанный сайт – необходимый элемент любого бизнеса. Находитесь ли Вы в командировке, в дороге, дома или на отдыхе имея компьютер, подключенный к сети Internet, Вы получаете доступ к управлению Вашим сайтом! |
|||
Глава 12. В глубине менюСтатьи → Программирование на С/С++В глубине меню Что такое меню, как оно создается и как с ним работать? Начальные сведения Меню обычно располагается под полосой заголовка в верхней части Элементы меню могут иметь различные типы и содержать ряд модификаторов (рис.
................................................1 Рис. 12.1. Заголовок и Главное меню приложения WordPad ........................................................2Рис. 12.2. Раскрытое меню пункта "Вид" .........................................................3Рис. 12.3. Команда, имеющая подменю Каждое окно, имеющее заголовок, может иметь системное меню, которое ..............................................................4 Рис. 12.4. Системное меню Система Windows предоставляет специальный тип меню, которое можно создавать в ] ................................................................5Рис. 12.5. Контекстное меню рабочей области окна приложения face="Courier New" size=2>WordPad Теперь, перечислив основные типы используемых в Windows меню, рассмотрим те Точно так же, как в Win32 API есть функции, специально предназначенные для ...................................................................6Рис. 12.6. Место класса СМenu в Класс СМenu инкапсулирует Рассмотрим основные члены этого класса.
Для создания "пустого" меню, т. е. меню, не содержащего ни одной строчки и ни
Для того чтобы добавить в существующее меню некоторое подменю, в классе
Если создание меню производится на основе шаблона, то можно воспользоваться
и
Если по каким-либо причинам понадобилось меню, которое не присоединяется ни к
Для изменения состава меню, созданного функцией
size=2>CreateMenu, в классе предусмотрена специальная группа Добавление элементов в меню производится функциями
size=2>AppendMenu и InsertMenu.
MF_CHECKED, MF_UNCHECKED
MF_DISABLED,
MF_GRAYED
MF_MENUBREAK
MF_MENUBARBREAK
MF_OWNERDRAW
MF_POPUP
MF_SEPARATOR
MF_STRING
При использовании этих флагов следует иметь в
color=#800000>nFlags
size=2>MF_OWNERDRAW
MF_SEPARATOR
MF_STRING
Параметр рВтр определяет указатель на объект
и
size=2>nFlags
face="Courier New" color=#800000 size=2> MF_BYCOMMAND
MF_BYPOSITION
Если требуется не вставлять новый, а заменить существующий элемент меню, то
и
Для удаления элемента из меню в классе предусмотрены две функции: BOOL CMenu:rDeleteMenu UINT nPosition, UINT nFlags) и
Для изменения состояния элемента меню используется функция:
У элемента можно проставить отметку с помощью следующих функций.
Вместо стандартных галочки или кружка, используемых для отметки элемента, в
После внесения в меню всех изменений необходимо вызвать функцию
Для поддержки работы с контекстными меню в классе реализована функция
Значения параметра nFlags для задания TMP_CENTERALIGN
TMP_LEFTALIGN
TMP_RIGHTALIGN
Значения параметра nFlags для кнопки TMP_LEFTBUTTON
TMP_RIGHTBUTTON
В классе представлены две виртуальные функции, которые произвольным образом Теперь, после теоретического знакомства с возможностями, заложенными в классе В системе Windows реализованы три способа создания меню: 1. На основе шаблона, задаваемого в файле ресурса. 2. Динамическое создание при помощи специальных функций. 3. На основе шаблона в оперативной памяти. Рассмотрим каждый из этих способов
Создание меню на основе шаблона Этот подход мы уже рассматривали и поэтому не будем останавливаться на нем ........................................................7Рис. 12.7. Атрибуты меню в окне свойств, открытом в режиме настройки Таблица 12.1. Описание атрибутов, определяющих внешний вид меню | ||
Атрибут | Описание | |
Separator | Элемент представляет собой горизонтальную | |
Checked | При выводе на экран элемент меню отмечается | |
Pop-up | Элемент определяет | |
Grayed | Элемент отображается серым цветом и находится в | |
Help | Элемент выравнивается по правому краю полосы | |
Break | Этот атрибут может принимать одно из трех | |
ID | Идентификатор элемента |
Атрибут
Описание
Caption
Текст, определяющий имя элемента меню;
текстовая строка может содержать символы: & — буква, перед которой
стоит этот знак, будет подчеркнута; t — включает в строку символ
табуляции; а — выравнивает текст по правой границе
меню
Prompt
Текст, который будет отображаться в строке
состояния и (после п) в окне всплывающей
подсказки
Inactive
Элемент находится в заблокированном состоянии,
но отображается обычным цветом
После того как создан шаблон, необходимо подключить меню к окну приложения.
Этот вопрос также достаточно подробно рассматривался, поэтому только перечислим
те места, где это можно сделать.
wc.lpszMenuName =
"IDR_MAINFRAME";
if
(!pMainFrame->LoadFrame(IDR_MAINFFLAME))
return FALSE;
pDocTemplate = new
CMultiDocTemplate(
IDR_NOTETYPE, //
идентификатор меню
size=2> RUNTIME_CLASS(CNoteDoc),
size=2>RUNTIME_CLASS(CNoteFrame),
size=2> RUNTIME_CLASS(CNoteView));
Примечание
Мы обязательно рассмотрим архитектуру
"документ/представление" более подробно.
При использовании любого из перечисленных способов подключения меню к окну
нет необходимости уничтожать меню перед завершением работы приложения, т. к.
библиотека MFC делает это сама.
Меню, созданное в системе Windows, где взаимодействие между объектами
осуществляется при помощи сообщений, также работает по этому принципу. Наиболее
важным из сообщений, которое передает меню, является
size=2>WM_COMMAND, означающее, что выбран тот или иной элемент меню. В
табл. 12.2 приведены этапы работы с меню и возникающие при этом сообщения.
Таблица 12.2. Этапы работы с меню и возникающие при этом сообщения
Этап работы
Сообщение
Обработчик
MFC
Инициализация меню
WM INITMENU
OnlnitMenu
Вывод всплывающего меню на
экран
WM INITMENUPOPUP
OnlnitMenuPopup
Инициализация и вывод на экран всплывающего
меню
WM INITMENU и WM
INITMENUPOPUP
OnlnitMenu и
OnlnitMenuPopup
Нахождение нужного элемента
меню
WM_MENUSELECT
OnMenuSelect
Выбор элемента меню
WM_COMMAND
OnCommand
Выбор элемента системного
меню
WM_SYSCOMMAND
size=2>OnSysCommand
Рассмотрим кратко роль перечисленных сообщений.
Примечание
Действие приведенных обработчиков справедливы и для двух
других способов создания меню.
Когда пользователь активизирует элемент системного меню, сначала вызывается
обработчик сообщения WM_SYSCOMMAND:
void
CWnd::OnSysCommand(
UINT nID, LPARAM
IParam)
Первый параметр nID может содержать
идентификатор строки системного меню, а также некоторые другие
значения:
SC_CLOSE
Удаление окна
SC_HOTKEY
Активизация определенным приложением окна,
связанного с командной клавишей
SC_HSCROLL
Прокрутка по горизонтали
SC_KEYMENU
Выбор элемента меню при помощи командной
клавиши
SC_MAXIMIZE или face="Courier New" color=#800000 size=2>SС_ICОМ
разворачивание окна
SC_MINIMIZE или
SC_ICON
Сворачивание окна
SC_MOUSEMOVE
Выбор элемента меню при помощи мыши
SC_MOVE
Перемещение окна
SC_NEXTWINDOW
Переключение на следующее окно
SC_PREVWINDOW
Переключение на предыдущее окно
SC_RESTORE
Восстановление нормального положения
и размеров окна
SC_SCREENSAVE
Запуск приложения-заставки (screen-saver
application), определенного в разделе [boot] файла <system.ini>
SC_SIZE
Изменение размеров окна
SC_TASKLIST
Запуск или активизация приложения Task
Manager
SC_VSCROLL
Прокрутка по вертикали
Примечание
Кроме перечисленных, можно использовать и свои
идентификаторы, как будет показано в рассматриваемом ниже примере.
Второй параметр IParam используется только при следующих
значениях первого параметра nID:
size=2>nID
IParam
size=2>SC_HOTKEY
Содержит идентификатор
активизируемого окна
size=2>SC_MOUSEMOVE
Младшее слово содержит х-координату,
а старшее — у-координату
Перед отображением всплывающего меню (один раз, при активизации) система
Windows посылает сообщение WM_INITMENU с
тем, чтобы приложение могло изменить меню перед тем, как его увидит
пользователь. При этом библиотека MFC вызывает обработчик:
void CWnd:lOnlnitMenu
(CMenu *pMenu)
Параметр рМепи задает указатель на объект
СМеnu, определяющий активизируемое меню. Обработка может заключаться в
активизации или деактивизации строк меню, изменении их состояния и т.
п.
Когда пользователь выбирает элемент, имеющий подменю, система Windows, прежде
чем отобразить всплывающее меню, посылает владельцу сообщение
face="Courier New" size=2>WM_INITMENUPOPUP. Библиотека MFC вызывает
обработчик, который дает приложению возможность изменить всплывающее меню перед
его отображением:
void
CWnd::OnlnitMenuPopup (
CMenu pPopupMenu,
DINT nlndex,
BOOL bSubMenu)
Параметр рРорuрМеnu— указатель на объект
"всплывающее меню"; nID— индекс всплывающего меню в меню верхнего уровня;
bSubMenu— определяет, является ли всплывающее меню системным (TRUE) или
нет.
Каждый раз, когда пользователь перемещается от одного элемента меню к
другому, Windows посылает сообщение
size=2>WM_MENUSELECT, которое идентифицирует текущий элемент, на что
библиотека MFC реагирует вызовом обработчика
void
CWnd::OnMenuSelect (
UINT nltemlD,
UINT nFlags,
HMENU hSubMenu)
Параметр nltemlD— идентификатор выделенного
элемента или индекс всплывающего меню. Параметр nFlags содержит комбинацию
следующих флагов, определяющих атрибуты элемента меню:
MF_BITMAP
Элемент является битовым
массивом
MF_CHECKED
Элемент отмечается галочкой
MF_DISABLED
Элемент является заблокированным, но
отображается в нормальном виде
MF_GRAYED
Элемент отображается серым цветом и не
может быть выбран
MF_MOUSESELECT
Элемент выделен мышью
MF_OWNERDRAW
Элемент рисуется окном, создавшим
меню
MF_POPUP
С данным элементом связывается всплывающее
меню
MF_SEPARATOR
Элемент является разделителем
MF_SYSMENU
Элемент принадлежит системному
меню
Последний параметр hSubMenu используется в
двух случаях, зависящих от состояния параметра nFlags:
size=2>nFlags face="Courier New" color=#800000 size=2> hSubMenu
MF_SYSMENU
Идентифицирует меню, ассоциированное с
сообщением
MF_POPUP
Идентифицирует дескриптор меню верхнего
уровня
Многие приложения используют сообщение
size=2>WM_MENUSELECT для отображения дополнительной информации о
выбранном элементе меню в строке состояния.
Случай, когда пользователь выбирает элемент меню, a Windows посылает
сообщение WM_COMMAND, уже не раз
описывался в предыдущих примерах — необходимо либо воспользоваться одним из
предопределенных обработчиков стандартных команд меню, либо создать свой,
например, так, как было продемонстрировано в главе 11.
С учетом новых сведений покажем, как можно организовать "переключение"
количества столбцов панели инструментов рисования в нашем приложении Graph:
1. Добавить в уже имеющуюся полосу меню элемент Tools, а в качестве его
подпунктов — "2 column" и "3 column" (рис. 12.8).
.........................................................................8Рис. 12.8. Добавляем новые элементы меню
2. Внести приведенный ниже код в карту сообщений класса CMainFmme:
BEGIN_MESSAGE_MAP(CMainFrame,
CFrameWnd)
//{(AFX_MSG_MAP(CMainFrame)
//}}AFX_MSG_MAP
ON_COMMAND_RANGE(ID_TOOLS_2
COLUMN, ID_TOOLS_3 COLUMN,
OnToolsColumn)
ON_UPDATE_COMMAND_UI_RANGE(ID_TOOLS_2COLUMN, IDJTOOLS_3COLUMN,
size=2>OnUpdateToolsCo.lumn)
END_MESSAGE_MAP()
3. В описание класса CMainFrame
добавить определение обработчиков:
size=2>//{{AFX_MSG(CMainFrame)
...
//}}AFX_MSG
afx_msg void
OnToolsColuinnfUINT nID) ;
afx_msg void
OnUpdateTooisColumn(CCmdUI* pCmdUI);
4. И, наконец, написать код самих обработчиков:
void
CMainFrame::OnToolsColumn(UINT nID)
{
// Запоминаем выбранное число
столбцов
m_nPaneCol = (nID =
ID_TOOLS_2COLUMN) ? 2 : 3;
// Устанавливаем столбцы на
панели инструментов рисования
size=2>SetColumns(mJnPaneCol);
}
void
CMainFrame::OnUpdateToolsColumn(CCmdUI* pCmdUI)
{
// Определяем выбранный пункт
меню
UINT nCol = (pCmdUI->m_nID
== ID_TOOLS_2COLUMN) ? 2 : 3;
// Устанавливаем маркер
состояния — галочку у выбранного
// пункта меню и сбрасываем у
остальных
if(m_nPaneCol ==
nCol)
size=2>pCmdUI->SetCheck(TRUE);
else
size=2>pCmdUI->SetCheck(FALSE);
}
Как видите, все довольно просто и, на мой взгляд, нет надобности больше
задерживаться на этом вопросе: ведь при использовании шаблонов подавляющую часть
работы выполняет операционная система, а все действия, необходимые для работы с
меню, представляют относительную сложность только при построении самого первого
меню.
Поэтому мы переходим к практическим примерам. Как вы помните, мы еще не
закончили наше приложение Graph. Здесь мы добавим в него возможность выбора
цвета как для рисования линий, так и для заливки. Но сделаем это, как и
договаривались, не общепринятым способом, а таким, который позволит
проиллюстрировать максимум возможностей работы с меню.
Добавление элемента в системное меню
Итак, мы создаем контекстное меню, появляющееся при нажатии правой кнопки
мыши в клиентской области окна и позволяющее выбрать цвет заливки (да и текущий
цвет линий). Одновременно в системное меню добавим пункт, разрешающий работу с
этим контекстным меню (рис. 12.9).
int
CMainFrame::OnCreate(LPCREATESTRUCT IpCreateStruct)
{
// Получаем доступ к
системному меню
CMenu* pSysMenu =
GetSystemMenu(FALSE);
// Определяем текст, которым
будет представлен
// созданный элемент
системного меню
CString
strColorMenu("Разрешить смену текущего цвета");
// Добавляем в него
разделитель и новый элемент
// "Разблокировать меню
Цвет"
size=2>pSysMenu->AppendMenu(MF_SEPARATOR);
size=2>pSysMenu->AppendMenu(MF_BYCOMMAND I MF_STRING,
IDM_COLOR,
StrColorMenu);
...
}
...........................................................9Рис. 12.9. Добавляем новый пункт в системное меню
Давайте коротко разберем наши действия. Прежде всего необходимо получить
доступ к системному меню, что мы и делаем с помощью функции
CMenu*
CWnd::GetSystemMenu(BOOL bRevert)
Позволяет получить идентификатор копии
системного меню, так что с ним можно проводить любые допустимые манипуляции.
При добавлении своего элемента в системное меню необходимо учитывать, что его
идентификаторы имеют номера, начинающиеся с OxFOOO. Поэтому собственный
идентификатор должен иметь номер, меньший этого значения. Кроме того, в
качестве последней цифры шестнадцатеричного представления номера команды
рекомендуется использовать 0. Параметр bRevert задает режим работы функции:
если он равен FALSE, можно выполнять модификацию системного меню. Значение
TRUE "возвращает" системное меню в состояние, приписанное ему по
умолчанию.
Следующая строка кода
CString
strColorMenu("Разрешить смену текущего цвета");
задает текст, который должен появиться в системном меню. Хотя его длина не
оговаривается, все же не надо делать его слишком длинным. Но в любом случае
система Windows сама "подгонит" размеры этого меню под максимальный текст.
Нам осталось только добавить те элементы меню, которые нам необходимы. Это,
безусловно, разделитель и собственно текст нашего меню. Выполняется это очень
просто с помощью функции
size=2>CMenu::AppendMenu.
Здесь необходимы некоторые пояснения. Как вы помните, при создании меню с
помощью редактора ресурсов мы обязательно должны были присвоить каждому его
элементу идентификатор, который является ничем иным, как некоторым целым числом.
Так вот, редактор ресурсов сам назначает числовой эквивалент введенному
идентификатору. Совсем по-другому обстоит дело в данном случае. При динамическом
добавлении элемента меню мы самостоятельно должны сопоставить идентификатор с
некоторым цифровым эквивалентом. Сделать это можно, непосредственно редактируя
файл <resource.h>, а можно с
помощью IDE. Именно такой подход мы
сейчас и рассмотрим:
1. Выберите пункт меню View | Resource
symbols и на экране появится одноименный диалог (рис. 12.10), в котором
вы найдете все имеющиеся идентификаторы.
..............................................................10Рис. 12.10. Здесь можно получить информацию по имеющимся
идентификаторам
2. Нажмите кнопку New, чтобы создать
новый идентификатор. На экране появится диалог
size=2>New Symbol.
3. В поле Name введите обозначение
идентификатора, а в поле Value — его
цифровое значение (рис. 12.11).
4. Нажмите последовательно кнопки ОК и
Close в соответствующих диалогах — новый
идентификатор создан.
...............................................................11Рис. 12.11. Создаем новый идентификатор
Вот, собственно, и все. Теперь можно смело вставлять новый элемент в
системное меню и создавать для него соответствующий обработчик.
Примечание
Некоторые из представленных в блоке диалога Resource
Symbols идентификаторы не снабжены галочкой в столбце In Use. И в поле Used by
для них выводится загадочная надпись "not used". Естественно, это не означает,
что данный идентификатор не нужен. Просто ClassWizard о нем ничего не
знает.
Обработка команд системного меню несколько отличается от стандартной, и об
этом необходимо сказать несколько слов. Я уже отмечал, что когда пользователь
выбирает элемент системного меню, вызывается обработчик сообщения
face="Courier New" size=2>WM_SYSCOMMAND. Следовательно, наша задача —
реализовать этот обработчик. Очевидно, что делать это мы будем с помощью мастера
ClassWizard, который создаст необходимые
компоненты карты сообщений и заготовку обработчика:
class CMainFrame : public
CFrameWnd
{
...
public:
BOOL m__bEnable; // индикатор
включения контекстного меню
protected:
size=2>//{{AFX_MSG(CMainFrame)
afx_msg void
OnSysCommand(UINT nID, LPARAM IParam);
//}}AFX_MSG
...
};
...
BEGIN_MESSAGE_MAP(CMainFrame,
CFrameWnd)
/ / {{AFX_MSG_MAP
(CMainFrame)
size=2>ON_WM_SYSCOMMAND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void
CMainFrame::OnSysCommand(UINT nID, LPARAM IParam)
{
// Прежде всего отсекаем
команды несистемного меню
if((nID & OxFFFO) ==
IDM_COLOR)
{
// Получаем доступ к
системному меню
CMenu *pSysMenu =
GetSystemMenu(FALSE);
// Определяем текущее
состояние элемента
UINT nState =
pSysMenu->GetMenuState(IDM_COLOR, MF_BYCOMMAND);
// Если элемент
отмечен, ...
if(nState ==
MF_CHECKED)
{
// сбрасываем маркер,
...
size=2>pSysMenu->CheckMenuItem(IDM_COLOR,
MF_BYCOMMAND |
MF_UNCHECKED);
// разрешаем работу с
контекстным меню mjDEnable = FALSE;
}
else //и
наоборот
{
size=2>pSysMenu->CheckMenu!tem(IDM_COLOR, MF_BYCOMMAND | MF_CHECKED);
// запрещаем работу с
контекстным меню m_bEnable = TRUE;
}
}
else
{
// Если команда "не наша",
необходимо передать ее
// библиотеке для корректной
обработки
CWnd::OnSysCommand(nID,
IParam);
}
}
В приведенном фрагменте обратите внимание на строчку
BOOL m_bEnable; // индикатор
включения контекстного меню
которая включена для того, чтобы как-то оправдать задачу создания новой
команды системного меню. Значение TRUE
этой переменной разрешит нам создать и работать с контекстным меню, a
face="Courier New" size=2>FALSE, соответственно, запретит.
Для решения поставленной задачи нам осталось выбрать команду, по которой
будет создаваться контекстное меню, и реализовать ее обработчик. Обычно
контекстные меню выводятся на экран при нажатии на правую кнопку мыши. Мы не
будем отступать от этого правила.
Создайте обработчик сообщения size=2>WM_RBUTTONDOWN. He забудьте, что помимо самой функции необходимо
определить компоненты карты сообщений.
void
CChildWnd::OnRButtonDown(UINT nFlags, CPoint point)
{
// Создавать и отображать
контекстное меню будем только
//в том случае, если "получили
разрешение" от системного меню
if(((CMainFrame
*)GetParentFrame{))->m_bEnable)
{
// Поскольку мы создаем меню,
нам нужен соответствующий объект
CColorMenu menu;
// Создаем контекстное
меню
size=2>menu.CreatePopupMenu();
for(int i = 0; i < 16;
i++)
{
// Добавляем в него
элементы
menu.AppendMenu(MF_OWNERDRAW,
ID^CLRO + i, "");
}
// Для расположения
контекстного меню относительно экрана
// необходимо
преобразовать положение курсора мыши
// в экранные
координаты
size=2>ClientToScreen(Spoint);
// Выводим контекстное меню
на экран
size=2> menu.TrackPopupMenu(TPM__LEFTALIGN I TPM_RIGHTBUTTON,
point.x, point.у,
this);
}
// Как всегда, не мешаем
нормальной работе
CWnd::OnRButtonDown(nFlags, point);
}
Первое, что бросается в глаза — это появление нового класса
face="Courier New" size=2>CColorMenu, о котором мы ничего не говорили. Да
и не могли ничего сказать, потому что только приступаем к его созданию. До сих
пор мы пользовались объектами класса
size=2>СМеnu, и этого было вполне достаточно. Зачем же нам понадобился
еще один класс? Все очень просто — мы хотим взять на себя задачу отрисовки
элементов меню. Этим мы займемся несколько позднее, а пока будем считать, что
работаем с объектом класса СМеnu.
Итак, после того как объект соответствующего класса создан, можно создавать
собственно контекстное меню:
size=2>menu.CreatePopupMenu();
Однако пока в нем нет ни одного элемента. Чтобы исправить такое положение,
добавим их :
for(int i = 0; i < 16;
i++)
menu.AppendMenu(MF_OWNERDRAW,
ID_CLRQ + i, "");
Следующим шагом является преобразование положения курсора мыши в экранные
координаты
size=2>ClientToScreen(Spoint);
чтобы место, где мы щелкнули правой кнопкой мыши, соответствовало левому
верхнему углу нашего контекстного меню. Если мы этого не сделаем, все будет
работать по-прежнему, но выглядеть не очень аккуратно.
Примечание
Эта точка может соответствовать и левому нижнему углу
контекстного меню, но такое перестроение автоматически выполняет сама система
Windows.
После того как мы все подготовили, осталось только вывести созданное меню на
экран. Что и делает, как вы помните, функция
size=2>menu.TrackPopupMenu(TPM_LEFTALIGN I TPM_RIGHTBUTTON,
point.x, point.у,
this);
Теперь выбор любого элемента нашего меню приведет к тому, что будет вызван
соответствующий обработчик, который, правда, мы еще не создали. Проделайте это
самостоятельно либо найдите его в полном тексте приложения
face="Courier New" size=2>Graph, который имеется на сопроводительной
дискете. А мы будем двигаться дальше.
Самоотображение элементов меню
Для того чтобы создать меню, внешний вид которого отличается от стандартного
(например, кроме текста мы хотим вывести там еще и пиктограмму), нам необходимо
перехватить и обработать два сообщения:
size=2>WM_MEASUREITEM и
size=2>WM_DRAWITEM. Библиотека MFC уже подготовила все для того, чтобы
этот процесс не занял много времени, и реализовала в классе
face="Courier New" size=2>СМеnu(и не только в нем) следующие две функции:
virtual void
CMenu::MaasureItem
(LPMEASUREITEMSTRUCT
IpMeasureltemStruct)
Вызывается библиотекой MFC при создании
меню, имеющего стиль самоотображения (owner-draw). В качестве параметра в
функцию передается указатель на структуру MEASUREITEMSTRUCT, с помощью которой
можно настроить размеры меню.
virtual void
CMenu::DrawItem (LPDRAWITEMSTRUCT IpDrawItemStruct)
Вызывается библиотекой MFC при создании
меню, имеющего стиль самоотображения. В качестве параметра в функцию
передается указатель на структуру DRAWITEMSTRUCT, которая содержит информацию
для рисования элемента меню.
При использовании стиля самоотображения обе функции должны быть обязательно
переопределены, т. к. их реализация в классе
size=2>СМеnu не содержит кода.
Таким образом, прежде всего нам необходимо создать свой класс, производный от
СМеnu. Рассмотрим один из возможных вариантов:
1. Раскроем окно мастера ClassWizard и
выполним команду New раскрывающейся
кнопки Add Class.
2. В появившемся диалоге введем имя создаваемого класса в поле face="Courier New" size=2>Name.
3. К сожалению, ClassWizard не
позволяет создавать класс на основе
size=2>СМеnu. Поэтому поступаем следующим образом: в раскрывающемся
списке Base class выбираем какой-нибудь
класс, например, CButton (рис. 12.12).
Нажимаем кнопку ОК, затем еще раз
face="Courier New" size=2>ОК — новый класс создан. Теперь нам надо внести
некоторые изменения.
............................................................12Рис. 12.12. Воспользоваться
size=2>ClassWizard можно даже в том случае, если в списке базовых классов
нет того, который нужен
4. В окне Workspace на вкладке
face="Courier New" size=2>ClassView дважды щелкните на имени
face="Courier New" size=2>CColorMenu, что приведет к открытию файла, где
этот класс определен. Замените имя
size=2>CButton на СМеnu — сделать
это надо в одном единственном месте.
5. Раскройте вкладку FileView в окне
Workspace и дважды щелкните по имени
файла <colormenu.cpp>. В строке
BEGIN_MESSAGE_MAP(CColorMenu,
CButton)
замените CButton на
face="Courier New" size=2>СМеnu. До этой строки можно "добраться" и
другим способом, я вам показываю только один из возможных вариантов.
6. Сохраните сделанные изменения. В результате мы получили собственный класс
CColorMenu, производный от
face="Courier New" size=2>СМеnu. Кстати, в данном конкретном случае
можете удалить из определения и реализации класса (обязательно в обоих местах)
все компоненты, кроме конструктора, — они нам не понадобятся.
После того как класс, базирующийся на
size=2>СМеnu, создан, нам необходимо переопределить две функции, которые
отвечают за обработку сообщений
size=2>WM_MEASUREITEM и
size=2>WM_DRAWITEM. Воспользуйтесь для этого мастером
face="Courier New" size=2>ClassWizard. В результате вы должны получить
следующий код:
#if !defined(
size=2>AFX_COLORMENU_H_OC5A82D5_B46D_11D3_BAB9_00600864785A_INCLUDED_)
#define
FX_COLORMENU_H_OC5A82D5_B46DJL1D3_BAB9_00600864785A_INCLUDED_
#if _MSC_VER >=
1000
#pragma once
#endif // _MSC_VER >=
1000
// ColorMenuJh : header
file
//
size=2>///////////////////////////////////////////////////////////////////
CColorMenu window
class CColorMenu : public
CMenu
{
// Construction
public:
CColorMenu();
// Implementation
public:
virtual void
MeasureItem(LPMEASUREITEMSTRUCT IpMeasureltemStruct);
virtual void
Drawltern(LPDRAWITEMSTRUCT IpDrawItemStruct); };
#endif // !defined(
size=2>AFX_COLORMENU_H_OC5A82D5_B46D_11D3_BAB9_00600864785A__INCLUDED_)
size=2>//////////////////////////////////////////////////////////////
ColorMenu.cpp :
implementation file
//
#include
"stdafx.h"
#include "Graph.h"
#include
"ColorMenu.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef.THIS_FILE
static char THIS_FILE[] =
_FILE_;
#endif
size=2>///////////////////////////////////////////////////////////////
CColorMenu
size=2>CColorMenu::CColorMenu()
{
}
size=2>/////////////////////////////////////////////////////////////////
CColorMenu message
handlers
void
CColorMenu::MeasureItem(
LPMEASUREITEMSTRUCT
IpMeasureltemStruct)
{
}
void
CColorMenu::DrawItem(LPDRAWITEMSTRUCT IpDrawItemStruct)
{
}
size=2>//////////////////////////////////////////////////////////
CColorMenu message
handlers
Нам осталось только наполнить содержанием созданные для нас оболочки функций
Drawltem и
size=2>Measurement. Однако сначала рассмотрим использованные в них
.структуры DRAWITEMSTRUCT и
face="Courier New" size=2>MEASUREITEMSTRUCT.
Структура DRAWITEMSTRUCT имеет
следующее определение:
typedef struct
tagDRAWITEMSTRUCT {
UINT CtlType;
UINT CtllD;
UINT iteralD;
UINT itemAction;
UINT itemState;
HWND hwndltem;
HOC hDC;
RECT rcltem;
DWORD itemData;
} DRAWITEMSTRUCT;
CtlType— тип самоотображаемого элемента
управления, внешний вид которого определяется пользователем. Может принимать
следующие значения:
ODT_BUTTON
Кнопка
ODT_COMBOBOX
Комбинированный список
ODT_LISTBOX
Список
ODT_MENU
Меню
CtllD— идентификатор кнопки, списка или
комбинированного списка, для меню этот параметр не используется; itemID —
идентификатор пункта меню или индекс записи в списке или комбинированном
списке. Для списка или комбинированного списка, не содержащих записей, этот
параметр должен иметь отрицательное значение, в результате чего будет выведен
прямоугольник с координатами, заданными в rcltem, и не содержащий никаких
элементов. itemAction — определяет, как должна осуществляться перерисовка. Он
может быть комбинацией следующих битов:
ODA_DRAWENTIRE
Весь элемент управления требует
перерисовки
ODA_FOCUS
Устанавливается, когда элемент управления
получает или теряет фокус ввода; поле itemState должно быть проверено для
определения, имеет ли элемент управления фокус ввода
ODA_SELECT
Устанавливается, если сделан выбор; поле
itemState должно быть использовано для определения нового выбора itemState —
задает состояние записи после перерисовки:
ODS_DISABLED
Запись недоступна
ODS_FOCUS
Запись имеет фокус ввода
ODS_SELECTED
Запись выбрана
hwndltem— дескриптор окна элемента
управления; hDC— контекст устройства вывода; rcltem — прямоугольник, задающий
границы перерисовки; itemData — для списка или комбинированного списка этот
параметр содержит значение, установленное функциями CComboBox::AddString,
CComboBox:;lnsertString, CListBox::AddString или CListBox::lnsertString, а для
меню— функциями СМепи:: AppendMenu, CMenu::lnsertMenu или
СМепи::ModifyMenu.
Структура MEASUREITEMSTRUCT имеет
следующий вид:
typedef struct
tagMEASUREITEMSTRUCT {
UINT CtlType;
UINT' CtllD;
UINT itemID;
UINT itemWidth;
UINT itemHeight;
DWORD itemData
}
MEASUREITEMSTRUCT;
CtlType — содержит информацию о типе
элемента управления (см. описание структуры DRAWITEMSTRUCT); CtllD—
идентификатор элемента управления. itemID— идентификатор элемента в списке
(для списка и комбинированного списка) — не используется для списков и
комбинированных списков с фиксированной высотой элементов. itemWidth —
используется для задания ширины элемента в меню (для списков и комбинированных
списков не используется). itemHeight— задает высоту записи в списке
(максимальное значение равно 255). itemData— назначение этого поля аналогично
одноименному полю структуры DRAWITEMSTRUCT.
Теперь задаем размеры элементов меню. Для этой задачи предназначена функция
MeasureItern:
void
CCoiorMenu::MeasureItem(
LPMEASUREITEMSTRUCT
IpMeasureltemStruct)
{
size=2>lpMeasureItemStruct->iteraWidth = 130;
size=2>lpMeasure!temStruct->itemHeight = 20;
}
Как вы понимаете, размеры вы можете задавать произвольные. Главное — сделать
это именно здесь.
Нам осталось только нарисовать в элементах меню то, ради чего, собственно,
все это и затевалось. Используем функцию:
void
CCoiorMenu::DrawItem(LPDRAWITEMSTRUCT IpDrawItemStruct)
{
// Определяем
прямоугольник,
// который будем заполнять
соответствующим цветом
CRect
rect(lpDrawItemStruct->rcItem.left,
size=2>lpDraw!temStruct->rcItem.top,
size=2> lpDrawItemStruct->rcItem.right — 110,
lpDrawItemStruct->rdtem.
bottom) ;
// При обработке сообщения
WM_DRAWITEM для нас уже создан
// контекст устройства,
и нам остается им воспользоваться
CDC *pDC =
CDC::FromHandle(lpDrawItemStruct->hDC);
// Дальнейшие действия
выполняем только в случае,
// если перерисовки требует
весь элемент меню
size=2> if(lpDraw!temStruct->itemAction & ODA_DRAWENTIRE)
{
// По, идентификатору
элемента меню находим его цвет
COLORREF cr =
colors[lpDraw!temStruct->itemID — ID_CLRO];
// Заполняем прямоугольник
выбранным цветом
pDC->FillSolidRect(Srect,
cr) ;
//и отделяем его от
последующего текста
pDC->MoveTo(rect.right +
I, 0);
pDC->LineTo(rect.right +
1, rect.bottom);
// Выводимый текст не должен
создавать "пятна"
size=2>pDC->SetBkMode(TRANSPARENT);
// Выводим текстовое
обозначение цвета
pDC-XTextOut(rect.right + 10,
rect.top + 4,
size=2>clrString[lpDrawItemStruct->itemID - ID_CLRO]);
}
}
Помимо этого не забудьте определить необходимые идентификаторы и тексты:
// MainFrm.cpp :
implementation of the CMainFrame class
//
// Определяем массив
цветов
COLORREF colors!] =
{
RGB{ О, О, О),
RGB(255, 255, 255),
RGB(128, 128, 128),
RGB(192, 192, 192),
RGB(128, 0, 0),
RGB(255, О, О),
RGB(128, 128, 0),
RGB(255, 255, 0),
RGB( 0, 128, 0),
RGB( 0, 255, 0),
RGB( 0, 128, 128),
RGB( 0, 255, 255),
RGB( 0, 0, 128),
RGB! 0, 0, 255),
RGB(128, 0, 128),
RGB (255, 0, 255) );
// ColorMenu.cpp :
implementation file
//
// Мы должны иметь доступ к
массиву цветов
extern COLORREF
colors[];
// Определяем массив названий
цветов
CString clrStringf]
=
{
"Черный",
"Белый",
"Темно-серый",
"Серый",
"Темно-красный",
"Красный",
"Темно-желтый",
"Желтый",
"Темно-зеленый",
"Зеленый",
"Темно-голубой",
"Голубой",
"Темно-синий",
"Синий",
"Темно-малиновый",
"Малиновый"
};
// Resource.h
//
...
// Для каждого цвета
необходим свой идентификатор
#define
ID_CLRO
32788
ttdefine
ID_CLR1
32789
ttdefine
ID_CLR2
32790
#define
ID_CLR3
32791
ttdefine
ID_CLR4
32792
ttdefine
ID_CLR5
32793-
#define
ID_CLR6
32794
#define
ID_CLR7
32795
#define
ID_CLR8
32796
#define
ID_CLR9
32797
#define
ID_CLR10
32798
#define
ID_CLR11
32799
#define
ID_CLR12
32800
#define
ID_CLR13
32801
#define
ID_CLR14
32802
#define
ID_CLR15
32803
...
Думаю, что нет никакой необходимости давать еще какие-либо пояснения в
дополнение к имеющимся комментариям. То, что получилось в результате наших
действий, представлено на рис. 12.13.
Создание собственных маркеров состояния
Последний пример, который мы рассмотрим в связи с меню, заключается в
реализации собственных маркеров, выводимых вместо "галочки".
Первое, что нам надо — это создать рисунки, которые будут показывать
отмеченное и неотмеченное состояния. Например, такие, как представленные на рис.
12.14. Я уже не буду объяснять, как это сделать.
Следующий необходимый шаг — написать код, позволяющий использовать созданные
маркеры. Как обычно, сначала определим необходимые переменные, что мы и делаем в
описании класса CMainFrame:
class CMainFrame : public
CFrameWnd
{
...
// Attributes
public:
CBitmap m_bmpCheck; // для
отмеченного состояния
CBitmap m_bmpUnCheck; // для
неотмеченного состояния
...
}
....................................................13Рис. 12.13. Результат наших действий по рисованию элементов меню
...................................................14Рис. 12.14. Так будет выглядеть "галочка" у отмеченного элемента меню
Затем добавляем код в обработчик события size=2>WM_CREATE:
int
CMainFrame::OnCreate(LPCREATESTRUCT IpCreateStruct)
{
// Загружаем битовые массивы
с новыми маркерами
size=2>m_bmpCheck.LoadBitmap(IDB_MENU_DOWN);
size=2>m_bmpUnCheck.LoadBitmap(IDB_MENU_UP);
// Получаем системные размеры
маркера
int ex =
::GetSystemMetrics(SM_CXMENUCHECK);
int cy =
::GetSystemMetrics(SM_CYMENUCHECK);
// Приводим текущие размеры
битовых массивов к системным
size=2>m_bmpUnCheck.SetBitmapDimension(сх, су);
size=2>m_bmpCheck.SetBitmapDimension(ex, cy);
// Получаем доступ к главному
меню
CMenu *pViewMenu =
NULL;
CMenu *pTopMenu =
AfxGetMainWnd()->GetMenu();
int iPos;
// Ищем элемент меню "2 column"
по идентификатору IDjroOLS_2COLUMN
for (iPos =
pTopMenu->GetMenu!temCount0-1; iPos >= 0; iPos—)
{
// Поиск выполняем по позиции
элемента
CMenu* pMenu =
pTopMenu->GetSubMenu(iPos);
if (pMenu &&
pMenu->GetMenu!temID(0) == ID_TOOLS_2COLUMN)
1
// Если соответствующий
идентификатор найден,
// прекращаем поиск, запомнив
указатель на этот элемент
pViewMenu = pMenu;
break;
}
}
// Мы обязательно должны
найти элемент меню
ASSERT(pViewMenu !=
NULL);
// Устанавливаем новые
маркеры для отмеченного и
// неотмеченного
состояний,
// которые теперь будут
появляться вместо "галочек"
size=2>pViewMenu->SetMenuItemBitmaps(IDJTOOLS_2COLUMN, MF_BYCOMMAND,
&m_bmpUnCheck,
&m_bmpCheck);
size=2>pViewMenu->SetMenuItemBitmaps(ID_TOOLS_3COLUMN, MF_BYCOMMAND,
&m_bmpUnCheck,
&m_bmpCheck);
...
}
И вот что у нас получилось в результате (рис. 12.15).
Конечно, рисунки маркеров оставляют желать лучшего, но вы можете сами
нарисовать что-то более красивое. А как "подключить" их к меню, я вам рассказал.
..............................................15Рис. 12.15. Так выглядят новые маркеры отмеченного и неотмеченного
состояний
По материаллам сайта: www.realcoding.net
16.02.2006

VSESMI.ru — новости в СМИ.
Один из больших по объему информации проектов, работающих под управлением HostCMS.
Tur-Hotel.ru — отзывы об отелях
На сайте представлено описание отелей, рейтинг отелей с отзывами туристов.
|
Copyright © 2002—2012 ООО "Хостмэйк" Телефон в Москве: +7 (495) 223-46-50 Телефон в Санкт-Петербурге: +7 (812) 448-38-90 Тел./Факс: +7 (8636) 237-836 E-mail: 2006 |