Вернуться наверх
aco.ifmo.ru photonic
вернуться в оглавление предыдущая глава предыдущий параграф следующий параграф следующая глава


3.4. Особенности использования элементов управления.

3.4.1. Кнопка (Button).

Кнопка - графический объект, который при нажатии на нее запускает какую-либо команду. При нажатии графическое изображение кнопки обычно "оживляется", показывая как бы действительное нажатие кнопки клавиатуры.

Основные методы класса CButton, который используется для создания кнопок различного вида.

Таблица 1. Основные методы класса CButton Метод Назначение
CButton(); Конструирует объект класса CButton
BOOL BOOL Create (LPCTSTR lpszCapition, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); Построение объекта класса CButton происходит в два этапа. Сначала вызывается конструктор, затем функция Create , которая создает командную кнопку Windows и подключает к ней объект класса CButton . Если задан стиль WS_VISIBLE , Windows передает кнопке все сообщения, необходимые для ее активизации и изображения.

Параметры:

  • dwStyle - задает стиль кнопки. Можно использовать произвольную комбинацию стилей.
  • rect - задает положение и размеры кнопки интерфейсного элемента. Является объектом класса CRect или структурой CRECT .
  • pParentWnd - задает родительское окно кнопки (обычно CDialog ). He может принимать значение NULL .
  • nID - задает идентификатор ( ID ) кнопки.
virtual void DrawItem (LPDRAWITEMSTRUCT lpDrawItemStruct); Вызывается каркасом приложения при изменении вида полъзовательской кнопки. Для таких кнопок устанавливается стиль BS_OWNERDRAW . Для реализации отображения пользовательского класса CButton переопределите эту функцию. Приложение должно восстановить все объекты GDI (интерфейса графических устройств), избранные для отображения содержимого, указываемого параметром lpDrawItemStruct , до того как закончится выполнение функци-члена.

Параметр lpDtawItemStruct - длинный указатель на структуру DRAWITEMSTRUCT , содержащую информацию об отображаемом элементе типе рисунка.

HBITMAP GetBitmap() const; Возвращает дескриптор ранее установленного функцией SetBitmap() растрового изображения, связанного с кнопкой
UINT GetButtonStyle() const; Эта функция возвращает значения стилей группы BS_ (и только этой группы)
int GetCheck() const; Определяет контрольное состояние переключателя или флажка
HCURSOR GetCursor(); Возвращает дескриптор установленного ранее функцией SetCursor() курсора, связанного с кнопкой
HICON GetIcon() const; Возвращает дескриптор установленного ранее функцией SetIcon() значка, связанного с кнопкой
UINT GetState() const; Определяет состояние переключателя
HBITMAP SetBitmap (HBITMAP hBitmap); Связывает с кнопкой новое растровое изображение. Растровое изображение будет автоматически помещено на кнопке и по умолчанию будет центрировано. Если размеры изображения слишком велики для кнопки, оно будет обрезано по размерам кнопки. Вы можете выбрать следующие параметры размещения:
  • BS_TOP ;
  • BS_LEFT ;
  • BS_RIGHT ;
  • BS_CENTER ;
  • BS_BOTTOM ;
  • BS_VCENTER .

В отличие от класса CBitmapButton , использующего четыре растровых изображения на кнопку, функция SetBitmap() требует только одно растровое изображение для кнопки. При нажатии кнопки растровое изображение сдвигается вниз и вправо. Параметр hBitmap - дескриптор растрового изображения

void SetButtonStyle (UINT nStyle, BOOL bRedraw = TRUE); Изменяет стиль кнопки. Функция-член GetButtonStyle() используется для того, чтобы определить стиль кнопки. Младшее слово полного стиля кнопки сообщает о специфичном для нее стиле. Параметры:
  • nStyle - задает стиль кнопки;
  • bRedraw - при указании ненулевого значения и по умолчанию задает перерисовку кнопки. Нулевое значение соответствует запрету на перерисовку.
void SetCheck (int nCheck); Устанавливает или переустанавливает контрольное состояние переключателя или флажка. Эта функция-член не влияет на обычную кнопку. Параметр nCheck задает контрольное состояние. Этот параметр может принимать одно из следующих значений:
  • 0 - установить помеченное состояние кнопки;
  • 1 - установить непомеченное состояние кнопки;
  • 2- установить промежуточное состояние кнопки. Это значение можно использовать только в том случае, если стиль кнопки установлен в BS_3STATE или BS_AUTO3STATE .
HCURSOR SetCursor (HCURSOR hCursor); Связывает с кнопкой новый курсор. Он автоматически помещается на кнопку и по умолчанию центрируется. Если курсор слишком велик для кнопки, он обрезается по размеру кнопки. Вы можете избрать следующие параметры размещения:
  • BS_TOP ;
  • BS_LEFT ;
  • BS_RIGHT ;
  • BS_CENTER ;
  • BS_BOTTOM ;
  • BS_VCENTER .
