    Окна сообщений и меню 

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

    63 

    В этой главе рассматриваются также ресурсы. По сути, ресурс — это объек 
определенный вне программы, но используемый в ней. Пиктограммы, курсоры, мен] 
и растровые образы — все это ресурсы. Они являются неотъемлемой частью все 
приложений Windows. 

    Окна сообщений 

    Фактически, простейшим диалоговым окном является окно сообщения. Он 
выводит сообщение пользователю и ожидает подтверждения. Можно создавать окн 
сообщений, в которых пользователю предоставляется возможность выбора из не 
скольких основных вариантов, но главное назначение таких окон — проинформиро 
вать пользователя о том, что произошло некое событие. 
    Для создания окна сообщения используется функция MessageBoxQ, являющаяо 
членом класса CWnd. Ее прототип таков: 

    int CWnd::MessageBox(LPCSTR IpszText, 
    LPCSTR lpszTitle=WLl,, MINT MBType=MB_OK) ; 

    Здесь параметр Ips^Text — это указатель на строку, которая появится в окн< 
сообщения. Параметр ipszTifle указывает на строку заголовка окна. Значение 
МВТур« определяет вид окна сообщения, включая тип кнопок и пиктограмм, которые 
буду] отображены в окне. Некоторые из наиболее распространенных значений 
представлены в табл. 4.1. Эти константы определены в файле AFXWIN.H, и при 
вызове функции MessageBoxQ можно с помощью операции побитового ИЛИ объединять 
несколько из них, так как они не являются взаимоисключающими. 

    Таблица 4.1. Наиболее распространенные значения параметра МВТуре 

    Значение	Результат 

    MBABORTRETRYIGNORE	Отображаются кнопки Abort, Retry и Ignore 
    MBJCONEXCLAMATION	Отображается пиктограмма с восклицательным знаком 
    MBJCONINFORMATION	Отображается информационная пиктограмма с буквой "I" 
    MBHAND	Отображается пиктограмма с надписью "Stop" 
    MBJCONQUESTION	Отображается пиктограмма с вопросительным знаком 
    MBJCONSTOP	То же, что и MBHAND 
    MBOK	Отображается кнопка ОК 
    MBOKCANSEL	Отображаются кнопки ОК и Cansel 
    MBRETRYCANCEL	Отображаются кнопки Retry и Cansel 
    MBYESNO	Отображаются кнопки Yes и No 
    MBYESNOCANSEL	Отображаются кнопки Yes, No и Cansel 


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

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

    Таблица 4.2. Значения, возвращаемые функцией MessageBox() 

    Нажатая кнопка                   Возвращаемое значение 

    Abort                           IDABORT 
    Retry                             IDRETRY 
    Ignore                              IDIGNORE 
    Cancel                             IDCANCEL 
    No                             IDNO 
    Yes                             I DYES 
    OK         ______________IDOK________________ 

    Помните, что в зависимости от значения МВТуре в окне будут представлены 
лишь некоторые кнопки, поэтому для конкретного окна функция может возвращать 
только определенные значения. 
    Для отображения окна сообщения необходимо просто вызвать функцию 
MessageBox(). Windows, отобразит его при первой возможности. Для этого не 
нужно получать контекст устройства или ждать сообщения WM_PAINT. Все действия 
' вьГполняет сама функция MessageBoxQ. Поскольку окна сообщений использовать 
достаточно просто, они являются прекрасным отладочным средством, облегчая 
вывод информации на экран. 
    В следующем коротком примере при нажатии кнопки мыши выводится окно 
