    Дополнительные элементы управления 

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

123


    Использование контрольных переключателей 

    Контрольный переключатель — это элемент управления, предназначенный для 
установки или снятия определенной опции. Он состоит из маленького 
прямоугольного поля, в котором может стоять метка выбора. Кроме того, с 
переключателем связано текстовое поле с описанием представляемой 
переключателем опции. Если в поле переключателя стоит метка выбора, то 
говорится, что переключатель выбран (или установлен), а связанная с ним опция 
активизирована (или включена). Если контрольное поле пустое, то опция не 
установлена. Контрольные переключатели в MFC описываются классом CButton. 
    Контрольный переключатель обычно является частью диалогового окна и 
задается при определении окна в файле ресурсов. Для этого используется 
оператор CHECKBOX, общий вид которого следующий: 

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

    Здесь параметр строка содержит текстовую строку, которая будет отображена 
рядом с контрольным переключателем. Верхний левый угол контрольного поля будет 
иметь координаты (X,Y), а размеры поля задаются параметрами Width и Height. 
Параметр Стиль определяет стиль переключателя. Если стиль явно не указан, то 
по умолчанию текстовое поле будет отображено справа, а нужный переключатель 
можно будет выбрать нажатием клавиши [TAB]. Как известно, контрольные 
переключатели могут изменять свое состояние. Каждый раз при щелчке на 
переключателе он меняет состояние с установленного на сброшенное или наоборот. 
Но это не означает, что такое изменение происходит автоматически. При 
использовании оператора CHECKBOX создается программный переключатель, 
состояние которого должно изменяться программой при каждом нажатии. (Как это 
сделать, будет вкратце рассказано позже.) Однако можно добиться, чтобы Windows 
выполняла эти функции автоматически, если создать автоматический 
переключатель. Это выполняется с помощью оператора AUTOCHECKBOX. Он имеет 
такой же вид, что и CHECKBOX. Каждый раз при щелчке на автоматическом 
переключателе Windows будет автоматически менять его состояние (установлен или сброшен). 

    Примечание: При работе в Windows 3.1 оператор AUTOCHECKBOX может не 
