C++ C++ C# C# ASP.NET Security ASP.NET Security ASM ASM Скачать Скачать Поиск Поиск Хостинг Хостинг  
  Программа для работы с LPT портом...
Язык: .NET — ©Alexey...
  "ASP.NET Atlas" – AJAX в исполнении Micro...
Язык: .NET — ©legigor@mail.ru...
  "Невытесняющая" Многопоточность...
Язык: C/C++ — ©...
  01.05.2010 — Update World C++: Сборник GPL QT исходников
  15.12.2007 — Весь сайт целиком можно загрузить по ссылкам из раздела Скачать
Хостинг:
Windows 2003, ASP.NET 2.0
бесплатный и от 80 руб./мес


   Отправить письмо
Кулабухов Артем, Беларусь




 Дополнительные элементы управления / Основы программирования с помощью библиотеки MFC / Visual C++

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

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

Контрольные переключатели

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

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

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

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

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

Чтобы установить контрольный переключатель в заданное состояние, нужно использовать функцию SetCheck() c прототипом:

 
 	void CButton::SetCheck(int Status);
 
 
Параметр определяет требуемое состояние: если он равен 1, то переключатель устанавливается, если 0 - сбрасывается. По умолчанию при первом вызове диалога переключатель будет сброшен. Автоматический переключатель также может быть установлен в требуемое состояние этой функцией.

Текущее состояние переключателя можно определить с помощью функции GetCheck():

 
 	int CButton::GetCheck() const;
 
 
