    Диалоговые окна 

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

87


    Взаимодействие между диалоговым окном и пользователем 

    Взаимодействие между_ диалоговым окном и пользователем осуществляется „ 
помощью одного или нескольких элементов управления. Элемент управления — это 
особый тип окна ввода или вывода. Он принадлежит окну-владельцу, которым в 
примерах данной главы будет являться диалоговое окно. Все версии Windows 
поддерживают несколько .базовых_элементов управления, к которым относятся: 
кнопки. контрольные переключатели, селекторные кнопки, списки, поля ввода, 
комбинированные списки, полосы прокрутки и статические элементы. Кратко опишем 
каждый из них: 
    • Кнопка* (push button) — это элемент, который пользователь "нажимает" 
(щелчком мыши или сначала активизировав с помощью клавиши [TAB], а затем нажав 
[ENTER]), чтобы вызвать выполнение какого-либо действия. Мы уже использовали 
кнопки в окнах сообщений. К примеру, кнопка ОК, имеющаяся в большинстве таких 
окон, является стандартной кнопкой. 
    • Контрольный переключатель (check box) — это элемент, который может быть 
либо выбран, либо нет. В первом случае опция, связанная с данным элементом, 
будет активизирована. Если в диалоговом окне присутствует несколько 
контрольных переключателей, то одновременно могут быть выбраны несколько из них. 
    • Селекторная кнопка (radio button) — это, по сути, тот же контрольный 
переключатель. Но при наличии нескольких таких кнопок выбранной может быть 
одна и только одна из них. Таким образом, селекторные кнопки являются 
взаимоисключающими контрольными переключателями. 
    • Список (list box) содержит набор значений, из которых пользователь может 
выбирать. Списки широко используются, в частности, для отображения имен файлов. 
    •  Поле ввода (edit box) — это элемент, с помощью которого пользователь 
может ввести строку символов. Поля ввода, как правило, поддерживают функции, 
необходимые для редактирования текста. 
    • Комбинированный список (combo box) представляет собой комбинацию из 
списка и поля ввода. 
    •  Полоса прокрутки, или линейка прокрутки, (scroll bar) обычно 
используется для просмотра текста в окне. 
    • Статический элемент (static control) предназначен для вывода текстовой 
или графической информации, но не предполагает ввода данных. 
    Примечание: Windows 95 и Windows NT поддерживают некоторые расширенные 
элементы управления, такие как панели инструментов, окна просмотра деревьев и 
закладки. Они будут рассмотрены позднее. 

    •   Правильнее называть ее стандартной кнопкой, чтобы не путать с другими 
типами кнопок. — Примеч. ред.              

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

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

    Классы управления 

    В MFC содержатся классы для всех стандартных элементов управления. Эти 
классы описывают сами элементы, а также содержат функции работы с ними. Классы 
управления, как их еще называют, порождаются от класса CWnd. Таким образом, 
каждый элемент имеет доступ к тем же основным функциям, что и любой другой тип 
окна. Классы управления приведены в табл. 5.1. 

    Таблица 5.1. Классы для стандартных элементов управления 

    Класс           Элементы управления 

    CButton         Кнопки, селекторные кнопки и контрольные переключатели 
    CEdit            Поля ввода 
    CListBox         Списки 
    CComboBox      Комбинированные списки 
    CScrollBar       Полосы прокрутки 
    CStatic          Статические элементы 

    Хотя в MFC допускается непосредственное обращение к различным элементам 
управления, на практике такое происходит редко. Гораздо проще управлять ими 
через соответствующие классы. Единственным исключением является самый простой 
случай использования кнопки. Но даже с ней приходится работать через класс, 
если требуется сложное управление. Наиболее часто элементы управления 
используются с диалоговыми окнами; можно создавать и отдельные элементы, 
расположенные в главном окне. Помните, что они являются просто особым типом окон. 

    Модальные и немодальные диалоговые окна 

    Есть два типа диалоговых окон: модальные и немодальные. Наиболее 
распространенными являются первые. В модальных диалоговых окнах ожидается 
некоторое действие со стороны пользователя, прежде чем программа сможет 
продолжить выполнение. Т.е. вы не сможете продолжить работу, пока не нажмете 
на одну из 

    5. Диалоговые окна                                                89 

    кнопок в модальном окне. Нсмодальное диалоговое окно не приостанавливает 
выполнение программы. Имеется в виду, что в нем не требуется выполнение 
какого-либо действия для того, чтобы управление было передано другой части 
программы. Сначала в главе будут рассмотрены модальные окна, поскольку они 
являются наиболее распространенными. Пример же использования немодального окна 
завершит главу. 

    Диалоговое окно как ресурс 

    Диалоговое окно является ресурсом, который содержится в файле ресурсов 
программы. Поэтому программа, использующая диалоговое окно, должна иметь файл 
ресурсов, в котором это окно описывается. Хотя содержимое диалогового окна 
может быть определено при помощи текстового редактора, а характеристики окна 
могут быть введены таким же образом, как и при создании меню, подобный метод 
используется крайне редко. Вместо этого большинство программистов работают с 
редактором диалоговых окон. Причина заключается в том, что процедура опредения 
диалоговых окон включает размещение элементов в нем, а это удобнее делать в 
интерактивном режиме. Но поскольку все RC-файлы в данной главе представлен;; з 
текстовом виде, вам придется ввести их с помощью текстового редактора. Просто 
г. забывайте, что для создания собственных диалоговых окон вам понадобится 
cooтветствующий редактор. 
    Примечание: Так как на практике большинство диалоговых окон обычно 
создае':'с помощью специального редактора, в данной главе приведены лишь крат, 
.•:: пояснения к примерам описаний диалоговых окон в файлах ресурсов. 
    Диалоговые окна определяются в файле ресурсов при помощи оператора DIALOG. 
Общий вид конструкции таков: 

    Имя-окна DIALOG [опции] X, У, Width, Height 

    Характеристики 
    { 
    Элементы-окна 
    } 

    Параметр Имя-окна задает имя диалогового окна. Верхний левый угол окна 
бу  расположен в точке с координатами (X,Y), а размер окна определяется 
параметра^;! Width и Height. Различные опции, которые определяют загрузку 
диалогового окн:. ••'. память и его удаление из нее, указываются в параметре 
опции. Однако при работе а среде Win32 большинство опций игнорируется. 
Единственной разрешенной опцией является DISCARDABLE. Она позволяет удалять 
диалоговое окно из памяти, ес.ти оно больше не используется. Кроме того, может 
быть задана одна или несколько характеристик диалогового окна. К ним, в 
частности, относятся заголовок и стиль ^ окна. В поле Элементы-окна приводятся 
определения элементов управления, содержащихся в диалоговом окне. Каждое 
определение будет подробно объясняться при описании соответствующего элемента. 

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


    Класс CDialog 

    Диалоговые окна определяются с помощью класса CDialog, который порождается 
от класса CWnd. В MFC все диалоговые окна являются экземплярами либо самого 
CDialog, либо порожденного от него класса. Но лишь самые простые диалоговые 
окна являются объектами касса CDialog, и их лучше определять как окна 
сообщений. В общем случае, если в программе требуется диалоговое окно, то для 
него необходимо определить собственный класс. Поэтому во всех случаях создания 
диалоговых окон мы будем порождать для них новый класс. 
    Для создания объекта диалогового окна конструктор порожденного класса 
вызовет конструктор класса CDialog. Его прототипы таковы: 

    ..'Dialog (LPCSTR IpszDialogName, CWnd *0wner = NULL); ^ 
    CDialog(UINT ID, CWnd * Owner = NULL); 
    CDialog (); 

    Параметр IpszDialogName определяет название шаблона диалогового окна из 
файла ресурсов. Параметр ID является идентификатором такого шаблона. (На 
большинство ресурсов можно указывать либо с помощью имени, либо с помощью 
числового значения. В примерах данной книги используются имена.) В обоих 
случаях параметр Owner служит указателем на объект родительского окна, 
являющегося владельцем диалогового окна. Если Owner равен NULL, то предком 
будет главное окно приложения. Третья форма конструктора CDialogQ 
предназначена для создания немодального диалогового окна, и пример ее 
использования будет описан позднее. 

    Обработка сообщений от диалоговых окон 

    Поскольку класс CDialog порождается от CWnd, все диалоговые окна являются, 
