    Особые виды меню 

    В этой главе мы вернемся к основам программирования под Windows: к меню. 
Как говорилось в главе 4, меню сами по себе довольно понятны, просты в 
управлении и, казалось бы, не требуют сложного программирования. Однако при 
более внимательном рассмотрении можно обнаружить, что подсистема меню включает 
большое число сложных, развитых и довольно интересных функций. Кроме того, 
этой подсистеме уделяется особое внимание в новых версиях Windows, например в 
Windows 95. Поэтому включение в программу особых видов меню позволит придать 
ей профессиональный и вполне современный вид. 

    395 

    В данной главе будут рассмотрены два особых вида выпадающих меню: 
динамические и всплывающие. Они предоставляют значительно больше возможностей 
для управления и использования меню в приложениях. Но, прежде чем приступить к 
работе с ними, необходимо получить представление о важном типе функций, 
имеющихся в MFC. Мы познакомимся с обработчиками изменений вменю, с помощью 
которых можно управлять состоянием элементов меню. Такие обработчики являются 
основой работы особых видов меню. 

    Обработчики изменений в меню 

    Обработчики изменений определяют статус отображения элемента меню. В 
MFC-программе есть специальный уровень обработки, выполняющийся в промежутке 
времени, когда выпадающее меню активизировано, но еще не отображено на экране. 
Между двумя этими событиями вызывается набор обработчиков, связанных с данным 
меню. Для каждого элемента выпадающего меню имеется собственный обработчик 
изменений, который определяет: доступен данный элемент или нет. Этот 1 
обработчик может также установить метку выбора рядом с названием элемента и i 
изменить текст самого названия. Меню отображается, когда все обработчики 
выпол- i нены. Таким образом, обработчики изменений используются для 
определения точного состояния выпадающего меню при каждом его отображении. 
    Вы, должно быть, удивлены тем, что об обработчиках изменений ничего не 
было сказано раньше, ведь они являются основой работы с меню в MFC. Ответ 
таков: со всеми меню связаны встроенные обработчики изменений, содержащиеся в 
MFC. Они • выполняли все необходимые действия самостоятельно, поэтому нам не 
приходилось ; заботиться о способе их использования. Однако при возрастании 
сложности меню ; данную подсистему необходимо взять под контроль. К счастью, 
это одна из самых | легких подсистем MFC. Прежде чем приступить к реализации 
обработчиков измене- | ний, давайте подробнее рассмотрим механизм внесения 
изменений в меню.        ; 

    Механизм внесения изменений в меню 

    При активизации выпадающего меню программе посылается сообщение 
WM_INITMENUPOPUP. Это сообщение автоматически перехватывается MFC и 
преобразуется в вызов соответствующего обработчика изменений. Как уже 
упоминалось, с каждым элементом выпадающего меню связан свой обработчик. Если 
для какого-либо элемента он не определен в программе, то вызывается 
стандартный обработчик. 
    Стандартные обработчики изменений, реализованные в MFC, предлагают 
простой, но эффективный способ управления меню. Их работа организована 
следующим образом: каждый элемент выпадающего меню, для которого в очередь 
сообщений включена макрокоманда ON_COMMAND(), считается доступным, а те 
элементы, которые не имеют такой макрокоманды, объявляются недоступными. Чтобы 
убедиться в этом, попробуйте выполнить следующий эксперимент: возьмите одну из 
рассмотренных ранее программ и удалите из очереди сообщений какую-нибудь 
макрокоманду ON_COMMAND(), а затем откомпилируйте и запустите программу. Вы 
увидите, что элемент, у которого была удалена макрокоманда, станет недоступным 
и будет выделен серым цветом. 

    396                                    MFC: основы программирования 

    Необходимо учитывать, что любые действия, производимые над меню, могут 
перекрываться действием стандартных обработчиков изменений, потому что эти 
обработчики всегда выполняются самыми последними. Например, если какой-то 
элемент меню объявить в файле ресурсов недоступным, то это объявление будет 
отменено, если для данного элемента в очередь сообщений включена макрокоманда 
ON_COMMAND(). В этом случае стандартный обработчик изменений автоматически 
сделает элемент доступным, и не имеет значения, какие действия выполнялись до того. 
    Механизм автоматического внесения изменений в меню можно отменить. Для 
этого необходимо установить значение переменной m_pAutoMenuEnable класса 
CFrameWnd равным FALSE. Тогда все действия, производимые в программе над меню, 
будут действительно выполняться. Это означает, что нужно будет создать 
программный механизм управления элементами меню. На практике это выполняется 
редко, так как проще реализовать, при необходимости, собственные обработчики изменений. 

    Определение обработчиков изменений 

    Определить собственный обработчик изменений довольно легко. Для этого 
необходимо выполнить два действия. Сначала нужно для каждого элемента меню, 
который будет обрабатываться, включить в очередь сообщений макрокоманду 
ON_UPDATE_COMMAND_UI(). Затем нужно определить тело обработчика. 
    Макрокоманда ON_UPDATE_COMMAND_UI() имеет следующий вид: 

    ON_UPDATE_COMMAND_UI{ID, Handler} 

    Здесь параметр ID является идентификатором обрабатываемого элемента меню. 
Параметр Handler содержит имя соответствующего обработчика изменений. Каждый 
обработчик изменений должен иметь такой прототип: 

    afx_msg void Handler(CCmdUI *UIOb}; 

    Здесь параметр ШОЬ является указателем на объект типа CCmdUI. CCmdUI — это 
специальный класс, который существует исключительно для поддержки механизма 
внесения изменений в меню. Он не является частью иерархии классов MFC, т.е. не 
порождается от класса CObject. Он используется только для обозначения типа 
параметров в обработчиках изменений. Класс CCmdUI содержит ряд функций, 
предназначенных для управления элементами меню. Одной из них является функция 
ЕпаЫе(). Ее прототип таков: 

    virtual void CCmdUI::Enable(BOOL OnOff=TRUE) ; 

    Данная функция устанавливает состояние элемента меню. Если параметр On Off 
не равен нулю, то элемент станет доступным. И наоборот, если параметр On Off 
равен нулю, то элемент станет недоступным. 
    Класс CCmdUI содержит также функции, позволяющие устанавливать метку 
выбора рядом с названием элемента и изменять текст самого названия. Эти 
функции вы можете изучить самостоятельно. 
    Когда оба действия по определению обработчика изменений выполнены, то он 
будет вызываться вместо стандартного обработчика. Работа с обработчиками 
изменений демонстрируется в последующих примерах из данной главы. 

    15. Особые виды меню                                          397 

    Напомним, что не нужно определять обработчики изменений для всех 
элементонж меню. Для тех из них, которые не имеют своих обработчиков, будут 
вызываться Д стандартные обработчики, содержащиеся в MFC. 
    Примечание: Не путайте обработчики изменений с обработчиками типа 
ON_COMMAND() — они совершенно различны. Обработчики изменений выполняют 
действия, связанные с отображением меню. Обработчики сообщений реагируют на 
выбор элементов меню. 

    Динамические меню 

    Хотя для большинства простых Windows-приложений меню в файлах ресурсов 
определяются статическим способом, в более сложных программах часто требуется 
динамически вносить или удалять элементы меню непосредственно во время работы 
приложения, при выполнении некоторых условий. Например, в текстовых редакторах 
в меню File могут содержаться различные команды, в зависимости от типа 
редакти- j руемого файла. Компилятор может содержать один набор опций отладки 
для про- '' грамм, написанных на С, и другой набор — для программ на C++. 
Меню, содержание ; которых может изменяться во время работы приложения, 
называются динамическими ; меню. Их преимуществом является то, что они 
предоставляют пользователю именно | тот набор опций, который соответствует 
текущему состоянию приложения.        ' 
    В Windows имеется несколько API-функций, предназначенных для управления . 
содержимым меню во время выполнения программы. Эти функции инкапсулируются ; в 
MFC с помощью класса СМепи. Давайте рассмотрим наиболее часто используемые ; функции. 

    Добавление элемента в меню 

    Для добавления элемента в меню во время работы приложения предназначена 
функция InsertMenuO, имеющая такой прототип: 
    BOOL CMenu::InsertMenu(UINT Where, UINT Flags, UINT ID^O, LPCSTR 
ItemName=-NULL) ; 
    Параметр Where определяет позицию в структуре меню, куда будет добавлен 