Функция возвращает 1, если переключатель установлен, и 0 в противном случае.

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

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

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

 	CheckBox.hpp
 
 	#include "stdafx.h"
 	#include "resource.h"
 
	class CMainWin: public CFrameWnd {
 	public:
 	CMainWin(CString Title, int HowShow);
 	afx_msg void OnCommand_Dialog();
 	afx_msg void OnCommand_Status();
 	afx_msg void OnCommand_Exit();
 	afx_msg void OnPaint();
 	virtual void SetInfoStr(CString str);
 	private:
 	CString infoStr;
 	DECLARE_MESSAGE_MAP()
 	};
 
	class CApp: public CWinApp {
 	public:
	BOOL InitInstance();
 	};
 
	class CMyDialog: public CDialog {
 	public:
 	CMyDialog(char *DialogName, CWnd *Owner = 0);
 	BOOL OnInitDialog();
 	// Обработчик кликов мышью на неавтоматическом
	// переключателе
 	afx_msg void OnCheckbox1();
 	afx_msg void OnOK();
 	private:
 	DECLARE_MESSAGE_MAP()
 	};
 
 	CheckBox.cpp
 
 	#include "stdafx.h"
 	#include "CheckBox.hpp"
 	#include "resource.h"
 
	// Переменные, хранящие состояния переключателей
 	int cb1status = 0, cb2status = 0;
 	CApp App;
 	//-------------------------------------------------
 	CMainWin::CMainWin(CString Title, int HowShow)
 	:infoStr("")
 	{
 	RECT rect;
 	rect.top = 10; rect.left = 50; rect.bottom = 460; rect.right = 630;
 	this->Create(0, Title, WS_OVERLAPPEDWINDOW, rect, 0, "MENU");
 	this->ShowWindow(HowShow);
 	this->UpdateWindow();
 	this->LoadAccelTable("MENU");
 	}
 
	afx_msg void CMainWin::OnCommand_Dialog()
 	{
 	CMyDialog dialog("DIALOG", this);
 	dialog.DoModal();
 	}
 
 	// Отображаем состояние переключателей
 	afx_msg void CMainWin::OnCommand_Status()
 	{
 	CString str;
 	if(cb1status != 0)
 	str = "Checkbox1 выбран.\n";
	else
 	str = "Checkbox1 очищен.\n";
 	if(cb2status != 0)
 	str = str + "Checkbox2 выбран.\n";
 	else
 	str = str + "Checkbox2 очищен.\n";
 	this->MessageBox(str, "Состояние");
 	}
 
 	afx_msg void CMainWin::OnCommand_Exit()
 	{
 	this->SendMessage(WM_CLOSE);
 	}
 
 	afx_msg void CMainWin::OnPaint()
 	{
 	CPaintDC paintDC(this);
 	paintDC.TextOut(10, 10, infoStr);
 	}
 
 	void CMainWin::SetInfoStr(CString str)
 	{
 	infoStr = str;
 	this->InvalidateRect(0);
 	}
 
 	BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
 	ON_COMMAND(IDM_DIALOG, OnCommand_Dialog)
 	ON_COMMAND(IDM_STATUS, OnCommand_Status)
 	ON_COMMAND(IDM_EXIT, OnCommand_Exit)
 	ON_WM_PAINT()
 	END_MESSAGE_MAP()

 	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 	BOOL CApp::InitInstance()
 	{
 	m_pMainWnd = new CMainWin("Использование контрольных
 	переключателей", 
 	SW_RESTORE);
 	return TRUE;
 	}
 
 	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
	CMyDialog::CMyDialog(char *DialogName, CWnd *Owner)
 	:CDialog(DialogName, Owner)
 	{
 	}
 
 	BOOL CMyDialog::OnInitDialog()
 	{
 	CDialog::OnInitDialog();
 	// Получаем адреса объектов переключателей
 	CButton *pcb1 = (CButton *) (this->GetDlgItem(IDC_CHECK1));
 	CButton *pcb2 = (CButton *) (this->GetDlgItem(IDC_CHECK2));
 	pcb1->SetCheck(cb1status);
 	pcb2->SetCheck(cb2status);
 	return TRUE;
 	}
 
	afx_msg void CMyDialog::OnCheckbox1()
 	{
 	// Получаем адрес объекта
 	CButton *pcb1 = (CButton *) (this->GetDlgItem(IDC_CHECK1));
 	// Реализуем переключение
 	if(pcb1->GetCheck() == 1)
 	pcb1->SetCheck(0);
 	else
 	pcb1->SetCheck(1);
 	}
 
 	afx_msg void CMyDialog::OnOK()
 	{
 	// Получаем адреса объектов
 	CButton *pcb1 = (CButton *) (this->GetDlgItem(IDC_CHECK1));
 	CButton *pcb2 = (CButton *) (this->GetDlgItem(IDC_CHECK2));
 	// Считываем состояния переключателей
 	cb1status = pcb1->GetCheck();
 	cb2status = pcb2->GetCheck();
 	//Печатаем в окне информацию о состоянии
	/ переключателей
 	CString str;
 	str.Format("Состояние первого переключателя %i,
 		   второго %i.", 
 	cb1status, cb2status);
 	((CMainWin *) (this->GetOwner()))->SetInfoStr(str);
                      this->EndDialog(0);
 	}
 
 	BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
 	// Обработка кликов на певом (неавтоматическом) переключателе
 	ON_BN_CLICKED(IDC_CHECK1, OnCheckbox1)
 	END_MESSAGE_MAP()
 

Рис. 11. Приложение с контрольными переключателями

Статические элементы управления

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

Если элементу присвоен идентификатор IDC_STATIC (-1), то он не будет принимать и генерировать сообщений. Но вообще статические элементы управления могут генерировать и принимать сообщения. Для этого элементу нужно присвоить другой идентификатор. Тогда элемент уже не будет статическим. Это часто используется. Например, можно поменять текст в текстовой строке с помощью функции SetWindowText(), чтобы отобразить некоторую информацию. Мы будем использовать это в примере, демонстрирующем работу с полосами прокрутки.

Радиокнопки

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

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

Радиокнопки управляются с помощью класса CButton. Также, как для контрольных переключателей, состояние радиокнопок можно изменять с помощью функции SetCheck() и читать с помощью функции GetCheck().

При создании диалога все радиокнопки сброшены. Таким образом, в функции OnInitDialog() нужно установить начальное состояние программно. Хотя из программы можно установить сразу несколько радиокнопок или сбросить все, нормальный стиль программирования под Windows предполагает, что всегда будет установлена одна и только одна радиокнопка. Таков принятый стиль.