по сути, обычными окнами (но особой их разновидностью). О событиях, 
происходящих в диалоговом окне, программа узнает через тот же механизм 
сообщений, который используется и в главном окне. Однако сообщения от 
диалогового окна не посылаются главному окну и не используют очередь сообщений 
последнего. Иными словам иТкаждое диалоговое окно должно иметь свою 
собственную очередь сообщений и свои собственные обработчики. 
    В общем случае каждому элементу управления в диалоговом окне присваивается 
свой идентификатор. Каждый раз, когда этот элемент активизируется 
пользователем, диалоговому окну посылается сообщение WM_COMMAND. С этим 
сообщением будет ассоциирован также идентификатор выбранного элемента. Для 
расшифровки и обработки каждого сообщения в очередь сообщений может быть 
помещена макрокоманда ON_COMMAND(). Этот процесс аналогичен обработке 
сообщений меню. Однако не следует забывать, что при этом используется 
собственная очередь сообщений диалогового окна. 
    Многие„элементы управления также генерируют специальный идентификационный 
код, который позволяет точно определить, какое именно действие имело место. Во 
многих случаях именно благодаря этому коду выбирается тот или иной обработчик 
сообщения. Как будет показано, в MFC имеется механизм, подобный макросу 
ON_COMMAND(), с помощью которого можно связывать идентификационные коды с их обработчиками. 

    5. Диалоговые окна                                               91 


    Вызов диалогового окна 

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

    virtual int CDialog::DoModal(); 

    Функция возвращает код завершения, генерируемый диалоговым окном up 
закрытии, или -1, если окно не может быть отображено. Если при отображении окн 
произошла ошибка, возвращается IDABORT. Функция DoModalQ не завершается пока 
диалоговое окно не будет закрыто. 

    Закрытие диалогового окна 

    По умолчанию модальное диалоговое окно закрывается при получении либо 
сообщения IDCANCEL, либо IDOK. Они определены в Windows и обычно связаны ( 
кнопками Cancel и ОК. Класс CDialog содержит встроенные обработчики для этш 
сообщений, называющиеся ОпСапсеЦ) и ОпОК(). Поскольку эти обработчики являются 
встроенными, их не нужно включать в очередь сообщений диалогового окна. 
Правда, их можно и переопределить, чтобы реализовать собственные функции 
завершения. Как это сделать, мы рассмотрим позднее. 

    Пример создания простого диалогового окна 

    Для начала давайте создадим простое диалоговое окно. Оно будет содержать 
три кнопки: Red, Green и Cancel. При нажатии первых двух будет выводиться окно 
сообщения, информирующее о сделанном выборе. Диалоговое окно удаляется с 
экрана при нажатии кнопки Cancel. 
    В программе есть меню верхнего уровня, содержащее два пункта: Dialog и 
Help Поскольку в этом и других примерах из данной главы диалоговые окна 
содержат не слишком много информации, данные примеры предназначены лишь для 
иллюстрации основных возможностей, которые доступны приложениям при 
использовании диалоговых окон. 

    Файл ресурсов для диалогового окна 

    В приводимом ниже файле ресурсов описывается диалоговое окно, используемое 
в первом примере. Здесь также определяется меню, с помощью которого можно 
вызвать данное окно, и клавиши быстрого доступа. Введите этот пример и 
откомпилируйте его. 

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


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


    IDialogMenu MENU 
    1 
    POPUP "&Dialog" 
    ( 
    MENUITEM "sSample Dialog", IDM_DIALOG 
    MENUITEM "E&xit", IDM_EXIT 
    } 
    MENUITEM "&Help", IDM_HELP 
    } 
    DialogMenu ACCELERATORS 
    { 
    VK_F1, IDM_HELP, VIRTKEY 
    VK_F2, IDM_DIALOG, VIRTKEY 
    VK_F3, IDM_EXIT, VIRTKEY 
    ) 
    SampleDialog DIALOG 18, 18, 142, 92 
    CAPTION "Sample Dialog Box" 
    STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 
    ( 
    DEFPUSHBUTTON "Red", IDD_RED, 32, 40, 30, 14, 
    '"      WS_CHILD | WS_VISIBLE | WS_TABSTOP 
    PUSHBUTTON "Green", IDD_GREEN, 74, 40, 30, 14, 
    WS_CHILD I WS_VISIBLE | WS_TABSTOP 
    PUSHBUTTON "Cancel", IDCANCEL, 52, 65, 37, 14, 
    HS_CHILD I WS_VISIBLE | WS_TABSTOP 
    } 

    В данном файле определяется диалоговое окно, называющееся SampleDialog, 
верхний левый угол которого имеет координаты (18,18). Ширина окна — 142, а 
высота — 92 пикселя. Строка, расположенная вслед за оператором CAPTION, 
является заголовком окна. Оператор STYLE определяет стиль создаваемого 
диалогового окна. Некоторые наиболее распространенные константы стиля, включая 
и те, которые используются в последующих примерах, приведены в табл. 5.2. 
Можно с помощью операции побитового ИЛИ объединять несколько таких констант, 
чтобы получить требуемый стиль. Эти стили могут использоваться и другими 
элементами управления. 

    Таблица 5.2. Наиболее распространенные стили диалоговых окон 

	Значение                Эффект 
    DS_MODALFRAME    Диалоговое окно с модальной рамкой; этот стиль может 
использоваться как для модальных, так и для немодальных окон 
    WS_BORDER          Окно с обычной рамкой 
    WS_CAPTION         Окно со строкой заголовка 
    WS_CHILD           Окно создается как дочернее 
    WS_HSCROLL         Окно с горизонтальной полосой прокрутки 


    5. Диалоговые окна                                                93 


    Значение	Эффект 

    WSMAXIMIZEBOX	Окно с кнопкой максимизации 
    WSMINIMIZEBOX Окно с кнопкой минимизации 
    WSPOPUP 	Окно отображается поверх всех других окон
    WSSYSMENU 	Окно с системным меню
    WSTABSTOP 	Элементы управления могут быть выбраны последовательным нажатием клавиши [TAB]
    WSVISIBLE 	Окно отображается при вызове
    WSVSCROLL 	Окно с вертикальной полосой прокрутки
     
    Внутри определения окна SampleDialog содержатся также определения трех 
кнопок. Первая из них задана как кнопка по умолчанию. Она автоматически 
выделяется при первом вызове окна. Общий вид объявления кнопки следующий: 

    PUSHBUTTON "строка", PBID, X, У, Width, Height [,Стиль] 

    Здесь параметр строка содержит текст надписи на кнопке. PBID — это 
значение, ассоциируемое сданной кнопкой. Оно передается в программу при 
нажатии кнопки. Верхний левый угол кнопки имеет координаты (X, Y), а ее 
размеры задаются параметрами Width и Height. Параметр Стиль определяет стиль 
кнопки. Для задания кнопки по умолчанию используется оператор DEFPUSHBUTTON. 
Он имеет те же параметры, что и в приведенной выше конструкции. 

    Библиотечный файл IDS.H 

    Библиотечный файл IDS.H, текст которого приведен ниже, содержит значения 
различных идентификаторов, используемых в программе. 
    
    // IDS.H 
    ^define IDM_DIALOG    100 
    #define IDM_EXIT      101 
    #define IDM_HELP      102 
    ^define IDD_RED       200 
    ^define IDD_GREEN     201 

    Наберите этот файл. 


    Файл порождаемых классов 

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

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


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


    CMainWin () ; 
    // объявления обработчиков меню 
    afxJ-nsg void OnDialogO; 
    afx msq void OnExit(); 
    afx msg void OnHelpO; 
    DECLARE_MESSAGE_MAP() 
    }; 
    // Это класс для создания приложения 
    class CApp : public CWinApp 
    f 
    public: 
    BOOL Initlnstance (); 
    1; 
    // Это класс для создания диалогового окна 
    class CSampleDialog : public CDialog 
    i 
    public: 
    CSampleDialog(char *DialogName, CWnd *0wner) : 
    CDialog(DialogName, Owner) (} 
    afx msg void OnRedO; 
    afx msg void OnGreen(); 
    DECLARE__MESSAGE_MAP() 
    l . 

    Обратите внимание, что конструктор CSampleDialog() просто передает свои 