сообщения. В начале приведен текст файла, содержащего объявления порождаемых классов. 

    // MESSBOX.H 
    // Это класс для создания главного окна 
    class CMainWin : public CFrameWnd 
    { 
    public: 
    CMainWin () ; 
    afx_msg void OnLButtonDown(UINT flags, CPoint loc); 
    afx_msg void OnRButtonDown(UINT flags, CPoint loc); 
    DECLARE_MESSAGE_MAP() 
    ); 
    // Это класс для создания приложения 
    class CApp : public CWinApp 
    ( 
    public: 
    BOOL Initlnstance() ; 
    ); 


    4. Окна сообщений и меню                                       65 


    Вот текст программы: 
    // Вывод окна сообщения 
    #include <afxwin.h> 
    #include "messbox.h" 
    CMainWin::CMainWin() 
    ( 
    Create(NULL, "Using Message Boxes"); 
    ) 
    // Инициализация приложения 
    BOOL CApp::Initlnstance<) 
    1 
    m pMainWnd = new CMainWin; 
    m pMainWnd->ShowWindow(m_nCmdShow) ; 
    m pMainWnd->UpdateWindow (); 
    return TRUE; 
    ( 
    // Это очередь сообщений приложения 
    BEGIN_MESS^GE_MAP (.CMainMin, CFrarne^nd) 
    ON_WM_LBUTTONDOWN() 
    ON_WM_RBUTTONDOWN 0 
    END_MESSAGE_MAP() 
    // Обработка нажатия левой кнопки мыши 
    afx msg void CMainWin::OnLButtonDown(UINT flags, CPoint loc) 
    { 
    int i; 
    i = MessageBox ("Press One", "Left Button", 
    MB_ABORTRETRYIGNORE) ; 
    swith(i)  ( 
    case IDABORT: 
    MessageBox ("", "Abort"); 
    break; 
    case IDRETRY: 
    MessageBox ("", "Retry"); 
    break; 
    case IDIGNORE: 
    MessageBox ("", "Ignore"); 
    break; 
    \ 
    i 
    // Обработка нажатия правой кнопки мыши 
    afx_msg void CMainWin::OnRButtonDown(UINT flags, CPoint loc) 
    { 


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


    int i; 
    >    i = MessageBox("Press One", "Right Button", 
    MB__ICONSTOP | MB_YESNO) ; 
    swith(i)  ( 
    case IDYES: 
    MessageBox ("", "Yes"); 
    breaks
    case IDNO: 
    MessageBoxC'", "No"); 
    break; 
    CApp App; // создание экземпляра приложения 

    При каждом нажатии кнопки мыши будет выводиться окно сообщения. Например, 
при нажатии левой кнопки выводится окно сообщения, показанное на рис. 4.1. Как 
видите, это окно содержит кнопки Abort, Retry и Ignore. При нажатии 
пользователем одной из них будет выведено второе окно сообщения, информирующее 
о том, какая кнопка была нажата. При нажатии правой кнопки мыши появится окно 
сообщения, содержащее пиктограмму с надписью "Stop". В этом окне пользователь 
может выбрать "Yes" или "No". 

    Рис. 4.1. Окно сообщения 


    Использование функции AfxMessageBox() 

    Иногда требуется вывести окно сообщения до того, как будет создано главное 
окно приложения. Например, при создании главного окна может произойти ошибка. 
В этом случае нужно проинформировать пользователя о возникшей проблеме. Для 
отображения окна сообщения раньше (или независимо от) главного окна необходимо 
использовать глобальную функцию Af\MessageBox(). Она имеет такой прототип: 

    int AfxMessageBox(LPSTR IpszText, UINT MBType=MQ_OK, 
    UINT HelpID^O); 

    Здесь параметр IpszText указывает на строку сообщения, выводимую в окне. 
Заголовком окна автоматически станет имя приложения, и переопределить это 
нельзя. Параметр МВТуре означает то же, что и в стандартной функции 
MessageBoxQ. 1 Параметр HelpID содержит идентификатор раздела контекстной 
помощи, связанного с окном сообщения. Если такового нет, то, по умолчанию, 
параметр равен нулю. 

    4. Окна сообщений и меню                                        67 

    (Система помощи Windows будет рассмотрена позднее.) Функция 
AfxMessageBox() возвращает ноль, если окно не может быть отображено. В 
противном случае возвращается информация о выборе, сделанном пользователем. 
    Ниже приводится измененная версия функции InitInstanceQ, в которой окно 
сообщения выводится до создания главного окна. Замените текст этой функции в 
предыдущем примере и проверьте результат. Вы увидите, что окно сообщения будет 
отображено до появления главного окна. 

    // Инициализация приложения BOOL САрр::Initlnstance () 
    { 
    // окно сообщения отображается до создания главного окна 
AfxMesssaqeBox("This is displayed first."); 
    m_pMainWnd = new CMainWin; 
    m_pMainWnd->ShowWindow(m_nCmdShow) ; 
    m_pMainWnd->UpdateWindow() ; 
    return TRUE; 

    \ 

    Прежде чем читать дальше, попробуйте поэкспериментировать с различными 
типами окон сообщений. 

    Работа с меню 

    В Windows широко распространенным элементом управления является меню. 
Фактически, с каждым главным окном связан определенный тип меню. Поскольку 
меню являются столь важным элементом приложений, в Windows имеются 
многочисленные встроенные средства их поддержки. Для создания меню необходимо 
выполнить следующие три относительно простых действия: 
    1. Определить вид меню в файле ресурсов. 
    2. Загрузить меню при создании главного окна приложения. 
    3. Обработать команды выбора пунктов меню. 
    Меню верхнего уровня в Windows отображается в виде линейки меню вдоль 
верхней части окна. Подменю отображаются в виде выпадающих меню. (Вам должен 
быть знаком такой принцип, потому что он используется во всех Windows-программах.) 
    Перед тем как приступить к подробному рассмотрению меню, необходимо 
выяснить, что такое ресурсы и файлы ресурсов. 

    Использование ресурсов 

    В Windows некоторые наиболее распространенные типы объектов определены как 
ресурсы. В 'начале" "главьГ уже упоминалось, что ресурсы, по сути, являются 
объектами, которые используются в программе, но определены вне ее. К ним 
относятся меню, пиктограммы, диалоговые окна и растровые изображения. 
Поскольку меню является ресурсом, необходимо разобраться с этим вопросом подробнее. 

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

    Ресурсы создаются отдельно от программы и добавляются в ЕХЕ-файл на этапе 
компиляции. Их описание содержится в так называемом файле ресурсов, который 
имеет расширение RC. Обычно имя файла ресурсов совпадает с именем 
соответствующего ЕХЕ-файла. Например, если программа называется PROG.EXE, то 
файл ресурсов должен называться PROG.RC. Конечно, никто не запрещает вам 
использовать и произвольное имя. Следует помнить, что с программой может быть 
связано несколько файлов ресурсов. 
    В зависимости от типа ресурса некоторые из них могут быть описаны с 
помощью текстовых определений. Например, стандартное меню является текстовым 
ресурсом. Текстовые ресурсы обычно полностью определяются в самом файле 
ресурсов. Другие, такие как пиктограммы, являются графическими ресурсами и 
создаются с помощью графических редакторов. Однако они также должны быть 
указаны в RC-файле программы. Кроме того, можно автоматизировать построение 
файла ресурсов с помощью редактора ресурсов, который поставляется с 
большинством программных оболочек. Он можеТ'оказаться очень полезным в 
реальных приложениях, где число ресурсов весьма велико. 
    Иногда возникает необходимость обращаться к различным константам, 
определенным в Windows. С этой целью файл ресурсов должен включать файл AFXRES.H. 

    Компиляция RC-файлов 

    Файлы ресурсов не являются файлами, написанными на С или C++. Вместо этого 
используется специальный язык описания ресурсов, или сценарий, который должен 
компилироваться с помощью компилятора ресурсов. Чаще всего компилятор 
называется RC.EXE. Как именно компилируется файл ресурсов, зависит от 
используемого компилятора. Кроме того, многие программные оболочки 
автоматически выполняют этот этап за вас. В любом случае результатом работы 
компилятора ресурсов является файл с расширением RES, который компонуется с 
программой при создании готового приложения. 
    Помните: Всегда необходимо уточнять в прилагаемом к компилятору 
руководстве пользователя, как правильно использовать файлы ресурсов в программе. 

    Создание простого меню 

    Перед включением меню в программу необходимо определить его вид в файле 
ресурсов. Все определения меню имеют такую структуру: 

    Имя-меню MENU [Опции] 1 элементы-меню 

    Здесь параметр Имя-меню обозначает имя меню. (Это может быть просто целое 
число, идентифицирующее меню, но во всех примерах в книге при ссылке на меню 
будет использоваться его имя.) Оператор MENU указывает компилятору ресурсов на 
то, что создается меню. При этом могут быть заданы несколько опций. Наиболее 
распространенные опции приведены в табл. 4.3. В примерах книги используются 
установки по умолчанию и опции не задаются. 

    4. Окна сообщений и меню                                       69 


    Таблица 4.3. Опции MENU 

    Опция              Значение 

    DISCARDABLEFIXED LOADONCALL MOVEABLE PRELOAD	Меню может быть удалено из 
памяти, если больше не используетсяМеню постоянно находится в памяти Меню 
загружается при обращении Меню может быть перемещено в другую область памяти 
Меню загружается при запуске программы 
    При создании меню могут использоваться два типа операторов: MENUITEM 
    POPUP. Оператор MENUITEM определяет конечный пункт меню, а оператс 
    POPUP — выпадающее меню, которое может содержать подпункты, также задана! 
мые с помощью MENUITEM и POPUP. Общий вид операторов следующий: 
    MENUITEM "ItemName", MenuID [,Опции] 
    POPUP "PopupName" [, Опции} 
    Здесь параметр ItemName задает название пункта меню, например File или 
Не1] MenuID — это уникальное целое значение, которое связано с пунктом меню 
посылается приложению при выборе данного пункта. Обычно эти значения содер 
жатся в виде констант в библиотечном файле, который затем включается как 
программный файл, так и в RC-файл ресурсов. Параметр PopupName определяе 
название выпадающего меню. Для обеих команд значения параметра Опции приве 
дены в табл. 4.4. 

    Таблица 4.4. Опции, используемые с операторами MENU] Т ЕМ и POPUP 

    Опция                 Значение 

    CHECKED GRAYED HELPINACTIVE MENUBARBREAKMENUBREAK	Рядом с названием пункта 
меню может стоять метка выбора. (Не используется для меню верхнего 
уровня.)Название пункта меню выделяется серым цветом и не может быть 
выбрано.Может быть связана с командой вызова помощи. Применяется только с 
MENUITEM.Пункт меню не может быть выбран.Если используется для меню верхнего 
уровня, то название пункта будет записано с новой строки. Если используется 
для выпадающих меню, то название пункта будет размещено в новом столбце. В 
этом случае оно будет отделено чертой.То же, что и MENUBARBREAK, но 
разделительная черта не используется. 
    Ниже приведен пример создания простого меню, который необходимо набрать. 
    Назовем файл MENU1.RC. 


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


    ; MENU1.RC 
    ; Пример файла ресурсов, содержащего определение меню Unclude "ids.h" 
    MYMENU MENU { 
    POPUP "&0ne" 
    ( 
    MENUITEM "&Alpha", IDM_ALPHA 
    MENUITEM "&Beta", IDM_BETA 
    } 
    i     POPUP "&Two" 
    i     I 
    j      MENUITEM "&Gamma", IDM_GAMMA 
    !       POPUP "&Delta" 
    ( 
    MENUITEM "SEpsilon", IDM_EPSILON 
    MENUITEM "SZeta", IDM_ZETA 
    j              ) 
    i       MENUITEM "&Eta", IDM_ETA 
    I       MENUITEM "STheta", IDM_THETA 
    |          > 
    I     MENUITEM "&Help", IDM_HELP 

    Это меню, названное MYMENU, включает три пункта верхнего уровня: One, Two 
и Help. Пункты One и Two содержат выпадающие подменю. Пункт Delta содержит 
свое собственное подменю. Обратите внимание, что в командах создания подменю 
не указываются никакие идентификаторы. Они приведены только для пунктов меню. 
В данном случае вес идентификаторы определены в виде констант, начинающихся с 
префикса IDM. (Эти константы содержатся в библиотечном файле IDS.H.) Названия 
же пунктов могут быть произвольными. 
    Если в названии пункта присутствует литера &, то это означает, что символ, 
которому она предшествует, соответствует ключевой клавише, связанной с этим 
пунктом. Т.е. когда соответствующее меню активно, нажатие такой клавиши 
приводит к выбору данного пункта. Это необязательно должен быть первый символ 
названия. Главное, чтобы при этом не возникало конфликтов. 

    Примечание: В файл ресурсов можно включать комментарии, которые 
записываются в отдельной строке и начинаются с двойной косой черты, как 
показано в первой строке следующего примера. Но можно также включать 
комментарии, записанные в соответствии с синтаксисом С или C++. 

    Библиотечный файл IDS.H, включаемый в файл MENU1.RC, содержит определения 
констант, соответствующих идентификаторам пунктов меню. Ниже приведен его 
текст. Наберите этот файл. 

    4. Окна сообщений и меню                                       71 


    /'/ IDb.H 
    ^define IDM_ALPHA      100 ^define IDM_BETA       101 
    #define IDM_GAMMA      102 ftdefine IDM_DELTA      103 
    #define IDM_EPSILON    104 ftdefine IDM_ZETA       105 
    #define IDM_ETA        106 
    #define IDM_THETA      107 
    #define IDM_HELP       108 

    Здесь определены идентификаторы, которые будут передаваться в программу 
при выборе соответствующего пункта меню. Этот файл обязательно должен 
включаться в программу, в которой используется данное меню. Помните, что 
названия и числовые значения, ассоциируемые с пунктами меню, выбираются 
произвольно. Однако значения идентификаторов не должны повторяться. Диапазон 
допустимых значений — от 0 до 65565. 

    Включение меню в программу 

    Когда меню определено, его можно включить в программу, указывая имя меню 
при создании окна. В частности, необходимо передать указатель на его имя в 
последнем аргументе функции CreateQ. Например, чтобы загрузить меню MYMENL, 
используйте следующий вызов функции CreateQ: 

    Create(NULL, "Using Menus", WSJ3VERLAPPEDWINDOW, rectDefault, NULL, "MYMENU"); 

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

    Обработка сообщения WM COMMAND 

    Каждый раз, когда пользователь выбирает какой-нибудь элемент меню, 
программе посылается сообщение WM_COMMAND. С этим сообщением передается 
идентификатор выбранного элемента. Таким образом, когда сообщение WM_COMMAND 
принято, по связанному с ним идентификатору можно определить, какой пункт меню 
был выбран. Как уже упоминалось в предыдущей главе, сообщение WM_COMMAND 
обрабатывается не так, как другие сообщения MFC. Это вызвано тем, что 
конкретный смысл каждого сообщения WM_COMMAND зависит от значения 
идентификатора Именно оно определяет, какой из обработчиков должен быть 
вызван. В традиционных Windows-программах значения идентификаторов обычно 
расшифровываются с помощью большой switch-конструкции, которая и неудобна, и 
чревата ошибками. В 

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

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

    ON_COMMAND(ID, HandlerName) 

    Здесь ID — это идентификатор пункта меню, a HandlerName — имя связанной с 
ним функции^обработчика. 
    Каждый обработчик для WM_COMMAND должен иметь спецификатор типа afx_msg и 
возвращать void. Эти обработчики (называемые командными) не имеют параметров. 
Имя каждого из ,ни^^станавливается произвольно; Но_большинстаопрограммистов 
используют префикс On и ту же структуру имени, что и у встроенных обработчиков. 

    Реакция на выбор элементов меню 

    Чтобы программа правильно реагировала на выбор элементов меню, необходимо 
выполнить следующие действия: 
    1. Включить в очередь сообщений программы отдельную макрокоманду 
ON_COMMAND() для каждого из идентификаторов меню. 
    2. Добавить прототип каждого командного обработчика в объявление класса, 
предназначенного для создания главного окна. 
    3. Поместить в программу определения всех командных обработчиков. Помните, 
что для каждого элемента меню необходим свой обработчик. 
    Пример программы, содержащей меню 
    В следующем примере к созданной ранее программе добавляется меню. Для 
наглядности при выборе элемента меню выдается подтверждение. В реальных же 
проектах подобная реакция будет, как правило, гораздо более сложной. 
    В данной программе используются файлы IDS.H и MENU1.RC, описанные ранее. 
Сначала приведен текст файла MENU.H, содержащего объявления порождаемых классов. 

    // MENU.H 
    // Это класс для создания главного окна 
    ;lass CMainWin : public CFrameWnd. 
    { 
    public: 
    I     CMainWin () ; 
    I     afxJ-nsq void OnLButtonDown (UINT flags, CPoint loc) ; 
    afx_msg void OnRButtonDown(UINT flags, CPoint loc); 
    // объявления обработчиков меню 
    afx_msg void OnAlphaO; 


    4. Окна сообщений и меню                                       73 


    afx msg void OnBetaO; 
    afx nisg void OnGammaO; 
    afx msg void OnEpsilon(); 
    afx msg void OnZetaO; 
    afx_msg void OnEtaO; 
    afx msg void OnTheta(); 
    afx_msg void OnHelpO; 
    DECLARE_MESSAGE_MAP() 
    }; 
    // Это класс для создания приложения 
    class CApp : public CWinApp ( public: 
    BOOL Initlnstance (); 
    }; 

    Это текст программы: 

    // Работа с меню 
    ^include <afxwin.h> 
    #include "menu.h" 
    ^include "ids.h" 
    CMainWin::CMainHin() ( 
    Create(NULL, "Using Menus", WS__OVERLAPPEDWINDOW, 
    rectDefault, NULL, "MYMENU"); 
    } 
    // Инициализация приложения 
    BOOL CApp::Initlnstance () 
    { 
    m pMainWnd = new CMainWin; 
    m pMainMnd->ShowWindow(m nCmdShow) ; 
    m pMainWnd->UpdateWindow() ; 
    return TRUE; 
    // Это очередь сообщений приложения 
    BEGIN_MESSAGE_MAP(CMainMin, CFrameWnd) 
    ON_WM_LBUTTONDOMN() 
    ON_WM_RBUTTONDOWN() 
    ON_COMMAND(IDM_ALPHA, OnAlpha) 
    ON_COMMAND(IDM_BETA, OnBeta) 
    ON_COMMAND(IDMJ3AMMA, OnGamma) 
    ON_COMMAND(IDM_EPSILON, OnEpsilon) 
    ON_COMMAND(IDM_ZETA, OnZeta) 
    ON_COMMAND(IDM_ETA, OnEta) 
    ON_COMMAND(IDM_THETA, OnTheta) 


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


    ON_COMMAND(IDM_HELP, OnHelp) 
    END_MESSAGE_MAP() 
    // Обработка нажатия левой кнопки мыши 
    afx msg void CMainWin::OnLButtonDown(UINT flags, CPoint loc) 
    { 
    ink i; 
    i = MessageBox("Press One", "Left Button", 
    MB_ABORTRETRYIGNORE) ; 
    swith(i)   ( 
    case IDABORT: 
    MessageBox("", "Abort"); 
    break; 
    case IDRETRY: 
    MessageBox ("", "Retry"); 
    break; 
    case IDIGNORE: 
    MessageBox ("", "Ignore"); 
    break; 
    // Обработка нажатия правой кнопки мыши 
    afx msg void CMainWin::OnRButtonDown(UINT flags, CPoint loc) 
    int i; 
    i = MessageBox("Press One", "Right Button", 
    MB_ICONHAND | MB_YESNO) ; 
    swith(i)  { 
    case IDYES: 
    MessageBox ("", "Yes"); 
    break; 
    case IDNO: 
    MessageBox ("", "No"); 
    break; 
    ) 
    // Обработка сообщения IDM_ALPHA 
    afx msg void CMainWin::OnAlpha() 
    MessageBox("Alpha", "Alpha") ; 
    ) 
    // Обработка сообщения IDM__BETA 
    afx msg void CMainWin::OnBeta() 
    f 
    MessageBox ( "Beta", "Beta") ; 


    4. Окна сообщений и меню                                       75 


    // Обработка сообщения IDM GAMMA 
    afx_msg void CMainWin::OnGamma() 
    ( 
    MessageBox("Gamma", "Gamma") ; 
    ) 
    // Обработка сообщения IDM_EPSILON 
    afx_msq void CMainWin::OnEpsilon() 
    ( 
    MessageBox("Epsilon", "Epsilon") ; 
    i 
    // Обработка сообщения IDM_ZETA 
    afx msg void CMainWin : .-OnZeta () 
    ( 
    MessageBox ("Zeta", "Zeta"); 
    } 
    // Обработка сообщения IDM ETA 
    afx msg void CMainWin::OnEta() 
    { 
    MessageBox("Eta", "Eta"); 
    t 
    // Обработка сообщения IDM__THETA 
    afx msg void CMainWin::OnTheta() 
    { 
    MessageBox ("Theta", "Theta") ; 
    } 
    // Обработка сообщения IDM HELP 
    afx msg void CMainWin::OnHelp() 
    ( 
    MessageBox("Menu Demo", "Help"); 
    } 
    CApp App; // создание экземпляра приложения 

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

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


    Рис. 4.2. Пример работы с меню 


    Использование клавиш быстрого доступа 

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

    Имя-таблицы ACCELERATORS ( 
    Keyl, MenuIDl [,тип} [,опции] 
    Кеу2, MenuID2 [,тип] [,опции] 
    КеуЗ, МепиЮЗ [, тип] [, опции] 
    ] 


    4. Окна сообщений и меню                                       77 

    Здесь параметр Имя-таблицы определяет имя таблицы. Клавиша выбора элемент 
меню задается параметром Key, a MenulD содержит идентификатор требуемого 
элемента,. Параметр тип определяет, является ли клавиша стандартной (значение 
по умолчанию) или виртуальной (это мы рассмотрим подробнее чуть позже). В 
качестве опций могут указываться следующие константы: NOINVERT, ALT, SHIFT и 
CONTROL. Если задан опция NOINVERT, то при нажатии клавиши быстрого доступа 
выбранный элемент мет не будет выделен другим цветом. Опция ALT указывает, что 
дополнительно должна быт нажата клавиша [ALT]. Аналогично, опция SHIFT 
определяет необходимость нажатм клавиши [SHIFT], а опция CONTROL — клавиши [CTRL]. 
    Значением Key может быть либо символ в кавычках, либо ASCII-код клавиш) 
(или виртуальной клавиши). Если задан символ в кавычках, то подразумевается, 
ч' это ASCIT-символ. Если задано целое значение, то необходимо явно указать 
коми лятору ресурсов, что это ASCII-код, определяя параметр тип равным ASCII. 
ECJ это виртуальная клавиша, то в параметр тип должен иметь значение VIRTKEY. 
    Если в качестве параметра Key дана заглавная литера в кавычках, то 
соответс вующий пункт меню будет выбран при одновременном нажатии указанной 
клавиш и клавиши [SHIFT]. Если указан строчный символ, то для выбора пункта 
менк достаточно нажать саму клавишу. При задании строчного символа и опции ALT 
необходимо одновременно с символом нажать [ALT]. (Если указан заглавный 
симво;,' и опция ALT, то дополнительно нужно будет нажать и [ALT], и [SHIFT].) 
И наконец. если пользователь должен нажимать клавишу [CTRL], перед символом 
нужно поставить знак л. 
    Виртуальная клавиша задается системно-независимым кодом, предназначенным 
для обозначения стандартных служебных клавиш. К виртуальным относятся 
функциональные клавиши от [FI] до [F12], клавиши со стрелками, а также 
He-ASClI-символы. Они определяются с помощью файла AFXRES.H. Все константы 
виртуальных клавиш начинаются с префикса VK_. Например, функциональным 
клавишам соответствуют константы OTVK_FI до VK_F12. Чтобы использовать 
виртуальную клавишу в качестве клавиши быстрого доступа, достаточно просто 
указать се константу в качестве параметра Key, а параметр тип выбрать равным 
VIRTKEY. Можно также задавать опции ALT, SHIFT или CONTROL, чтобы определить 
нужную комбинацию клавиш. Вот несколько примеров: 
    "A", IDM х ; выбор по нажатию [Shift-A] 
    "а", IDM х ; выбор по нажатию [а] 
    "^а", IDM х ; выбор по нажатию [Ctrl-a] 
    "a", IDM_x, ALT ; выбор по нажатию [Alt-a] 
    VK_F2, IDM_x, VIRTKEY ; выбор по нажатию [F2] 
    VK_F2, IDM_x, VIRTKEY, SHIFT ; выбор по нажатию [Shift-F2] 
    В следующем файле ресурсов содержатся определения клавиш быстрого доступа 
для меню из примера в предыдущем naparpaqic. Назовем этот файл MENU2.RC. 

    ; MENU2.RC 
    ; Пример файла ресурсов, содержащего определение меню 
    ^include <afxres.h> 


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


    Гипс1ийе "ids.h" 
    MYMENU MENU 
    POPUP "&0ne" 
    ( 
    MENUITEM "&Alpha\tF2", IDM_ALPHA 
    MENUITEM "?Beta\tF3", IDM_BETA 
    I     } 
    '     POPUP "STwo" 
    i 
    MENUITEM "&Gamma\tShift-G", IDM_GAMMA 
    1       POPUP "SDelta" 
    ( 
    MENUITEM "SEpsilon\tCntl-E", IDM_EPSILON 
    MENUITEM "&Zeta\tCntl-Z", IDM_ZETA 
    ) 
    MENUITEM "&Eta\tCntl-F4", IDM_ETA 
    MENUITEM "&Theta\tF5", IDM_THETA 
    } 
    MENUITEM "SHelp", IDM_HELP 
    1 
    ; Определение клавиш быстрого доступа 
    HYMENU ACCELERATORS 
    ( 
    VK_F2, IDM_ALPHA, VIRTKEY 
    VK_F3, IDM_BETA, VIRTKEY 
    "G", IDM_GAMMA, 
    '"•E" , IDM_EPSILON, t     ""Z", IDM_ZETA, 
    VK_F4, IDM_ETA, VIRTKEY, CONTROL 
    VK_F5, IDM_THETA, VIRTKEY 
    VK_F1, IDM_HELP, VIRTKEY 
    ) 

    Обратите внимание, что названия пунктов меню теперь расширены таким 
образом, чтобы указывать на соответствующую комбинацию клавиш быстрого 
доступа. Название пункта отделено от названия комбинации клавиш с помощью 
символа табуляции. Файл AFXRES.H включен сюда потому, что он содержит 
определения констант виртуальных клавиш. 

    4. Окна сообщений и меню                                       79 


    Загрузка таблицы быстрого доступа 

    ^Хотя определения клавиш быстрого доступа и содержатся в том же файл 
ресурсов, что и определения меню, они должны загружаться отдельно с помощь; 
    функции LoadAccelTableO, которая является членом класса CFrameWnd. Ее 
прототв таков: 

    BOOL CFrameWnd: .-LoadAccelTable (LPCSTR IpszName) ; 

    Здесь параметр lpszName определяет название таблицы быстрого доступа. Функ 
ция возвращает ненулевое значение в случае успешного завершения загрузки и 
ноль если таблица не может быть загружена. 
    Функция LoadAccelTableO должна вызываться сразу же после отображения окна 
Обычно это выполняется внутри конструктора класса, предназначенного для созда 
ния главного окна. В следующем примере показано, как загрузить таблицу MYMENI 
для объекта типа CMainWin: 

    CMainWin::CMainWin() < 
    Create(NULL, "Demonstrate Accelerators", 
    WS_OVERLAPPEDWINDOW, rectDefault, 
    NULL, "MYMENU") ; 
    // Загрузка таблицы быстрого доступа 
    iff!LoadAccelTable("MYMENU")) 
    MessageBox ("Cannot Load Accelerators", "Error"); 
    } 

    Чтобы убедиться в функционировании клавиш быстрого доступа, замените тело 
конструктора в программе работы с меню приведенным выше кодом и подключите в 
качестве файла ресурсов MENU2.RC. 

    Создание горячих клавиш 

    Хотя клавиши быстрого доступа чаще всего используются для выбора элементов 
меню, их роль только этим не ограничивается. Можно, к примеру, определить 
комбинацию клавиш, которой не соответствует ни один из пунктов меню. Такой тип 
клавиш быстрого доступа иногда называют горячими клавишами. Они используются 
для непосредственного вызова какой-либо функции. В частности, можно 
использовать горячие клавиши для ввода команд с клавиатуры, активизации 
наиболее часто используемых опций или в качестве команды "экстренного выхода". 
Чтобы определить горячую клавишу, не связанную с пунктом меню, ее надо 
включить в таблицу быстрого доступа, но присвоить уникальный идентификатор. 
    Давайте добавим в нашу программу горячую клавишу. Это будет комбинация 
[CTRL-T], и каждый раз при ее нажатии будет выводиться окно сообщения, 
содержащее текущие значения даты и времени. Сначала добавим комбинацию 
[CTRL-T] в таблицу в файле MENU2.RC. Текст файла теперь будет выглядеть 
следующим образом: 

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


    ; MENU2.RC 
    ; Пример файла ресурсов, содержащего определение меню 
    ^include <afxres.h> 
    #include "ids.h" 
    MYMENU MENU 
    ( 
    POPUP "&0ne" 
    MENUITEM "&Alpha\tF2", IDM_ALPHA 
    MENUITEM "&Beta\tF3", IDM_BETA 
    } 
    POPUP "&Two" ( 
    MENUITEM "&Gamma\tShift-G", IDM_GAMMA 
    POPUP "SDelta" 
    ( 
    MENUITEM "&Epsilon\tCntl-E", IDM_EPSILON 
    MENUITEM "&Zeta\tCntl-Z", IDM_ZETA 
    ) 
    MENUITEM "&Eta\tCntl-F4", IDM_ETA 
    MENUITEM "&Theta\tF5", IDM_THETA 
    t 
    MENUITEM "&Help", IDM_HELP } 
    ; Определение клавиш быстрого доступа 
    MYMENU ACCELERATORS 
    ( 
    VK_F2, IDM_ALPHA, VIRTKEY 
    VK_F3, IDM_BETA, VIRTKEY 
    "G", IDM_GAMMA, 
    ""E", IDM_EPSILON, 
    ""Z", IDM_ZETA, 
    VK_F4, IDM_ETA, VIRTKEY, CONTROL 
    VK_F5, IDM_THETA, VIRTKEY 
    VK_F1, IDM_HELP, VIRTKEY 
    '^T", IDM_TIME } 

    Добавим следующую строку в файл IDS.H: 

    «define IDM_TIME      500 

    Это идентификатор, связанный с комбинацией клавиш [CTRL-T]. Теперь добавим 
прототип обработчика OnTime() в описание класса CMainWin, объявленного в файле 
MENU.H. Вот текст этого файла: 

    // MENU.H 
    // Это класс для создания главного окна class CMainWin : public CFrameWnd 


    4. Окна сообщений и меню                                       87 


    public:                                                            1 
    CMainWin() ; 
    afx msg void OnLButtonDown (UINT flags, CPoint loc);               / 
    afx_msg void OnRButtonDown(UINT flags, CPoint loc); 
    // объявления обработчиков меню 
    afx msg void OnAlphaO; 
    afx msg void OnBeta(); 
    afx_msg void OnGamma(); 
    afx_msg void OnEpsilon(); 
    afx_msg void OnZetaO; 
    afx_msg void OnEtaO; 
    afx msg void OnThetaO; 
    afx msg void OnHelpO; 
    afx msg void OnTimeO; // обрабатывает нажатие горячей клавиши 
    DECLARE_MESSAGE_MAP() }; 
    // Это класс для создания приложения                                 | 
    class CApp : public CWinApp 
    {                                                                          
                                                       j 
    public: 
    BOOL Initlnstance (); 
    ); 
    И наконец, включим реализацию функции OnTimeO в текст программы. 
    // Использование горячей клавиши 
    ^include <afxwin.h> 
    ^include <string.h> 
    ^include "menu.h" 
    #include "ids.h" 
    CMainWin::CMainHin() 
    ( 
    Create (NULL, "Adding a Hot Key", 
    WS_OVERLAPPEDWINDOW, rectDefault, 
    NULL, "MYMENU"); 
    // Загрузка таблицы быстрого доступа 
    if(!LoadAccelTable("MYMENU")) 
    MessageBox("Cannot Load Accelerators", "Error"); 
    i 
    // Инициализация приложения 
    BOOL CApp::Initlnstance () 
    ( 
    m_pMainWnd = new CMainWin; 
    82                                     MFC: основы программирования 

    m_pMainWnd->ShowWindow(m nCmdShow) ; 
    m_pMainWnd->UpdateWindow(); 
    return TRUE; 
    f 
    // Это очередь сообщений приложения 
    BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) 
    ON_WM_LBUTTONDOWN() 
    ON_WM_RBUTTONDOWN() 
    ON_COMMAND(IDM_ALPHA, OnAlpha) 
    ON_COMMAND(IDM_BETA, OnBeta) 
    ON_COMMAND(IDM_GAMMA, OnGamma) 
    ON_COMMAND(IDM_EPSILON, OnEpsilon) 
    ON_COMMAND(IDM_ZETA, OnZeta) 
    ON_COMMAND(IDM_ETA, OnEta) 
    ON_COMMAND(IDM_THETA, OnTheta) 
    ON_COMMAND(IDM_HELP, OnHelp) 
    ON_COMMAND(IDM_TIME, OnTime) // обрабатывает нажатие 
    // горячей клавиши 
    END_MESSAGE_MAP() 
    // Обработка нажатия левой кнопки мыши 
    afxJ-nsg void CMainWin::OnLButtonDown(UINT flags, CPoint loc) 
    ( 
    int i; 
    i = MessageBox("Press One", "Left Button", 
    MB_ABORTRETRYIGNORE) ; 
    swith(i)  ( 
    case IDABORT: 
    MessageBox ("", "Abort"it
    break; 
    case IDRETRY: 
    MessageBox ("", "Retry"); 
    break; 
    case IDIGNORE: 
    MessageBox ("", "Ignore"); 
    break; 
    } 
    // Обработка нажатия правой кнопки мыши 
    afx_msg void CMainWin::OnRButtonDown(UINT flags, CPoint loc) 
    ( 
    int. i ; 
    i = MessageBox ("Press One", "Right Button", 
    MB_ICONHAND I MB_YESNO) ; 
    •..with'i)  [ 


    4. Окна сообщений и меню                                          83 


    case IDYES: 
    MessageBox("", "Yes"); 
    break; 
    case IDNO:                                                     I 
    MessageBox ("", "No");                                        1 break; 
    } 
    \ 
    // Обработка сообщения IDM_ALPHA 
    afx_msg void CMainWin::OnAlpha() 
    { 
    MessageBox("Alpha", "Alpha") ; 
    ) 
    // Обработка сообщения IDM_BETA 
    afx msg void CMainHin::OnBeta() 
    ( 
    MessageBox ("Beta", "Beta"); 
    ) 
    // Обработка сообщения IDM GAMMA 
    afx msg void CMainWin::OnGamma() 
    ( 
    MessageBox("Gamma", "Gamma"); 
    } 
    // Обработка сообщения IDM_EPSILON 
    afx_msg void CMainWin::OnEpsilon () 
    { 
    MessageBox ("Epsilon"/ "Epsllon"} ; 
    }                                                                          
                                               j 
    // Обработка сообщения IDM_ZETA 
    afx msg void CMainWin::OnZeta() 
    { 
    MessageBox ("Zeta", "Zeta"); 
    )                                                                          
                                                       , 
    // Обработка сообщения IDM ETA 
    afx msg void CMainWin::OnEta() 
    { 
    MessageBox ("Eta", "Eta"); 
    ) 
    // Обработка сообщения IDM_THETA 
    afx msg void CMainWin::OnTheta() 
    ( 
    MessageBox("Theta", "Theta") ; 
    } 
    // Обработка сообщения IDM_HELP 


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


    afx msg void CMainWin::OnHelp() 
    j 
    MessageBox("Menu Demo", "Help"); 
    I 
    // Обработка сообщения IDM_TIME 
    afx_msg void CMainWin::OnTime() 
    ( 
    char str [80]; 
    CTime curtime = CTime::GetCurrentTime(); 
    struct tm *newtime; 
    newtime = currtime.GetLocalTm(); 
    wsprintf(str, asctime (newtime)); 
    str [strlen (str)-1] = '\0'; // удаляет символы /r/n 
    MessageBox (str, "Time and Date");           "'"" 
    ) 
    CApp App; // создание экземпляра приложения 
    Запустив программу и нажав комбинацию клавиш [CTRL-T], вы получите окно 
сообщения, подобное изображенному на рис. 4.3. 

    Рис. 4.3. Окно сообщения, вызываемое по нажатию горячей клавиши 

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

    4. Окна сообщений и меню    85