Пример программы с радиокнопками и статическими элементами управления

 	Radio.hpp
 
 	#include "stdafx.h"
 	#include "resource.h"
 	class CMainWin: public CFrameWnd {
 	public:
 	CMainWin(CString Title, int HowShow);
 	afx_msg void OnCommand_Dialog();
 	afx_msg void OnCommand_Status();
 	afx_msg void OnCommand_Exit();
 	afx_msg void OnPaint();
 	virtual void SetInfoStr(CString str);
 	private:
 	CString infoStr;
 	DECLARE_MESSAGE_MAP()
 	};
 	class CApp: public CWinApp {
 	public:
 	BOOL InitInstance();
 	};
 
 	// Класс диалога с радиокнопками и статическими
 	// элементами управления
 	class CMyDialog: public CDialog {
 	public:
 	CMyDialog(char *DialogName, CWnd *Owner);
 	BOOL OnInitDialog();
 	afx_msg void OnOK();
 	private:
 	DECLARE_MESSAGE_MAP()
 	};
 
 	Radio.cpp
 	#include "stdafx.h"
 	#include "radio.hpp"
 	#include "resource.h"
 	// Переменные, хранящие состояния радиокнопок
 	// и контрольных переключателей
 	int cb1status = 0, cb2status = 0;
 	int rb1status = 1, rb2status = 0;
 	CApp App;
 	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 	CMainWin::CMainWin(CString Title, int HowShow)
 	:infoStr("")
 	{
 	RECT rect;
 	rect.top = 10; rect.left = 50; rect.bottom = 460; rect.right = 630;
 	this->Create(0, Title, WS_OVERLAPPEDWINDOW, rect, 0, "MENU");
 	this->ShowWindow(HowShow);
 	this->UpdateWindow();
 	this->LoadAccelTable("MENU");
 	}
 	afx_msg void CMainWin::OnCommand_Dialog()
 	{
 	CMyDialog dialog("DIALOG", this);
 	dialog.DoModal();
 	}
 	// Вывод окна состояния
 	afx_msg void CMainWin::OnCommand_Status()
 	{
 	CString str;
 	if(cb1status != 0)
 	str = "Checkbox1 выбран.\n";
 	else
 	str = "Checkbox1 очищен.\n";
 	if(cb2status != 0)
 	str = str + "Checkbox2 выбран.\n";
 	else
 	str = str + "Checkbox2 очищен.\n";
 	if(rb1status != 0)
 	str = str + "Radio1 выбран.\n";
 	else
 	str = str + "Radio1 очищен.\n";
 	if(rb2status != 0)
 	str = str + "Radio2 выбран.";
 	else
 	str = str + "Radio2 очищен.";
 	this->MessageBox(str, "Состояние");
 	}
 	afx_msg void CMainWin::OnCommand_Exit()
 	{
 	this->SendMessage(WM_CLOSE);
 	}
	afx_msg void CMainWin::OnPaint()
 	{
 	CPaintDC paintDC(this);
 	paintDC.TextOut(10, 10, infoStr, infoStr.GetLength());
 	}
 
 	void CMainWin::SetInfoStr(CString str)
 	{
 	infoStr = str;
 	this->InvalidateRect(0);
 	}
 
	BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
 	ON_COMMAND(IDM_DIALOG, OnCommand_Dialog)
 	ON_COMMAND(IDM_STATUS, OnCommand_Status)
 	ON_COMMAND(IDM_EXIT, OnCommand_Exit)
 	ON_WM_PAINT()
 	END_MESSAGE_MAP()
 	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
	BOOL CApp::InitInstance()
 	{
 	m_pMainWnd = new CMainWin("Радиокнопки", 
                                   SW_RESTORE);
 	return TRUE;
 	}
 
 	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 	CMyDialog::CMyDialog(char *DialogName, CWnd *Owner)
 	:CDialog(DialogName, Owner)
 	{
 	}
 
 	BOOL CMyDialog::OnInitDialog()
 	{
 	CDialog::OnInitDialog();
 	CButton *pcb1 = (CButton *) (this->GetDlgItem(IDC_CHECK1));
 	CButton *pcb2 = (CButton *) (this->GetDlgItem(IDC_CHECK2));
 	pcb1->SetCheck(cb1status);
 	pcb2->SetCheck(cb2status);
 	// Получаем адреса радиокнопок
 	CButton *prb1 = (CButton *) (this->GetDlgItem(IDC_RADIO1));
 	CButton *prb2 = (CButton *) (this->GetDlgItem(IDC_RADIO2));
 	// Устанавливаем состояние радиокнопок
 	prb1->SetCheck(rb1status);
 	prb2->SetCheck(rb2status);
 	return TRUE;
 	}
 	// Здесь читаем состояние кнопок и запоминаем
 	// его в переменных
 
 	afx_msg void CMyDialog::OnOK()
 	{
 	CButton *pcb1 = (CButton *) (this->GetDlgItem(IDC_CHECK1));
 	CButton *pcb2 = (CButton *) (this->GetDlgItem(IDC_CHECK2));
 	cb1status = pcb1->GetCheck();
 	cb2status = pcb2->GetCheck();
 	// Получем адреса радиокнопок
 	CButton *prb1 = (CButton *) (this->GetDlgItem(IDC_RADIO1));
 	CButton *prb2 = (CButton *) (this->GetDlgItem(IDC_RADIO2));
 	// Запоминаем состояние радиокнопок
 	rb1status = prb1->GetCheck();
 	rb2status = prb2->GetCheck();
 	// Выводим состояние в окно
 	CString str;
 	str.Format("Check1: %i, Check2: %i, " 
 	"Radio1: %i, Radio2: %i.",
 	cb1status, cb2status, rb1status, rb2status);
 
 	((CMainWin *) (this->GetOwner()))->SetInfoStr(str);
                        this->EndDialog(IDOK);
 
	}
 	BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
 	END_MESSAGE_MAP()
 
 