В отличие от класса CBitmapButton , который использует четыре изображения на кнопку, функция SetCursor() требует только один курсор. Когда кнопка нажата, курсор сдвигается вниз и вправо. Параметр hCursor - дескриптор курсора.
HICON SetIcon (HICON hIcon); Связывает с кнопкой новый значок. Значок автоматически помещается на кнопку и по умолчанию центрируется. Если значок слишком велик для кнопки, он обрезается по размерам кнопки. Вы можете выбрать следующие параметры размещения:
  • BS_TOP ;
  • BS_LEFT ;
  • BS_RIGHT ;
  • BS_CENTER ;
  • BS_BOTTOM ;
  • BS_VCENTER .
В отличие от класса CBitmapButton , который использует четыре изображения на кнопку, функция SetIcon() требует только один значок на кнопку. Когда кнопка нажата, значок сдвигается вниз и вправо. Параметр hIcon - дескриптор значка.
void SetState (BOOL bHighlight); Устанавливает выделенное состояние кнопки. Эта функция работает только с обычными командными кнопками и не оказывает влияние на флажки или переключатели. Интерфейсный элемент кнопки автоматически выделяется, когда пользователь щелкает на нем и удерживает левую кнопку мыши. Выделение снимается, когда пользователь отпускает кнопку мыши. Параметр bHighlight устанавливает, должна ли кнопка быть выделена. Ненулевое значение означает выделить кнопку, нулевое снимает любое выделение

 

3.4.2. Флажок (Check Box).

Контрольный переключатель ("флажок") - это элемент управления, предназначенный для установки или снятия определенной опции. Он состоит из маленького прямоугольного поля, в котором может стоять метка выбора. Кроме того, с переключателем связано текстовое поле с описанием предоставляемой переключателем опции. Если в переключателе стоит метка выбора, то говорится, что он выбран (установлен). Контрольные переключатели в 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() для установки начальных состояний.

 

3.4.3. Переключатель (Radio Button).

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

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

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

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

 

3.4.4. Поле для ввода (Edit Box).

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

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

Для получения текущего содержимого поля вода, состоящего из одной строки, используется функция GetWindowText(). Ее прототип таков:

	int CWnd::GetWindowText(LPSTR StringVariable,
						int MaxStringLen) const;
В результате ее выполнения содержимое поля ввода будет скопировано в строку по адресу StringVariable. Эта функция позволяет получить текст, связанный с любым окном или элементом управления. Применительно к обычному окну функция получает заголовок окна.

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

	void CWnd::SetWindowText(LPCSTR String);

 

3.4.5. Подпись (Static Text).

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

 

3.4.6. Рамка (Group Box).

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

 

3.4.7. Список (List Box).

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

Список является одним из наиболее распространенных элементов управления. В MFC работа со списком осуществляется через класс CListBox.

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

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

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

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

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

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

 	ON_LBN_DBLCLK(ИдентификаторСписка,
 		      ИмяОбработчика)

Многие сообщения обрабатываются подобным образом. Названия всех макросов для таких сообщений начинаются с префикса ON_LBN_.

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

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

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

	int CListBox::AddString(LPCSTR StringToAdd); 
	int CListBox::GetCurSel() const; 
	int CListBox::GetText(int Index, LPCSTR StringVariable);
Функция AddString() вставляет указанную строку в список. По умолчанию она вставляется в конец списка. Начало списка имеет индекс 0. Функция GetCurSel() возвращает индекс текущего выделенного элемента. Если ни один элемент не выбран, возвращает LB_ERR. Функция GetText() получает строку, связанную с указанным индексом. Строка копируется в символьный массив по адресу StringVariable.

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

Функции CListBox работают с объектами CListBox. Значит, необходимо получить указатель на объект списка, что делается с помощью функции GetDlgItem(), являющейся членом класса CWnd:

	СWnd *CWnd::GetDlgItem(int ItemIdentifier) const;
Эта функция возвращает указатель на объект, чей идентификатор передан как параметр. Если такой объект не существует, то возвращается 0. Необходимо понимать, что указатель, возвращаемый функцией, является временным. Он действителен только в пределах текущего обработчика.

Значение, возвращенное функцией, должно быть приведено к типу указателя на конкретный класс управления. Например, в нашем случае это тип CListBox*.

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

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

 

3.4.8. Комбинированный список (Combo Box).

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

 

3.4.9. Закладки (Tabs).

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

В MFC есть встроенный класс по работе с Tab control - класс CTabCtrl.

Для практики напишем программу, которая будет использовать класс CTabCtrl и в которой будет три "закладки" - диалога.

Шаги создания проекта:

  • 1) Сначала создадим проект tab_control типа диалог.
  • 2) В редакторе ресурсов добавить Tab Control в шаблон диалога и назначим ему ID = IDC_TAB.
  • 3) Используя ClassWizard, добавим переменную-член типа CTabCtrl со свойством Control.
  • 4) В OnInitDialog проинициализируем необходимые переменные для CTabCtrl.
  • 5) Используя ClassWizard, добавим обработку необходимых сообщений от Tab control 'я.
  • 6) Удалим за собой ненужные переменные.