поддерживаться. В этом случае можно либо работать с программным 
переключателем, либо создавать автоматический, но уже с помощью оператора 
CONTROL, задавая класс BUTTON и стиль BS_AUTOCHECKBOX. 

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

    ; DIALOG.RC 
    #include <afxres.h> 
    •If-include "ids.h" 
    DialogMenu MENU 


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


    { 
    POPUP "SDialog" 
    { 
    MENUITEM "&Dialog", IDM_DIALOG 
    MENUITEM "SStatus", IDM_STATUS 
    MENUITEM "E&xit", IDM_EXIT 
    } 
    MENUITEM "&Help", IDM_HELP » 
    DialogMenu ACCELERATORS 
    ( 
    VK_F1, IDM_HELP, VIRTKEY 
    VK_F2, IDMJ3IALOG, VIRTKEY 
    VK_F3, IDM_STATUS, VIRTKEY 
    VK_F4, IDM_EXIT, VIRTKEY 
    } 
    SampleDialog DIALOG 18, 18, 142, 92 
    CAPTION "Test Dialog Box" 
    STYLE DS_MODALFRAME I WS_POPUP I WS_CAPTION I WS_SYSMENU 
    { 
    PUSHBUTTON "OK", IDOK, 77, 44, 30, 13, 
    WS_CHILD I WS_VISIBLE | WS_TABSTOP 
    
    PUSHBUTTON "Cancel", IDCANCEL, 74, 65, 37, 14, 
    WS_CHILD I WS_VISIBLE | WS_TABSTOP 
    
    CHECKBOX "Checkbox I", IDD_CB1, 4, 10, 52, 12 
    
    AUTOCHECKBOX "Checkbox 2", IDD_CB2, 4, 22, 52, 12 
    ) 

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

    // IDS.H 
    #define IDMJ3IALOG    100 
    #define IDM_EXIT      101 
    #define IDM_HELP      102 
    #define IDM_STATUS    103 
    #define IDD_CB1       200 
    #define IDD_CB2       201 
    #define IDD_CT1       300 
    #define IDD_RB1       400 
    #define IDD_RB2       401 
    #define IDD_GB1       500 
    #define IDD_GB2       501 


    6. Дополнительные элементы управления                             J25 


    Сообщения контрольного переключателя 

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

    Установка и проверка состояния контрольного переключателя 

    Чтобы установить контрольный переключатель в заданное состояние, 
необходимо использовать функцию SetCheckQ, являющуюся членом класса CButton. 
Ее прототип таков: 

    void CButton::SetCheck (int How); 

    При вызове SetCheckQ параметр How определяет требуемое состояние 
переключателя. Если этот параметр равен 1, то состоянием переключателя будет 
"установлено", в противном случае — "сброшено". По умолчанию при первом вызове 
диалогового окна контрольный переключатель будет сброшен. 
    Помните, что при работе с автоматическим переключателем его состояние 
меняется автоматически в момент выбора. Для этого не нужно вызывать функцию 
SetCheckQ. Но она может быть использована при инициализации такого переключателя. 
    Текущее состояние контрольного переключателя можно определить с помощью 
функции GetCheck(). Ее прототип таков: 

    int CButton::GetCheck() const; 

    Функция возвращает 1, если переключатель установлен, и 0 — в противном случае. 
    Ниже приводится текст программы, в которой демонстрируется работа с 
автоматическим и программным переключателем. Отличие между ними состоит в том, 
что если программный переключатель установлен, то он останется в этом 
состоянии постоянно. Сбросить его будет нельзя. Как управлять программными 
переключателями, показано в следующем примере. 
    Вот текст файла DIALOG.H, содержащего объявления порождаемых классов. 
Обратите внимание, что функция ОпОК() класса CDialog переопределяется в классе CSampleDialog. 

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


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


    CMainWinO ; 
    // объявления обработчиков меню 
    afx_msg void OnDialogO; 
    afx msg void OnStatusO; 
    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) (} 
    afx_msg void OnOK(); 
    afx msg void OnCBl(); 
    DECLARE_MESSAGE_MAP() 
    ); 
    Вот текст программы: 
    // Работа с контрольными переключателями 
    tinclude <afxwin.h> 
    #include <string.h> 
    ^include "dialog.h" 
    #include "ids.h" 
    // состояния контрольных переключателей 
    int cbstatusl=0, cbstatus2=0; 
    CMainWin::CMainWin() 
    { 
    Create(NULL, "Using Check Boxes", 
    WS_OVERLAPPEDWINDOW, rectDefault, 
    NULL, "DialogMenu") ; 
    // Загрузка таблицы быстрого доступа 
    if('LoadAccelTable("DialogMenu") ) 


    6. Дополнительные элементы управления                             127 


    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_STATUS, OnStatus) 
    ON_COMMAND(IDM_EXIT, OnExit) 
    ON_COMMAND(IDM_HELP, OnHelp) 
    END_MESSAGE_MAP() 
    // Обработка сообщения IDM_DIALOG 
    afx msg void CMainWin::OnDialog() 
    ( 
    CSampleDialoq diagOb("SampleDialog", this); 
    diagOb.DoModal(); // активизирует модальное диалоговое окно 
    // Обработка сообщения IDM_STATUS 
    afx msq void CMainWin::0n3tatus () 
    ( 
    char str[255] ; 
    if (cbstatusl) 
    wsprintf (str, "Checkbox 1 is checked.\n"); 
    else 
    wsprintf (str, "Checkbox 1 is cleared.\n"); 
    if (cbstatus2) 
    strcatfstr, "Checkbox 2 is checked."); 
    else 
    strcatfstr, "Checkbox 2 is cleared."); 
    MessageBox (str, "Status"); 
    // Обработка сообщения IDM_EXIT 
    afx_msg void CMainWin::OnExit() 
    { 
    int response; 


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


    response = MessageBox("Quit the Program?", "Exit", MB YESNO); 
    if(response == IDYES) 
    SendMessage(WM_CLOSE); // завершение приложения 
    ) 
    // Обработка сообщения IDM_HELP 
    afx msg void CMainWin::OnHelp() 
    ( 
    MessageBox("More Controls", "Help"); 
    i 
    // Это очередь сообщений диалогового окна 
    BEGIN_MESSAGE_MAP(CSampleDialog, CDialog) 
    ON_BN_CLICKED(IDD_CB1, OnCBl) 
    END_MESSAGE_MAP() 
    // Явная обработка сообщения IDOK 
    afx msg void CSampleDialog::OnOK() 
    ( 
    // Обновление состояния контрольных переключателей 
    CButton *cblptr = (CButton *) GetDlgItem(IDD_CB1); 
    CButton *cb2ptr = (CButton *) GetDlgItem(IDD_CB2); 
    cbstatusl = cblptr->GetCheck() ; 
    cbstatus2 = cb2ptr->GetCheck(); 
    EndDialog(0) ; 
    } 
    // Обработка сообщения ON BN_CLICKED для первого переключателя 
    afx msg void CSampleDialog::OnCBl() 
    ( 
    CButton *cblptr = (CButton *) GetDlgItem(IDD_CB1); 
    cbipti-->SetCheck(1) ; 
    CApp App; // создание экземпляра приложения 

    Чтобы вызвать на экран диалоговое окно, содержащее контрольные 
переключатели, выберите в меню Dialog команду Dialog. Чтобы проверить 
состояние переключателей, выберите в том же меню команду Status. Пример работы 
программы показан на рис. 6.1. 

    6. Дополнительные элементы управления                             129 


    Рис. 6.1. Диалоговое окно с контрольными переключателями 

    Давайте подробнее рассмотрим некоторые части программы. Во-первых, 
обратите внимание, что она содержит две глобальные переменные, cbstatusl и 
cbstatus2, которые хранят состояние двух контрольных переключателей. Их 
значения обновляются функцией ОпОК() при нажатии кнопки ОК в диалоговом окне, 
но не изменяются при нажатии кнопки Cancel. Это отражает один из 
фундаментальных принципов программирования под Windows: действия над элементом 
управления дают результат только в том случае, если пользователь закрывает 
диалоговое окно с помощью кнопки ОК, а не Cancel. 
    Во-вторых, следует помнить, что для получения указателя типа CButton на 
объект контрольного переключателя в программе используется функция 
GetDlgItem(). Это позволяет функции-члену класса CButton выполнять действия 
над переключателем. Данный принцип был описан в главе 5, где также 
использовалась функция GetDlgItem(). 
    И наконец, с помощью функции ОпСВ1() обрабатывается сообщение BN_CLICKED, 
генерируемое при щелчке мыши на первом контрольном переключателе (который 
является программным). Эта функция устанавливает в контрольном поле метку выбора. 

    Управление контрольными переключателями 

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

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

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

    Изменение состояния переключателя 

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

    // Обработка сообщения ON_BN_CLICKED для первого переключателя 
    afx msg void CSampleDialoq::OnCBl() 
    { 
    CButton *cblptr = (CButton *) GetDlgItem(IDD_CBl); 
    // переключение состояния 
    if(cblptr->GetCheck() ) cblptr->SetCheck(0) ; 
    else cblptr->SetCheck(l); 


    Инициализация переключателя 

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

    // Инициализация диалогового окна 
    BOOL CSampleDialog::OnInitDialog() 
    1 
    CDialog::OnInitDialog (); // Вызов функции базового класса 
    // Инициализация контрольных переключателей 
    CButton *cblptr = (CButton *) GetDlgItem(IDD_CBl); 
    CButton *cb2ptr = (CButton *) GetDlgItem(IDD_CB2) ; 
    cblptr->SetCheck(cbstatusl) ; 
    cb2ptr->SetCheck(cbstatus2) ; 
    return TRUE; 


    6. Дополнительные элементы управления                             131 

    Ниже приводится полный текст программы, включающей инициализацию 
контрольных переключателей и управление программным переключателем. Сравните 
ее с программой из предыдущего примера. Как и следовало ожидать, она ведет 
себя так, как и подобает "настоящему" приложению Windows. Вот текст файла 
DIALOG.H, в котором к объявлению класса CSampleDialog добавлена функция OnInitDialogQ: 

    // DIALOG.H 
    // Обновленная версия, включающая функцию OnInitDialog() 
    // Это класс для создания главного окна 
    class CMainWin : public CFrameWnd 
    { 
    public: 
    CMainWin() ; 
    // объявления обработчиков меню 
    afx msg void OnDialog(); 
    afx msg void OnStatusO; 
    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) (} 
    afx msg void OnOK () ; 
    BOOL OnInitDialog(); 
    afx msg void OnCBl (); 
    DECLARE_MESSAGE_MAP() }; 
    Ниже приведен обновленный текст программы: 
    // Работа с контрольными переключателями — обновленная версия 
    #include <afxwin.h> 
    #include <string.h> 


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


    #include "dialog.h" 
    ^include "ids.h" 
    // состояние контрольных переключателей 
    int cbstatusl=0, cbstatus2=0; 
    CMainWin::CMainWin() ( 
    Create(NULL, "Using Check Boxes", 
    WS_OVERLAPPEDWINDOW, rectDefault, 
    NULL, "DialogMenu"); 
    // Загрузка таблицы быстрого доступа 
    if ( !LoadAccelTable("DialogMenu")) 
    MessageBox("Cannot Load Accelerators", "Error"); 
    ) 
    // Инициализация приложения 
    BOOL CApp::Initlnstance() 
    ( 
    m pMainWnd = new CMainWin; 
    m pMainWnd->ShowMindow(m nCmdShow) ; 
    m_pMainHnd->UpdateWindow(); 
    return TRUE; 
    \ 
    I 1 Это очередь сообщений приложения 
    BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) 
    ON_COMMAND(IDM_DIALOG, OnDialog) 
    ON_COMMAND(IDM_STATUS, OnStatus) 
    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_STATUS 
    afx msg void CMainWin::OnStatus () 
    ( 
    char str[255] ; 
    if(cbstatusl) 
    wsprintf(str, "Checkbox 1 is checked.\n"); 
    else 
    wsprintf(str, "Checkbox 1 is cleared.\n"); 


    6. Дополнительные элементы управления                             133 


    if(cbstatus2) 
    strcat(str, "Checkbox 2 is checked."); 
    else 
    strcat (str, "Checkbox 2 is cleared."); 
    MessageBox(str, "Status"); 
    } 
    // Обработка сообщения IDM EXIT 
    afx msq void CMainWin::OnExit() 
    1 
    int responses
    response = MessageBox("Quit the Program?", "Exit", MB_YESNO); 
    if (response == IDYES) 
    SendMessage(WM_CLOSE); // завершение приложения 
    // Обработка сообщения IDM HELP 
    afx msg void CMainWin::OnHelp () 
    { 
    MessageBox ("More Controls", "Help"); 
    } 
    // Это очередь сообщений диалогового окна 
    BEGIN_MESSAGE_MAP(CSampleDialog, CDialog) 
    
    ON_BN__CLICKED(IDD_CB1, OnCBl) 
    END_MESSAGE_MAP() 
    
    // Инициализация диалогового окна 
    BOOL CSampleDialog::OnInitDialog () { 
    CDialog::OnInitDialog (); // Вызов функции базового класса 
    
    // Инициализация контрольных переключателей 
    CButton *cblptr = (CButton *) GetDlgItem(IDD_CB1); 
    CButton *cb2ptr = (CButton *) GetDlgItem(IDD_CB2); 
    cblptr->SetCheck(cbstatusl) ; 
    cb2ptr->SetCheck (cbstatus2) ; 
    return TRUE; 
    } 
    // Явная обработка сообщения IDOK 
    afx msg void CSampleDialog::OnOK () 
    1 
    
    // Обновление состояния контрольных переключателей 
    CButton *cblptr = (CButton *) GetDlgItem(IDD_CBl); 
    CButton *cb2ptr = (CButton *) GetDlgItem(IDD_CB2) ; 
    cbstatusl = cblptr->GetCheck(); 


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


    cbstatus2 = cb2ptr->GetCheck() ; 
    EndDialog(0) ; 
    ) 
    // Обработка сообщения ON BN CLICKED для первого переключателя 
    afx_msg void CSampleDialog::OnCBl() 
    ( 
    CButton *cblptr = (CButton *) GetDlgItem(IDD_CBl) ; 
    // переключение состояния 
    if(cblptr->GetCheck()) cblptr->SetCheck(0) ; 
    else cblptr->SetCheck(1); 
    CApp App; // создание экземпляра приложения 


    Создание статических элементов 

    Статическим называется элемент, который не принимает и не генерирует 
никаких сообщений. Формально, термином "статический элемент" обозначают то, 
что просто отображается в диалоговом окне, например текстовая строка или 
простая рамка, предназначенная для объединения нескольких элементов управления 
в общую группу. Поэтому двумя статическими элементами, которые мы рассмотрим в 
данном параграфе, будут так называемые центрированный текст и групповая рамка. 
Они должны быть включены в определение диалогового окна в файле ресурсов с 
помощью операторов СТЕХТ и GROUPBOX, соответственно. 
    Оператор СТЕХТ задает текстовую строку, которая будет отцентрирована в 
пределах заданной области диалогового окна. Общий вид оператора следующий: 

    СТЕХТ "строка", CTID, X, Y, Width, Height [,Стиль] 

    Здесь параметр строка задает отображаемый текст. CTID — это связанный с 
текстовой строкой идентификатор. Текст будет выведен в поле, верхний левый 
угол которого имеет координаты (X, Y), а размеры задаются параметрами Width и 
Height. Параметр Стиль определяет стиль поля. Если он не указан, то по 
умолчанию текст будет отображен центрировано в пределах поля. Следует помнить, 
что самого поля видно не будет. Оно просто определяет область, занимаемую текстом. 
    Оператор GROUPBOX задает прямоугольную рамку. Она обычно используется для 
визуального выделения нескольких элементов управления в общую группу. Эта 
рамка может также иметь заголовок. Общий вид оператора GROUPBOX таков: 

    GROUPBOX "заголовок", GBID, X, У, Width, Height [,Стиль] 

    Здесь параметр заголовок задает заголовок рамки. GBID — это идентификатор 
рамки. Ее верхний левый угол будет иметь координаты (Х,У), а размеры рамки 
задаются параметрами Width и Height. Параметр Стиль определяет стиль рамки, 
но, как правило, установок по умолчанию вполне достаточно. 

    6. Дополнительные элементы управления                             135 

    Для использования статических элементов в программе, добавьте следующие 
строки к определению диалогового окна в файле ресурсов, созданному в 
предыдущем примере: 
    
    GROUPBOX "Checkboxes", IDD_GB1, I, 1, 56, 34 
    СТЕХТ "This is text", IDD_CT1, 1, 44, 50, 24 

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

    Рис. 6.2. Пример использования статических элементов 


    Создание селекторных кнопок 

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

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

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

    AUTORADIOBUTTON "сгрока", RBID. X, У, Width, Height [,Стиль] 

    Здесь параметр строка задает текстовую строку, которая будет отображена 
рядом с кнопкой. RBID — это идентификатор кнопки. Верхний левый угол 
невидимого поля, в котором находится кнопка, будет иметь координаты (X,Y), а 
размеры кнопки и связанного с нею текстового поля задаются параметрами Width и 
Height. Параметр Стиль определяет стиль кнопки. Если он не указан, то по 
умолчанию текст отображается справа от кнопки, а саму кнопку можно будет 
выбрать с помощью последовательного нажатия клавиши [TAB]. 

    Примечание: При работе в среде Windows 3.1 оператор AUTORADIOBUTTON может 
не поддерживаться. В этом случае можно либо программно управлять селекторными 
кнопками, либо создавать автоматические кнопки при помощи оператора CONTROL, 
задавая класс BUTTON и указывая стиль BS_AUTORADIOBUTTON. 

    Как уже было сказано, селекторные кнопки обычно используются для создания 
групп взаимоисключающих опций. Если для создания такой группы используются 
автоматические селекторные кнопки, то Windows будет автоматически переключать 
их таким образом, чтобы установленной оказалась лишь одна. Следовательно, при 
выборе какой-либо кнопки ранее выбранная кнопка будет автоматически сброшена. 
Понятно, что нельзя выбрать одновременно несколько таких кнопок. 
    Селекторная кнопка (включая автоматическую кнопку) может быть установлена 
программой в заданное состояние путем вызова функции CButton::SetCheck(). А 
текущее состояние кнопки можно получить с помощью функции CButton::GetCheck(). 
Обе они работают с селекторными кнопками точно так, как и с контрольными переключателями. 

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

    Чтобы включить селекторные кнопки в предыдущую программу, сначала добавьте 
приводимые ниже команды к определению диалогового окна в файле ресурсов. 
Обратите внимание, что для селекторных кнопок также создается своя групповая 
рамка. Это, конечно, необязательное требование, просто таков принятый стиль 
оформления диалоговых окон. 
    
    AUTORADIOBUTTON "Radio I", IDD_RB1, 66, 10, 48, 12 
    AUTORADIOBUTTON "Radio 2", IDD_RB2, 66, 22, 48, 12 
    GROUPBOX "Radio Group", IDD_GB2, 64, 1, 51, 34 
    

    6. Дополнительные элементы управления                             137 

    Поскольку мы периодически вносили изменения в файл ресурсов, приведем 
полный его текст: 

    ; DIALOG.RC 
    #include <afxres.h> 
    #include "ids.h" 
    DialogMenu MENU { 
    POPUP "&Dialog" 
    { 
    MENUITEM "&Dialog", IDM_DIALOG 
    MENUITEM "SStatus", IDM_STATUS 
    MENUITEM "E&xit", IDM_EXIT 
    ) 
    
    MENUITEM "SHelp", IDM_HELP 
    } 
    
    DialogMenu ACCELERATORS 
    { 
    VK_F1, IDM_HELP, VIRTKEY 
    VK_F2, IDM_DIALOG, VIRTKEY 
    VK_F3, IDM_STATUS, VIRTKEY 
    VK_F4, IDM_EXIT, VIRTKEY 
    } 
    SampleDialog DIALOG 18, 18, 142, 92 
    CAPTION "Test Dialog Box" 
    STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 
    ( 
    PUSHBUTTON "OK", IDOK, 11, 44, 30, 13, 
    MS_CHILD I WS_VISIBLE | WS_TABSTOP 
    
    PUSHBUTTON "Cancel", IDCANCEL, 74, 65, 37, 14, 
    WS_CHILD I WS_VISIBLE | WS_TABSTOP 
    CHECKBOX "Checkbox I", IDD_CB1, 4, 10, 52, 12 
    AUTOCHECKBOX "Checkbox 2", IDD_CB2, 4, 22, 52, 12 
    
    GROUPBOX "Checkboxes", IDD_GB1, 1, 1, 56, 34 
    СТЕХТ "This is text", IDD_CT1, 1, 44, 50, 24 
    
    AUTORADIOBUTTON "Radio I", IDD_RB1, 66, 10, 48, 12 
    AUTORADIOBUTTON "Radio 2", IDD_RB2, 66, 22, 48, 12 
    GROUPBOX "Radio Group", IDD_GB2, 64, 1, 51, 34 
    } 

    Ниже приводится текст программы, расширенной таким образом, чтобы включить 
селекторные кнопки. (Файл DIALOG.H остался без изменений.) Обратите внимание, 
что поскольку эти кнопки являются автоматическими, в программу внесены лишь 
незначительные дополнения. Состояние селекторных кнопок хранится в глобальных 
переменных rbstatusl и rbstatus2. Их значения изменяются функцией ОпОК() при 
закрытии диалогового окна. Эти значения используются для определения 

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

    начального состояния кнопок при вызове диалогового окна, а также для 
отображения статуса кнопок при выборе команды Select из меню Dialog. 

    // Работа с селекторными кнопками 
    ^include <afxwin.h> 
    #include <string.h> 
    ^include "dialog.h" 
    #inrlude "ids.h" 
    // состояние контрольных переключателей 
    int cbstatusl=0, cbstatus2=0; 
    // состояние селекторных кнопок 
    int rbstatusl=0, rbstatus2=0; 
    CMainWin::CMainWin() ( 
    Create (NULL, "Using Radio Buttons", 
    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_STATUS, OnStatus) 
    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(); // активизирует модальное диалоговое окно 
    

    6. Дополнительные элементы управления                             139 


    // Обработка сообщения IDM_STATUS 
    afx_msg void CMainWin::OnStatus() 
    { 
    char str [255] ; 
    if (cbstatusl) 
    wsprintf (str, "Checkbox 1 is checked.\n"); 
    else 
    wsprintf (str, "Checkbox 1 is cleared.\n"); 
    if (cbstatus2) 
    strcat(str, "Checkbox 2 is checked.\n"); 
    else 
    strcattstr, "Checkbox 2 is cleared.\n"); 
    if (rbstatusl) 
    strcattstr, "Radio 1 is checked.\n"); 
    else 
    strcattstr, "Radio 1 is cleared.\n"); 
    if (rbstatus2) 
    strcattstr, "Radio 2 is checked."); 
    else 
    strcat(str, "Radio 2 is cleared."); 
    MessageBox(str, "Status"); 
    } 
    // Обработка сообщения 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("More Controls", "Help"); 
    ) 
    // Это очередь сообщений диалогового окна 
    BEGIN_MESSAGE_MAP(CSampleDialog, CDialog) 
    
    ON_BN_CLICKED(IDD_CB1, OnCBl) 
    END_MESSAGE_MAP() 
    
    // Инициализация диалогового окна 
    BOOL CSampleDialog::OnInitDialog() 


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


    { 
    CDialog::OnInitDialog(); // Вызов функции базового класса 
    
    // Инициализация контрольных переключателей 
    CButton *cblptr = (CButton *) GetDlgItem(IDD_CB1); 
    CButton *cb2ptr = (CButton *) GetDlgItem(IDD_CB2); 
    cblptr->SetCheck(cbstatusl) ; 
    cb2ptr->SetCheck(cbstatus2) ; 
    
    // Инициализация селекторных кнопок 
    CButton *rblptr = (CButton *) GetDlgItem(IDD_RB1); 
    CButton *rb2ptr = (CButton *) GetDlgItem(IDD_RB2); 
    rblptr->SetCheck (rbstatusl) ; 
    rb2ptr->SetCheck(rbstatus2) ; 
    return TRUE; 
    } 
    // Явная обработка сообщения IDOK 
    afx msg void CSampleDialog::OnOK() 
    ( 
    
    // Обновление состояния контрольных переключателей 
    CButton *cblptr = (CButton *) GetDlgItem(IDD_CB1); 
    CButton *cb2ptr = (CButton *) GetDlgItem(IDD_CB2); 
    cbstatusl = cblptr->GetCheck(); 
    cbstatus2 = cb2ptr->GetCheck() ; 
    
    // Обновление состояния селекторных кнопок 
    CButton *rblptr = (CButton *) GetDlgItem(IDD_RB1); 
    CButton *rb2ptr = (CButton *) GetDlgItem(IDD_RB2) ; 
    rbstatusl = rblptr->GetCheck() ; 
    rbstatus2 = rb2ptr->GetCheck() ; 
    EndDialog(0) ; 
    i 
    // Обработка сообщения ON BN_CLICKED для первого переключателя 
    afx msg void CSampleDialog::OnCBl() 
    ( 
    CButton *cblptr = (CButton *) GetDlgItem(IDD_CB1) ; 
    
    // переключение состояния 
    if (cblptr->GetCheck()) cblptr->SetCheck(0) ; 
    else cblptr->SetCheck(l); 
    } 
    CApp App; // создание экземпляра приложения 

    Пример работы программы представлен на рис. 6.3. 

    6. Дополнительные элементы управления                             141 

    Рис. 6.3. Диалоговое окно с двумя селекторными кнопками 


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

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

    Создание стандартных полос прокрутки 

    Если требуется, чтобы окно содержало стандартные полосы прокрутки, они 
должны быть явно заданы. Применительно к главному окну приложения это 
означает, что при вызове функции Create() в качестве параметров стиля должны 
быть указаны опции WS_VSCROLL и/или WS_HSCROLL. Для диалогового окна эти опции 
должны быть указаны при определении окна в файле ресурсов. Как можно 

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

    предположить из их названия, опции WS_VSCROLL соответствует вертикальная 
полоса прокрутки, а опции WS_HSCROLL — горизонтальная полоса. Если такие опции 
указаны, то соответствующие им элементы отображаются автоматически. 

    Создание независимых полос прокрутки 

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

    SCROLLBAR SBID, X, У, Width, Height [,Стиль] 

    Здесь параметр SBID является идентификатором полосы прокрутки. Ее верхний 
левый угол будет иметь координаты (X,Y), а ее размеры задаются параметрами 
Width и Height. Параметр Стиль определяет стиль полосы. Стилем по умолчанию 
считается SBS_HORZ, т.е. горизонтальное расположение. Для вертикальной полосы 
необходимо задать опцию SBS_VERT. Если полоса прокрутки может быть 
активизирована последовательным нажатием клавиши [TAB], то необходимо указать 
опцию WS_TABSTOP. В следующей строке описывается вертикальная полоса прокрутки: 

    SCROLLBAR ID_SB1, 130, 10, 10, 70, SBS_VERT | WS_TABSTOP 


    Обработка сообщений полосы прокрутки 

    В отличие от других элементов управления, полосы прокрутки не генерируют 
сообщение WM_COMMAND. Вместо этого может посылаться либо сообщение WM_VSCROLL, 
либо WM_HSCROLL при активизации вертикальной или горизонтальной полосы 
прокрутки, соответственно. Эти сообщения обрабатываются функциями OnVScroll() 
и OnHScroll(), прототипы которых таковы: 

    afx_msg void CWnd::OnVScroll(UINT SBCode, INT Pos, 
    CScrollBar *SB) ; 
    afx_msg void CWnd::OnHScroll(HINT SBCode, INT Pos, 
    CScrollBar *SB) ; 

    Параметр SBCode содержит код выполненного над полосой прокрутки действия. 
Наиболее распространенные его значения таковы: 

    SB_LINEUP         SB_LINEDOWN    SB_PAGEUP   SB_PAGEDOWN 
    SB_LINELEFT       SB_LINERIGHT   SB_PAGELEFT SB_PAGER1GHT 
    SB_THUMBPOSITION SB_THUMBTRACK 

    Если работа ведется с вертикальной полосой прокрутки, то при каждом 
изменении положения передвижного маркера (или ползунка, как его иногда 
называют) на одну позицию вверх посылается сообщение SB_LINEUP. При изменении 
его положения на одну позицию вниз посылается сообщение SB_LINEDOWN. 
Аналогично, при постраничном перемещении генерируются сообщения SBJPAGEUP и SBJPAGEDOWN. 
    Если работа ведется с горизонтальной полосой прокрутки, то при каждом 
изменении положения ползунка на одну позицию влево посылается сообщение 
SB_LINELEFT. При изменении его положения на одну позицию вправо посылается 
сообщение SB_LINERIGHT. При постраничном перемещении генерируются сообщения 
SB_PAGELEFT и SBJPAGERIGHT. 

    6. Дополнительные элементы управления                             143 

    Для обеих типов полос прокрутки при перемещении ползунка на новую позицию 
посылается сообщение SB_THUMBPOSITION. Если при этом кнопка мыши удерживается 
нажатой и не отпускается, то дополнительно генерируется сообщение 
SB_THUMBTRACK. Это позволяет отслеживать перемещения ползунка, прежде чем он 
будет отпущен. Значение параметра Pos указывает на текущую позицию ползунка. 
    Если сообщение сгенерировано стандартной полосой прокрутки, то параметр SB 
будет равен NULL. Если же оно было сгенерировано независимой полосой 
прокрутки, то параметр SB будет содержать указатель на объект полосы. 

    Задание и получение диапазона полосы прокрутки 

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

    void CWnd::SetScroliRange(int Which, ink Min, int Max, BOOL Redraw^TRUE); 

    Значение параметра Which определяет, для какого типа полос прокрутки 
задается диапазон. Если это вертикальная полоса, то значением Which должно 
быть SB_VERT, а если горизонтальная — то SB_HORZ. Параметры Min и Мах задают 
требуемые границы диапазона. Если параметр Redraw равен TRUE, то после задания 
диапазона полоса прокрутки будет перерисована. В противном случае этого не произойдет. 
    Для задания диапазона независимой полосы прокрутки используется функция 
SetScrollRange(), принадлежащая классу CScrollBar. Ее прототип таков: 

    void CScrollBar:: SetScroliRange (int Min, int Max, BOOL Redraw=T'WE) ; 

    Здесь параметры Min, Мах и Redraw означают то же, что и в предыдущем 
случае. Иногда может возникнуть необходимость получить диапазон полосы 
прокрутки. Для этого используется функция GetScrolIRangeQ, сходные варианты 
которой включены в классы CWnd и CScrollBar: 

    void CWnd::GetScrollRange(int Which, LPINT IpMin, LPINT IpMax}const; 
    void CScrollBar::GetScrollRange(LPINT IpMin, LPINT IpMax) const; 

    В первом варианте значением параметра Which должно быть либо SB_VERT, либо 
SB_HORZ. В обоих случаях значения нижней и верхней границы диапазона 
возвращаются в переменных, на которые указывают параметры IpMin и IpMax, соответственно. 

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


    Задание и получение позиции ползунка 

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

    int CHnd: : SetScrollPos (int Which, int Pos, BOOL Redraw=TWE) ; 

    Значение параметра Which определяет, для какого типа полос прокрутки 
задается положение ползунка. Если это вертикальная полоса, то значением Which 
должно быть SB_VERT, а если горизонтальная — SB_HORZ. Параметр Pos определяет 
позицию ползунка. Его значение должно находиться в пределах допустимого 
диапазона данной полосы прокрутки. Если параметр Redraw равен TRUE, то после 
изменения положения ползунка полоса прокрутки будет перерисована. В противном 
случае этого не произойдет. Функция возвращает значение предыдущей позиции ползунка. 
    Для задания позиции ползунка в независимых полосах прокрутки используется 
функция SetScrolIPosQ, определенная в классе CScrolIBar. Ее прототип таков: 

    int CScrolIBar :: SetScrollPos (int Pos, BOOL Pedraiv=TRUE) ; 

    Здесь смысл параметров Pos и Redraw тот же, что и в первом случае. Функция 
также возвращает значение предыдущей позиции ползунка. 
    Получить текущую позицию ползунка можно с помощью функции GetScrollPosQ. 
Она включена в классы CWnd и CScrolIBar и имеет следующие прототипы: 

    int CWnd::GetScrollPos(int Which) const; 
    int CScrolIBar::GetScrollPos() const; 

    В первом случае параметр Which должен иметь значение либо SB_VERT, либо 
SB_HORZ. В обеих функциях возвращается значение текущей позиции ползунка. 

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

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

    ; SCROLL.RC 
    ; Работа с полосами прокрутки 
    ^include <afxres.h> 
    #include "scrlids.h" 
    DialoqMenu MENU 
    ( 
    POPUP "SDialog" 
    ( 
    MENUITEM "&Scroll Bar Dialog", IDM_DIALOG 
    MENUITEM "E&xit", IDM_EXIT 
    } 
    MENUITEM "SHelp", IDM__HELP 
    } 
    6. Дополнительные элементы управления                             145 

    DialogMenu ACCELERATORS 
    ( 
    VK_F1, IDM_HELP, VIRTKEY 
    VK_F2, IDM_DIALOG, VIRTKEY 
    VK_F3, IDM_EXIT, VIRTKEY 
    ) 
    SampleDialog DIALOG 18, 18, 142, 92 
    CAPTION "Using a Scroll Bar Control" 
    STYLE DS_MODALFRAME I WS_POPUP | WS_CAPTION | WS_SYSMENU 
    ( 
    GROUPBOX "Slider Position", IDD_GB1, 15, 5, 60, 30 
    SCROLLBAR IDD__SB1, 130, 10, 10, 70, SBS_VERT I WS_TABSTOP 
    ) 
    Это текст библиотечного файла SCRLIDS.H: 
    // SCRLIDS.H 
    #define IDM_DIALOG 100 
    #define IDM_EXIT    101 
    ftdefine IDM_HELP    102 
    #define IDD_SB1     200 
    #define IDD_GB1     300 

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

    // SCROLL.H 
    // Это класс для создания главного окна 
    class CMainWin : public CFrameWnd 
    { 
    public: 
    CMainWin() ; 
    // объявления обработчиков меню 
    afx msg void OnDialogO; 
    afx_msg void OnExitO; 
    afx msg void OnHelpO; 
    afx_msg void OnVScroll(UINT SBCode, UINT Pos, CSrollBar *SB) ; 
    afx_msg void OnHScroll(UINT SBCode, UINT Pos, CSrollBar *SB) ; 
    DECLARE_MESSAGE_MAP() 
    }; 
    // Это класс для создания приложения 
    class CApp : public CMinApp 
    { 
    public: 
    BOOL Initlnstance () ; 
    }; 

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

    // Это класс для создания диалогового окна 
    class CSampleDialog : public CDialog 
    ( 
    public: 
    CSampleDialog(char *DialogName, CWnd *0wner) : 
    CDialog(DialogName, Owner) () 
    BOOL OnInitDialog(); 
    afx_msg void OnVScroll(UINT SBCode, UINT Pos, CSrollBar *SB) ; 
    DECLARE_MESSAGE_MAP() i . 
    Вот текст самой программы: 
    // Работа с полосами прокрутки 
    ^include <afxwin.h> 
    ^include "scroll.h" 
    ftinclude "scrlids.h" 
    #define RANGEMAX 50 
    int ctlsbpos = 0; 
    int vsbpos = 0; 
    int hsbpos = 0; 
    CMainWin::CMainWin() 
    ( 
    Create(NULL, "Using Scroll Bars", 
    
    WS_OVERLAPPEDWINDOW I WS_VSCROLL I WS_HSCROLL, 
    rectDefault, NULL, "DialogMenu"); 
    
    // Загрузка таблицы быстрого доступа 
    if('LoadAccelTable("DialogMenu")) 
    MessageBox("Cannot Load Accelerators", "Error"); 
    SetScrollRange(SB_VERT, 0, RANGEMAX); 
    SetScrollRange(SB_HORZ, 0, RANGEMAX); 
    } 
    
    // Инициализация приложения 
    BOOL CApp::Initlnstance () 
    { 
    m_pMainWnd = new CMainWin; 
    m pMainHnd->ShowWindow(m nCmdShow) ; 
    m pMainWnd->UpdateWindow(); 
    return TRUE; 
    ) 
    // Это очередь сообщений приложения 


    6. Дополнительные элементы управления                             147 


    BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) 
    ON_COMMAND(IDM_DIALOG, OnDialog) 
    ON_COMMAND(IDM_EXIT, OnExit) 
    ON_COMMAND(IDM_HELP, OnHelp) 
    ON_WM_VSCROLL() 
    ON_WM_HSCROLL() 
    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("Scroll Bars", "Help"); 
    } 
    
    // Обработка сообщения WM_VSCROLL 
    afx_msg void CMainWin::OnVScroll(UINT SBCode, UINT Pos, 
    CSrollBar *SB) 
    ( 
    char str [255] ; 
    
    if(SB) return; // Ошибка, SB должно быть NULL 
    CClientDC dc(this) ; 
    
    switch(SBCode) ( 
    case SB_LINEDOWN: 
    vsbpos++; 
    if(vsbpos>RANGEMAX) vsbpos = RANGEMAX; 
    break; 
    case SB_LINEUP: 
    vsbpos--; 
    if(vsbpos<0) vsbpos = 0; 
    break; 

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

    case SB_THUMBPOSITION: 
    
    vsbpos = Pos; // получение текущей позиции 
    break; 
    case SB_THUMBTRACK: 
    
    vsbpos = Pos; // получение текущей позиции 
    break; 
    case SB_PAGEDOWN: 
    vsbpos +=5; 
    if(vsbpos>RANGEMAX) vsbpos = RANGEMAX; 
    break; 
    case SB_PAGEUP: 
    vsbpos -=5; 
    if(vsbpos<0) vsbpos = 0; 
    break; 
    1 
    SetScrollPos(SB_VERT, vsbpos); 
    wsprintf (str, "Vertical Scroll Bar: %d    ", vsbpos); 
    dc.TextOut (2, 2, str, strlen (str)); 
    
    // Обработка сообщения WM_HSCROLL 
    afx_msg void CMainWin::OnHScroll(UINT SBCode, UINT Pos, 
    
    CSrollBar *SB) 
    ( 
    char str[255] ; 
    
    if (SB) return; // Ошибка, SB должно быть NULL 
    CClientDC dc(this); 
    
    switch(SBCode) ( 
    case SB_LINERIGHT: 
    hsbpos++; 
    if(hsbpos>RANGEMAX) hsbpos = RANGEMAX; 
    break; 
    case SB_LINELEFT: 
    hsbpos--; 
    if(hsbpos<0) hsbpos = 0; 
    break; 
    case SB_THUMBPOSITION: 
    hsbpos = Pos; // получение текущей позиции 
    break; 
    case SB_THUMBTRACK: 
    hsbpos = Pos; // получение текущей позиции 
    break; 
    case SB_PAGERIGHT: 
    hsbpos +=5; 
    if(hsbpos>RANGEMAX) hsbpos = RANGEMAX; 
    break; 
    case  SB_PAGELEFT: 
    hsbpos -=5; 
    if(hsbpos<0) hsbpos = 0; 


    6. Дополнительные элементы управления                             149 


    break; 
    ) 
    SetScrollPos(SB_HORZ, hsbpos); 
    wsprintf(str, "Horizontal Scroll Bar: %d    ", hsbpos); 
    dc.TextOut (200, 2, str, strlen(str)); 
    } 
    
    // Это очередь сообщений диалогового окна 
    BEGIN_MESSAGE_MAP(CSampleDialog, CDialog) 
    
    ON_WM_VSCROLL() 
    END_MESSAGE_MAP() 
    
    // Инициализация диалогового окна 
    BOOL CSampleDialog::OnInitDialog() 
    { 
    CDialog::OnInitDialog(); // Вызов функции базового класса 
    CScrollBar *SB = (CScrollBar *) GetDlgItem(IDD_SBl) ; 
    SB->SetScrollRange(0, RANGEMAX) ; 
    ctlsbpos = 0; // сброс позиции ползунка полосы прокрутки 
    return TRUE; 
    ) 
    
    // Обработка сообщения WM_VSCROLL 
    afx_msg void CSampleDialog::OnVScroll(UINT SBCode, UINT Pos, 
    
    CSrollBar *SB) 
    { 
    char str[255] ; 
    if(!SB) return; // Ошибка, SB должен указывать на 
    // объект независимой полосы прокрутки 
    CClientDC dc(this); 
    
    switch(SBCode) { 
    case SB_LINEDOWN: 
    ctlsbpos++; 
    if(ctlsbpos>RANGEMAX) ctlsbpos = RANGEMAX; 
    break; 
    case SB_LINEUP: 
    ctlsbpos--; 
    if(ctlsbpos<0) ctlsbpos = 0; 
    break; 
    case SB_THUMBPOSITION: 
    ctlsbpos = Pos; // получение текущей позиции 
    break; 
    case SB_THUMBTRACK: 
    ctlsbpos = Pos; // получение текущей позиции 
    break; 
    case SB_PAGEDOWN: 
    ctlsbpos +=5, 


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


    if(ctlsbpos>RANGEMAX) ctlsbpos = RANGEMAX; 
    break; 
    case SB_PAGEUP: 
    ctlsbpos -=5; 
    if(ctlsbpos<0) ctlsbpos = 0; 
    break; 
    ) 
    SB->SetScrollPos (ctlsbpos) ; 
    wsprintf(str, "%d", ctlsbpos); 
    dc.TextOut (55, 30, "     ", 5); 
    dc.TextOut (55, 30, str, strlen(str)); 
    ) 
    CApp App; // создание экземпляра приложения 

    Главное окно программы имеет стандартные горизонтальную и вертикальную 
полосы прокрутки. Диалоговое окно содержит независимую вертикальную полосу 
прокрутки. Диапазон каждой полосы — от 0 до 50 позиций. Все полосы прокрутки 
реагируют на различные сообщения соответствующими перемещениями ползунков. 
Если менять состояние полос прокрутки главного окна, то текущая позиция 
ползунка будет отображаться в рабочей области. Положение же ползунка в 
диалоговом окне отображается в пределах групповой рамки "Slider Position". 
Конечно, чтобы поработать с независимой полосой прокрутки, необходимо сначала 
вызвать диалоговое окно. Пример работы программы показан на рис. 6.4. 

    Рис. 6.4. Пример работы с полосами прокрутки 


    6. Дополнительные элементы управления                                151 

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

    Функции SetScrolllnfoQ и GetScrolllnfoQ 

    При использовании библиотеки MFC для написания программ под Windows 95 
становятся доступны некоторые дополнительные функции управления полосами 
прокрутки. Функция GetScrolIInft>() позволяет узнать атрибуты полосы 
прокрутки, а функция SetScrollInfo() предназначена для задания таких 
атрибутов. Данные функции доступны как для стандартных, так и для независимых 
полос прокрутки. 
    В среде Windows 95 функции GetScrollInfo() и SetScrollInfo() позволяют 
заменить такие функции, как GetScrolIPosQ и SetScrollRange(). Новые функции 
работают с 32-разрядными данными, тогда как старые — только с 16-разрядными. 
Новые функции рекомендуется применять в Windows 95 потому, что они позволяют 
использовать новые, 32-разрядные свойства полос прокрутки. (Конечно, если 
такие свойства в программе не требуются, то не имеет значения, с каким 
вариантом функции работать.) В этом параграфе мы подробнее рассмотрим функции 
GetScroIIInfoO и SetScrollInfo(). 
    Для стандартных полос прокрутки используется функция SetScroIlInfo(), 
определенная в классе CWnd. Ее прототип таков: 

    BOOL CWnd::SetScrollInfo (int Which, LPSCROLLINFO IpSI, BOOL Redraw=TRUE); 

    Значение параметра Which указывает на то, с каким типом полосы ведется 
работа. Если это вертикальная полоса, то значение Which должно быть SB_VERT. 
Для горизонтальной полосы задается SB_HORZ. На информацию, связанную с 
установ-   | кой атрибутов, указывает параметр IpSI (который мы вскоре 
рассмотрим). Если параметр Redraw равен TRUE, то после задания атрибутов 
полоса прокрутки будет перерисована. В противном случае этого не произойдет. 
Функция возвращает ненулевое значение при успешном завершении, в противном 
случае — ноль. 
    Прототип функции SetScrollInfoO, используемой для независимых полос 
прокрутки, таков: 

    BOOL CScrollBar: :SetScrollInfo (LPSCROLLINFO IpSI, BOOL Redraw=TRVE); 

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

    BOOL CWnd::GetScrollInfo<int Which, LPSCROLLINFO IpSI, UINT Mas^=SIF_ALL) ; 


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

    Параметр Which может иметь значение либо SB_VERT, либо SB_HORZ. 
Информация, получаемая функцией, записывается в структуру, адресуемую 
указателем IpSI. Функция возвращает ненулевое значение при успешном завершении 
и ноль — в противном случае. Значение параметра Mask указывает на то, какая 
именно информация должна быть получена. Об этом говорится чуть ниже. 
    Прототип функции GetScrollInfoO из класса CScrollBar таков: 

    BOOL CScrollBar::GetScrollInfо(LPSCROLLINFO IpSI, UINT Mask=SIF_A~L,~L) ; 

    Значение параметров этой функции аналогично предыдущему случаю. Во всех 
вариантах функций параметр //^/указывает на структуру типа SCROLLINFO, 
определенную следующим образом: 

    typedef struct tagSCROLLINFO 
    ( 
    UINT cbSize; // размер структуры SCROLLINFO 
    UINT fMask; // сведения о полученных атрибутах 
    int nMin; // нижняя граница диапазона 
    int nMax; // верхняя граница диапазона 
    UINT nPage; // размер страницы 
    int nPos; // позиция ползунка 
    int nTrackPos; // текущая позиция ползунка при перемещении 
    ) SCROLLINFO; 

    Здесь в поле cbSize содержится размер структуры. Значение (или значения) 
поля fMask определяет, какие из последующих полей структуры содержат требуемую 
информацию. Эти значения могут быть следующими (табл. 6.1): 

    Таблица 6.1. Значения поля fMask 

    Значение                  Смысл 

    SIFALL 
    SIFDISABLE 
    NOSCROLL 
    SIFPAGE 
    SIFPOS 
    S1FRANGE 
    SIFTRACKPOS 

    To же, что SIF PAGE | SIF POS | SIF RANGE | SIFTRACKPOS 

    Если диапазон полосы прокрутки равен нулю, то она станет недоступной, но 
не будет удаленаПоле nPage содержит информацию Поле nPos содержит информацию 
Поля nMin и nMax содержат информацию Поле nTrackPos содержит информацию 
    Можно объединять сразу несколько из перечисленных констант с помощью 
операции побитового ИЛИ. Те же самые значения можно указывать и в параметре 
Mask функции GetScrollInfoO. 
    В поле nPage содержится текущий размер страницы для пропорциональных полос 
прокрутки. Поле nPos определяет позицию ползунка. Поля nMin и nMax содержат 
нижнюю и верхнюю границу диапазона полосы прокрутки. В поле nTrackPos 
содержится позиция ползунка, но не статическая, а динамическая, т.е. 
меняющаяся в процессе его перемещения. Это значение не может быть установлено. 

    6. Дополнительные элементы управления                             153 


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

    Чтобы научиться работать с новыми функциями, давайте перепишем предыдущую 
программу таким образом, чтобы вместо функций SetScrollRangeQ и SetScrollPos() 
в ней использовалась функция SetScrollInfo(). Помните, что эта программа может 
работать только в Windows 95. В программе используется тот же файл SCROLL. H, 
что и в предыдущем примере. Вот ее текст: 

    // Использование функции SetScrollInfo () 
    #include <afxwin.h> 
    #include <string.h> 
    ^include "scroll.h" 
    ^include "scrlids.h" 
    ftdefine RANGEMAX 50 
    int ctlsbpos = 0; 
    int vsbpos = 0; 
    int hsbpos = 0; 
    CMainWin::CMainWin() 
    { 
    SCROLLINFO si; // Информация о полосе прокрутки 
    Create (NULL, "Using Scroll Bars", 
    
    WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, 
    rectDefault, NULL, "DialogMenu"); 
    
    // Загрузка таблицы быстрого доступа 
    if(!LoadAceel Table("DialogMenu")) 
    MessageBox ( "Cannot Load Accelerators", "Error"); 
    si.cbSize = sizeof (SCROLLINFO); 
    si.fMask = SIF_RANGE; 
    si.nMin = 0; 
    si.nMax = RANGEMAX; 
    SetScrollInfo(SB_VERT, Ssi); 
    SetScrollInfo(SB_HORZ, Ssi); 
    ) 
    
    // Инициализация приложения 
    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) 


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


    ON_COMMAND(IDM_EXIT, OnExit) 
    ON_COMMAND(IDM_HELP, OnHelp) 
    ON_WM_VSCROLL() 
    ON_WM_HSCROLL() 
    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("Scroll Bars", "Help"); 
    t 
    // Обработка сообщения MM_VSCROLL 
    afx_msg void CMainWin::OnVScroll(UINT SBCode, UINT Pos, CSrollBar *SB) 
    ( 
    char str[255] ; 
    SCROLLINFO si; // Информация о полосе прокрутки 
    
    if(SB) return; // Ошибка, SB должно быть NULL 
    CClientDC dc(this); 
    
    switch(SBCode) { 
    case SB_LINEDOWN: 
    vsbpos++; 
    if(vsbpos>RANGEMAX) vsbpos = RANGEMAX; 
    break; 
    case SB_LINEUP: 
    vsbpos--; 
    if(vsbpos<0) vsbpos = 0; 
    break; 
    case SB_THUMBPOSITION: 
    vsbpos = Pos; // получение текущей позиции 
    break; 
    case SB_THUMBTRACK: 


    6. Дополнительные элементы управления                             155 


    vsbpos = Роз; // получение текущей позиции 
    break; 
    case SB_PAGEDOWN: 
    vsbpos +=5; 
    if(vsbpos>RANGEMAX) vsbpos = RANGEMAX; 
    break; 
    case SB_PAGEUP: 
    vsbpos -=5; 
    if(vsbpos<0) vsbpos = 0; 
    break; 
    ) 
    si.cbSize = sizeof(SCROLLINFO); 
    si.fMask = SIF_POS; 
    si.nPos = vsbpos; 
    SetScrollInfo(SB_VERT, &si); 
    wsprintf (str, "Vertical Scroll Bar: %d    ", vsbpos); 
    dc.TextOut (2, 2, str, strlen (str)); 
    } 
    // Обработка сообщения WM_HSCROLL 
    afx_msg void CMainHin::OnHScroll(UINT SBCode, UINT Pos, 
    CSrollBar *SB) 
    { 
    char str [255] ; 
    SCROLLINFO si; // Информация о полосе прокрутки 
    if (SB) return; // Ошибка, SB должно быть NULL 
    CClientDC dc(this); 
    switch(SBCode) { 
    case SB_LINERIGHT: 
    hsbpos++; 
    if(hsbpos>RANGEMAX) hsbpos = RANGEMAX; 
    break; 
    case SB_LINELEFT: 
    hsbpos--; 
    if(hsbpos<0) hsbpos = 0; 
    break; 
    case SB_THUMBPOSITION: 
    hsbpos = Pos; // получение текущей позиции 
    break; 
    case SB_THUMBTRACK: 
    hsbpos = Pos; // получение текущей позиции 
    break; 
    case SB_PAGERIGHT: 
    hsbpos +=5; 
    if (hsbpOS>RANGEMAX) hsbpos = RANGEMAX; 
    break; 
    case SB_PAGELEFT: 
    hsbpos -=5; 
    if(hsbpos<0) hsbpos = 0; 
    break; 
    } 


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


    si.cbSize = sizeof(SCROLLINFO) ; 
    si.fMask = SIF_POS; 
    si.nPos = hsbpos; 
    SetScrollInfo(SB_HORZ, &si); 
    wsprintf (str, "Horizontal Scroll Bar: %d    ", hsbpos); 
    dc.TextOut (200, 2, str, strlen (str)); 
    } 
    
    // Это очередь сообщений диалогового окна 
    BEGIN_MESSAGE_MAP(CSampleDialog, CDialog) 
    
    ON_WM_VSCROLL() 
    END_MESSAGE_MAP() 
    
    // Инициализация диалогового окна 
    BOOL CSampleDialog::OnInitDialog () 
    { 
    SCROLLINFO si; // Информация о полосе прокрутки 
    CDialog::OnInitDialog(); // Вызов функции базового класса 
    CScrollBar *SB = (CScrollBar *) GetDlgItem(IDD_SB1) ; 
    si.cbSize = sizeof(SCROLLINFO); 
    si.fMask = SIF_RANGE; 
    si.nMin = 0; 
    si.nMax = RANGEMAX; 
    SB->SetScrollInfo(&si) ; 
    ctlsbpos =0; // Сброс позиции ползунка полосы прокрутки 
    return TRUE; 
    } 
    
    // Обработка сообщения WM_VSCROLL 
    afx_msg void CSampleDialog::OnVScroll(UINT SBCode, UINT Pos, 
    CSrollBar *SB) 
    ( 
    char str [255] ; 
    SCROLLINFO si; // Информация о полосе прокрутки 
    if(!SB) return; // Ошибка, SB должен указывать на 
    // объект независимой полосы прокрутки 
    CClientDC dc(this); 
    
    switch(SBCode) ( 
    case SB_LINEDOWN: 
    ctlsbpos++; 
    if(ctlsbpos>RANGEMAX) ctlsbpos = RANGEMAX; 
    break; 
    case SB_LINEUP: 
    ctlsbpos--; 
    if(ctlsbpos<0) ctlsbpos = 0; 
    break; 
    case SB_THUMBPOSITION: 


    6. Дополнительные элементы управления                             157 


    ctlsbpos = Роз; // получение текущей позиции 
    break; 
    case SBJTHUMBTRACK: 
    ctlsbpos = Pos; // получение текущей позиции 
    break; 
    case SB_PAGEDOWN: 
    ctlsbpos +=5; 
    if(ctlsbpos>RANGEMAX) ctlsbpos = RANGEMAX; 
    break; 
    case SB_PAGEUP: 
    ctlsbpos -=5; 
    if(ctlsbpos<0) ctlsbpos = 0; 
    break; 
    } si.cbSize = sizeof(SCROLLINFO); 
    si.fMask = SIF_POS; 
    si.nPos = ctlsbpos; 
    SB->SetScrollInfo (&si) ; 
    wsprintf(str, "%d", ctlsbpos); 
    dc.TextOut(55, 30, "     ", 5) ; 
    dc.TextOut (55, 30, str, strlen(str)); 
    } 
    CApp App; // создание экземпляра приложения 


    DDX- и DDV-функции 

    Прежде чем завершить тему элементов управления, необходимо вкратце 
упомянуть о двух категориях функций, позволяющих в определенных ситуациях 
существенно упростить работу с этими элементами. Чтобы понять, для чего они 
нужны, вспомним следующее. В программах часто встречаются переменные, 
связанные с некоторым элементом управления. Например, в приведенной в начале 
главы программе работы с контрольными переключателями переменные cbstatusl и 
cbstatus2 использовались для хранения состояния переключателей. Если помните, 
для обновления значений этих переменных требовался дополнительный код. Поэтому 
пришлось переопределить функцию ОпОК() таким образом, чтобы она обновляла 
значения cbstatusl и cbstatus2, когда пользователь закрывал диалоговое окно 
нажатием кнопки ОК. Эти значения не обновлялись (т.е. не менялись), если окно 
закрывалось при нажатии Cancel. Подобное часто происходит и при работе с 
другими типами элементов управления или диалоговыми окнами. Данные должны быть 
переданы в диалоговое окно при его инициализации и извлечены из него при 
закрытии. Кроме того, иногда данные, вводимые пользователем (например строка в 
поле ввода), должны проверяться на правильность. 
    Хотя подобные ситуации достаточно легко обрабатывать, в реальных 
программах они могут встречаться слишком часто. Поэтому в MFC предусмотрен 
механизм автоматизации такого процесса. Здесь используются две категории 
функций: DDX и DDV. DDX-функции реализуют обмен данными между переменными и 
элементами управления. DDV-функции позволяют проверять эти данные на 
правильность. Поскольку некоторые элементы (например контрольные 
переключатели, кнопки, селекторные 

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

    кнопки и, в большинстве случаев, полосы прокрутки) не предоставляют 
информацию, требующую проверки, то DDX-функции используются более широко. 
Функции обоих типов выполняются над переменными-членами класса диалогового 
окна. Таким образом, при работе с элементом управления для него должна быть 
объявлена соответствующая переменная-член. 
    Вот наиболее распространенные DDX-функции (табл. 6.2): 

    Таблица 6.2. Наиболее распространенные DDX-функции 

    Функция	Назначение 

    DDX,	Check	Управляет переменной	для	контрольного переключателя 
    DDX	Text	Управляет переменной	для	строки	поля ввода 
    DDX	Radio	Управляет переменной	для	набора	селекторных кнопок 
    DDX.	Scroll	Управляет переменной	ДЛЯ	полосы	прокрутки 

    А это наиболее распространенные DDV-функции (табл. б.З): 

    Таблица 6.3. Наиболее распространенные DD У-срункции 

    Функция	Назначение 

    DDV	MaxChars	Проверяет длину строки 
    DDV	MinMaxDouble	Проверяет значение типа	double 
    DDV	MinMaxDWord	Проверяет значение типа	DWORD 
    DDV	MinMaxInt	Проверяет значение типа	int 
    DDV	MinMaxLong	Проверяет значение типа	long int 
    DDV	MinMaxUnsigned	Проверяет значение типа	unsigned int 

    Чтобы получить возможность использовать DDX- и DDV-функции, необходимо 
переопределить функцию DoDataExcliangeQ в классе диалогового окна. Она 
является виртуальным членом класса CWnd и имеет такой прототип: 

    void CWnd::DoDataExchange(CDataExchange *Data}; 

    Здесь параметр Data является указателем на объект типа CDataExchange, 
используемый DDX- и DDV-функциями (к нему не нужно обращаться напрямую). 
Внутри DoDataExchange() можно размещать все требуемые вызовы этих функций. За 
каждым вызовом DDX-функции должен сразу же следовать вызов соответствующей 
DDVфункции (если это необходимо). 
    Внутри DoDataExchangeQ DDX/DDV-функция связывает переменную класса 
диалогового окна с элементом управления и выполняет обмен данными или 
необходимую проверку. Например, функция DDX_Check() имеет такой прототип: 

    void AFXAPI DDX_Check(CDataExchange *Data, int ID, int &var) ; 

    Здесь параметр Data является указателем на объект типа CDataExchange, 
передаваемый в функцию DoDataExchangeQ. ID — это идентификатор элемента управления, 

    6. Дополнительные элементы управления                             159 

    a var — переменная класса, значение которой должно быть изменено. Когда 
эта функция вызывается внутри DoDataExchange(), то информация о требуемом 
элементе автоматически записывается в указанную переменную. 
    Итак, для использования DDX/DDV-функций необходимо выполнить следующие действия: 
    1. Переопределить функцию DoDataExchangeQ в классе диалогового окна. 
    2. В описание этого же класса добавить соответствующие переменные для 
каждого элемента управления. 
    3. Внутри функции DoDataExchange() разместить вызовы требуемых DDX-функций 
для каждого элемента. 
    4. При необходимости, за каждым вызовом DDX-функции поместить вызов 
DDVфункции. Учтите, что последние могут использоваться только с некоторыми 
элементами управления. 
    После выполнения этих действий данные будут автоматически передаваться в 
диалоговое окно при его создании и обновляться при нажатии кнопки ОК. При 
нажатии Cancel ничего происходить не будет. 
    Честно говоря, гораздо легче работать непосредственно с элементами 
управления, чем делать это через DDX/DDV-функции. Особенно это относится к 
простым элементам, таким как кнопки, контрольные переключатели и т.п. Однако 
использование этих функций может оказаться полезным при создании каркасов 
программ с помощью так называемых мастеров (wizards). Поскольку целью данной 
книги всетаки является изучение принципов программирования с использованием 
MFC, то в последующих примерах работа с элементами управления будет вестись 
напрямую, без обращения к DDX/DDV-функциям. Но если автоматизация процесса 
обмена и проверки данных, связанных с элементами управления диалогового окна, 
важна для вас, можете подробнее изучить подсистему этих функций в MFC. 

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