Рис. 12. Пример приложения с радиокнопками

Полосы прокрутки

В Windows есть два типа полос прокрутки. Элементы первого типа являются частью окна (включая диалоговое окно), поэтому их называют полосами прокрутки окна. Элементы второго типа существуют независимо и называются независимыми полосами прокрутки. Элементы первого типа описываются классом CWnd, а второго - CScrollBar. Работа с независимыми полосами прокрутки требует чуть больше усилий. Мы рассмотрим здесь оба типа.

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

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

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

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

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

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

Полосы прокрутки при выполнении над ними действий посылают сообщения WM_VSCROLL и WM_HSCROLL при активизации соответственно вертикальной или горизонтальной полосы прокрутки. Эти сообщения обрабатываются функциями со следующими прототипами:

 
 	afx_msg void CWnd::OnVScroll(UINT SBCode, int Pos, CScrollBar *SB);
 
 	afx_msg void CWnd::OnHScroll(UINT SBCode, int Pos, CScrollBar *SB);
 
 
Следует отметить, что при наличии нескольких горизонтальных или вертикальных полос прокрутки для всех них будет вызываться один и тот же обработчик. Первый параметр, SBCode, содержит код выполненного над полосой прокрутки действия. Все наиболее распространенные коды представлены в листинге программы-примера.

Если работа ведется с вертикальной полосой прокрутки, то при каждом изменении положения ползунка на одну позицию вверх посылается код SB_LINEUP. При изменении позиции на одну вниз посылается код SB_LINEDOWN. Аналогично, при постраничном перемещении генерируются коды SB_PAGEUP и SB_PAGEDOWN.

Если работа ведется с горизонтальной полосой прокрутки, то при каждом передвижении ползунка на одну позицию влево посылается код SB_LINELEFT. При изменении его положения на одну позицию вправо посылается код SB_LINERIGHT. При постраничном перемещении генерируются сообщения SB_PAGELEFT и SB_PAGERIGHT.

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

Если сообщение сгенерировано стандартной полосой прокрутки, то параметр SB будет равен 0. Если же оно было сгенерировано независимой полосой прокрутки, то этот параметр будет содержать указатель на объект. Это предоставляет весьма неуклюжий способ различать, какая конкретно независимая полоса прокрутки сгенерировала сообщение. Для этого нужно использовать функцию CWnd:: GetDlgCtrlID(), которая возвращает идентификатор элемента управления. Такое неудобство связано с тем, что MFC повторяет внутреннее устройство Windows, а не является библиотекой сверхвысокого уровня для быстрой разработки приложений.

Управление полосой прокрутки