аргументы конструктору CDialogQ. 

    Программа 

    Ниже приведен текст программы, которая работает с диалоговым окном. После 
запуска программы отображается главное окно, содержащее меню верхнего уровня. 
При выборе пункта Sample Dialog на экране появится диалоговое окно. Нажатие в 
нем каких-либо кнопок вызывает соответствующую реакцию. Пример работы 
программы представлен на рис. 5.1. 

    // Работа с диалоговым окном 
    ^include <afxwin.h> 
    #include "dialog.h" 
    #include "ids.h" 
    CMainWin::CMainWin () 
    ( 
    Create(NULL, "Demonstrate Dialog Boxes", 
    WS_OVERLAPPEDWINDOW, rectDefault, 
    NULL, "DialogMenu") ; 


    5. Диалоговые окна                                               95 


    // Загрузка таблицы быстрого доступа 
    if(!LoadAccelTable("DialogMenu")) 
    MessageBox ("Cannot Load Accelerators", "Error"); 
    } 
    // Инициализация приложения BOOL CApp::Initlnstance() { 
    m pMainWnd = new CMainWin; 
    m_pMainWnd->ShowWindow(m_nCmdShow) ; 
    m_pMainWnd->UpdateWindow(); 
    return TRUE; 
    ; 
    // Это очередь сообщений приложения 
    BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) 
    ON_COMMAND(IDM_DIALOG, OnDialoaJ 
    ON_COMMAND(IDM_EXIT, OnExit) 
    ON_COMMAND(IDM_HELP, OnHelp) 
    END_MESSAGE_MAP() 
    // Обработка сообщения IDM_DIALOG 
    afx msg void CMainWin::OnDialog () 
    ( 
    CSampleDialog diagOb("SampleDialog", this); 
    diagOb.DoModal(); // активизирует модальное диалоговое окно 
    )               "    '       ' 
    // Обработка сообщения 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 ("Dialog Demo", "Help"); 
    } 
    // Это очередь сообщений диалогового окна 
    BEGIN_MESSAGEJ4AP(CSampleDialog, CDialog) 
    ON_COMMAND(IDD_RED, OnRed) 
    ON_COMMAND(IDD_GREEN, OnGreen) 
    END_MESSAGE_MAP() 


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


    »// Обработка сообщения IDD_RED 
    afx_msg void CSampleDialog::OnRed() 
    ( 
    MessageBox("Red", "Color Selected"); 
    II Обработка сообщения IDD_GREEN 
    [  afx_msg void CSampleDialog::OnGreen () 
    i   ( 
    MessageBox("Green", "Color Selected"); 
    ) 
    CApp App; // создание экземпляра приложения 


    Рис. 5.1. Пример диалогового окна 


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

    Каждый раз при выборе какого-нибудь элемента управления в диалоговом окне 
программе посылается сообщение WM_COMMAND. Это сообщение обрабатывается в 
очереди сообщений, связанной с классом CSampleDialog. В этой очереди 
обрабатываются два из трех сообщений, которые могут быть сгенерированы в 
данном окне. Если пользователь нажимает кнопку Red, генерируется сообщение 
IDD_RED, для которого вызывается обработчик OnRed(). При нажатии кнопки Green 
генерируется сообщение IDD_GREEN и вызывается обработчик OnGreen(). 
    Как видите, здесь нет обработчика для кнопки Cancel. Как уже упоминалось 
ранее, причина этого в"том, что класс CDialog содержит стандартные обработчики 
для двух сообщений: IDCANCEL и IDOK. Первое из них обрабатывается с помощью 
функции-члена ОпСапсеЦ), второе — с помощью ОпОК(). Результатом выполнения 

    5. Диалоговые окна                                                97 

    обеих функций будет закрытие диалогового окна. При изучении файла ресурсов 
можно заметить, что кнопке Cancel присвоен идентификатор IDCANCEL. Это 
означает, что при ее нажатии будет автоматически вызвана встроенная функция 
ОпСапсе1(). Прототипы функций ОпСапсе1() и ОпОК() таковы: 

    virtual void CDialog::OnCancel (); 
    virtual void CDialog::OnOK (); 

    В программе можно переопределять оба этих обработчика. При этом вам 
придется осуществлять закрытие диалогового окна программно. В следующем 
параграфе описывается, как это сделать. Необходимо помнить, что, поскольку эти 
обработчики являются встроенными, они не включаются в очередь сообщений 
диалогового окна 
    Мы уже говорили о том, что в MFC имеется стандартный класс CButton. 
определяющий кнопки, включая стандартные (push buttons). Этот класс не 
используется в примере, так как между кнопкой и программой не происходит 
никакого взаимодействия, за исключением того, что при нажатии кнопки в 
программу передается ее идентификатор — в виде сообщения WM_COMMAND. Поэтому 
нет и необходимости создавать специально объект кнопки. Тем не менее класс 
CButton будет широко использоваться при работе с другими типами кнопок. 
    Стоит сказать пару слов и о главном меню программы. Посмотрите на текст 
обработчика, связанного с пунктом Exit. Данный обработчик завершает выполнение 
программы, посылая главному окну сообщение WM_CLOSE. Это сообщение указывает, 
что программа должна произвести все необходимые действия по выгрузке, а затем 
завершить выполнение. Чтобы послать такое сообщение, используется функция 
SendMessageQ, являющаяся членом класса CWnd. Ее прототип таков: 

    LRESULT CWnd::SendMessage(UINT Message, WPARAM wParam = О, 
    LPARAM 1 Pa ram = 0) ; 

    Эта функция посылает указанное сообщение вызывающему ее окну. Параметр 
Message определяет посылаемое сообщение. Но для некоторых из них требуется 
также сопроводительная информация. В таком случае она передается в параметрах 
wParam и IParam. Стандартные типы WPARAM и LPARAM определены в Windows 
специально для этой цели. 

    Программное закрытие диалогового окна 

    Как уже упоминалось, стандартные обработчики ОпСапсеЦ) и ОпОК() могут быть 
переопределены в программе. При этом новые обработчики должны самостоятельно 
закрывать диалоговое окно. Для этого необходимо использовать 
функциюЕп(Ю|аlog(), которая является членом класса CDialog. Ее прототип таков: 

    void CDialog::EndDialog(int Status); 

    Здесь параметр Status определяет код статуса, возвращаемый функцией DoModaIQ. 

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

    Вот, например, один из способов реализации функции ОпСапсеЦ) в предыдущей программе: 

    afx_msg void CSampleDialog::OnCancel () ( 
    int response; 
    response = MessageBox("Are You Sure?", "Cancel", MB_YESNO) ; 
    if (response == IDYES) EndDialog(0); 
    )                                                "~——— 

    Эта функция выводит окно сообщения, в котором у пользователя запрашивается 
подтверждение на отмену диалогового окна. Чтобы проверить работу этой функции, 
включите ее прототип в объявление класса CSampleDialog в файле DIALOG.H, а 
затем добавьте следующий обработчик в очередь сообщений указанного класса: 
    
    ON_COMMAND(IDCANCEL, OnCancel) 

    Если вы внесете эти изменения, то при нажатии кнопки Cancel сможете 
увидеть окно сообщения, показанное на рис. 5.2. 

    Рис. 5.2. Окно сообщения, связанное с кнопкой Cancel 

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

    Инициализация диалогового окна 

    Иногда различные переменные и элементы управления, связанные с диалоговым 
окном, должны быть инициализированы до того, как оно будет отображено. Чтобы 
позволить диалоговому окну выполнить подобные действия. Windows автоматически 
' посылает ему сообщение WMJGNITDIALOG в момент создания. При получении 
такого^сообщения MFC "автоматически вызывает функцию OnInitDiaIogQ, которая 
является стандартным обработчиком, определенным в классе CDiaIog. Эта функция 
должна переопределяться в программе, если необходимо выполнение различных 
инициализационных действий. Прототип функции таков: 

    virtual BOOL CDialog::OnInitDialog(); 

    Эта функция вызывается до того, как диалоговое окно будет отображено. Она 
