    Общие элементы управления (продолжение) 

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

    265 

    Помните, что общие элементы управления поддерживаются только в Windows 95, 
Windows NT (версии 3.51 или более поздней) и Win32s 1.3. В Windows 3.1 эти 
элементы не поддерживаются.                                                    
   ; 

    Работа со стрелками 

    Во многих ситуациях может оказаться полезным такой элемент, как стрелки. 
Фактически, это полоса прокрутки без самой полосы! Т.е. данный элемент состоит 
из стрелок, обычно располагаемых на концах полосы, но полосы между ними нет. 
Иногда при работе с приложениями Windows встречаются ситуации, когда полоса 
прокрутки настолько мала, что становится, по сути, бессмысленной. Кроме того, 
в некоторых случаях концепция полосы прокрутки не оправдывает себя, но 
использование стрелок оказывается весьма полезным. Для этого и был создан 
такой элемент управления, как стрелки. Работа с ним во многом напоминает 
работу с полосой прокрутки. 
    Стрелки могут использоваться двумя различными способами. В первом способе 
они представляют собой как бы независимую полосу прокрутки. Во втором способе 
они используются совместно с другим элементом управления, называемым 
приятельским окном (buddy window). В большинстве случаев таким окном является 
поле ввода. При этом говорят, что создается поле с прокруткой. Почти все 
управление им осуществляется автоматически. Это позволяет легко добавлять его 
в приложения. В данной главе будут рассмотрены два примера работы со 
стрелками. В первом из них отдельно создаются сами стрелки. Во втором примере 
с помощью приятельского окна создается поле с прокруткой. Стрелки описываются 
в MFC с помощью класса CSpinBattonCtrl. Этот класс используется для создания 
как самих стрелок, так и полей с прокруткой. 

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

    Для создания стрелок используется функция Create(), являющаяся членом 
класса CSpinButtonCtrl. Прототип этой функции таков: 
    BOOL CSpinButtonCtrl: :Create (DWORD dwStyle, const RECT fiSizePos, CWnd 
*0wner, UINT ID); 
    Здесь параметр dwStyle определяет стиль стрелок. Обычно в данном параметре 
указываются стандартные стили, такие как WS_CHILD, WS_VISIBLE и WS_BORDER. Но 
могут также использоваться некоторые специальные стили стрелок, приведенные в 
табл. 11.1. Позиция и размер стрелок содержатся в структуре типа RECT, на 
которую ссылается параметр SiyPos. Указатель на объект родительского окна 
передается в параметре Owner. Число, являющееся идентификатором создаваемого 
элемента, содержится в параметре ID. 

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


    Таблица 11.1. Стили, применяемые для стрелок и полей с прокруткой 

    Стиль	Значение

UDSALIGNLEFT 
UDSALIGNRIGHT 
UDSARROWKEYSTBSA
UTOBUDDY 
UDSHORZ
UDSNOTHOUSANDS 
UDSSETBUDDYINT 
UDSWRAP
Стрелки располагаются слева от приятельского окна. 
Стрелки располагаются справа от приятельского окна.Разрешается использовать 
клавиатуру. (Т.е. клавиши управления курсором могут также служить в качестве 
стрелок.) Применяется только для полей с прокруткой.Приятельским окном 
является предыдущее окно.
Стрелки располагаются горизонтально. (По умолчанию расположение вертикальное.)
При вводе больших чисел разделительные запятые не используются. (Только для 
полей с прокруткой.)При щелчке на стрелках в приятельском окне автоматически 
отображается текущее состояние стрелок.з)Состояние стрелок будет циклически 
изменяться при достижении верхней или нижней границы диапазона. 
    а) Дело в том, что стрелки имеют собственный счетчик, значение которого 
при щелчке на стрелках изменяется в пределах определенного диапазона. 
Состоянию стрелок соответствует значение этого счетчика. См. также следующий пункт. 
    Функция CreateQ возвращает ненулевое значение при успешном завершении и 
ноль — в противном случае. 

    Сообщения, поступающие от стрелок 

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

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

    Параметр SBCode содержит идентификационный код, описывающий тип 
произведенного действия. Поскольку управление стрелками полностью 
автоматизировано, в использовании данного параметра нет необходимости. 
Параметр Pos содержит значение, соответствующее состоянию стрелок. Если 
сообщение WM_VSCROLL было сгенерировано собственно стрелками, то параметр 
5'Дбудет содержать указатель на объект стрелок. В противном случае данный 
параметр будет содержать либо NULL, если сообщение поступило от стандартной 
полосы прокрутки, либо указатель на соответствующий объект, если сообщение 
поступило от независимой полосы прокрутки. Поскольку такое сообщение может 
поступать от различных элементов, необходимо проверять значение параметра SB, 
чтобы определить сообщение, поступившее именно от стрелок. 

    11. Общие элементы управления                                   267 


    Некоторые функции класса CSpinButtonCtrl 

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

    int CSpinButtonCtrl::GetPos() const; 

    Функция возвращает текущее значение счетчика. 

    Чтобы установить новое значение счетчика стрелок, необходимо вызвать 