Раньше для установки различных параметров полосы прокрутки использовались отдельные функции, которые были в Windows 3.1. С появлением Windows 95 появилась возможность управления полосами прокрутки с помощью одной функции SetScrollInfo(). Только эта функция позволяет сделать полосу прокрутки пропорциональной (в этом случае, чем меньше диапазон полосы прокрутки, тем длиннее будет ее ползунок). Функция GetScrollInfo() предназначена для чтения параметров полосы прокрутки. В отличие от старых функций, эти функции работают с 32-разрядными данными. Поэтому мы будем использовать только новые функции.

Для стандартных полос прокрутки используется функция:

 
 	BOOL CWnd::SetScrollInfo(int Which, LPSCROLLINFO pSI, 
 
 	BOOL Redraw = TRUE);
 
 
Значение Which указывает, с горизонтальной или вертикальной полосой ведется работа. Параметр pSI указывает на структуру, содержащую информацию для полосы прокрутки. Последний параметр задает необходимость перерисовки полосы прокрутки. Обычно используется значение по умолчанию.

Для независимых полос прокрутки используется функция:

 	BOOL CScrollBar::SetScrollInfo(LPSCROLLINFO pSI, BOOL Redraw = TRUE);
 
Оба параметра имеют такой же смысл.

Для чтения параметров стандартных полос прокрутки используется функция:

 	BOOL CWnd::GetScrollInfo(int Which, LPSCROLLINFO pSI, 
 	UINT Mask = SIF_ALL);
 
Информация, получаемая от полосы прокрутки, записывается в структуру по адресу pSI. Значение параметра Mask определяет, какая информация записывается в структуру. По умолчанию заполняются все поля.

Для независимых полос прокрутки вариант функции таков:

 	BOOL CScrollBar::SetScrollInfo(LPSCROLLINFO pSI, UINT Mask = SIF_ALL);
 
Значение параметров аналогично предыдущему случаю.
Во всех вариантах функций используется следующая структура типа SCROLLINFO:
 	typedef struct tagSCROLLINFO {
 	UINT cbSize; // размер самой структуры
 	UINT fMask; // маска для параметров
 	int nMin; // нижняя граница диапазона
 	int nMax; // верхняя граница диапазона
 	UINT nPage; // размер страницы
 	int nPos; // позиция ползунка
 	int nTrackPos; // позиция ползунка во время перемещения
 	} SCROLLINFO;
 
Поле fMask определяет, какое из полей структуры содержит требуемую информацию. Используются константы с префиксом SIF_, которые можно объединять операцией "|". Поле nPos содержит статическую позицию ползунка. Поле nPage содержит размер страницы для пропорциональных полос прокрутки. Для получения обычной пропорциональной полосы прокрутки в этом поле нужно задать значение 1. Поля nMin и nMax содержат нижнюю и верхнюю границы диапазона полосы прокрутки. Поле nTracksPos содержит позицию ползунка при его перемещении, это значение не может быть установлено.