должна возвращать TRUE, чтобы позволить Windows передать фокус ввода (т.е. сделать 

    5. Диалоговые окна                                                99 

    активным) на первый элемент управления в окне. Поскольку функция 
OnInitDialogQ является встроенным обработчиком, ее не нужно помещать в очередь 
сообщений. ; 
    Есть одно важное правило, которого необходимо строго придерживаться npir 
переопределении функции OnInitDialogQ. Первым действием, которое выполняет эта 
[ функция, обязательно должен быть вызов функции CDiaIog::OnInitDialog(). Т.е. 
сначала должна вызываться версия OnInitDiaIogO, определенная в CDialog. 
    В некоторых из последующих примеров эта функция будет использована. 

    Работа со списками 

    Продолжая изучение диалоговых окон, давайте добавим в нашу программу 
список. Он также является одним из наиболее распространенных элементов 
управления. В MFC работа со списками организуется через класс CListBox. 

    Определение списка в файле ресурсов 

    Чтобы добавить список в диалоговое окно, необходимо включить оператор 
LISTBOX в определение диалогового окна в файле ресурсов. Общий вид этого 
оператора таков: 

    LISTBOX LBID, X, У, Width, Height [, Стиль} 

    Здесь параметр LBID является идентификатором списка. Верхний левый угол 
окна списка будет иметь координаты (X,Y), а размеры окна задаются параметрами 
Width и Height. Параметр Стиль определяет стиль списка. 
    Для задания списка необходимо изменить определение диалогового окна в 
файле DIALOG.RC. Сначала нужно включить описание списка: 

    LISTBOX J:DD__LB1, 2, 10, 47, 32, LBSJMOTIFY I WS_CHILD | 
    WS_VISIBLE I WS_BORDER | WS_VSCROLL I WS_TABSTOP 
    Затем нужно добавить описание такой кнопки: 
    PUSHBUTTON "Select Fruit", IDD_SELFRUIT, 5, 45, 42, 14, 
    WS_CHILD I WS_VISIBLE I WS_TABSTOP 

    И наконец, позиции кнопок Red и Green должны быть слегка сдвинуты: для 
кнопки Red — в позицию (56, 45), а для кнопки Green — в позицию (96, 45). 
После этого определение диалогового окна должно выглядеть следующим образом: 

    SampleDialog DIALOG 18, 18, 142, 92 
    CAPTION "Sample Dialog Box" 
    STYLE DS_MODALFRAME I WS_POPUP | HS_CAPTION | WS_SYSMENU 
    {                                                                     ' 
    DEFPUSHBUTTON "Red", IDD_RED, 56, 45, 30, 14,                       , 
    WS_CHILD I WS_VISIBLE | WS_TABSTOP                              ! 
    PUSHBUTTON "Green", IDD_GREEN, 96, 45, 30, 14, 
    WS_CHILD I WS_VISIBLE I WS_TABSTOP 
    PUSHBUTTON "Cancel", IDCANCEL, 52, 65, 37, 14, 
    WS_CHILD I WS_VISIBLE I WSJTABSTOP                               ; 
    LISTBOX IDD_LB1, 2, 10, 47, 32, LBS_NOTIFY | WS_CHILD | 

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

    WS_VISIBLE I WS_BORDER i WS_VSCROLL | WS_TABSTOP PUSHBUTTON "Select 
Fruit", IDD_SELFRUIT, 5, 45, 42, 14, WS_CHILD I WS_VISIBLE | WS_TABSTOP 
    } 

    Нужно также добавить две константы в файл IDS.H: 

    Mefine IDD_SELFRUIT     202 
    ftdefine IDD_LB1          203 

    Константа IDD_LB1 является идснтификатором_сриска в файле ресурсов. 
Константа IDD_SELFRUIT служит идентификатором кнопки Select Fruit. 

    Основы работы со списками 

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

    Прием идентификационных кодов списка 

    Список может генерировать сообщения различных типов. Например, сообщения 
посылаются при двойном щелчке на элементе списка, при потере списком фокуса 
ввода (т.е. когда он становится неактивным) и при выборе другого элемента из 
списка. Каждое такое событие описывается соответствующим идентификационным 
кодом. Этот код является частью сообщения WM_COMMAND и, по сути, представляет 
собой второе сообщение, описывающее произошедшее событие. Как вы увидите, 
некоторые другие элементы управления также используют идентификационные коды. 
    Единственный код, которым мы пока воспользуемся, называется LBN_DBLCLK. Он 
посылается, когда пользователь выполняет двойной щелчок на элементе списка. (В 
описание списка должна быть включена опция LBS_NOTIFY, чтобы он мог 
генерировать такое сообщение.) Когда выбор произведен, необходимо запросить 
список, чтобы узнать о том, какой элемент выбран. 
    Для обработки сообщения LBN_DBLCLK необходимо поместить его обработчик в 
очередь сообщений. Но это будет не функция ON_COMMAND(). Вместо нее в данном 
случае используются специальные макрокоманды обработки идентификационных 
кодов. Для LBN_DBLCLK такой макрокомандой является ON_LBN_DBLCLK(). Она имеет 
такой вид: 

    ON_LBN_DBLCLK(ID, HandlerName) 

    Здесь параметр ID содержит значение обрабатываемого идентификатора, а 
НапdIerNqme — это имя соответствующей идентификатору функции. 
    Хотя ON_LBN_DBLCLK() — это единственное из идентификационных сообщений 
списка, которое обрабатывается в программе, другие подобные сообщения 
обрабатываются аналогичным образом. Названия всех обработчиков для этих 
сообщений начинаются с префикса ON_LBN. Вот, в частности, некоторые из них 
(табл. 5.3): 


    5. Диалоговые окна                                               101 


    Таблица 5.3. Некоторые обработчики идентификационных сообщений 

    Обработчик                        Событие 

    ON.	.LBN.	.ERRSPACE	He хватает памяти 
    ON.	-LBN.	.KILLFOCUS	Список потерял фокус ввода 
    ON.	-LBN.	SELCHANGE	Выбран другой элемент списка 
    ON.	-LBN.	.SETFOCUS	Список получил фокус ввода 

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

    Передача сообщений спискам 

    Обычно в Windows-программах сообщения посылаются элементам управления 
через API-функции, например SendDlgItemMessage(). Но в программах, написанных 
с использованием MFC, для этих целей применяются соответствующие функциичлены. 
Они автоматически посылают нужное сообщение элементу управления. В этом 
заключается преимущество использования библиотеки MFC по сравнению с 
традиционными методами программирования. 
    Списку может быть послано несколько разных сообщений. Поэтому класс 
CListBox содержит для каждого из них отдельную функцию. Но в примере из данной 
главы используются только три: 

    int CListBox::AddString(LPCSTR IpszStr); 
    int CListBox::GetCurSel() const; 
    int CListBox::GetText(int Index, LPCSTR IpszStr) const; 

    Давайте рассмотрим каждую из них: 
    • Функция AddStringO вставляет указанную строку в список. По умолчанию 
строка вставляется в конец текущего списка. (Но если определение списка 
содержит опцию LBS_SORT, то строка будет вставлена согласно порядку 
сортировки.) Функция возвращает индекс строки в списке. Начало списка имеет 
индекс ноль. В случае ошибки возвращаются либо LB_ERR, либо LB_ERRSPACE (если 
не хватает памяти). 
    • Функция GetCurSeIQ возвращает индекс текущего выделенного (т.е. 
выбранного) элемента списка. Если ничего не выбрано, то возвращается LB_ERR. 
    • Функция GetText() получает строку, связанную с указанным индексом. Она 
возвращает число символов в строке или LB_ERR, если по указанному индексу нет 
строки. Строка копируется в символьный массив, адресуемый параметром /pszStr. 
    Среди других широко используемых функций: DeIeteStringO (удаляет строку из 
списка), FindStringO (находит строку), SetCurSelQ (устанавливает курсор выбора 
на элемент), InsertStringO (вставляет строку по указанному индексу) и 
GetCoiintQ (возвращает число элементов в списке). Эти и другие функции-члены 
класса CListBox предлагаются для самостоятельного изучения. 

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


    Получение указателя на список 

    Поскольку функции-члены CListBox работают с объектами типа CListBox, 
возникает вопрос: как же они применяются к списку, который определен в виде 
ресурса вфаилеоесурсов? Ответ весьма прост. Достаточно^получить указатель на 
такой_список с_помощыо функции GetDlgItemO, являющейся членом класса CWnd. 

    CUnd *CWnd::GetDlgItem(int ID) const; 

    Эта функция возвращает указатель на объект, чей идентификатор указан в 
параметре ID. Если такой объект не существует, возвращается NULL. Необходимо 
понимать, что указатель, возвращаемый функцией GetDlgItemO, является 
временным. Это означает, что его нужно каждый раз получать заново. Его нельзя, 
к примеру, сохранить в глобальной переменной для последующего использования. 
    Значение, возвращаемое функцией GetDlgItemO, должно быть приведено к типу 
указателя, стоящего в левой части оператора присваивания. Для объекта списка 
это означает, что возвращаемое значение должно быть приведено к типу CListBox*. 

    Инициализация списка 

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

    // Инициализация диалогового окна. 
    BOOL CSainpleDialog : : OnInitDialogO 
    CDialog::OnInitDialog(); // Вызов функции базового класса 
    CListBox *lbptr = (CListBox *) GetDlgItem(IDDJLB1); 
    // Инициализация списка 
    Ibptr^ddString ("Apple") ; 
    Ibptr-^ddString("Orange") ; 
    Ibptr-^AddString ("Pear") ; 
    lbptr_-Add3tring("Grape") ; 
    return TRUE; 
    
    В данной функции загружается список со строками "Apple" (яблоко), "Orange" 
(апельсин), "Реаг" (груша) и "Grape" (виноград). Каждая строка добавляется в 
список путем вызова функции AddStringO. Обратите внимание, каким образом с 
помощью GetDlgItemO получается указатель на список, который затем используется 
для вызова AddStringO. Этот метод является общим и впоследствии будет 
использован для вызова других функций-членов класса CListBox. В дальнейшем 
такой же подход применяется и при взаимодействии с другими элементами управления. 
    Каждая строка помешается в список в порядке поступления. (Однако при 
соответствующем построении списка возможно размещение строк в алфавитном 

    5. Диалоговые окна                                                103 

    порядке.) Если число строк, добавляемых в список, превышает то количество, 
которое может быть одновременно отображено в окне, автоматически появляется 
вертикальная полоса прокрутки. 

    Пример программы работы со списком 

    Ниже приведен полный текст программы работы с диалоговым окном, которое 
теперь также включает и список. Вначале показан текст обновленного файла 
DIALOG. Н, содержащего объявления порождаемых классов. 

    // DIALOG.Н 
    // Это класс для создания главного окна 
    class CMainWin : public CFrameWnd 
    ( 
    public: 
    CMainWin(); 
    // объявления обработчиков меню 
    afx msg void OnDialogO; 
    afx msg void OnExitO; 
    afx_msg void OnHelpO; 
    DECLARE_MESSAGE_MAP() 
    }; 
    // Это класс для создания приложения 
    class CApp : public CWinApp 
    { 
    public: 
    BOOL Initlnstance () ; 
    ); 
    // Это класс для создания диалогового окна 
    class CSampleDialog : public CDialog 
    < 
    public: 
    CSampleDialog(char *DialogName, CWnd *0wner) : 
    CDialog(DialogName, Owner) {} 
    BOOL OnInitDialog (); 
    afx msg void OnRedO; 
    afx msg void OnGreenO; 
    afx msg void OnSelectO; 
    DECLARE_MESSAGE_MAP() 
    }; 


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

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

    // Работа со списком 
    ^include <afxwin.h> 
    tinclude <string.h> 
    tinclude "dialog.h" 
    #inciude "ids.h" 
    CMainWin::CMainWin() 
    ( 
    Create(NULL, "Demonstrate Dialog Boxes", 
    WS_OVERLAPPEDWINDOW, rectDefault, 
    NULL, "DialogMenu"); 
    // Загрузка таблицы быстрого доступа 
    if('LoadAccelTable("DialogMenu")) 
    MessageBox("Cannot Load Accelerators", "Error"); 
    ) 
    // Инициализация приложения 
    BOOL CApp::Initlnstance () 
    ( 
    m pMainWnd = new CMainWin; 
    m pMainWnd->ShowWindow(m nCmdShow) ; 
    m_pMainWnd->UpdateWindow() ; 
    return TRUE; 
    // Это очередь сообщений приложения 
    BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) 
    ON_COMMAND(IDM_DIALOG, OnDialog) 
    ON_COMMAND(IDM_EXIT, OnExit) 
    ON_COMMAND(IDM_HELP, OnHelp) 
    END_MESSAGE_MAP() 
    // Обработка сообщения IDM_DIALOG 
    afx msg void CMainWin::OnDialog() 
    { 
    CSampleDialog diagOb("SampleDialog", this); 
    diagOb.DoModal(); // активизирует модальное диалоговое окно 
    ) 
    // Обработка сообщения IDM_EXIT 
    afx msg void CMainWin::OnExit() 
    ( 
    int response; 
    response = MessageBox("Quit the Program?", "Exit", MB_YESNO) ; 


    5. Диалоговые окна                                               105 


    if(response == IDYES) 
    SendMessage(WM CLOSE); // завершение приложения 
    ) 
    // Обработка сообщения IDM_HELP 
    afx_msg void CMainWin::OnHelp() 
    ( 
    MessageBox("Dialog Demo", "Help"); 
    } 
    // Это очередь сообщений диалогового окна 
    BEGIN_MESSAGE_MAP(CSampleDialog, CDialog) 
    ON_COMMAND(IDD_RED, OnRed) 
    ON_COMMAND(IDD_GREEN, OnGreen) 
    ON_COMMAND(IDD_SELFRUIT, OnSele_ct) 
    ON_LBN_DBLCLK (I DD_LB 1, OnSeTect") 
    END_MESSAGE_MAP () 
    // Инициализация диалогового окна. 
    BOOL CSampleDialog::OnInitDialog() 
    ( 
    CDialog::OnInitDialog(); // Вызов функции базового класса 
    CListBox *lbptr = (CListBox *) GetDlgItem(IDD_LB1); 
    // Инициализация списка 
    lbptr->Add3tring("Apple"); 
    Ibptr->AddString("Orange") ; 
    lbptr->AddString("Pear") ; 
    lbptr->AddString ("Grape") ; 
    return TRUE; 
    ) 
    // Обработка сообщения IDD_RED 
    afx msg void CSampleDialog::OnRed() 
    MessageBox("Red", "Color Selected"); 
    ) 
    // Обработка сообщения IDD_GREEN 
    afx msg void CSampleDialog::OnGreen() 
    ( 
    MessageBox("Green", "Color Selected") ; 
    ) 
    // Обработка сообщений IDD SELFRUIT и LBN DBLCLK 
    afx_msg void CSampleDialog::OnSelect() 
    { 
    CListBox *lbptr = (CListBox *) GetDlgItem(IDD_LB1); 
    char strl[80], str2[80]; 
    int i; 


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


    i = lbptr->GetCurSel (); // индекс выбранного элемента 
    if(i==LB_ERR) wsprintf(strl, "No Selection"); 
    else { 
    lbptr->GetText (i, strl); // получение строки 
    wsprintf (str2, " at index %d.", i); 
    strcat(strl, str2) ; 
    ) 
    MessageBox(strl, "Selection Made"); 
    } 
    CApp App; // создание экземпляра приложения 

    При компиляции программы не забудьте использовать обновленные версии 
файлов DIALOG.RC и 1DS.H, описанные ранее. Пример работы программы представлен 
на рис. 5.3. 

    Рис. 5.3. Работа со списком 

    Фактически, есть два способа ^выбора элементов списка. Во-первых, можно 
выполнить двойной щелчок на элементе. В результате будет послано 
идентификационное сообщение LBN_DBLCLK. Таким образом, программа будет 
немедленно проинформирована о выборе, сделанном пользователем. Другой способ 
заключается в том, чтобы отметить требуемый элемент (одинарным щелчком мыши 
или с помощью клавиш управления курсором), а затем нажать кнопку Select Fruit 
(выбор фрукта). В примере используются оба способа. Каждый из них 
обрабатывается с помощью одной и той же функции: OnSelectQ. 

    5. Диалоговые окна                                               107 

    Внутри OnSelectQ для получения индекса выбранного элемента используется 
функция GetCurSelQ. Если она вызывается до того, как что-либо выбрано, то 
возвращается LB_ERR. Сама выбранная строка получается с помощью вызова функции 
GetTextQ. Выбранная строка и связанный с ней индекс отображаются затем в окне сообщения. 

    Создание поля ввода 

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

    SampleDialog DIALOG 18, 18, 142, 92 
    CAPTION "Sample Dialog Box" 
    STYLE DS_MODALFRAME | WS_POPUP I WS_CAPTION I WS_SYSMENU 
    { 
    DEFPUSHBUTTON "Red", IDD_RED, 56, 45, 30, 14, 
    WS_CHILD I WS_VISIBLE I MS_TABSTOP 
    PUSHBUTTON "Green", IDD_GREEN, 96, 45, 30, 14, 

    WS_CHILD I WS_VISIBLE | WS_TABSTOP 
    PUSHBUTTON "Cancel", IDCANCEL, 52, 65, 37, 14, 
    
    WS_CHILD I MS_VISIBLE | WSJTABSTOP 
    LISTBOX IDD_LB1, 2, 10, 47, 32, LBS_NOTIFY | WS_CHILD | 
    
    WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_TABSTOP 
    PUSHBUTTON "Select Fruit", IDD_SELFRUIT, 5, 45, 42, 14, 
    
    WS_CHILD I WS_VISIBLE | WS_TABSTOP 
    PUSHBUTTON "Edit OK", IDD_EDITOK, 68, 22, 30, 14, 

    WS_CHILD I WS^VISIBLE | HS_TABSTOP 
    EDITTEXT IDD_EB1, 68, 8, 72, 12, ES_LEFT I ES_AUTOHSCROLL | 

    WS_CHILD I WS_VISIBLE | WS_BORDER I WS_TABSTOP 
    } 

    Теперь к диалоговому окну добавляется кнопка Edit OK, по нажатию которой 
программе будет сообщаться о том, что пользователь закончил редактировать 
текст в поле ввода. Здесь также описывается само поле ввода. Его 
идентификатором является IDD_EB1. Идентификатором для кнопки Edit OK служит IDD_EDITOK. 
    Общий вид оператора EDITTEXT таков: 

    EDITTEXT EDID, X, У, Width, Height [,Стиль] 

    Здесь параметр EDID является идентификатором поля ввода. Верхний левый 
угол поля будет иметь координаты (X,Y), а его размеры определяются параметрами 
Width и Height. Параметр Стиль определяет стиль поля. Поля ввода бывают двух 
основных типов: состоящие из одной строки и состоящие из нескольких строк. 
Поля первого типа позволяют вводить лишь одну текстовую строку. В нашем 
примере создается именно такой тип поля ввода. 

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

    Затем добавьте следующие макроопределения в файл IDS.H: 

    ftdefine IDD_EB1         204 
    #define IDD_EDITOK      205 

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

    int CWnd::GetWindowText(LPSTR IpszStr, int Max) const; 

    В результате выполнения функции содержимое поля ввода будет скопировано в 
строку, адресуемую указателем Ips^Str. Максимальное число символов, которое 
должно быть скопировано, определяется параметром Мах. Функция возвращает длину 
полученной строки. С помощью GetWindowTextQ можно получить текст, связанный с 
любым окном или элементом управления. Применительно к окну функция получает 
заголовок окна. Применительно к элементу управления функция получает текст, 
содержащийся в нем. Мы будем использовать GetWindowText() только для полей 
ввода, состоящих из одной строки. (Для полей с несколькими строками 
используются функции-члены класса CEdit.) 
    В момент создания поле ввода является пустым. Для инициализации его 
содержимого используется еще одна функция-член класса CWnd, называющаяся 
SetWindowText(). Она отображает текстовую строку в элементе управления, 
который вызвал эту функцию. Ее прототип таков: 

    void CWnd::SetWindowText(LPCSTR JpszStr) ; 

    Здесь параметр IpszSfr является указателем на строку, которая будет 
отображена в поле ввода. 
    В следующей программе к диалоговому окну будет добавлено поле ввода и 
кнопка Edit OK. Вот текст обновленного файла DIALOG.H, содержащего объявления 
порождаемых классов: 

    // DIALOG.H 
    // Это класс для создания главного окна 
    class CMainWin : public CFrameWnd 
    ( 
    public: 
    CMainWin () ; 
    // объявления обработчиков меню 
    afx msg void OnDialogO; 
    afx__msg void OnExit(); 
    afx msg void OnHelpO; 


    5. Диалоговые окна                                               109 


    DECLARE_MESSAGE_MAP() }; 
    // Это класс для создания приложения 
    class CApp : public CWinApp 
    ( 
    public: 
    BOOL Initlnstance (); 
    }; 
    // Это класс для создания диалогового окна 
    class CSampleDialog : public CDialog 
    ( 
    public: 
    CSampleDialog(char *DialogName, CWnd *0wner) : 
    CDialog(DialogName, Owner) {} 
    BOOL OnInitDialog() ; 
    afx_nisg void OnRedO; 
    afx_msg void OnGreenf); 
    afx msg void OnSelect(); 
    afx msg void OnEditOK(); 
    DECLARE_MESSAGE_MAP() }; 
    Вот текст программы, включающей поддержку поля ввода: 
    // Работа с полем ввода 
    ^include <afxwin.h> 
    ^include <string.h> 
    ^include "dialog.h" 
    #include "ids.h" 
    CMainWin: .-CMainWin () 
    ( 
    Create(NULL, "Demonstrate Dialog Boxes", 
    WS_OVERLAPPEDWINDOW, rectDefault, 
    NULL, "DialogMenu") ; 
    // Загрузка таблицы быстрого доступа 
    if(!LoadAccelTable("DialogMenu")) 
    MessageBox ("Cannot Load Accelerators", "Error"); 
    } 
    // Инициализация приложения 
    BOOL CApp::Initlnstance() 
    { 
    т pMainWnd = new CMainWin; 
    m_pMainWnd->ShowWindow(m_nCmdShow) ; 
    m_pMainWnd->UpdateWindow() ; 


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


    return TRUE; 
    ) 
    // Это очередь сообщений приложения 
    BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) 
    ON_COMMAND(IDM_DIALOG, OnDialog) 
    ON__COMMAND(IDM_EXIT, OnExit) 
    ON_COMMAND(IDM_HELP, OnHelp) 
    END_MESSAGE_MAP() 
    // Обработка сообщения IDM_DIALOG 
    afx msg void CMainWin::OnDialog() 
    ( 
    CSampieDialog diagOb("SampleDialog", this); 
    diagOb.DoModal(); // активизирует модальное диалоговое окно 
    } 
    // Обработка сообщения 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("Dialog Demo", "Help"); 
    } 
    // Это очередь сообщений диалогового окна 
    BEGIN_MESSAGE_MAP(CSampieDialog, CDialog) 
    ON__COMMAND(IDD_RED, OnRed) 
    ON_COMMAND(IDD_GREEN, OnGreen) 
    ON_COMMAND(IDD_SELFRUIT, OnSelect) 
    ON_COMMAND(IDD_EDITOK, _OnEditOK) 
    ON_LBN_DBLCLK(IDD_LB1, 'OnSelect) 
    END_MESSAGE_MAP() 
    // Инициализация диалогового окна. 
    BOOL CSampieDialog::OnInitDialog() 
    { 
    CDialog::OnInitDialog(); // Вызов функции базового класса 
    CListBox *lbptr = (CListBox *) GetDlgItem(IDD_LB1); 
    // Инициализация списка 
    lbptr->AddString("Apple") ; 
    lbptr->AddString("Orange") ; 
    lbptr->AddString("Pear") ; 


    5. Диалоговые окна                                               111 


    lbptr->AddString("Grape") ; 
    // Инициализация поля ввода 
    CEdit *ebptr = (CEdit *) GetDlgItem(IDD_EB1); 
    ebptr->SetWindowText("Sample Text") ; 
    return TRUE; 
    ) 
    // Обработка сообщения IDD RED 
    afx msg void CSampleDialog::OnRed() 
    ( 
    MessageBox("Red", "Color Selected"); 
    ) 
    // Обработка сообщения IDD_GREEN 
    afx msg void CSampleDialog::OnGreen() 
    ( 
    MessageBox("Green", "Color Selected"); 
    ) 
    // Обработка сообщений IDD_SELFRUIT и LBN_DBLCLK 
    afx msg void CSampleDialog::OnSelect() 
    ( 
    CListBox *lbptr = (CListBox *) GetDlgItem(IDD_LB1); 
    char strl[80], str2 [80]; 
    int i; 
    i = !bptr->GetCurSel (); // индекс выбранного элемента 
    if(i==LB_ERR) wsprintf(strl, "No Selection"); 
    else { 
    lbptr->GetText(i, strl); // получение строки 
    wsprintf (str2, " at index %d.", i); 
    strcat(strl, str2) ; 
    ) 
    MessageBox (strl, "Selection Made"); 
    } 
    // Обработка сообщения IDD EDITOK 
    afx msg void CSampleDialog::OnEditOK() 
    ( 
    CEdit *ebptr = (CEdit *) GetDlgItem(IDD_EB1); 
    char strl [80] ; 
    int i ; 
    i = ebptr->GetWindowText(str, sizeof str-1); 
    MessageBox (str, "Edit Box Contains"); 
    } 
    CApp App; // создание экземпляра приложения 


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

    Как видите, содержимое поля ввода инициализируется внутри функции 
OnlnitDialogO. При каждом нажатии кнопки Edit OK текущее содержимое поля 
отображается в окне сообщения. На рис. 5.4. приведен пример работы программы. 

    Рис. 5.4. Пример работы с полем ввода 


    Немодальные диалоговые окна 

    И напоследок давайте рассмотрим, каким образом модальное диалоговое окно, 
созданное в предыдущем примере, можно преобразовать в немодальное. Как 
правило, работа с немодальным окном требует чуть больше усилий. Основная 
причина состоит в том, что немодальное окно является в большей степени 
независимым. В частности, когда оно отображено на экране, остальная часть 
программы по-прежнему активна. Кроме того, и немодальное окно, и главное окно 
приложения продолжают принимать сообщения независимо друг отдруга. Поэтому 
работа с немодальным окном влечет за собой дополнительные операции. 
    Для создания немодального диалогового окна необходимо выполнить два 
действия. Во-первых, нужно создать "пустой" объект типа CDialog. Т.е. он не 
должен быть связан с шаблоном диалогового окна из файла ресурсов. Привязка к 
шаблону выполняется следующим шагом, но не через конструктор CSampleDialog, а 
через функцию CreateQ, являющуюся членом класса CDialog. Давайте рассмотрим 
оба этих действия.' 
    Для создания объекта немодального диалогового окна используется следующий 
вызов конструктора класса CDialog: 

    CDialog() ; 


    5. Диалоговые окна                                               113 

    Как видите, этот конструктор не несет никаких параметров. Единственное его 
назначение — активизировать механизм, необходимый для управления немодальным 
окном. Данный конструктор объявлен в разделе protected класса CDialog. Это 
означает, что он может быть вызван только членами класса, порожденного от 
CDialog. (Т.е. вне порожденного класса он использоваться не может.) Таким 
образом, данная версия конструктора CDialogO обычно вызывается из конструктора 
порожденного класса диалогового окна. 
    Когда экземпляр объекта диалогового окна создан, можно подключить его к 
шаблону диалогового окна с помощью функции CreateQ, прототипы которой следующие: 

    BOOL CDialog::Create(LPCSTR IpszDialogName, CWnd *Owner=NULL) ; 
    BOOL CDialog: .-Create (UINT ID, CWnd *Owner=WL,L) ; 

    В первом случае параметр Ips^DialogName определяет имя шаблона диалогового 
окна в файле ресурсов. Во втором случае шаблон задается с помощью 
идентификатора ID. В обоих случаях параметр Owner служит указателем на объект 
окна, которое является владельцем (или предком) создаваемого. Если Owner равен 
NULL, то владельцем будет главное окно приложения. Функция возвращает 
ненулевое значение при успешном завершении и ноль в противном случае. После 
вызова функции CreateQ немодальное диалоговое окно станет активным. 
    Необходимо также помнить о том, что создаваемый объект немодального окна 
должен существовать в течение всего времени, пока это окно используется. Это 
означает, что объект должен быть объявлен либо глобальным, либо статическим 
локальным. Вне области его видимости диалоговое окно будет уничтожено 
деструктором класса CDialog. Этого не происходило с модальными окнами, так как 
функция DoModalQ не завершала своего выполнения до тех пор, пока окно не будет 
закрыто пользователем. А функция CreateQ завершается немедленно после 
отображения окна. 
    В отличие от модальных окон, немодальные окна не становятся автоматически 
видимыми при вызове, если только в определение шаблона окна не была включена 
опция WS_VISIBLE. Вместо этого необходимо вызывать функцию ShowWindowQ. В то 
же время использование WS_VISIBLE значительно проще, поскольку окно будет 
отображаться автоматически. 
    Для закрытия немодального окна должна вызываться функция DestroyWindow(), 
а не EndDialogO, используемая только с модальными окнами. Это означает, что в 
классе диалогового окна должны быть переопределены функции OnCancelQ и/или 
ОпОК() (если в программе используются кнопки Cancel и ОК), так как по 
умолчанию они вызывают функцию EndDialogO. Функция DestroyWindow() является 
членом класса CWnd и имеет следующий прототип: 

    BOOL CWnd::DestroyWindow(); 

    Функция возвращает ненулевое значение при успешном завершении и ноль в 
противном случае. 

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


    Создание немодального окна 

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

    ; Пример файла ресурсов, содержащего ; определение диалогового окна и меню 
    #include <afxres.h> ^include "ids.h" 
    DialogMenu MENU 
    { 
    POPUP "&Dialog" < 
    MENUITEM "sSample Dialog", IDM_DIALOG 
    MENUITEM "E&xit", IDM_EXIT 
    ) 
    MENUITEM "SHelp", IDM_HELP 
    DialogMenu ACCELERATORS 
    ( 
    VK_F1, IDM_HELP, VIRTKEY 
    VK_F2, IDM_DIALOG, VIRTKEY 
    VK_F3, IDM_EXIT, VIRTKEY 
    } 
    SampleDialog DIALOG 18, 18, 142, 92 
    CAPTION "Sample Dialog Box" 
    
    STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | 
    WS_SYSMENU I WS_VISIBLE 
    
    DEFPUSHBUTTON "Red", IDD_RED, 56, 45, 30, 14, 
    
    WS_CHILD ] WS_VISIBLE I WS_TABSTOP 
    PUSHBUTTON "Green", IDD_GREEN, 96, 45, 30, 14, 
    
    WS_CHILD | WS_VISIBLE I WS_TABSTOP 
    PUSHBUTTON "Cancel", IDCANCEL, 52, 65, 37, 14, 
    
    WS^CHILD | WS_VISIBLE I WS_TABSTOP 
    LISTBOX IDD_LB1, 2, 10, 47, 32, LBS_NOTIFY | WS_CHILD | 
    
    WS_VISIBLE I WS_BORDER I WS_VSCROLL | WS_TABSTOP 
    PUSHBUTTON "Select Fruit", IDD_SELFRUIT, 5, 45, 42, 14, 
    
    WS_CHILD I WS_VISIBLE I WSJTABSTOP 
    PUSHBUTTON "Edit OK", IDD_EDITOK, 68, 22, 30, 14, 
    
    WS_CHILD | WS_VISIBLE I WSJTABSTOP 
    EDITTEXT IDD_EB1, 68, 8, 72, 12, ES_LEFT | ES_AUTOHSCROLL | 
    
    WS_CHILD | WS_VISIBLE I WS_BORDER | WS_TABSTOP 
    } 
    

    5. Диалоговые окна                                               115 

    В файл IDS.H также вносились изменения, поэтому приведем и его текст: 

    // IDS.H 
    #define IDM_DIALOG    100 
    #define IDM_EXIT      101 
    #define IDM_HELP      102 
    tdefine IDD_RED       200 
    #define IDD_GREEN     201 
    #define IDD_SELFRUIT 202 
    #define IDD_LB1       203 
    #define IDD_EB1       204 
    #define IDD_EDITOK    205 

    Затем в программу необходимо внести следующие изменения: 
    1. Создать глобальный объект типа CDialog. 
    2. Активизировать диалоговое окно с помощью функции CreateQ, а не DoModalQ. 
    3. Переопределить функцию ОпСапсеЦ) таким образом, чтобы она закрывала 