Для начала сделайте первые три пункта, создайте переменную m_ctrTab класса CTabCtrl. После этого в функцие BOOL CTab_controlDlg::OnInitDialog() добавте следующее:

	...
	TC_ITEM TabItem;
	TabItem.mask = TCIF_TEXT;
	TabItem.pszText = "Закладка1";
	m_ctrTab.InsertItem( 0, &TabItem );
	TabItem.pszText = "Закладка2";
	m_ctrTab.InsertItem( 1, &TabItem );
	TabItem.pszText = "Закладка3";
	m_ctrTab.InsertItem( 2, &TabItem );
	... 

Это код инициализации Tab Control, мы создаём три закладки. Теперь нам надо, чтобы при нажатие на любую закладку, на экране появлялось то, что нам нужно. Самый простой вариант - это использовать на каждую закладку по диалогу - и потом просто в области Tab Control'а - выводить нужный диалог, в зависимости от текущей закладки.

Сделаем это. Добавим три диалога в редакторе ресурсов и создадим каждому из них по классу - наследнику от CDialog. Назовем эти классы CPage1, CPage2 и CPage3( файлы Page1.cpp(h), Page2.cpp(h), Page3.cpp(h) ) .

В свойствах этих трёх диалогов поставте Style как "Child" и Border как "none" - это очень важно, а в самих диалогах создайте какие либо элементы ( например, типа Static Text ), чтобы было видно отличие.

Напишите эти три строчки в начале файла tab_controlDlg.cpp

	#include "Page1.h"
	#include "Page2.h"
	#include "Page3.h"

Продолжим в OnInitDialog:

Надо последовательно создать все страницы, причём указатели на них хранятся в самом m_ctrTab !!! В этом примере мы ипользовали lParam структуры TCITEM как хранилище указателя. Теперь переменные pPage1, pPage2 и pPage3 больше не нужны - указатели хранятся в надежном месте! Для каждой страницы вызывается метод ShowWindow() - для отображения первой, и скрытия остальных страниц.

	...
	CPage1* pPage1;
	pPage1 = new CPage1;
	TabItem.mask = TCIF_PARAM;
	TabItem.lParam = (LPARAM)pPage1;
	m_ctrTab.SetItem(0, &TabItem);
	VERIFY(pPage1->Create(CPage1::IDD, &m_ctrTab));
	pPage1->SetWindowPos(NULL, 10, 30, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
	pPage1->ShowWindow(SW_SHOW);
		CPage2* pPage2;
	pPage2 = new CPage2;
	TabItem.mask = TCIF_PARAM;
	TabItem.lParam = (LPARAM)pPage2;
	m_ctrTab.SetItem(1, &TabItem);
	VERIFY(pPage1->Create(CPage2::IDD, &m_ctrTab));
	pPage2->SetWindowPos(NULL, 10, 30, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
	pPage2->ShowWindow(SW_HIDE);
		CPage3* pPage3;
	pPage3 = new CPage3;
	TabItem.mask = TCIF_PARAM; 
	TabItem.lParam = (LPARAM)pPage3;
	m_ctrTab.SetItem(2, &TabItem);
	VERIFY(pPage1->Create(CPage3::IDD, &m_ctrTab));
	pPage3->SetWindowPos(NULL, 10, 30, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
	pPage3->ShowWindow(SW_HIDE);
	... 

Теперь добавим код по отображению текущей страницы и сокрытию предыдущей. Для этого добавим обработчики сообщений TCN_SELCHANGE и TCN_SELCHANGING:

	void CTab_controlDlg::OnSelchangingTab(NMHDR* pNMHDR, LRESULT* pResult) {
		// TODO: Add your control notification handler code here
		int nTab = m_ctrTab.GetCurSel();
		TC_ITEM tci;
		tci.mask = TCIF_PARAM;
		m_ctrTab.GetItem(nTab, &tci);
		ASSERT(tci.lParam);
		CWnd* pWnd = (CWnd *)tci.lParam;
		pWnd->ShowWindow(SW_HIDE);
		*pResult = 0;
		}
	
	void CTab_controlDlg::OnSelchangingTab(NMHDR* pNMHDR, LRESULT* pResult) {
		int nTab = m_ctrTab.GetCurSel();
		TC_ITEM tci;
		tci.mask = TCIF_PARAM;
		m_ctrTab.GetItem(nTab, &tci);
		ASSERT(tci.lParam);
		CWnd* pWnd = (CWnd *)tci.lParam;
		pWnd->ShowWindow(SW_HIDE);
		*pResult = 0;
		} 

Здесь используются те самые указатели, которые мы спрятали в OnInitDialog

Теперь освободим память и разрушим диалоговые окна при выходе из приложения.

Добавим функцию OnDestroy:

	void CTab_controlDlg::OnDestroy() {
		CDialog::OnDestroy();
		CWnd* pWnd;
		TC_ITEM tci;
		tci.mask = TCIF_PARAM;
		for (int i = 2; i>=0; i--) {
			m_ctrTab.GetItem(i, &tci);
			ASSERT(tci.lParam);
			pWnd = (CWnd *)tci.lParam;
			pWnd->DestroyWindow();
			delete pWnd;
			}
		} 

Ну вот и всё, приложение готово.