новый элемент. Параметр ID является идентификатором добавляемого элемента. Имя 
элемента указывается в параметре ItemName. Однако точное значение параметров 
Where, ID и [temName, как и других атрибутов, определяется значением параметра 
Flags (гябл. 15.1). 

    Таблица 15.1. Значения параметра Flags функции InsertMenuO 

    Константа             Смысл 

    MF_BYCOMMAND      Параметр Where должен содержать идентификатор 
существующего элемента меню. Новый элемент вставляется перед ним. 
    MF_BYPOSITION       Параметр Where должен содержать индекс позиции, в 
которую вставляется новый элемент. Индексы начинаются с нуля. При добавлении 
элемента в конец меню в параметре Where можно указать -1. 

    398                                     MFC: основы программирования 

    Константа             Смысл 

    MF_CHECKED         Рядом с названием элемента проставляется метка выбора. 
    MF_DISABLED         Элемент становится неактивным. 
    MF_ENABLED         Элемент становится активным. По умолчанию все элементы активны. 
    MF_GRAYED          Элемент становится неактивным и выделяется серым цветом. 
    MF_HILITE            Элемент выделяется цветом. 
    MF_MENUBARBREAK  При добавлении элемента к меню верхнего уровня он будет 
расположен на новой строке. Для выпадающих меню элемент будет расположен в 
новой колонке. В этом случае элемент отделяется чертой. 
    MF_MENUBREAK      To же, что и MF_MENUBARBREAK, только разделительная 
черта не используется. 
    MF_OWNERDRAW      Вид элемента определяется пользователем. 
    MF_POPL)P            Новый элемент является выпадающим меню. В этом случае 
параметр ID содержит дескриптор данного меню. 
    MF_SEPARATOR        Новый элемент является разделителем. В этом случае 
параметры ID и ItemName не используются. 
    MF_STRING            Новый элемент является обычной командой меню. В этом 
случае его имя указывается в параметре IlemName, а параметр ID содержит 
идентификатор элемента. 
    MF_UNCHECKED      Метка выбора, стоящая рядом с названием элемента, снимается. 
    MF_UNHILITE         Выделение элемента цветом снимается. По умолчанию ни 
один из элементов не выделен. 
    Например, если в меню MenuOb в позицию с индексом 2 добавляется элемент 
"New Item" с идентификатором 244, то вызов функции InsertMenu() будет 
выглядеть примерно следующим образом: 

    MenuOb.InsertMenu(2, MF_BYPOSITION | MF_STRING, 244, "New Item"); 

    Функция InsertMenu() возвращает ненулевое значение при успешном завершении 
и ноль — в противном случае. 
    Обратите внимание на флаги MFJENABLED и MF_DISABLED (табл. 15.1). Они 
определяют, будет ли добавляемый элемент активным или нет. Нельзя забывать о 
том, что стандартные обработчики изменений в MFC перекрывают действие этих 
флагов. Поэтому, чтобы изменить состояние элемента меню (активен/неактивен), 
лучше всего определить собственный обработчик изменений, а затем вызвать 
функцию CCmdUl::Enable(). 
    Примечание: Есть перегруженная версия функции InsertMenuf), которая 
позволяет добавлять в меню растровое изображение. Для добавления нового 
элемента в конец меню можно использовать функцию CMenu::AppendMenu(). 

    75. Особые виды меню                                           399 


    Удаление элемента меню                           ' 

    Для удаления элемента из меню предназначена функция DeleteMenu(), имеющая 
i такой прототип:                                                          i 

    BOOL CMenu::DeleteMenu(UINT ID, UINT How};                          \ 

    Удаляемый элемент определяется параметром ID. Параметр How указывает на 
то, каким образом должен интерпретироваться параметр ID. Если параметр How 
равен MF_BYPOSITION, то параметр ID должен содержать индекс удаляемого 
элемента, i Данный индекс определяется позицией элемента в структуре меню, 
причем первый i элемент меню имеет индекс 0. Если параметр How равен 
MF_BYCOMMAND, то' параметр ID является идентификатором элемента. Функция 
DeleteItemQ возвращает, ненулевое значение при успешном завершении и ноль — в 
противном случае.     ' 

    Определение числа элементов меню 

    Часто при работе с динамическими меню требуется узнать, сколько элементов 
имеется в настоящий момент в меню. Для получения числа элементов меню 
используется функция GetMeniiItemCoiintO, прототип которой таков: 

    UINT CMenu::GetMenuItemCount() const; 

    Функция возвращает число элементов меню или -1 при возникновении ошибки. 

    Изменение состояния элементов меню 

    Класс CMenu содержит множество функций, предназначенных для управления 
меню. Одной из них является функция EiiableMenuItem(), которая используется 
для изменения состояния элемента меню (активен/неактивен). Однако 
необходимость в ней возникает редко, потому что ее действия перекрываются 
стандартными обработчиками изменений в меню. Например, если с помощью этой 
функции сделать какой-то элемент меню неактивным, то стандартный обработчик 
изменений вновь сделает его активным, при условии, что с данным элементом 
связана макрокоманда ON_COMMAND(). Поэтому для управления состоянием элементов 
одновременно с использованием механизма автоматического внесения изменений в 
меню необходимо определять собственные обработчики изменений, а затем вызывать 
функцию CCmdUI::Enable(). 
    Вы, наверное, подумали, что функция EnableMenuItem() совершенно 
бессмысленна, ведь ее действия отменяются обработчиками изменений. Но это 
верно лишь отчасти. Если отключить механизм автоматического внесения изменений 
в меню, то эффект работы функции станет заметен. Все же при работе с меню 
лучше определять собственные обработчики изменений, чем отключать весь механизм. 

    Конструктор класса CMenu 

    Поскольку работа с меню ведется через функции-члены класса CMenu, то для 
использования этих функций необходим либо указатель на объект типа CMenu, либо 
сам объект. Чаше всего используется указатель на главное меню приложения. Но 
иногда приходится создавать все меню с самого начала. Для этого необходимо 
объявить объект типа CMenu с помощью следующего конструктора: 

    CMenu(); 

    400                                    MFC: основы программирования 

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

    Получение указателя на главное меню 

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

    CMenu *CWnd::GetMenu() const.

    Обратите внимание, что эта функция является членом класса CWnd. Она 
возвращает указатель на объект главного меню окна. При возникновении ошибки 
(или если у окна нет главного меню) возвращается NULL. Указатель, возвращаемый 
функцией GetMenuQ, является временным, поскольку объект меню может быть 
перемещен из одной области памяти в другую. 
    При наличии указателя на объект главного меню можно легко получить 
указатели на объекты подменю, содержащихся в главном меню. Это выполняется с 
помощью функции GetSubMenuO, которая имеет такой прототип: 

    CMenu *CMenu::GetSubMenu(int Index) const; 

    Здесь параметр Index является индексом подменю в родительском меню. 
(Индекс первого элемента равен нулю.) Функция возвращает указатель на объект 
подменю или NULL при возникновении ошибки. Данный указатель также является временным. 

    Динамическое размещение элементов меню 

    Теперь, когда основные функции работы с меню рассмотрены, пора приступить 