диалоговое окно вызовом функции DestroyWindow(). Вот текст файла, содержащего 
объявления порождаемых классов: 

    // DIALOG.H — версия для немодального диалогового окна 
    // Это класс для создания главного окна 
    class CMainWin : public CFrameWnd 
    ( 
    public: 
    CMainWin() ; 
    // объявления обработчиков меню 
    afx_msg void OnDialogO; 
    afx msg void OnExitO; 
    afx msg void OnHelpO; 
    DECLARE_MESSAGE_MAP() 
    }; 
    // Это класс для создания приложения 
    class CApp : public CWinApp 
    ( 
    public: 
    BOOL Initlnstance(); 
    }; 
    // Это класс для создания диалогового окна 
    class CSampleDialog : public CDialog 
    { 
    public: 
    CSampleDialog() : CDialog() (} 


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


    BOOL OnInitDialog(); 
    void OnCancel() ; 
    afx msg void OnRed(); 
    afx msg void OnGreenO; 
    afx msg void OnSelectO; 
    afx msg void OnOK () ; 
    DECLARE_MESSAGE_MAP() 
    }; 

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

    // Работа с немодальным диалоговым окном 
    ^include <afxwin.h> 
    ^include <string.h> 
    ftinclude "dialog.h" 
    ftinclude "ids.h" 
    // создание объекта немодального диалогового окна 
    CSampleDialog diagOb; 
    int DIgActive = 0; // не ноль, если диалоговое окно активно 
    CMainWin::CMainWin() ( 
    Create(NULL, "Demonstrate a Modeless Dialog Boxes", 
    WS_OVERLAPPEDWINDOW, rectDefault, 
    NULL, "DialogMenu"); 
    // Загрузка таблицы быстрого доступа 
    if ( 'LoadAccelTable("DialogMenu")) 
    MessageBox ("Cannot Load Accelerators", "Error"); 
    } 
    // Инициализация приложения 
    BOOL CApp::Initlnstance() 
    ( 
    m_pMainWnd = new CMainWin; 
    m pMainWnd->ShowWindow(m_nCmd3how) ; 
    m_pMainWnd->UpdateWindow() ; 
    return TRUE; 
    > 
    // Это очередь сообщений приложения 
    BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) 
    ON_COMMAND(IDM_DIALOG, OnDialog) 
    ON_COMMAND(IDM_EXIT, OnExit) 
    ON_COMMAND(IDM_HELP, OnHelp) 
    END_MESSAGE_MAP() 


    5. Диалоговые окна                                               717 


    // Обработка сообщения IDM_DIALOG 
    afx_msg void CMainWin::OnDialog() 
    < 
    if(DIgActive) return; // диалоговое окно уже активно 
    DIgActive = 1; 
    // связывает объект немодального диалогового окна 
    // с файлом ресурсов 
    diagOb.Create("SampleDialog", this) ; 
    // Обработка сообщения 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("Dialog Demo", "Help"); 
    ) 
    // Это очередь сообщений диалогового окна 
    BEGIN_MESSAGE_MAP(CSampleDialog, CDialog) 
    ON__COMMAND(IDD_RED, OnRed) 
    ON_COMMAND(IDD_GREEN, OnGreen) 
    ON_COMMAND(IDD_SELFRUIT, OnSelect) 
    ON_COMMAND(IDD_EDITOK, OnEditOK) 
    ON_LBN_DBLCLK(IDD_LB1, OnSelect) 
    END_MESSAGE_MAP() 
    // Инициализация диалогового окна. 
    BOOL CSampleDialog: .-OnInitDialog () 
    { 
    CDialog::OnInitDialog (); // Вызов функции базового класса 
    CListBox *lbptr = (CListBox *) GetDlgItem(IDD_LB1) ; 
    // Инициализация списка 
    lbptr->AddString("Apple") ; 
    lbptr->Add3tring("Orange") ; 
    lbptr->AddString("Pear") ; 
    lbptr->AddString("Grape") ; 
    // Инициализация поля ввода 
    CEdit *ebptr = (CEdit *) GetDlgItem(IDD_EB1) ; 
    ebptr->SetWindowText("Sample Text") ; 


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


    return TRUE; 
    i 
    // Закрытие немолального диалогового окна 
    void CSampleDialog::OnCancel() 
    DIgActive = 0; // диалоговое окно неактивно 
    DestroyWindow(); 
    } 
    // Обработка сообщения IDD RED 
    afx msg void CSampleDialog::OnRed() 
    < 
    MessageBox("Red", "Color Selected"); 
    t 
    // Обработка сообщения IDD_GREEN 
    afx_msg void CSampleDialog::OnGreen() 
    { 
    MessageBox("Green", "Color Selected"); 
    ) 
    // Обработка сообщений IDD_SELFRUIT и LBN_DBLCL'K 
    afx_msg void CSampleDialog::OnSelect() 
    { 
    CListBox *lbptr = (CListBox *) GetDlgItem(IDD_LB1); 
    char strl [80], str2 [80] ; 
    int i; 
    i = !bptr->GetCurSel(); // индекс выбранного элемента 
    
    if(i==LB_ERR) wsprintf(strl, "No Selection"); 
    else { 
    lbptr->GetText (i, strl); // получение строки 
    wsprintf (str2, " at index %d.", i) ; 
    strcat (strl, str2) ; 
    ) 
    MessageBox(strl, "Selection Made"); 
    // Обработка сообщения IDD_EDITOK 
    afx_msg void CSampleDialog::OnEditOK() 
    { 
    CEdit *ebptr = (CEdit *) GetDlgItem(IDD_EB1); 
    char strl [80] ; 
    int i; 
    i = ebptr->GetWindowText (str, sizeof str-1); 
    MessageBox (str, "Edit Box Contains"); 
    } 
    CApp App; // создание экземпляра приложения 


    5. Диалоговые окна                                                119 

    Пример работы программы показан на рис. 5.5. Обратите внимание, что и 
диалоговое окно, и приложение одновременно являются активными (о чем 
свидетельствует появление окна сообщения Exit). 

    Рис. 5.5. Пример работы с немодальным диалоговым окном 

    Просматривая текст программы, вы увидите, что в ней объявлена глобальная 
переменная DIgActive, начальное значение которой равно нулю. Она используется 
для предотвращения многократных вызовов диалогового окна. Хотя многократные 
вызовы не являются чем-то неправильным, чаще всего они нежелательны. Более 
того, поскольку для хранения объекта типа CDialog в программе используется 
глобальная переменная, то повторный вызов окна привел бы к перезаписи ее 
содержимого, а это, в свою очередь, вызвало бы ошибку периода выполнения 
и^экстренное завершение программы. '""""~"""~————— 
    В данной главе были лишь вкратце затронуты вопросы работы с диалоговыми 
окнами и различными элементами управления. Позднее будут рассмотрены и другие 
элементы. Пока же вы можете самостоятельно поэксперимснтировать с описанными в 
этой главе функциями и примерами программ. 

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