функцию SetPos(). Она имеет следующий прототип: 

    int CSpinButtonCtrl::SetPos(int NewPos}; 

    Здесь параметр NewPos содержит новое значение счетчика. Функция возвращает 
предыдущее значение. 
    По умолчанию значение счетчика изменяется в диапазоне от 0 до 100. Задать 
другой диапазон можно с помощью функции SetRangeQ, имеющей такой прототип: 

    void CSpinButtonCtrl::SetRange(int min, int max); 

    Параметр min должен быть больше либо равен константе UD_MINVAL, а параметр 
wax должен быть меньше либо равен константе UD_MAXVAL. Если min больше, чем 
тах, то значение счетчика стрелок будет изменяться в обратную сторону. 
    Получить значение диапазона счетчика стрелок можно с помощью функции 
GetRangeQ. Она имеет такой прототип: 

    void CSpinButtonCtrl::GetRange(int imin, int &max) const; 

    После вызова функции параметр min будет содержать значение нижней границы, 
а параметр тах — верхней границы диапазона. Есть также перегруженная версия 
функции GetRange(), которая возвращает значение диапазона, закодированное в 
32-разрядном числе. 

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

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

    ; UPDOWN.RC 
    #include <afxres.h> ^include "ids.h" 
    UpDownMenu MENU { 
    POPUP "Demo"                                                        , 


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


    ( 
    MENUITEM "SUp-Down Demo\tF2", IDM_DIALOG 
    MENUITEM "E&xit\tF3", IDM_EXIT 
    } 
    MENUITEM "SHelp", IDM_HELP 
    ) 
    UpDownMenu ACCELERATORS 
    { 
    VK_F1, IDM_HELP, VIRTKEY 
    VK_F2, IDM_DIALOG, VIRTKEY 
    VK_F3, IDM_EXIT, VIRTKEY 
    } 
    UpDownDialog DIALOG 18, 18, 142, 92 
    CAPTION "Demonstrate Up-Down Control" 
    STYLE DS__MODALFRAME | WS_POPUP I WS_CAPTION I WS_SYSMENU 
    ( 
    PUSHBUTTON "Cancel", IDCANCEL, 52, 65, 37, 14, 
    WS_CHILD | WS_VISIBLE I WS_TABSTOP } 

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

    // IDS.H 
    tdefine IDM_DIALOG    100 
    #define IDM_HELP      101 
    tdefine IDM_EXIT      102 
    tdefine IDM_SPIN      200 
    #define IDM_EB1       201 
    #define IDM_TRACKBAR 202 
    tdefine IDM_PROGBAR   203 
    tdefine IDM_STATIC    204 
    Вот текст файла UPDOWN.H, содержащего объявления порождаемых классов: 
    // UPDOWN.H 
    // Это класс для создания главного окна 
    class CMainWin : public CFrameWnd 
    ( 
    public: 
    CMainWin() ; 
    // объявления обработчиков меню 
    afx msg void OnDialogO; 
    afx msg void OnExitO; 
    afx msg void OnHelpO; 
    DECLARE_MESSAGE_MAP() 
    }; 


    11. Общие элемент управления                                  269 


    // Это класс для создания приложения 
    class CApp : public CWinApp 
    ( 
    public: 
    BOOL Initlnstance () ; 
    ); 
    // Это класс для создания диалогового окна 
    class CSampleDialog : public CDialog 
    ( 
    CSpinButtonCtrl m_UpDown; // объект стрелок 
    public: 
    CSampleDialog(char *DialogName, CWnd *0wner) : 
    CDialog(DialogName, Owner) (} 
    BOOL OnInitDialog() ; 
    afx_msg void OnVScroll(UINT SBCode, UINT Pos, 
    CScrollBar *SB) ; 
    DECLARE_MESSAGE_MAP() 
    }; 
    Это текст самой программы: 
    // Работа со стрелками 
    #include <afxwin.h> 
    #include <afxcmn.h> 
    #include <string.h> 
    ftinclude "updown.h" 
    #include "ids.h" 
    CMainWin::CMainWin() 
    ( 
    Create(NULL, "Demonstrate Up-Down Control", 
    WS_OVERLAPPEDWINDOW, rectDefault, 
    NULL, "UpDownMenu"); 
    InitCommonControls (); 
    
    // Загрузка таблицы быстрого доступа 
    if('LoadAccelTable("UpDownMenu")) 
    MessageBox("Cannot Load Accelerators", "Error"); 
    ) 
    
    // Инициализация приложения 
    BOOL CApp::Initlnstance () 
    ( 
    m_pMainWnd = new CMainWin; 
    m pMainWnd->ShowWindow(m nCmdShow) ; 
    m_pMainWnd->UpdateWindow() ; 
    return TRUE; 


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


    // Это очередь сообщений приложения 
    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("UpDownDialog", this); 
    diagOb.DoModal<); // активизирует модальное диалоговое окно 1 
    
    // Обработка сообщения IDM_EXIT 
    afx msg void CMainWin::OnExit() 
    ( 
    int response; 
    response = MessageBox("Quit the Program?", "Exit", MB_YESNO) ; 
    if(response == IDYES) 
    SendMessage(WM_CLOSE); // завершение приложения 
    1 
    // Обработка сообщения IDM HELP 
    afx msg void CMainWin::OnHelp() 
    ( 
    MessageBox("Up-Down Control", "Help"); 
    } 
    
    // Это очередь сообщений диалогового окна 
    BEGIN_MESSAGE_MAP(CSampleDialog, CDialog) 
    
    ON_WM_VSCROLL() 
    END_MESSAGE__MAP() 
    
    // Инициализация диалогового окна 
    BOOL CSampleDialog::OnInitDialog() 
    ( 
    RECT r; 
    CDialog::OnInitDialog(); // Вызов функции базового класса 
    
    // создание стрелок 
    г.left = г.top = 10; 
    г.right = г.bottom = 50; 
    
    m_UpDown.Create(WS_VISIBLE | WS_CHILD | WS_BORDER, 
    r, this, IDD_SPIN); 
    
    // задание диапазона и начального состояния стрелок 
    m_UpDown.SetRange(0, 99); 


    71. Общие элементы управления                                   271 


    m_UpDown.SetPos(50) ; 
    return TRUE; 
    ) 
    
    // Обработка сообщения WM_VSCROLL, генерируемого стрелками 
    afx_msg void CSampleDialog::OnVScroll(UINT SBCode, UINT Pos, 
    
    CScrollBar *SB) 
    { 
    char str[255] ; 
    
    // Параметр SB должен указывать на объект стрелок 
    if(SB != (CScrollBar *)&m_UpDown) return; 
    CClientDC dc(this) ; 
    dc.SetBkColor(RGB(200, 200, 200)); 
    wsprintf(str, "%d", m_UpDown.GetPos()) ; 
    dc.TextOut (50, 22, "     ", 5) ; 
    dc.TextOut(50, 22, str, strlen (str)); 
    } 
    
    CApp App; // создание экземпляра приложения 
    Пример работы программы показан на рис. 11.1. 
    
    Рис. 11.1. Пример работы со стрелками 


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


    Стрелки отображаются в диалоговом окне. Они создаются при инициализации 
окна, когда вызывается функция OnInitDialogQ: 

    // Инициализация диалогового окна 
    BOOL CSampleDialog::OnInitDialogО 
    ( 
    RECT r; 
    CDialog::OnInitDialog(); // Вызов функции базового класса 
    
    // создание стрелок 
    г.left = г.top = 10; 
    г.right = г.bottom = 50; 
    
    m_UpDown.Create(WS_VISIBLE | WS_CHILD I WSJ30RDER, 
    r, this, IDD_SPIN) ; 
    
    // задание диапазона и начального состояния стрелок 
    m_UpDown.SetRange (0, 99); 
    m_UpDown.SetPos(50) ; 
    return TRUE; 
    } 

    Стрелки описываются с помощью объекта m_UpDown, являющегося членом класса 
CSampleDialog. Они отображаются в диалоговом окне после вызова функции 
CreateQ, причем область, занимаемая стрелками, начинается в точке с 
координатами (10,10) и имеет размеры 50х50 пикселей. Поскольку стрелки 
являются дочерним окном, в качестве параметра функции передается указатель на 
объект диалогового окна (представленный ключевым словом this). Идентификатором 
стрелок является константа IDD_SPIN. Диапазон счетчика стрелок задан от 0 до 
99, а начальное значение счетчика равно 50. 
    Каждый раз при щелчке на стрелках диалоговому окну посылается сообщение 
WM_VSCROLL. Это сообщение обрабатывается с помощью функции OnVScroll(): 
    
    // Обработка сообщения WM VSCROLL, генерируемого стрелками 
    afx__msg void CSampleDialog::OnVScroll(UINT SBCode, UINT Pos, 
    
    CScrollBar *SB) 
    ( 
    char str[255] ; 
    
    // Параметр SB должен указывать на объект стрелок 
    if(SB != (CScrollBar *) &rn_UpDown) return; 
    CClientDC dc(this) ; 
    dc.SetBkColor(RGB(200, 200, 200)); 
    wsprintf (str, "%d", m_UpDown.GetPos()); 
    dc.TextOut(50, 22, "     ", 5); 
    dc.TextOut (50, 22, str, strlen (str)); 
    } 


    11. Общие элементы управления                                   273 

    Обратите внимание, что параметр SB проверяется на соответствие адресу 
переменной m_UpDown. Это позволяет выяснить, действительно ли данное сообщение 
поступило от стрелок. Хотя для данного примера это несущественно, в реальных 
приложениях сообщение WM_VSCROLL может генерироваться несколькими элементами, 
поэтому такая проверка обязательно должна выполняться. 
    Для получения нового значения счетчика стрелок используется функция 
GetPos(). Ее присутствие здесь необязательно, поскольку данное значение 
содержится также в параметре Pos функции OnVScroll(). Значение счетчика 
отображается в рабочей области диалогового окна. 

    Создание поля с прокруткой 

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

    CWnd *CSpinButtonCtrl::SetBuddy(CWnd *BuddyWnd}; 

    Здесь параметр BuddyWnd является указателем на объект приятельского окна, 
которое будет объединено со стрелками для создания поля с прокруткой. Функция 
возвращает указатель на объект предыдущего приятельского окна (если таковое было). 

    Пример программы 

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

    ; SPIN.RC 
    #include <afxres.h> 
    #include "ids.h" 
    SpinMenu MENU { 
    POPUP "Demo" 
    { 


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


    MENUITEM "&Spin Control Demo\tF2", IDMJ3IALOG 
    MENUITEM "E&xit\tF3", IDM_EXIT 
    } 
    MENUITEM "&Help", IDM_HELP 
    ) 
    SpinMenu ACCELERATORS 
    ( 
    VK_F1, IDM_HELP, VIRTKEY 
    VK_F2, IDM_DIALOG, VIRTKEY 
    VK_F3, IDM_EXIT, VIRTKEY 
    } 
    SpinDialog DIALOG 18, 18, 142, 92 
    CAPTION "Demonstrate Spin Control" 
    STYLE DS_MODALFRAME I WS_POPUP | WS_CAPTION I WS_SYSMENU 
    ( 
    PUSHBUTTON "Cancel", IDCANCEL, 52, 65, 37, 14, 
    WS_CHILD I WS_VISIBLE I WSJTABSTOP 
    EDITTEXT IDD_EB1, 10, 10, 26, 12, ES_LEFT I WS_CHILD | 
    WS_VISIBLE | WS_BORDER 
    } 

    Это текст файла SPIN.H: 

    // SPIN.H 
    
    // Это класс для создания главного окна 
    class CMainWin : public CFrameWnd 
    ( 
    public: 
    
    CMainWin() ; 
    
    // объявления обработчиков меню 
    afx msg void OnDialogO; 
    afx msg void OnExitt); 
    afx__msg void OnHelpO; 
    DECLARE_MESSAGE__MAP() 
    ); 
    
    // Это класс для создания приложения 
    class CApp : public CWinApp 
    { 
    public: 
    BOOL Initlnstance() ; 
    ); 
    // Это класс для создания диалогового окна 
    class CSampleDialog : public CDialog 
    { 
    CSpinButtonCtrl m_Spin; // объект поля с прокруткой 


    11. Общие элементы управления                                   275 


    public: 
    CSampleDialog(char *DialogName, CWnd *0wner) : 
    CDialog(DialogName, Owner) {} 
    BOOL OnInitDialog() ; 
    DECLARE_MESSAGE_MAP() 
    }; 
    Вот текст самой программы: 
    // Работа с полем прокрутки 
    #include <afxwin.h> 
    #include <afxcmn.h> 
    #include "spin.h" 
    #include "ids.h" 
    CMainWin::CMainWin() 
    { 
    Create(NULL, "Demonstrate Spin Control", 
    WSJ3VERLAPPEDWINDOW, rectDefault, 
    NULL, "SpinMenu") ; 
    InitCommonControls () ; 
    
    // Загрузка таблицы быстрого доступа 
    if('LoadAccelTable("SpinMenu")) 
    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() 
    1 
    CSampleDialog diagOb("SpinDialog", this); 

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

    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("Spin Control", "Help"); 
    ) 
    
    // Это очередь сообщений диалогового окна 
    BEGIN_MESSAGE_MAP(CSampleDialog, CDialog) 
    END_MESSAGE_MAP() 
    
    // Инициализация диалогового окна 
    BOOL CSampleDialog::OnlnitDialog() 
    ( 
    RECT r; 
    CDialog::OnlnitDialog(); // Вызов функции базового класса 
    
    // создание поля с прокруткой 
    г.left = г.top = 10; 
    г.right = г.bottom = 50; 
    m_Spin.Create(WS_VISIBLE | WS_CHILD | WS_BORDER | 
    UDS_SETBUDDYINT | UDS_ALIGNRIGHT, 
    r, this, IDD_SPIN) ; 
    
    // задание диапазона поля с прокруткой 
    m_Spin.SetRange(0, 99); 
    
    // задание приятельского окна 
    m_Spin.SetBuddy(GetDlgItem(IDD_EB1)) ; 
    
    // задание начального состояния стрелок 
    m_Spin.SetPos(50) ; 
    return TRUE; 
    } 
    CApp Арр; // создание экземпляра приложения 


    11. Общие элементы управления                                    277 

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

    Рис. 11.2. Работа с полем прокрутки 

    Как видно из текста функции OnInitDialogO, приятельское окно связывается 
со стрелками путем вызова функции SetBuddyQ: 

    m_Spin.SetBuddy(GetDlgItem(IDD_EBl)) ; 

    Как вы, наверное, помните, в главе 5 говорилось о том, что функция 
GetDlgItemQ является членом класса CWnd. Она возвращает указатель на объект, 
чей идентификатор передан ей в качестве параметра. 
    Когда стрелки созданы и поле ввода объявлено их приятельским окном, оба 
элемента автоматически объединяются, образуя поле с прокруткой. Обратите 
внимание, что в программе не обрабатывается сообщение WM_VSCROLL. В этом нет 
необходимости, так как поле с прокруткой автоматически обновляет содержимое 
своего приятельского окна при каждом изменении состояния стрелок. 

    Работа с регулятором 

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

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

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

    Создание регулятора 

    Регулятор создается с помощью функции CreateQ, которая имеет такой прототип: 

    BOOL CSliderCtrI: :Create (DWORD dwStyle, const RECT &S-[zePos, 
    CWnd * Owner, UINT ID); 

    Здесь параметр dwStyle определяет стиль регулятора. Обычно этот параметр 
включает стандартные стили WS_CHILD, WS_VISIBLE и WS_BORDER. Кроме того, можно 
задавать один или несколько специальных стилей регуляторов. Они приведены в 
табл. 11.2. Чаще всего используется стиль TBS_AUTOTICKS, так как в этом случае 
вдоль шкалы регулятора автоматически проставляются маленькие метки, задающие 
его масштаб. 

    Таблица 11.2. Стили регуляторов 

    Стиль                   Эффект 

    TBS_AUTOTICKS         Вдоль шкалы автоматически проставляются метки 
    TBS_HORZ              Регулятор располагается горизонтально (это установка 
по умолчанию) 
    TBS_VERT               Регулятор располагается вертикально 
    TBS_BOTTOM           Метки проставляются снизу от шкалы (это установка по умолчанию) 
    TBSJTOP               Метки проставляются сверху от шкалы 
    TBS_LEFT              Метки проставляются слева от шкалы 
    TBS_RIGHT             Метки проставляются справа от шкалы (это установка 
по умолчанию) 
    TBS_BOTH             Метки проставляются по обеим сторонам шкалы 
    Позиция и размер регулятора содержатся в структуре типа RECT, на которую 
ссылается параметр SizePos. Указатель на объект родительского окна передается 
в параметре Owner. Параметр ID содержит идентификатор регулятора. Функция 
Create() возвращает ненулевое значение при успешном завершении и ноль — в 
противном случае. 

    Обработка сообщений регулятора 

    При щелчке на регуляторе программе посылается сообщение WM_HSCROLL. Чтобы 
обработать это сообщение, необходимо переопределить стандартную функцию 
OnHScroll(), которая, если помните, имеет такой прототип: 

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

    71. Общие элементы управления                                   279 

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

    Таблица 11.3. Идентификационные коды сообщений регуляторов 

    Код                  Значение 

    ТВ_ВОТТОМ          Нажата клавиша [END]. Ползунок перемещается в позицию 
шкалы с минимальным значением. 
    TB_ENDTRACK        Конец события, связанного с клавиатурой. (Клавиша отпущена.) 
    TB_LINEDOWN        Нажата клавиша [RIGHT] или [4.]. 
    TB_LINEUP           Нажата клавиша [LEFT] или [Т]. 
    TB_PAGEDOWN       Нажата клавиша [PAGE DOWN] или произошел щелчок мыши 
перед ползунком. 
    ТВ PAGEUP          Нажата клавиша [PAGE UP] или произошел щелчок мыши за ползунком. 
    TB_THUMBPOSITION  Ползунок был перемешен с помощью мыши. 
    TB_THUMBTRACK     Ползунок продолжает перемещаться с помощью мыши. 
    ТВ_ТОР               Нажата клавиша [HOME]. Ползунок перемешается в 
позицию шкалы с максимальным значением. 

    Параметр Pos содержит позицию ползунка. Если сообщение WM_HSCROLL было 
сгенерировано регулятором, то параметр 5'Д будет содержать указатель на объект 
соответствующего регулятора. В противном случае параметр SB будет содержать 
либо NULL, если сообщение поступило от стандартной полосы прокрутки, либо 
указатель на объект полосы, если сообщение поступило от независимой полосы 
прокрутки. Поскольку данное сообщение может поступать от различных элементов, 
необходимо всегда выполнять проверку значения данного параметра. 

    Функции работы с регуляторами 

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

    int CSliderCtrl::GetPos() const; 

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

    void CSliderCtrl::SetPos(int NewPos); 


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

    Здесь параметр NewPos содержит новое значение счетчика. Функция возвращает 
предыдущее значение. 
    Задать диапазон счетчика регулятора можно с помощью функции SetRangeQ, 
имеющей такой прототип: 

    void CSliderCtrl: : SetRange (int rnin, ink may.., BOOL Redra w= FALSE); 

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

    void CSliderCtrl::GetRange(int imin, int Smax) ; 

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

    Демонстрационная программа работы с регулятором 

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

    Рис. 11.3. Пример работы с регулятором 


    11. Общие элементы управления                                       281 

    Вот текст файла ресурсов программы: 

    ; SLIDER.RC 
    ^include <afxres.h> 
    #include "ids.h" 
    SliderMenu MENU 
    ( 
    POPUP "Demo" 
    { 
    
    MENUITEM "SSlider Demo\tF2", IDM_DIALOG 
    MENUITEM "E&xit\tF3", IDM_EXIT 
    } 
    
    MENUITEM "SHelp", IDM_HELP 
    ) 
    
    SliderMenu ACCELERATORS 
    ( 
    VK_F1, IDM_HELP, VIRTKEY 
    VK_F2, IDM_DIALOG, VIRTKEY 
    VK_F3, IDM_EXIT, VIRTKEY » 
    SliderDialoq DIALOG 18, 18, 142, 92 
    CAPTION "Demonstrate Slider Control" 
    STYLE DS_MODALFRAME I WS_POPUP | WS_CAPTION | WS_SYSMENU 
    ( 
    PUSHBUTTON "Cancel", IDCANCEL, 52, 65, 37, 14, 
    
    WS_CHILD I WS_VISIBLE | WS_TABSTOP 
    EDITTEXT IDD_EB1, 10, 10, 26, 12, ES_LEFT | WS_CHILD | 
    
    WS_VISIBLE | WS_BORDER 
    ) 
    Вот текст файла SLIDER.H, содержащего объявления порождаемых классов: 
    // SLIDER.H 
    
    // Это класс для создания главного окна 
    class CMainWin : public CFrameWnd 
    { 
    public: 
    CMainWin () ; 
    
    // объявления обработчиков меню 
    afx msq void OnDialogO; 
    afx_msg void OnExitO; 
    afx msg void OnHelpO; 
    DECLARE_MESSAGE_MAP() 
    }; 
    // Это класс для создания приложения 


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


    class CApp : public CWinApp 
    ( 
    public: 
    BOOL Initlnstance (); 
    ); 
    // Это класс для создания диалогового окна 
    class CSampleDialog : public CDialog 
    { 
    CSpinButtonCtrl m Spin; // объект поля с прокруткой 
    CSliderCtrl m Slider; // объект регулятора 
    public: 
    CSampleDialog(char *DialogName, CWnd *0wner) : 
    CDialog(DialogName, Owner) () 
    BOOL OnInitDialog () ; 
    afx_msg void OnVScroll(UINT SBCode, UINT Pos, 
    CScrollBar *SB) ; 
    afxJ-nsg void OnHScroll (UINT SBCode, UINT Pos, 
    CScrollBar *SB) ; 
    DECLARE_MESSAGE_MAP() 
    ); 
    Это текст самой программы: 
    // Работа с регулятором 
    ^include <afxwin.h> 
    #include <afxcmn.h> 
    #include "slider.h" 
    #include "ids.h" 
    CMainWin::CMainWin() 
    ( 
    Create(NULL, "Demonstrate Slider Control", 
    WS__OVERLAPPEDWINDOW, rectDefault, 
    NULL, "SliderMenu"); 
    InitCommonControls(); 
    
    // Загрузка таблицы быстрого доступа 
    if(!LoadAccelTable("SliderMenu")) 
    MessageBox("Cannot Load Accelerators", "Error"); 
    } 
    // Инициализация приложения 
    BOOL CApp::Initlnstance() 
    ( 
    m_pMainWnd == new CMainWin; 
    m_pMainWnd->ShowWindow(m_nCmdShow) ; 
    m_pMainWnd->UpdateWindow () ; 


    7 7. Общие элементы управления                                   283 


    return TRUE; 
    1 
    
    // Это очередь сообщений приложения 
    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("SliderDialog", this); 
    diagOb.DoModal(); // активизирует модальное диалоговое окно 1 
    
    // Обработка сообщения 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("Slider Control", "Help"); 
    } 
    
    // Это очередь сообщений диалогового окна 
    BEGIN_MESSAGE_MAP(CSampleDialog, CDialog) 
    ON_WM_VSCROLL() 
    
    ON_WM_HSCROLL() 
    END_MESSAGE_MAP() 
    
    // Инициализация диалогового окна 
    BOOL CSampleDialog::OnInitDialog() 
    ( 
    RECT r; 
    CDialog::OnInitDialog(); // Вызов функции базового класса 
    
    // создание поля с прокруткой 
    г.left = г.top = 10; 
    г.right = г.bottom = 50; 
    m_Spin.Create<WS_VISIBLE I WS_CHILD I WS_BORDER | 
    UDSJ3ETBUDDYINT | UDS_ALIGNRIGHT, 
    r, this, IDD_SPIN) ; 


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


    // задание диапазона поля с прокруткой 
    m_Spin.SetRange(0, 10); 
    
    // задание приятельского окна 
    m_Spin.SetBuddy(GetDlgItem(IDD_EBl)) ; 
    
    // задание начального состояния стрелок 
    m_Spin.SetPos(5) ; 
    
    // создание регулятора 
    г.left = 10; г.top = 50; 
    г.right = 200; r.bottom = 80; 
    m_Slider.Create(WS_VISIBLE | WS_CHILD | WS_BORDER | 
    TBS_AUTOTICKS | WSJTABSTOP, 
    r, this, IDD_TRACKBAR); 
    
    // задание диапазона и начального состояния регулятора 
    m_Slider.SetRange (О, 10); 
    m_Slider.SetPos(5) ; 
    return TRUE; 
    ) 
    
    // Обработка сообщения WM HSCROLL, генерируемого регулятором 
    afx_msg void CSampleDialog::OnHScroll(UINT SBCode, UINT Pos, 
    
    CScrollBar *SB) 
    ( 
    // Параметр SB должен указывать на объект регулятора 
    if(SB != (CScrollBar *)&m_Slider) return; 
    
    // изменение состояния поля с прокруткой 
    m Spin.SetPos(m^Slider.GetPos()) ; 
    ) 
    
    // Обработка сообщения WM_VSCROLL, 
    // генерируемого полем с прокруткой 
    
    
    afx_msg void CSampleDialog::OnVScroll(UINT SBCode, UINT Pos, 
    CScrollBar *SB) 
    ( 
    
    // Параметр SB должен указывать на объект поля с прокруткой 
    if(SB != (CScrollBar *)&m_Spin) return; 
    
    // изменение состояния регулятора 
    m_Slider.SetPos(m_Spin.GetPos()) ; 
    } 
    CApp App; // создание экземпляра приложения 

    И регулятор, и поле с прокруткой создаются в программе при инициализации 
диалогового окна. Диапазон регулятора задается от 0 до 10, а начальное 
значение счетчика устанавливается равным 5. (Тот же диапазон и начальное 
значение задаются и для поля с прокруткой.) При изменении состояния поля с 
прокруткой принимается 

    11. Общие элементы управления                                   285 

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

    Работа с индикатором 

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

    Создание индикатора 

    Индикатор создается с помощью функции Create(), которая имеет такой прототип: 

    BOOL CProgressCtrl::Create(DWORD dwStyle, const RECT &SizePos, CWnd 
*0wner, UINT ID); 

    Параметр dwStyle определяет стиль индикатора. Обычно этот параметр 
включает стандартные стили WS_CHILD, WS_VISIBLE и WS_BORDER. Положение и 
размер индикатора содержатся в структуре типа RECT, на которую ссылается 
параметр SiyPos. Указатель на объект родительского окна передастся в параметре 
Owner. Параметр ID содержит идентификатор создаваемого элемента. 
    Функция Create() возвращает ненулевое значение при успешном завершении и 
ноль — в противном случае. 

    Функции работы с индикаторами 

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

    int CProgressCtrl::SetPos(int NewPos); 

    Здесь параметр NewPos содержит новое значение счетчика. Функция возвращает 
предыдущее значение. 
    Задать диапазон значений счетчика индикатора можно с помощью функции 
SetRangeQ, которая имеет такой прототип: 

    void CProgressCtrl::SetRange(int min, int max) ; 


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

    По умолчанию диапазон индикатора задан от 0 до 100. Но можно устанавливать 
любые значения в пределах от 0 до 65535. 
    Чтобы заполнить индикатор очередным прямоугольником, необходимо вызвать 
функцию Steplt(). Она увеличивает значение счетчика индикатора на заданное 
число, называемое шагом. По умолчанию шаг равен 10, но его можно изменять. При 
увеличении значения счетчика поле индикатора заполняется новыми 
прямоугольниками. А поскольку индикатор отображает степень выполнения 
определенной задачи, то эта задача должна завершаться одновременно с 
заполнением индикатора. Прототип функции StepItQ таков: 

    int CProgressCtrl::StepIt (); 

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

    int CProgressCtrl::SetStep(int Amount); 

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

    Программа работы с индикатором 

    В следующей программе демонстрируется работа с индикатором. В ней 
создается диалоговое окно, в котором располагается сам индикатор и кнопка 
Progress. Индикатор имеет диапазон от 0 до 120 и шаг 5. Каждый раз при нажатии 
кнопки Progress индикатор заполняется очередным прямоугольником. Когда 
индикатор полностью заполняется, диалоговое окно автоматически закрывается. 
    Вот текст файла ресурсов программы: 

    ; PROGRESS.RC 
    ^include <afxres.h> 
    ^include "ids.h" 
    ProgMenu MENU 
    ( 
    POPUP "Demo" 
    { 
    
    MENUITEM "&Progress Bar Demo\tF2", IDM_DIALOG 
    MENUITEM "E&xit\tF3", IDM_EXIT 
    } 
    
    MENUITEM "SHelp", IDM_HELP 
    } 
    
    ProgMenu ACCELERATORS 
    ( 
    VK_F1, IDM_HELP, VIRTKEY 
    VK_F2, IDM_DIALOG, VIRTKEY 
    VK_F3, IDM_EXIT, VIRTKEY 


    7 7. Общие элементы управления 287 


    ProgDialog DIALOG 18, 18, 140, 92 
    CAPTION "Demonstrate a Progress Bar" 
    STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 
    { 
    
    DEFPUSHBUTTON "Progress", IDD_PROGBAR, 52, 40, 37, 14, 
    WS_CHILD I WS_VISIBLE | WS_TABSTOP 
    
    PUSHBUTTON "Cancel", IDCANCEL, 52, 65, 37, 14, 
    WS_CHILD I WS_VISIBLE I WS_TABSTOP 
    
    LTEXT "Progress Bar", IDD_STATIC, 10, 22, 100, 10 
    } 
    Вот текст файла PROGRESS.Н, содержащего объявления порождаемых классе» 
    // PROGRESS.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 
    { 
    CProgressCtrl m Prog; // объект индикатора 
    int m_ProgPos; // состояние индикатора 
    public: 
    CSampleDialog(char *DialogName, CWnd *0wner) : 
    CDialog(DialogName, Owner) () 
    BOOL OnInitDialog() ; 
    afx msg void OnProgress(); 
    DECLARE_MESSAGE_MAP() 
    }; 


    288                                     MFC: основы программирована. 

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

    // Работа с регулятором 
    
    ^include <afxwin.h> 
    ^include <afxcmn.h> 
    finclude "progress.h" 
    #include "ids.h" 
    
    CMainWin::CMainWin () 
    { 
    Create(NULL, "Demonstrate Progress Bar", 
    WS_OVERLAPPEDWINDOW, rectDefault, 
    NULL, "ProgMenu") ; 
    InitCommonControls (); 
    
    // Загрузка таблицы быстрого доступа 
    if(!LoadAccelTable("ProgMenu")) 
    MessageBox("Cannot Load Accelerators", "Error"); 
    } 
    
    // Инициализация приложения 
    BOOL CApp::Initlnstance () f 
    m_pMainWnd = new CMainWin; 
    m pMainWnd->ShowWindow(m nCmdShow); 
    m pMainWnd->UpdateWindow(); 
    return TRUE; 
    } 
    
    // Это очередь сообщений приложения 
    BEGIN_MESSAGE_MAP(CMainWin, CPrameWnd) 
    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("ProgDialog", this); 
    diagOb.DoModal(); // активизирует модальное диалоговое окно 
    } 
    
    // Обработка сообщения IDM_EXIT 
    afx_msg void CMainWin::OnExit() 
    ( 
    int response; 


    71. Общие элементы управления                                    289 


    response = MessageBox("Quit the Program?", "Exit", MB_YESNO) ; 
    if (response == IDYES) 
    SendMessage(WM CLOSE); // завершение приложения } 
    
    // Обработка сообщения IDM HELP 
    afx msg void CMainWin::OnHelp0 
    { 
    MessageBox("Progress Bar", "Help"); 
    i 
    
    // Это очередь сообщений диалогового окна 
    BEGIN_MESSAGE_MAP(CSampleDialog, CDialog) 
    
    ON_COMMAND(IDD_PROGBAR, OnProgress) 
    END_MESSAGE_MAP() 
    
    // Инициализация диалогового окна 
    BOOL CSampleDialog::OnInitDialog() 
    
    (                                                                   i 
    RECT r;                                                           ' 
    CDialog::OnInitDialog(); // Вызов функции базового класса 
    
    // создание индикатора                                            I 
    г.left = 10; г.top = 10;                                          I 
    r.right = 200; r.bottom = 30;                                     I 
    m_Prog.Create(WS_VISIBLE | WS_CHILD | WS_BORDER |                 j 
    r, this, IDD_PROGBAR);                              \ 
    
    / / задание диапазона и шага индикатора 
    m_Prog.SetRange(0, 120); 
    m_Prog.Set3tep(5) ; 
    
    m ProgPos =0; // инициализация счетчика 
    return TRUE; 
    // Изменение состояния индикатора 
    afx msg void CSampleDialog::OnProgress() 
    { 
    m Prog.Stepit(); // изменение состояния на один шаг 
    m ProgPos += 5; 
    if(m_ProgPos == 120) EndDialog(0) ; 
    } 
    САрр Арр; // создание экземпляра приложения 

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

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


    Рис. 11.4. Пример работы с индикатором 

    При работе с индикатором не следует забывать о том, что его назначение — 
убедить пользователя в том, что программа продолжает нормально работать. 
Следовательно, состояние индикатора должно изменяться относительно часто. Это 
своего рода обратная связь, благодаря которой пользователь знает, что 
программа все еще работает. Если менять состояние индикатора не слишком часто, 
то чересчур нервный пользователь чего доброго подумает, что с программой не 
все в порядке и нажмет кнопку Reset! 
    В следующей главе будет продолжено знакомство с общими элементами 
управления. Мы рассмотрим работу со строками состояния, закладками и окнами 
просмотра деревьев. 

    11. Общие элементы управления                                  291 