к реализации приведенных выше положений. Начнем с динамического добавления и 
удаления элементов меню. Для этого будет модифицирована программа работы с 
графикой, созданная в главе 9. В новой версии программы содержится два меню. 
Первое называется Options, второе — Graphics. В меню Options содержатся опции, 
связанные с управлением работой программы. В меню Graphics содержатся команды 
рисования различных графических объектов. В следующей программе 
демонстрируется динамическое управление меню на примере добавления или 
удаления элемента из меню Options. Этот элемент называется Reset, и при его 
выборе текущее содержимое окна программы стирается. Пример работы программы 
показан на рис. 15.1. 

    15. Особые виды меню                                          401 


    Рис. 15.1. Пример динамического добавления элементов в меню 

    В программе используется следующий файл ресурсов: 
    ; DYNMENU.RC 
    #include <afxres.h> 
    #include "ids.h" 
    DynMenu MENU 
    { 
    POPUP "SOptions" 
    { 
    MENUITEM "SAdd Item\tF2", IDM_ADDITEM 
    MENUITEM "&Delete Item\tF3", IDM_DELITEM 
    MENUITEM "E&xit\tF7", IDM_EXIT 
    } 
    POPUP "SGraphics" 
    ( 
    MENUITEM "&Lines\tF4", IDM_LINES 
    MENUITEM "SRectangles\tF5", IDM_RECTANGLES 
    MENUITEM "&Ellipses\tF6", IDM_ELLIPSES 
    ) 
    MENUITEM "SHelp", IDM_HELP 
    } 
    DynMenu ACCELERATORS 
    ( 


    402                                    MFC: основы программирования 


    VK_F1, IDM_HELP, VIRTKEY 
    VK_F2, IDM_ADDITEM, VIRTKEY 
    VK__F3, IDM_DELITEM, VIRTKEY 
    VK_F4, IDM_LINES, VIRTKEY 
    VK_F5, IDM_RECTANGLES, VIRTKEY 
    VK_F6, IDM_ELLIPSES, VIRTKEY 
    VK_F7, IDM_EXIT, VIRTKEY 
    ) 
    Вот текст библиотечного 4'>айла IDS.H: 
    // IDS.H 
    ftdefine IDM_LINES       100 
    ftdefine IDM_RECTANGLES 101 
    #define IDM_ELLIPSES    102 
    #define IDM_ADDITEM     103 
    ftdefine IDM_DELITEM     104 
    ftdefine IDM_HELP        105 
    ftdefine IDM_EXIT        106 
    ftdefine IDM_RESET       107 
    ftdefine IDM_DSTINVERT   108 
    ftdefine IDM_SHOWTEXT    109 
    Это текст файла DYNMENU.H, содержащего объявления порождаемых классов: 
    // DYNMENU.H 
    // Это класс для создания главного окна 
    class CMainWin : public CFrameWnd 
    ( 
    CDC m memDC; // контекст устройства, связанный с 
    // виртуальным окном 
    CBitmap m bmp; // образ виртуального окна 
    CBrush m bkbrush; // кисть для виртуального окна 
    // объявление перьев 
    CPen m_RedPen, m_YellowPen, m_GreenPen, m_BluePen; 
    CPen m_01dPen; 
    // флаги для элементов меню Add Item и Delete Item 
    int m AddActive, m DelActive; 
    public: 
    CMainWin() ; 
    afx msg void OnPaintO; 
    // обработчики изменений в меню 
    afx_msg void CMainWin::OnUIAdd(CCmdUI *UI) ; 
    afx_msg void CMainWin::OnUIDel(CCmdUI *UI); 
    afx_msg void OnLinesO; 


    15. Особые виды меню                                          403 


    afx_msg void OnRectangles(); 
    afx msg void OnEllipses() ; 
    afx msg void OnReset(); 
    afx msg void OnAddO; 
    afx_msg void OnDeleteO; 
    afx msg void OnExitO; 
    afx msg void OnHelpO; 
    DECLARE_MESSAGE_MAP() 
    }; 
    
    // Это класс для создания приложения 
    class CApp : public CWinApp 
    ( 
    public: 
    BOOL Initlnstance () ; 
    }; 
    Ниже приведен текст самой программы: 
    // Динамическое размещение элементов меню 
    #include <afxwin.h> 
    ^include "dynmenu.h" 
    ^include "ids.h" 
    int maxX, maxY; // размеры экрана 
    CMainWin::CMainWin() 
    { 
    Create (NULL, "Demonstrate Dynamic Menus", 
    WSJ3VERLAPPEDWINDOW, rectDefault, 
    NULL, "DynMenu"); 
    // Загрузка таблицы быстрого доступа 
    if(!LoadAccelTable("DynMenu")) 
    MessageBox ("Cannot Load Accelerators", "Error"); 
    maxX = GetSystemMetrics(SM_CXSCREEN) ; 
    maxY = GetSystemMetrics(SM_CYSCREEN) ; 
    CClientDC DC(this); 
    
    // создание образа виртуального окна 
    m_memDC.CreateCompatibleDC(&DC) ; 
    m_bmp.CreateCompatibleBitmap(&DC, maxX, maxY) ; 
    m memDC.SelectObject(&m bmp) ; 
    
    // выбор стандартного фона 
    m_bkbrush.CreateStockObject(WHITE_BRUSH) ; 
    m memDC.SelectObject(&m bkbrush) ; 
    
    // закраска фона виртуального окна 
    m_memDC.PatBlt(0, 0, maxX, maxY, PATCOPY) ; 


    404                                    MFC: основы программирования 


    // создание перьев 
    m_RedPen.CreatePen(PS_SOLID, 1, RGB(255, 0, 0)) ; 
    m_GreenPen.CreatePen(PS_SOLID, 2, RGB(0, 255, 0) ) ; 
    m_BluePen.CreatePen(PS_SOLID, 3, RGB(0, 0, 255)); 
    m__YeilowPen.CreatePen(PS_SOLID, 4, RGB(255, 255, 0)) ; 
    m_01dPen.CreateStockObj ect(BLACK_PEN) ; 
    m AddActive =1; // инициализация флагов 
    m DelActive = 0; 
    \ 
    II Инициализация приложения 
    300L САрр::Initlnstance() 
    m_pMainWnd = new CMainWin; 
    m pMainWnd->ShowWindow(m nCmdShow); 
    m_pMainHnd->UpdateWindow(); 
    return TRUE; 
    ) 
    // Это очередь сообщений приложения 
    BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) 
    ON_WM_PAINT() 
    ON_UPDATE_COMMAND_UI(IDM_ADDITEM, OnUIAdd) 
    ON_UPDATE_COMMAND_UI(IDM_DELITEM, OnUIDel) 
    ON_COMMAND(IDM_LINES, OnLines) 
    ON_COMMAND(IDM_RECTANGLES, OnRectangles) 
    ON_COMMAND(IDM_ELLIPSES, OnEllipses) 
    ON_COMMAND(IDM_RESET, OnReset) 
    ON_COMMAND(IDM_ADDITEM, OnAdd) 
    ON_COMMAND(IDM_DELITEM, OnDelete) 
    ON_COMMAND(IDM_EXIT, OnExit) 
    ON_COMMAND(IDM_HELP, OnHelp) 
    END_MESSAGE_MAP() 
    // Вывод на экран содержимого виртуального окна 
    afx msq void CMainHin::OnPaint () 
    ( 
    CPaintDC DC(this) ; 
    DC.BitBlt(0, 0, maxX, maxY, &m_memDC, О, О, SRCCOPY) ; 
    // Обработка сообщения IDM EXIT 
    afx msq void CMainWin::OnExit() 
    ( 
    int response; 
    response = MessageBox("Quit the Program?", "Exit", MB_YESNO) ; 


    15. Особые виды меню                                          405 


    if (response == IDYES)                                              • 
    SendMessage(WM_CLOSE); // завершение приложения                
    
    // Обработка сообщения IDM_HELP                                     Ц 
    afx msg void CMainWin::OnHelp()                                      f 
    ( 
    MessageBox("Dynamic Menus", "Help"); 
    } 
    
    // Отображение линий и точек 
    afx msg void CMainWin::OnLines() 
    ( 
    //отображение четырех точек 
    m_memDC.SetPixel(40, 14, RGB(255, 0, 0) ) ; 
    m_memDC.SetPixel (41, 14, RGB(0, 255, 0)); 
    m_memDC.SetPixel (42, 14, RGB(0, 0, 255)); 
    m_memDC.SetPixel (43, 14, RGB(0, 0, 0)) ; 
    m_memDC.LineTo(100, 50); 
    m_niemDC.MoveTo (100, 50); 
    
    // изменение цвета пера на зеленое 
    m memDC.SelectObject(&m GreenPen); 
    m_memDC.LineTo(200, 100); 
    
    // изменение цвета пера на желтое 
    m_memDC.SelectObject(&m_YellowPen) ; 
    m_memDC.LineTo(0, 200); 
    
    // изменение цвета пера на синее 
    m memDC.SelectObject(&m BluePen) ; 
    m_memDC.LineTo(200, 200); 
    
    // изменение цвета пера на красное 
    m_memDC.SelectObject(&m_RedPen) ; 
    m_memDC.LineTo(0, 0) ; 
    m_memDC.LineTo(100, 50); 
    m_memDC.MoveTo(0, 0) ; 
    m_memDC.LineTo(100, 250); 
    m_memDC.MoveTo(0, 0) ; 
    m_memDC.LineTo(100, 350); 
    
    // восстановление первоначального пера 
    m_memDC.SelectObject(&m_01dPen) ; 
    m_memDC.Arc(О, О, 300, 300, 0, 50, 200, 0) ; 
    
    //отображение пересекающихся линий, которые определяют дугу 
    m_memDC.MoveTo(150, 150); 
    m_memDC.LineTo(0, 50); 
    m_memDC.MoveTo(150, 150); 


    406                                    MFC: основы программирования 


    m_memDC.LineTo(200, 0) ; 
    InvalidateRect(NULL) ; 
    'i 
    // Отображение прямоугольников 
    afx msg void CMainWin::OnRectangles() 
    { 
    CBrush HollowBrush; 
    
    // создание "пустого" заполнения 
    HollowBrush.CreateStockObject(HOLLOW_BRUSH) ; 
    m_memDC.SelectObject (&HollowBrush) ; 
    
    // отображение нескольких прямоугольников 
    m_memDC.Rectangle(50, 50, 300, 300); 
    m_memDC.RoundRect(125, 125, 220, 240, 15, 13); 
    
    // использование красного пера 
    m_memDC.SelectObject(&m_RedPen) ; 
    m_memDC.Rectangle(100, 100, 200, 200); 
    m_memDC.SelectObject(&m_01dPen); // восстановление прежнего пера 
    
    // восстановление прежней кисти 
    m memDC.SelectObject(&m bkbrush) ; 
    InvalidateRect(NULL) ; 
    ) 
    // Отображение эллипсов 
    afx msg void CMainWin::OnEllipses () 
    ( 
    CBrush Brush; 
    
    // создание синей кисти 
    Brush.CreateSolidBrush(RGB(0, 0, 255)); 
    m_memDC.SelectObject(SBrush) ; 
    
    // отображение эллипсов, заполненных синим цветом 
    m_inemDC. Ellipse (50, 200, 100, 280); 
    m_memDC.Ellipse(75, 25, 280, 100); 
    
    // использование красного пера 
    m_memDC.SelectObject(&m_RedPen) ; 
    // создание зеленой кисти 
    Brush.DeleteObject (); // удаление синей кисти 
    Brush.CreateSolidBrush(RGB(0, 255, 0) ) ; 
    
    m_memDC.SelectObject(&Brush); // выбор зеленой кисти 
    m_memDC.Ellipse(100, 100, 200, 200); 
    // отображение сектора 


    15. Особые виды меню                                            407 


    mJ-nemDC.Pie (200, 200, 340, 340, 225, 200, 200, 250);              | 
    
    m_memDC.SelectObject(&m_01dPen); // восстановление прежнего пера 
    m_memDC.SelectObject(&m_bkbrush); // восстановление прежней кисти 
    InvalidateRect(NULL) ; 
    } 
    // Очистка окна 
    afx msg void CMainWin::OnReset() 
    {                                                                          
                                                     I 
    m_memDC.PatBlt(0, 0, maxX, maxY, PATCOPY); 
    InvalidateRect(NULL) ; 
    | 
    
    // Добавление элемента в меню 
    afx_msg void CMainWin::OnAdd() 
    ( 
    CMenu *m MainMenu; // указатель на объект главного меню 
    CMenu *SubMenu; // указатель на объект подменю 
    unsigned count; 
    
    // получение указателя на объект главного меню 
    m MainMenu = GetMenu(); 
    
    // получение указателя на объект первого выпадающего меню 
    SubMenu = m MainMenu->GetSubMenu(0); 
    
    // определение числа элементов выпадающего меню 
    count = SubMenu->GetMenuItemCount(); 
    
    // добавление разделителя 
    SubMenu->InsertMenu(count, MF_BYPOSITION | MF_SEPARATOR); 
    // добавление нового элемента меню 
    
    SubMenu->InsertMenu(count+l, MF_BYPOSITION I MF_STRING, 
    IDM_RESET, "SReset"); 
    
    // обновление состояния флагов 
    m_AddActive = 0; 
    m_DelActive = 1; 
    } 
    // Удаление элемента из меню 
    afx msg void CMainWin::OnDelete() 
    ( 
    CMenu *m MainMenu; // указатель на объект главного меню 
    CMenu *SubMenu; // указатель на объект подменю 
    unsigned count; 
    // получение указателя на объект главного меню 
    m_MainMenu = GetMenu(); 


    408                                     MFC: основы программирования 


    // получение указателя на объект первого выпадающего меню 
    SubMenu = m MainMenu->GetSubMenu(0); 
    
    // определение числа элементов выпадающего меню 
    count = SubMenu->GetMenuItemCount (); 
    
    // удаление нового элемента 
    SubMenu->DeleteMenu(count-1, MF_BYPOSITION) ; 
    
    // удаление разделителя 
    SubMenu->DeleteMenu(count-2, MF_BYPOSITION) ; 
    
    // обновление состояния флагов 
    m_AddActive =1; 
    m_DelActive = 0; 
    } 
    // Отображение элемента Add Item 
    afx_msg void CMainWin::OnUIAdd(CCmdUI *UI) 
    ( 
    UI->Enable(m_AddActive) ; 
    ) 
    
    // Отображение элемента Delete Item 
    afx_msg void CMainWin::OnUIDel(CCmdUI *UI) 
    { 
    UI->Enable(m_DelActive) ; 
    } 
    CApp App; // создание экземпляра приложения 


    Подробнее о работе программы 

    Когда программа начинает работу, в меню Options содержится только три 
элемента: Add Item, Delete Item и Exit. Первоначально опция Delete Item 
неактивна и, следовательно, не может быть выбрана. Чтобы добавить в это меню 
команду Reset, выберите опцию Add Item. Когда команда Reset добавлена, 
активизируется опция Delete Item, а опция Add Item становится неактивной. При 
выборе опции Delete Item команда Reset удаляется из меню, опция Add Item 
становится активной, а опция Delete Item — снова неактивной. Такая процедура 
предотвращает возможность того, что новый элемент будет добавлен или удален 
более одного раза. 
    Для опций Add Item и Delete Item в программе переопределены стандартные 
обработчики изменений, делающие данные опции активными или неактивными. В этих 
обработчиках используются переменные m_AddActive и m_DelActive класса 
CMainWin, хранящие состояние соответствующего им элемента меню. 
    Посмотрите внимательно на текст функции OnAddQ. Обратите внимание, каким 
образом следует получать указатель на выпадающее меню Options. Сначала с 
помощью функции GetMenuQ получают указатель на родительское меню, которым, в 
данном случае, является главное меню программы. Затем с помощью функции 
GetSubMenuQ получают указатель на первое выпадающее меню, т.е. меню Options. 

    75. Особые виды меню                                           409 

    Далее в программе вычисляется количество элементов выпадающего меню. С 
технической точки зрения такое действие здесь лишнее, поскольку это значение 
заранее известно. Тем более, что в данном случае можно было просто добавить 
элемент в конец подменю. Но эти действия приведены здесь для иллюстрации 
возможностей j работы с данными функциями. Затем с помощью функции 
Insertltem() программа i добавляет к выпадающему меню разделитель и команду 
Reset,                   i 


    Создание динамических выпадающих меню 

    Помимо добавления новых элементов к уже существующему меню, можно 
динамически создавать целые выпадающие меню. (Т.е. можно создавать выпадающее 
меню прямо во время работы приложения.) Когда такое меню создано, его можно 
добавить к существующему меню. Для динамического создания выпадающего меню 
используется функция CreatePopupMenu(), прототип которой таков: 

    BOOL CMenu::CreatePopupMenu(); 

    Эта функция создает пустое выпадающее меню. Она возвращает ненулевое ( 
значение при успешном завершении и ноль — в противном случае.              ' 
    Когда меню создано, в него нужно добавить элементы с помощью функции ; 
    InsertMenu(). Затем оно само должно быть добавлено к существующему меню, 
что также \ выполняется с помощью функции InsertMenu(). При этом необходимо 
установить флаг MFJPOPUP и в третьем параметре функции указать дескриптор 
выпадающего меню. Получить данный дескриптор очень легко, так как он находится 
в переменной | m_hMenu класса CMenu.                                           
    Меню, созданное с помощью функции CreatePopupMenu(), должно быть 
впоследствии удалено из памяти. Если меню связано с окном, то это произойдет 
автоматически. Меню также автоматически удаляется при вызове функции 
Deleteltem(). Кроме того, динамические меню могут быть удалены явно с помощью 
функции DestroyMenu(). 

    Работа с динамическими выпадающими меню 

    Следующая программа является усовершенствованной версией предыдущей 
программы. В ней динамически создается выпадающее меню, содержащее три 
элемента: Reset, Invert и Show Text. При выборе команды Invert содержимое окна 
инвертируется. Это выполняется путем вызова функции PatBItQ с использованием 
опции DSTINVERT. При выборе команды Show Text в левом верхнем углу окна 
отображается строка "Sample Text". Обратите особое внимание на то, как 
создается выпадающее меню и как оно добавляется к меню Options. 
    В программе используется тот же библиотечный файл IDS.H, что и в 
предыдущем примере. Вот текст файла ресурсов программы: 

    ; DYNMENU2.RC 
    ^include <afxres.h> 
    ftinclude "ids.h" 
    DynMenu MENU 
    ( 


    410                                    MFC: основы программирования 


    POPUP "SOptions" 
    ( 
    MENUITEM "&Add Pop-up\tF2", IDM_ADDITEM 
    MENUITEM "SDelete Pop-up\tF3", IDM_DELITEM 
    MENUITEM "E&xit\tF7", IDM_EXIT 
    > 
    POPUP "SGraphics" 
    ( 
    MENUITEM "&Lines\tF4", IDM_LINES 
    MENUITEM "&Rectangles\tF5", IDM_RECTANGLES 
    MENUITEM "&Ellipses\tF6", IDM_ELLIPSES 
    } 
    MENUITEM "SHelp", IDM_HELP 
    } 
    DynMenu ACCELERATORS 
    { 
    VK_F1, IDM_HELP, VIRTKEY 
    VK_F2, IDM_ADDITEM, VIRTKEY 
    VK_F3, IDM_DELITEM, VIRTKEY 
    VK_F4, IDM_LINES, VIRTKEY 
    VK_F5, IDM_RECTANGLES, VIRTKEY 
    VK_F6, IDM_ELLIPSES, VIRTKEY 
    VK_F7, IDM_EXIT, VIRTKEY 

    Это текст файла DYNMENU2.H, содержащего объявления порождаемых классов: 

    // DYNMENU2.H 
    // Это класс для создания главного окна 
    class CMainMin : public CFrameWnd 
    ( 
    
    CDC m memDC; // контекст устройства, связанный с 
    // виртуальным окном 
    CBitmap m bmp; // образ виртуального окна 
    CBrush m bkbrush; // кисть для виртуального окна 
    
    // объявление перьев 
    CPen m_RedPen, m YellowPen, m GreenPen, m BluePen; 
    CPen m_01dPen; 
    CMenu *m_MainMenu; // указатель на объект главного меню 
    
    // флаги для элементов меню Add Pop-up и Delete Pop-up 
    int m AddActive, m DelActive; 
    
    CMenu m Popup; // объект динамического меню 
    public: 
    CMainWinO ; 
    afx msg void OnPaintO; 


    15. Особые виды меню                                           411 


    // обработчики изменений в меню                                   f 
    afx_msg void CMainWin::OnUIAdd(CCmdUI *UI); 
    afx_msg void CMainWin::OnUIDel(CCmdOl *UI); 
    afx_msg void OnLinesO; 
    afx msg void OnRectangles(); 
    afx_msg void OnEllipses () ; 
    afx_msg void OnResetO;                                           | 
    afx_msg void OnAdd();                                             Я 
    afx_nisg void OnDeleteO;                                          у 
    afx_msg void OnDstInvert();                                       | 
    afx_msg void OnShowText();                                         | 
    afx msg void OnExitO; 
    afx msg void OnHelp(); 
    DECLARE_MESSAGE_MAP() 
    }; 
    // Это класс для создания приложения 
    class CApp : public CWinApp                                         ' 
    ( 
    public: 
    BOOL Initlnstance ();                                               i 
    }; 
    Ниже приведен текст самой программы: 
    // Использование динамического меню 
    
    #include <afxwin.h> 
    ^include "dynmenu2.h" 
    #include "ids.h" 
    int maxX, maxY; // размеры экрана                                    i 
    
    CMainWin::CMainWin() 
    ( 
    
    Create(NULL, "Demonstrate Dynamic Pop-up Menus",                   I 
    WS_OVERLAPPEDWINDOW, rectDefault, 
    NULL, "DynMenu") ; 
    
    // Загрузка таблицы быстрого доступа 
    if(!LoadAccelTable("DynMenu")) 
    MessageBox("Cannot Load Accelerators", "Error"); 
    maxX = GetSystemMetrics(SM_CXSCREEN) ; 
    maxY = GetSystemMetrics(SM_CYSCREEN) ; 
    CClientDC DC(this); 
    
    // создание образа виртуального окна 
    m_memDC.CreateCompatibleDC(&DC) ; 


    412                                    MFC: основы программирования 


    m bmp.CreateCompatibleBitmap(&DC, maxX, maxY); 
    m memDC.SelectObject(&m bmp); 
    
    // выбор стандартного фона 
    m_bkbrush.CreateStockObject(WHITE_BRUSH) ; 
    m_memDC.SelectObject(&m_bkbrush) ; 
    
    // закраска фона виртуального окна 
    m_memDC.PatBlt(0, 0, maxX, maxY, PATCOPY); 
    
    // создание перьев 
    m^RedPen.CreatePen(PS_SOLID, 1, RGB(255, 0, 0)) ; 
    m_GreenPen.CreatePen(PS_SOLID, 2, RGB(0, 255, 0) ) ; 
    m_BluePen.CreatePen(PS_SOLID, 3, RGB(О, О, 255)); 
    m_YellowPen.CreatePen(PS_SOLID, 4, RGB(255, 255, 0)) ; 
    m_01dPen.CreateStockObject(BLACK_PEN) ; 
    
    m AddActive =1; // инициализация флагов 
    m DelActive = 0; 
    t 
    
    // Инициализация приложения 
    BOOL CApp::Initlnstance() 
    ( 
    m_pMainWnd = new CMainWin; 
    m_pMainWnd->ShowWindow(m_nCmdShow) ; 
    m_pMainWnd->UpdateWindow() ; 
    return TRUE; 
    } 
    
    // Это очередь сообщений приложения 
    BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) 
    ON_WM_PAINT() 
    ON_UPDATE_COMMAND_UI(IDM_ADDITEM, OnUIAdd) 
    ON_UPDATE_COMMAND_UI(IDM_DELITEM, OnUIDel) 
    ON_COMMAND(IDM_LINES, OnLines) 
    ON_COMMAND(IDM_RECTANGLES, OnRectangles) 
    ON_COMMAND(IDM__ELLIPSES, OnEllipses) 
    ON_COMMAND(IDM_RESET, OnReset) 
    ON_COMMAND(IDM__ADDITEM, OnAdd) 
    ON_COMMAND(IDM__DELITEM, OnDelete) 
    ON_COMMAND(IDM_DSTINVERT, OnDstInvert) 
    ON_COMMAND(IDM_SHOWTEXT, OnShowText) 
    ON_COMMAND(IDM_EXIT, OnExit) 
    
    ON_COMMAND(IDM_HELP, OnHelp) 
    END_MESSAGE_MAP() 
    
    // Вывод на экран содержимого виртуального окна 
    afx msg void CMainWin::OnPaint() 
    { 


    15. Особые виды меню                                          413 


    CPaintDC DC(this); 
    DC.BitBlt(0, 0, maxX, maxY, &m_memDC, О, О, SRCCOPY); 
    ) 
    // Обработка сообщения IDM_EXIT 
    afx_msg void CMainWin::OnExit () 
    { 
    int response; 
    response = MessageBox("Quit the Program?", "Exit", MB YESNO); 
    if (response == IDYES) 
    SendMessage(WM_CLOSE); // завершение приложения 
    ) 
    
    // Обработка сообщения IDM HELP 
    afx_msg void CMainWin::OnHelp() 
    ( 
    MessageBox("Dynamic Pop-up Menus", "Help"); 
    } 
    
    // Отображение линий и точек 
    afx msg void CMainWin::OnLines() 
    { 
    //отображение четырех точек 
    m_memDC.SetPixel(40, 14, RGB(255, 0, 0)); 
    m_memDC.SetPixel(41, 14, RGB(0, 255, 0) ) ; 
    m_memDC.SetPixel(42, 14, RGB(0, 0, 255)); 
    m_memDC.SetPixel(43, 14, RGB(0, 0, 0)); 
    { 
    m_memDC.LineTo(100, 50);                                           '' 
    m_memDC.MoveTo(100, 50);                                           I 
    
    // изменение цвета пера на зеленое                                 { 
    m_memDC.SelectObject(&m_GreenPen);                                 ', 
    m_memDC.LineTo(200, 100); 
    
    // изменение цвета пера на желтое 
    m memDC.SelectObject(&m YellowPen) ; 
    m_memDC.LineTo(0, 200); 
    
    // изменение цвета пера на синее 
    m_memDC.SelectObject(&m_BluePen) ; 
    m_memDC.LineTo(200, 200); 
    
    // изменение цвета пера на красное 
    m_memDC.SelectObject(&m_RedPen) ; 
    m_memDC.LineTo(0, 0) ; 
    m_memDC.LineTo(100, 50); 
    m_memDC.MoveTo(0, 0) ; 


    414                                    MFC: основы программирования 


    m_memDC.LineTo(100, 250); 
    m memDC.MoveTo(0, 0) ; 
    m__memDC.LineTo(100, 350); 
    
    // восстановление первоначального пера 
    m_memDC.SelectObject(&m_01dPen) ; 
    m_memDC.Arc(0, 0, 300, 300, 0, 50, 200, 0) ; 
    
    //отображение пересекающихся линий, которые определяют дугу 
    m_mernDC .MoveTo (150, 150); 
    m_memDC.LineTo(0, 50); 
    m_memDC.MoveTo(150, 150); 
    m_memDC.LineTo(200, 0) ; 
    InvalidateRect(NULL) ; 
    } 
    II Отображение прямоугольников 
    afx msg void CMainWin::OnRectangles () 
    ( 
    CBrush HollowBrush; 
    
    // создание "пустого" заполнения 
    HollowBrush.CreateStockObject(HOLLOW_BRUSH) ; 
    m_memDC.SelectObject(sHollowBrush) ; 
    
    // отображение нескольких прямоугольников 
    m_memDC.Rectangle(50, 50, 300, 300); 
    m__memDC.RoundRect(125, 125, 220, 240, 15, 13); 
    
    // использование красного пера 
    m_meniDC. SelectObject (&m_RedPen) ; 
    m_memDC.Rectangle(100, 100, 200, 200); 
    m memDC.SelectObject(&m OldPen); // восстановление прежнего пера 
    
    // восстановление прежней кисти 
    m_memDC.SelectObject(&m_bkbrush) ; 
    InvalidateRect(NULL) ; 
    // Отображение эллипсов 
    afx msg void CMainWin::OnEllipses() 
    ( 
    CBrush Brush; 
    
    // создание синей кисти 
    Brush.CreateSolidBrush(RGB(0, 0, 255)); 
    m_memDC.SelectObject(SBrush) ; 
    
    // отображение эллипсов, заполненных синим цветом 
    m_memDC.Ellipse (50, 200, 100, 280); 


    15. Особые виды меню                                          415 


    m_memDC.Ellipse(75, 25, 280, 100); 
    
    // использование красного пера 
    m_meinDC. SelectObject(&m_RedPen) ; 
    // создание зеленой кисти 
    Brush.DeleteObject(); // удаление синей кисти 
    Brush.CreateSolidBrush(RGB(0, 255, 0)) ; 
    
    m memDC.SelectObject(&Brush); // выбор зеленой кисти 
    mJ-nernDC.Ellipse (100, 100, 200, 200); 
    
    // отображение сектора 
    m_memDC.Pie(200, 200, 340, 340, 225, 200, 200, 250); 
    
    m_inernDC . SelectObject (&m_01dPen) ; // восстановление прежнего пера 
    m memDC.SelectObject(&m bkbrush); // восстановление прежней кисти 
    InvalidateRect(NULL) ; 
    ) 
    // Очистка окна 
    afx_msg void CMainWin::OnReset() 
    < 
    m_memDC.PatBlt (0, 0, maxX, maxY, PATCOPY); 
    InvalidateRect(NULL) ; 
    // Отображение текста в окне 
    afx msg void CMainWin::OnShowText() 
    { 
    m_memDC.TextOut (1, 1, "Sample Text", 11); 
    InvalidateRect(NULL) ; 
    ) 
    // Инвертирование содержимого окна                                   ; 
    afx_msg void CMainWin::OnDstInvert() 
    (                                                                     ' 
    m_memDC.PatBlt(0, 0, maxX, maxY, DSTINVERT) ; 
    InvalidateRect(NULL) ; 
    } 
    
    // Добавление элемента в меню 
    afx msg void CMainWin::OnAdd() 
    ( 
    CMenu *m MainMenu; // указатель на объект главного меню 
    CMenu *SubMenu; // указатель на объект подменю 
    unsigned count; 
    
    // получение указателя на объект главного меню 
    m_MainMenu = GetMenuO; 
    
    // получение указателя на объект первого выпадающего меню 
    SubMenu = m MainMenu->GetSubMenu(0) ; 


    416                                     MFC: основы программирования 


    // определение числа элементов выпадающего меню 
    count = SubMenu->GetMenuItemCount(); 
    
    // создание нового выпадающего меню 
    m Popup.CreatePopupMenu(); 
    
    // добавление элементов в новое выпадающее меню 
    m_Popup.InsertMenu(О, MF_BYPOSITION | MF_STRING, 
    IDM_RESET, "bReset"); 
    m_Popup.InsertMenu(1, MF_BYPOSITION | MF_STRING, 
    IDM_DSTINVERT, "Sinvert Window"); 
    m_Popup.InsertMenu(2, MF_BYPOSITION | MF_STRING, 
    IDM_SHOWTEXT, "&Show Text"); 
    
    // добавление разделителя 
    SubMenu->InsertMenu (count, MF_BYPOSITION | MF_SEPARATOR); 
    
    // добавление выпадающего меню к меню Options 
    SubMenu->InsertMenu(count+1, MF_POPUP I MF_BYPOSITION, 
    (unsigned)m Popup.m hMenu, "&New Options"); 
    
    // обновление состояния флагов 
    m AddActive = 0; 
    m_DelActive = 1; 
    // Удаление элемента из меню 
    afx msg void CMainMin::OnDelete() 
    { 
    CMenu *m MainMenu; // указатель на объект главного меню 
    CMenu *SubMenu; // указатель на объект подменю 
    unsigned count; 
    
    // получение указателя на объект главного меню 
    m MainMenu = GetMenuO; 
    
    // получение указателя на объект первого выпадающего меню 
    SubMenu = m MainMenu->GetSubMenu(0); 
    
    // определение числа элементов выпадающего меню 
    count = SubMenu->GetMenuItemCount() ; 
    
    // удаление выпадающего меню 
    SubMenu->DeleteMenu(count-1, MF_BYPOSITION) ; 
    
    // удаление разделителя 
    SubMenu->DeleteMenu(count-2, MF_BYPOSITION) ; 
    
    // обновление состояния флагов 
    m AddActive = 1; 
    m DelActive = 0; 
    ) 
    // Отображение элемента Add Pop-up 


    15. Особые виды меню                                            417 


    afx_msg void CMainWin::OnUIAdd(CCmdUI *UI) 
    ( 
    UI->Enable(m_AddActive) ; 
    ) 
    
    // Отображение элемента Delete Pop-up 
    afxJnsg void CMainWin::OnUIDel(CCmdUI *UI) 
    ( 
    UI->Enable(m_DelActive) ; 
    ) 
    CApp App; // создание экземпляра приложения 

    Пример работы программы показан на рис. 15.2. 

    Рис. 15.2. Пример динамического создания выпадающего меню 

    Большая часть программы должна быть понятна. Не следует упускать, однако, 
один важный момент. Когда выпадающее меню создано, оно добавляется к меню 
Options путем вызова функции InsertMenu(). При этом устанавливается флаг 
MFJPOPUP, и в качестве третьего параметра функции передается дескриптор 
выпадающего меню. Как уже упоминалось, данный дескриптор хранится в переменной 
m_hMenu класса CMenu. Наконец, приведение значения дескриптора к типу unsigned 
необходимо для того, чтобы компилятор не генерировал сообщение о 
несоответствии типов. 

    418                                     MFC: основы программирования 


    Использование всплывающих меню 

    Хотя меню, располагаемые отдельно, или всплывающие, уже довольно давно 
известны программистам, работающим под Windows, лишь недавно эти меню 
приобрели особую важность как необходимый элемент интерфейса. Для них также 
было добавлено несколько новых возможностей. Интерес к всплывающим меню 
объясняется тем, что они теперь стали неотъемлемой частью интерфейса 
пользователя в Windows 95 и Windows NT. Например, при щелчке правой кнопки 
мыши рядом с любым элементом интерфейса активизируется всплывающее меню. А при 
щелчке правой кнопки мыши на рабочем столе появляется меню, позволяющее 
выполнять действия, связанные с рабочим столом. Поэтому в конце главы будет 
уместно поговорить о всплывающих меню. 

    Вызов всплывающего меню 

    Для вызова всплывающего меню используется функция TrackPopupMenu(). Ее 
прототип таков: 

    BOOL CMenu::TrackPopupMenu(UINT Flags, int X, int У, 
    CWnd *0wner, RECT * Limit s=WLL) ; 

    Параметр Flags может содержать любую доступную (т.е. не противоречащую 
самой себе) комбинацию значений, из числа приведенных в табл. 15.2. 

    Таблица 15.2. Значения параметра Flags функции Track Popup Мепи() 

    Константа__________Смысл_____________________________ 

    TPM_CENTERALIGN    Всплывающее меню отображается центрировано относительно 
координаты Х 
    TPM_LEFTALIGN       Всплывающее меню отображается слева относительно 
координаты Х (это установка по умолчанию) 
    TPM_LEFTBUTTON      Выбор элементов всплывающего меню осуществляется по 
нажатию левой кнопки мыши (это установка по умолчанию) 
    TPM_RIGHTALIGN      Всплывающее меню отображается справа относительно 
координаты Х 
    TPM_RIGHTBUTTON    Выбор элементов всплывающего меню осуществляется по 
нажатию правой кнопки мыши____________ 

    В качестве значения параметра Flags можно указывать 0. При этом будут 
использованы установки по умолчанию. 
    Координаты экрана, относительно которых должно отображаться всплывающее 
меню, содержатся в параметрах Х и К Следовательно, данные координаты должны 
указываться в экранных единицах, а не в единицах, установленных для главного 
или диалогового окна. Для преобразования координат из одной системы вдругую 
необходимо использовать функцию CWnd::ClientToScreen() или функцию 
CWnd::ScreenToClient(). По умолчанию функция TrackPopupMenu () отображает 
всплывающее меню таким образом, что его верхний левый угол находится в точке с 
координатами, определяемыми параметрами Хч Y. Но с помощью параметра Flags 
можно менять это расположение. 
    Указатель на объект окна, которому принадлежит всплывающее меню, 
передается в параметре Owner. 
    Всплывающее меню автоматически удаляется, когда пользователь выполняет 
щелчок мыши за пределами области, занимаемой меню. По умолчанию размер этой 


    75. Особые виды меню                                           419 


    области равен размеру самого меню. Чтобы установить другие значения 
размера, их необходимо задать в структуре типа RECT, на которую указывает 
параметр Limits. Если он равен NULL (или не указан), то всплывающее меню будет 
удаляться с экрана при щелчке мыши в любой области экрана вне меню. 

    Загрузка меню 

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

    BOOL CMenu::LoadMenu(LPCSTR IpszMenuName); 
    BOOL CMenu::LoadMenu(UINT MenuID); 

    Здесь параметр IpszMenuName является указателем на строку, содержащую имя 
меню. Во втором случае меню выбирается с помощью идентификатора, содержащегося 
в параметре MenuID. Функция возвращает ненулевое значение при успешном 
завершении и ноль — в противном случае. Когда меню загружено, оно становится 
частью вызвавшего его объекта типа CMenu. 
    Загруженное меню, которое не является частью окна, должно уничтожаться 
путем вызова функции CMenu::Destroy(), если больше не используется. 

    Работа со всплывающими меню                      j 

    В следующей программе меню Graphics из предыдущего примера преобразовано \ 
во всплывающее меню. Таким образом, оно больше не является частью главного 
меню и вызывается по нажатию правой кнопки мыши. При этом всплывающее меню 
отображается в точке, где произошел щелчок мыши. Пример работы программы 
показан на рис. 15.3. 

    Рис. 5.3. Пример использования всплывающего меню. 

    420                                          MFC: основы программирования 

    В программе используется тот же библиотечный файл 1DS.H, что и ранее. 
Текст файла ресурсов приведен ниже. Важно, что здесь меню Graphics определено 
отдельно от главного меню. Таким образом, оно не будет отображаться при вызове 
главного меню. 

    ; DYNMENU3.RC 
    #include <afxres.h> 
    #include "ids.h" 
    DynMenu MENU 
    < 
    POPUP "{.Options" 
    ( 
    MENUITEM "&Add Pop-up\tF2", IDM_ADDITEM 
    MENUITEM "SDelete Pop-up\tF3", IDM_DELITEM 
    MENUITEM "E?xit\tF7", IDM_EXIT 
    } 
    
    MENUITEM "&Help", IDM_HELP 
    ) 
    ; Это меню будет всплывающим 
    GraphMenu MENU 
    ( 
    POPUP "SGraphics" 
    
    MENUITEM "&Lines\tF4", IDM_LINES 
    MENUITEM "&Rectangles\tF5", IDM_RECTANGLES 
    MENUITEM "&Ellipses\tF6", IDM_ELLIPSES 
    } 
    DynMenu ACCELERATORS 
    ( 
    VK_F1, IDM_HELP, VIRTKEY 
    VK_F2, IDM_ADDITEM, VIRTKEY 
    VK_F3, IDM_DELITEM, VIRTKEY 
    VK_F4, IDM_LINES, VIRTKEY 
    VK_F5, IDM_RECTANGLES, VIRTKEY 
    VK_F6, IDM_ELLIPSES, VIRTKEY 
    VK_F7, IDM_EXIT, VIRTKEY 
    } 
    Вот текст файла DYNMENLJ3.H, содержащего определения порождаемых классов: 
    // DYNMENU3.H 
    // Это класс для создания главного окна 
    class CMainWin : public CFrameWnd 
    { 
    CDC m_memDC; // контекст устройства, связанный с 
    // виртуальным окном 


    15. Особые виды меню                                          421 


    CBitmap m_bmp; // образ виртуального окна 
    CBrush m_bkbrush; // кисть для виртуального окна 
    
    // объявление перьев 
    CPen m_RedPen, m_YellowPen, m_GreenPen, m_BluePen; 
    CPen m_01dPen; 
    CMenu *m_MainMenu; // указатель на объект главного меню 
    
    // флаги для элементов меню Add Pop-up и Delete Pop-up 
    int m AddActive, m DelActive; 
    
    CMenu m_Popup; // объект динамического меню 
    CMenu m_FloatMenu; // объект всплывающего меню 
    public: 
    CMainWin() ; 
    afx msg void OnPaintO; 
    afx_msg void OnRButtonDown(UINT Flags, CPoint Loc) ; 
    
    // обработчики изменений в меню 
    afx_msg void CMainWin::OnUIAdd(CCmdUI *UI) ; 
    afx_msg void CMainWin::OnUIDel(CCmdUI *UI) ; 
    afx_msg void OnLinesO; 
    afx_msg void OnRectangles(); 
    afx msg void OnEllipses (); 
    afx msg void OnReset(); 
    afx msg void OnAdd() ; 
    afx_msg void OnDelete() ; 
    afx msg void OnDstInvert() ; 
    afx msg void OnShowText(); 
    afx_msg void OnExitO; 
    afx msg void OnHelpO; 
    DECLARE_MESSAGE_MAP() 
    }; 
    
    // Это класс для создания приложения 
    class CApp : public CWinApp 
    { 
    public: 
    BOOL Initlnstance () ; 
    ); 
    Это текст самой программы: 
    // Использование всплывающего меню 
    #include <afxwin.h> 
    #include "dynmenu3.h" 
    #include "ids.h" 


    422                                    MFC: основы программирования 


    int maxX, maxY; // размеры экрана 
    CMainWin::CMainWin() 
    ( 
    Create(NULL, "Demonstrate Floating Pop-up Menus", 
    WS_OVERLAPPEDWINDOW, rectDefault, 
    NULL, "DynMenu"); 
    
    // Загрузка таблицы быстрого доступа 
    if(!LoadAccelTable("DynMenu")) 
    MessageBox("Cannot Load Accelerators", "Error"); 
    maxX = GetSystemMetrics(SM_CXSCREEN); 
    maxY = GetSystemMetrics(SM_CYSCREEN); 
    CClientDC DC(this) ; 
    
    // создание образа виртуального окна 
    m_memDC.CreateCompatibleDC(&DC) ; 
    m bmp.CreateCompatibleBitmap(&DC, maxX, maxY); 
    m memDC.SelectObject(&m bmp) ; 
    
    // выбор стандартного фона 
    m_bkbrush.CreateStockObject(MHITE_BRUSH) ; 
    m memDC.SelectObject(&m bkbrush); 
    
    // закраска фона виртуального окна 
    m_memDC.PatBlt(0, 0, maxX, maxY, PATCOPY); 
    
    // создание перьев 
    m_RedPen.CreatePen(PS_SOLID, I, RGB(255, 0, 0) ) ; 
    m_GreenPen.CreatePen(PS_SOLID, 2, RGB(0, 255, 0)) ; 
    m_BluePen.CreatePen(PS_SOLID, 3, RGB(0, 0, 255)); 
    m_YellowPen.CreatePen(PS_SOLID, 4, RGB(255, 255, 0)); 
    m_01dPen.CreateStockObject(BLACK_PEN) ; 
    m AddActive =1; // инициализация флагов 
    m DelActive = 0; 
    1 
    
    // Инициализация приложения 
    BOOL CApp::Initlnstance () 
    ( 
    m pMainWnd = new CMainWin; 
    m_pMainWnd->ShowWindow(m_nCmdShow) ; 
    m pMainWnd->UpdateWindow() ; 
    return TRUE; 
    1 
    
    // Это очередь сообщений приложения 
    BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) 


    75. Особые виды меню                                           423 


    ON_WM_PAINT () 
    ON_WM_RBUTTONDOWN() 
    
    ON_UPDATE_COMMAND_UI(IDM_ADDITEM, OnUIAdd) 
    ON_UPDATE_COMMAND_UI(IDM_DELITEM, OnUIDel) 
    ON_COMMAND(IDM_LINES, OnLines) 
    ON_COMMAND(IDM_RECTANGLES, OnRectangleS) 
    ON_COMMAND(IDM_ELLIPSES, OnEllipses) 
    ON_COMMAND(IDM_RESET, OnReset) 
    ON_COMMAND(IDM_ADDITEM, OnAdd) 
    ON_COMMAND(IDM_DELITEM, OnDelete) 
    ON_COMMAND(IDM_DSTINVERT, OnDstInvert) 
    ON_COMMAND(IDM_SHOWTEXT, OnShowText) 
    ON_COMMAND(IDM_EXIT, OnExit) 
    ON_COMMAND(IDM_HELP, OnHelp) 
    END_MESSAGE_MAP() 
    
    // Вывод на экран содержимого виртуального окна 
    afx msg void CMainWin::OnPaint () 
    i 
    CPaintDC DC(this) ; 
    DC.BitBltfO, 0, maxX, maxY, &m_memDC, О, О, SRCCOPY); 
    ) 
    
    // Обработка сообщения IDM_EXIT 
    afx msg void CMainWin::OnExit() 
    (  -                                                               ! 
    int response; 
    response = MessageBox ("Quit the Program?", "Exit", MB_YESNO); 
    if(response == IDYES)                                             ' 
    SendMessage(WM CLOSE); // завершение приложения 
    i 
    // Обработка сообщения IDM_HELP                                     ; 
    afx_msg void CMainWin::OnHelp()                                      [ 
    ( 
    MessageBox("Floating Pop-up Menus", "Help"); 
    } 
    
    // Отображение линий и точек                                        i 
    afx msg void CMainWin::OnLines()                                    '• 
    { 
    //отображение четырех точек 
    m_memDC.SetPixel (40, 14, RGB(255, 0, 0)) ; 
    m_memDC.SetPixel(41, 14, RGB(0, 255, 0)) ; 
    m_memDC.SetPixel (42, 14, RGB(0, 0, 255)); 
    m_memDC.SetPixel (43, 14, RGB(0, 0, 0)) ; 
    m_memDC.LineTo(100, 50); 
    m_memDC.MoveTo(100, 50); 
    // изменение цвета пера на зеленое 


    424                                     MFC: основы программирования 


    m_memDC.Select0bject(&m Green Pen); 
    ni_memDC.LineTo(200, 100); 
    
    // изменение цвета пера на желтое 
    m memDC.SelectObject(Srn YellowPen); 
    m_memDC.LineTo(0, 200) ;~ 
    
    // изменение цвета пера на синее 
    rn_memDC.SelectObject(&m_BluePen) ; 
    m_memDC.LineTo(200, 200); 
    
    // изменение цвета пера на красное 
    m_memDC.SelectObject<&m_RedPen) ; 
    m_memDC.LineTo(О, 0); 
    m_memDC.LineTo(100, 50); 
    m_memDC.MoveTo(0, 0) ; 
    m_memDC.LineTo(100, 250); 
    m_memDC.MoveTo(О, О); 
    m_memDC.LineTo(100, 350); 
    
    // восстановление первоначального пера 
    m_memDC.SelectObject(&m_01dPen) ; 
    m_memDC.Arc(0, 0, 300, 300, 0, 50, 200, 0) ; 
    
    //отображение пересекающихся линий, которые определяют дугу 
    m_memDC.MoveTo(150, 150); 
    m_memDC.LineTo(0, 50); 
    m_memDC.MoveTo(150, 150); 
    m_memDC.LineTo(200, 0) ; 
    InvalidateRect(NULL) ; 
    ) 
    
    // Отображение прямоугольников 
    afx_msg void CMainWin::OnRectangles() 
    ( 
    CBrush HollowBrush; 
    
    // создание "пустого" заполнения 
    HollowBrush.CreateStockObject(HOLLOW_BRUSH) ; 
    m_memDC.SelectObject(SHollowBrush) ; 
    
    // отображение нескольких прямоугольников 
    m_memDC.Rectangle(50, 50, 300, 300); 
    m_memDC.RoundRect(125, 125, 220, 240, 15, 13); 
    
    // использование красного пера 
    m_memDC.SelectObject(&m_RedPen) ; 
    m_memDC.Rectangle(100, 100, 200, 200); 
    m memDC.SelectObject(&m OldPen); // восстановление прежнего пера 


    15. Особые виды меню                                           425 


    // восстановление прежней кисти 
    m_memDC.SelectObject(&m_bkbrush) ; 
    InvalidateRect(NULL) ; 
    }                                                                          
                                                     ] 
    // Отображение эллипсов 
    afx_msg void CMainWin::OnEllipses0 
    ( 
    CBrush Brush; 
    
    // создание синей кисти 
    Brush.CreateSolidBrush(RGB (0, 0, 255)); 
    m_memDC.SelectObject(SBrush) ; 
    
    // отображение эллипсов, заполненных синим цветом 
    m_memDC.Ellipse(50, 200, 100, 280); 
    m_memDC.Ellipse(75, 25, 280, 100); 
    
    // использование красного пера 
    m_memDC.SelectObject(&m_RedPen) ; 
    // создание зеленой кисти 
    Brush.DeleteObject(); // удаление синей кисти 
    Brush.CreateSolidBrush(RGB(0, 255, 0)) ; 
    
    m_memDC.SelectObject(&Brush); // выбор зеленой кисти 
    m_memDC.Ellipse(100, 100, 200, 200); 
    
    // отображение сектора 
    m_memDC.Pie(200, 200, 340, 340, 225, 200, 200, 250); 
    
    m_memDC.SelectObject(&m_01dPen); // восстановление прежнего пера 
    m_memDC.SelectObject(&m_bkbrush); // восстановление прежней кисти 
    InvalidateRect(NULL) ; 
    ) 
    // Очистка окна 
    afx msg void CMainWin::OnReset() 
    { 
    m_memDC.PatBlt(0, 0, maxX, maxY, PATCOPY) ; 
    InvalidateRect(NULL) ; 
    ) 
    
    // Отображение текста в окне 
    afx_msg void CMainWin::OnShowText() 
    ( 
    m_memDC.TextOut(1, 1, "Sample Text", 11); 
    InvalidateRect(NULL) ; 
    ) 
    // Инвертирование содержимого окна 


    426                                     MFC: основы программирования 


    afx_msg void CMainWin::OnDstInvert() ( 
    m_memDC.PatBlt(0, 0, maxX, maxY, DSTINVERT); 
    InvalidateRect(NULL) ; 
    } 
    
    // Добавление элемента в меню 
    afx msg void CMainWin::OnAdd() 
    ( 
    CMenu *m MainMenu; // указатель на объект главного меню 
    CMenu *SubMenu; // указатель на объект подменю 
    unsigned count; 
    
    // получение указателя на объект главного меню 
    m MainMenu = GetMenuf); 
    
    // получение указателя на объект первого выпадающего меню 
    SubMenu = m__MainMenu->GetSubMenu(0); 
    
    // определение числа элементов выпадающего меню 
    count = SubMenu->GetMenuItemCount() ; 
    
    // создание нового выпадающего меню 
    m_Popup.CreatePopupMenu() ; 
    
    // добавление элементов в новое выпадающее меню 
    m_Popup.InsertMenu(0, MF_BYPOSITION | MF_STRING, 
    IDM_RESET, "SReset"); 
    m_Popup.InsertMenu(l, MF_BYPOSITION | MF_STRING, 
    IDM_DSTINVERT, "&Invert Window"); 
    m_Popup.InsertMenu(2, MP_BYPOSITION | MF_STRING, 
    IDM_SHOWTEXT, "SShow Text"); 
    
    // добавление разделителя 
    SubMenu->InsertMenu(count, MF_BYPOSITION | MF_SEPARATOR) ; 
    
    // добавление выпадающего меню к меню Options 
    SubMenu->InsertMenu(count+1, MF_POPUP | MF_BYPOSITION, 
    (unsigned)m Popup.m hMenu, "&New Options"); 
    
    // обновление состояния флагов 
    m AddActive = 0; 
    m DelActive =1; 
    } 
    // Удаление элемента из меню 
    afx msg void CMainWin::OnDelete() 
    { 
    CMenu *m MainMenu; // указатель на объект главного меню 
    CMenu *SubMenu; // указатель на объект подменю 
    unsigned count; 


    15. Особые виды меню                                          427 


    // получение указателя на объект главного меню 
    m MainMenu = GetMenu(); 
    
    // получение указателя на объект первого выпадающего меню 
    SubMenu = m MainMenu->GetSubMenu(0); 
    
    // определение числа элементов выпадающего меню 
    count = SubMenu->GetMenuItemCount(); 
    
    // удаление выпадающего меню 
    SubMenu->DeleteMenu(count-l, MF_BYPOSITION) ; 
    
    // удаление разделителя 
    SubMenu->DeleteMenu(count-2, MF_BYPOSITION) ; 
    
    // обновление состояния флагов 
    m AddActive = 1; 
    m_DelActive = 0; 
    
    // Отображение элемента Add Pop-up 
    afx_msq void CMainWin::OnUIAdd(CCmdUI *UI) 
    ( 
    UI->Enable(m_AddActive) ; 
    ( 
    
    // Отображение элемента Delete Pop-up 
    afx_msg void CMainWin::OnUIDel(CCmdUI *UI) 
    { 
    UI->Enable(m_DelActive) ; 
    // Вызов всплывающего меню 
    afx_msg void CMainWin : .-OnRButtonDown (UINT Flags, CPoint Loc) 
    { 
    CMenu *SubMenu; // указатель на объект подменю 
    ClientToScreen(&Loc) ; 
    m FloatMenu.LoadMenu("GraphMenu") ; 
    SubMenu = m_FloatMenu.GetSubMenu(0) ; 
    SubMenu->TrackPopupMenu(0, Loc.x, Loc.y, this); 
    m_FloatMenu.DestroyMenu() ; 
    CApp App; // создание экземпляра приложения 


    428                                     MFC: основы программирования 


    Подробнее о работе программы 

    Большая часть программы не отличается от ее предыдущей версии. Необходимо 
обратить внимание только на текст обработчика OnRButtonDown(). Этот обработчик 
используется для вызова меню Graphics, которое будет отображаться таким 
образом, чтобы его верхний левый угол находился в точке, где произошел щелчок 
правой кнопки мыши. Но поскольку координаты, задаваемые в функции 
TrackPopupMenu(), должны указываться в экранных единицах, то в программе 
требуется выполнить преобразование координат курсора мыши (переданных 
обработчику в системе единиц окна) в экранные координаты, для чего вызывается 
функция ClientToScreen(). После этого меню загружается в память с помощью 
функции LoadMenuQ, а затем определяется указатель на его первое (и 
единственное) выпадающее меню. Когда перечисленные действия выполнены, 
всплывающее меню отображается. 

    Эксперименты с меню 

    Примеры данной главы демонстрируют лишь малую часть того, что можно делать 
с меню. Лучший способ узнать о других возможностях меню — это 
поэкспериментировать. Попробуйте, например, при обработке изменений в меню 
вызвать функцию CCmdUI::SetRadio(). Можно также поэкспериментировать с 
различными опциями функции TrackPopupMenuO или указать расширенную область для 
всплывающего меню. 
    Вот еще один простой эксперимент. В первом примере данной главы команда 
Reset добавляется и удаляется из меню Option программным путем в ответ на 
действия пользователя. Такой подход использован только ради иллюстрации. Но 
можно выполнять то же самое действие и автоматически. Например, если окно 
пусто (в момент запуска программы), не отображайте команду Reset, а когда 
пользователь нарисует что-либо в окне, активизируйте ее. Когда пользователь 
сотрет содержимое окна, сделайте команду Reset неактивной. Автоматическое 
управление командой Reset демонстрирует то, как динамические меню используются 
в современных приложениях. 
    Хотя меню кажутся чем-то устаревшим по сравнению с новыми возможностями 
Windows, они по-прежнему являются основой большинства приложений. Помните, 
что, как правило, пользователи взаимодействуют с меню чаще, чем с любым другим 
элементом программы. Поэтому создание меню требует особого внимания и 
тщательной проработки. 
    Как уже упоминалось, всплывающие меню были доступны еще в ранних версиях 
Windows, но лишь с появлением Windows 95 им стало придаваться особое значение 
как неотъемлемому элементу интерфейса. Поэкспериментируйте со всплывающими 
меню, чтобы определить, как наилучшим образом использовать их в своих приложениях. 

    15. Особые виды меню                                           429