Пример программы с пропорциональными полосами прокрутки

 	Scrollbars.hpp
 	#include "stdafx.h"
 	class CMainWin: public CFrameWnd {
 	public:
 	CMainWin();
 	afx_msg void OnDialog();
 	afx_msg void OnExit();
 	// Обработчики сообщений стандартных полос
 	// прокрутки
 	afx_msg void OnVScroll(UINT SBCode, UINT Pos, CScrollBar *SB);
 	afx_msg void OnHScroll(UINT SBCode, UINT Pos, CScrollBar *SB);
 	DECLARE_MESSAGE_MAP()
 	};
 	class CApp: public CWinApp {
 	public:
 	BOOL InitInstance();
 	};
 	class CSampleDialog: public CDialog {
 	public:
 	CSampleDialog(char *DialogName, CWnd *Owner = 0)
 	:CDialog(DialogName, Owner) { }
 	BOOL OnInitDialog();

 	// Обработчик сообщения независимой
 	// вертикальной полосы прокрутки
 	// в диалоге
 	afx_msg void OnVScroll(UINT SBCode, UINT Pos, CScrollBar *SB);
 	DECLARE_MESSAGE_MAP()
 	};
 
 
 	Scrollbars.cpp
 	#include "stdafx.h"
 	#include "Scrollbars.hpp"
 	#include "resource.h"

 	// Диапазон значений для полос прокрутки
 	const int RANGEMAX = 10;
 	int ctlsbpos = 0; 

	// Текущее положение независимой полосы прокрутки диалога 
 	int vsbpos = 0;

 	// То же, для вертикальной полосы прокрутки окна
 	int hsbpos = 0;

 	// То же, для горизонтальной полосы прокрутки окна
 	CMainWin::CMainWin()
 	{
 	Create(0, "Полосы прокрутки (32-битные функции)",
                WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
                rectDefault, 0, "MENU");
 
 	LoadAccelTable("MENU");
 	// Устанавливаем свойства пропорциональной
 	// полосы прокрутки
 	SCROLLINFO si;
 	si.cbSize = sizeof(si);
 	si.fMask = SIF_RANGE | SIF_PAGE;
 	si.nMin = 0;
 	si.nMax = RANGEMAX;
 	si.nPage = 1;
 	this->SetScrollInfo(SB_VERT, &si);
 	this->SetScrollInfo(SB_HORZ, &si);
 	}
 
 	BOOL CApp::InitInstance()
 	{
 	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_WM_VSCROLL()
 	ON_WM_HSCROLL()
 	END_MESSAGE_MAP()
 
 	afx_msg void CMainWin::OnDialog()
 	{
 	CSampleDialog dlg("SAMPLEDIALOG");
 	dlg.DoModal();
 	}
 	afx_msg void CMainWin::OnExit()
 	{
 	SendMessage(WM_CLOSE);
 	}

 	// Оработчик сообщений вертикальной полосы
 	// прокрутки окна
 	afx_msg void CMainWin::OnVScroll(UINT SBCode, UINT Pos,
 					 CScrollBar *SB)
 	{
 	// Анализируем коды полосы прокрутки
 	if(SBCode == SB_LINEDOWN) {
 	++vsbpos;
 	if(vsbpos > RANGEMAX)
 	vsbpos = RANGEMAX;
 	}
 	if(SBCode == SB_LINEUP) {
 	--vsbpos;
 	if(vsbpos < 0)
 	vsbpos = 0;
 	}
 	if(SBCode == SB_THUMBPOSITION) {
 	vsbpos = Pos;
 	}
 	if(SBCode == SB_THUMBTRACK) {
 	vsbpos = Pos;
 	}
 	if(SBCode == SB_PAGEDOWN) {
 	vsbpos += 5;
 	if(vsbpos > RANGEMAX)
 	vsbpos = RANGEMAX;
 	}
 	if(SBCode == SB_PAGEUP) {
 	vsbpos -= 5;
 	if(vsbpos < 0)
 	vsbpos = 0;
 	}
 	// Устанавливаем новое состояние
 	SCROLLINFO si;
 	si.cbSize = sizeof(si);
 	si.fMask = SIF_POS;
 	si.nPos = vsbpos;
 	this->SetScrollInfo(SB_VERT, &si);
 	// Выводим состояние полосы прокрутки в окно.

 	// Это только пример, так не следует делать в
	// реальных программах!
 	char str[255];
 	CClientDC dc(this);
 	wsprintf(str, "Vertical Scroll Bar: %i ", vsbpos);
 	dc.TextOut(2, 2, str);
 	}
 
 	// Оработчик сообщений горизонтальной полосы
 	// прокрутки окна
 	afx_msg void CMainWin::OnHScroll(UINT SBCode, UINT Pos, CScrollBar *SB)
 	{
 	// Анализируем коды полосы прокрутки
 	if(SBCode == SB_LINERIGHT) {
 	++hsbpos;
 	if(hsbpos > RANGEMAX)
 	hsbpos = RANGEMAX;
 	}
 	if(SBCode == SB_LINELEFT) {
 	--hsbpos;
 	if(hsbpos < 0)
 	hsbpos = 0;
 	}
 	if(SBCode == SB_THUMBPOSITION) {
 	hsbpos = Pos;
 	}
 	if(SBCode == SB_THUMBTRACK) {
 	hsbpos = Pos;
 	}
 	if(SBCode == SB_PAGERIGHT) {
 	hsbpos += 5;
 	if(hsbpos > RANGEMAX)
 	hsbpos = RANGEMAX;
 	}
 	if(SBCode == SB_PAGELEFT) {
 	hsbpos -= 5;
 	if(hsbpos < 0)
 	hsbpos = 0;
 	}
 	// Устанавливаем новое состояние
 	SCROLLINFO si;
 	si.cbSize = sizeof(si);
 	si.fMask = SIF_POS;
 	si.nPos = hsbpos;
 	this->SetScrollInfo(SB_HORZ, &si);

 	// Выводим состояние полосы прокрутки в окно.
 
 	// Это только пример, так не следует делать в
 	// реальных программах!
 
 	char str[255];
 	CClientDC dc(this);
 	wsprintf(str, "Horizontal Scroll Bar: %i ", hsbpos);
 	dc.TextOut(200, 2, str);
 	}
 
 	BEGIN_MESSAGE_MAP(CSampleDialog, CDialog)
 	ON_WM_VSCROLL()
 	END_MESSAGE_MAP()
 	BOOL CSampleDialog::OnInitDialog()
 	{
 	CDialog::OnInitDialog();
 	// Получаем адрес объекта элемента управления
 	CScrollBar *SB = (CScrollBar *) GetDlgItem(IDC_SCROLLBAR);
 	// Устанавливаем свойства полосы прокрутки
 	SCROLLINFO si;
 	si.cbSize = sizeof(si);
 	si.fMask = SIF_RANGE | SIF_PAGE;
 	si.nMin = 0;
 	si.nMax = RANGEMAX;
 	si.nPage = 1;
 	SB->SetScrollInfo(&si);
	ctlsbpos = 0;

 	// Меняем текст в элементе управления
 	// "статический текст"
 	CStatic *staticText = (CStatic *) (this->GetDlgItem(IDC_ST_SCRLINF));
 	char s[255];
 	wsprintf(s, "%d", SB->GetScrollPos()); 
 	staticText->SetWindowText(s);
 	return TRUE;
 	} 

 	// Обработчик сообщения от независимой
 	// вертикальной полосы прокрутки
 
 	afx_msg void CSampleDialog::OnVScroll(UINT SBCode, UINT Pos,
 					      CScrollBar *SB)
 	{
 	// Анализируем коды полосы прокрутки
 	if(SBCode == SB_LINEDOWN) {
 	++ctlsbpos;
 	if(ctlsbpos > RANGEMAX)
 	ctlsbpos = RANGEMAX;
 	}
 	if(SBCode == SB_LINEUP) {
 	--ctlsbpos;
 	if(ctlsbpos < 0)
 	ctlsbpos = 0;
 	}
	if(SBCode == SB_THUMBPOSITION) {
 	ctlsbpos = Pos;
 	}
 	if(SBCode == SB_THUMBTRACK) {
 	ctlsbpos = Pos;
 	}
 	if(SBCode == SB_PAGEDOWN) {
 	ctlsbpos += 5;
 	if(ctlsbpos > RANGEMAX)
 	ctlsbpos = RANGEMAX;
 	}
 	if(SBCode == SB_PAGEUP) {
 	ctlsbpos -= 5;
 	if(ctlsbpos < 0)
 	ctlsbpos = 0;
 	}
 	// Устанавливаем новое состояние
 	SCROLLINFO si;
 	si.cbSize = sizeof(si);
 	si.fMask = SIF_POS;
 	si.nPos = ctlsbpos;
 	SB->SetScrollInfo(&si);
 	// Выводим значение текущей позиции
 	// в элемент статического текста
 	char str[255];
 	wsprintf(str, "%i", SB->GetScrollPos());
 	CStatic *staticText = (CStatic *) (this->GetDlgItem(IDC_ST_SCRLINF));
 	staticText->SetWindowText(str);
 	}
 
 	CApp App;
 

Рис. 13. Приложение с использованием пропорциональных полос прокрутки

Обратите внимание, как в программе используются новые 32-разрядные функции работы с полосами прокрутки. Помните, что эта программа не будет работать под Windows NT 3.51. Также посмотрите, как изменяется текст в элементе управления "статический текст", который имеет уникальный идентификатор.