Н. А. Литвиненко


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



Pdf просмотр
страница9/15
Дата28.11.2016
Размер8.29 Mb.
Просмотров3147
Скачиваний1
1   ...   5   6   7   8   9   10   11   12   ...   15
Общие элементы управления

Элементы управления, которые появились лишь при создании ОС Windows 95, по- лучили название общих элементов управления (Common Controls). Они расширяют возможности стандартных элементов управления и придают программам совре- менный вид. Один из этих элементов мы уже рассмотрели в главе 2 — это панель
инструментов (Tool Bar). Сейчас рассмотрим еще 3 элемента — наборный счет-
чик (Spin Control), ползунковый регулятор (Slider Control) и индикатор выполне-
ния (Progress Bar Control).
Техника их использования мало чем отличается от стандартных элементов управ- ления: в редакторе ресурсов перемещаем нужные элементы с палитры инструмен- тов в диалоговое окно, присваиваем идентификаторы и устанавливаем необходи- мые свойства.
Особенностью же общих элементов управления является то, что они не входят в стандартную библиотеку и для них необходимо добавить файл включений commctrl.h
, а также подключить к проекту библиотеку общих элементов управле- ния comctl32.lib
, как мы это делали в предыдущей главе (см. рис. 2.3).
К тому же, до использования общих элементов управления приложение должно инициализировать библиотеку функцией
InitCommonControls()
П
Р И МЕ Ч А Н И Е

Хотя панель инструментов и является общим элементом управления, для ее исполь- зования нет необходимости в вызове функции InitCommonControls()
Так же, как и стандартные элементы управления, общие элементы являются специ- альными элементами, которые посылают родительскому окну сообщение
WM_COMMAND
или
WM_NOTIFY
. Управление осуществляется посылкой им соответствующих сообще- ний. Демонстрационная задача приведена на рис. 3.7 и далее в листинге 3.4.
Создадим диалоговое окно, которое вызывается в пункте меню StdDialog, и раз- местим там элементы управления: наборный счетчик (спин), ползунковый регуля- тор (ползунок), индикатор выполнения и два статических элемента для вывода значений спина и ползунка. При закрытии окна кнопкой OK возвращаем установ- ленные значения элементов управления и выводим в окне, при нажатии на кнопку
Cancel ничего возвращать не будем.
П
Р И МЕ Ч А Н И Е

Все рассмотренные общие элементы управления (
Spin Control, Slider Control, Progress
Bar Control
) имеют внутреннюю память для хранения состояния элемента.
Для обмена данными опишем на глобальном уровне три переменные: spin, track, progress
. Вывод в окно осуществим в сообщении
WM_PAINT
, где сформируем строку

Окна и элементы управления

129
вывода функцией форматного вывода в строку
_stprintf()
. Причем используем универсальную функцию, работающую как с С-строкой, так и со строкой Unicode.
Для вывода воспользуемся функцией
DrawText()
, поскольку она позволяет форма- тировать выводимый текст. Так что мы сможем одним оператором вывести три строки текста, выравнивая данные табуляцией.

Рис.
3.7.
Диалоговое окно с общими элементами управления
Slider (Track Bar Control)


ползунок

Ползунок или ползунковый регулятор состоит из указателя, движущегося вдоль шкалы. Он является разновидностью горизонтальной полосы прокрутки и генери- рует сообщение
WM_HSCROLL
. В lParam передается дескриптор элемента управления, породившего сообщение, и мы можем решить вопрос о том, каким элементом управления оно вызвано, если таких элементов несколько.
В функции диалогового окна опишем переменную для хранения состояния ползун- ка и его дескриптор: static int track; static HWND hTrack;
В сообщении об инициализации диалога необходимо определить дескриптор, ми- нимальную и максимальную позиции ползунка и установить начальное значение: track = ::track; hTrack = GetDlgItem(hDlg, IDC_SLIDER1);
SendMessage(hTrack, TBM_SETRANGEMIN, 0, 0);
SendMessage(hTrack, TBM_SETRANGEMAX, 0, 100);
SendMessage(hTrack, TBM_SETPOS, TRUE, track);
Здесь минимальное и максимальное значения позиции ползунка определяются по- сылкой ему сообщения
TBM_SETRANGEMIN и
TBM_SETRANGEMAX
, а само значение пе- редается в lParam
, у нас —
[0,100]
. Текущая позиция задается посылкой сообще- ния
TBM_SETPOS
, где значение
TRUE в wParam означает необходимость перерисовки ползунка, а позиция track передается в lParam

Глава 3

130
П
Р И МЕ Ч А Н И Е

Вместо двух сообщений для установки минимального и максимального значений пол- зунка можно использовать сообщение TBM_SETRANGE, где минимум задается в млад- шем слове lParam, а максимум —
в старшем.
SendMessage(hTrack, TBM_SETRANGE, 0, 100<<16);
Теперь осталось вывести значение track в статическом элементе
IDC_TR1
SetDlgItemInt(hDlg, IDC_TR1, track, 0);
Для манипуляций с ползунком в сообщении
WM_HSCROLL
передадим ему сообщение
TBM_GETPOS.
Позицию ползунка получим из младшего слова возвращаемого значе- ния функции
SendMessage()
. После чего выведем это значение в статическом эле- менте
IDC_TR1
так же, как мы это делали ранее при инициализации ползунка.
П
Р И МЕ Ч А Н И Е

Когда ползунок находится в фокусе ввода (элемент управления, находящийся в фоку- се, готов принимать команды с клавиатуры), его состоянием можно управлять клави- шами управления курсором: <
End>

минимальное значение, <
Home>

максималь- ное значение, < > и <
>

увеличение значения на 1, < > и <
>

уменьшение значения на 1, <
Page Up>

увеличение значения на шаг, <
Page Down>

уменьше- ние значения на шаг. Шаг ползунка определяется автоматически в 1/5 от диапазона его изменения.
Spin (Up-Down Control)


наборный счетчик

Наборный счетчик представляет собой специальный вид полосы вертикального скроллинга, он состоит из кнопок вверх и вниз так же, как и элемент управления
Vertical Scroll Bar, и порождает сообщение
WM_VSCROLL
. В lParam передается деск- риптор элемента управления, породившего сообщение.
В оконной функции диалогового окна необходимо описать дескриптор спина hSpin
, а в сообщении об инициализации диалога определить этот дескриптор и ус- тановить его начальное значение.
Опишем в функции диалогового окна дескриптор спина: static HWND hSpin;
Для отображения значения спина в статическом элементе можно поступить как для ползунка — вывести значение функцией
SetDlgItemInt()
. Однако счетчик предо- ставляет пользователю дополнительный сервис. Если при построении диалогового окна установить свойство спина Set Buddy Integer, то можно этому спину назна- чить "приятельское" (buddy) окно
IDC_SP1
, а, посылая ему сообщение
UDM_SETBUDDY
, в wParam передать дескриптор "приятеля"
hBuddy
: hSpin = GetDlgItem(hDlg, IDC_SPIN1); hBuddy = GetDlgItem(hDlg, IDC_SP1);
SendMessage(hSpin, UDM_SETBUDDY, (WPARAM)hBuddy, 0);
В этом случае счетчик сам позаботится о выводе своего значения в приятельское окно, и нам об этом беспокоиться больше не нужно.

Окна и элементы управления

131
Сообщение
UDM_SETRANGE устанавливает диапазон изменения счетчика так же, как для ползунка.
SendDlgItemMessage(hDlg, IDC_SPIN1, UDM_SETRANGE, 0, 100);
П
Р И МЕ Ч А Н И Е

Если в диапазоне значений спина минимальное значение превышает максимальное,
LOWORD(lParam)
> HIWORD(lParam)
, инвертируется направление изменения спина.
Сообщение
UDM_SETPOS устанавливает позицию спина, передаваемую в lParam
SendMessage(hSpin, UDM_SETPOS, 0, spin);
Поскольку мы установили для наборного счетчика (спина) приятельское окно, нет необходимости в обработке сообщения
WM_VSCROLL
для индикации числового зна- чения.
Progress Bar Control


индикатор выполнения

Индикатор выполнения — это элемент управления, который показывает течение процесса, например, при копировании файла в файловом менеджере. Управление осуществляется передачей сообщений, сам же индикатор сообщений не генерирует.
Как и для предыдущих элементов управления, опишем в диалоговой оконной функции дескриптор индикатора: static HWND hProgress; и определим его при инициализации диалогового окна. hProgress = GetDlgItem(hDlg, IDC_PROGRESS1);
Для индикатора необходимо определить диапазон изменения и задать начальную позицию. Диапазон
[0,100]
определим посылкой сообщения
PBM_SETRANGE
, где минимум задается в младшем слове lParam
, а максимум — в старшем. Шаг инди- катора, равный
1
, зададим в wParam сообщения
PBM_SETSTEP
. Начальную позицию, равную t
, зададим посылкой сообщения
PBM_SETPOS
. Здесь t
— статическая пере- менная целого типа, описанная в оконной функции диалога, будет контролировать состояние индикатора.
SendMessage(hProgress, PBM_SETRANGE, 0, 100<<16);
SendMessage(hProgress, PBM_SETSTEP, 1, 0);
SendMessage(hProgress, PBM_SETPOS, t, 0);
Для создания иллюзии движения создадим таймер с интервалом в 0,1 секунды:
SetTimer(hDlg, 1, 100, NULL);
Теперь в обработчике сообщения от таймера
WM_TIMER
будем увеличивать значение переменной на
1
и переустанавливать состояние индикатора. if (++t > 99) t = 0;
SendMessage(hProgress, PBM_SETPOS, t, 0);
Оператором if через каждые 10 секунд сбрасываем индикатор в начальное со- стояние.

Глава 3

132
Осталось только перед закрытием диалогового окна кнопкой OK передать гло- бальной переменной значение индикатора и уничтожить таймер: progress = t;
KillTimer(hDlg,1);
Поскольку состояние ползунка отслеживает переменная track
, передадим ее на глобальный уровень:
::track = track;
Считываем состояние наборного счетчика, передавая ему сообщение
UDM_GETPOS
, и перерисовываем главное окно функцией
InvalidateRect()
(см. листинг 3.4).
После чего диалоговое окно можно закрыть.
При нажатии кнопки Cancel уничтожаем таймер и закрываем диалоговое окно без перерисовки главного окна.
Листинг 3.4. Тест общих элементов управления

#include
INT_PTR CALLBACK Dialog1(HWND, UINT, WPARAM, LPARAM); static int spin, track, progress;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa- ram)
{
PAINTSTRUCT ps;
HDC hdc;
TCHAR str[256];
RECT rt; switch (message)
{ case WM_CREATE:
InitCommonControls(); break; case WM_COMMAND: switch (LOWORD(wParam))
{ case ID_COMMCTRL:
DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, Dialog1); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} break; case WM_PAINT:
SetRect(&rt, 0, 0, 100, 100); hdc = BeginPaint(hWnd, &ps);

Окна и элементы управления

133
_stprintf(str,_T("spin\t= %d\ntrack\t= %d\nprogress= %d"), spin, track, progress);
DrawText(hdc, str,_tcslen(str), &rt, DT_LEFT |DT_EXPANDTABS);
EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} return 0;
}
///////////////////////////////////////////////////////////////////
INT_PTR CALLBACK Dialog1(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPa- ram)
{ static int t, track; static HWND hSpin, hBuddy, hTrack, hProgress; switch (message)
{ case WM_INITDIALOG: track = ::track;
SetDlgItemInt(hDlg, IDC_TR1, track, 0); hTrack = GetDlgItem(hDlg, IDC_SLIDER1);
SendMessage(hTrack, TBM_SETRANGE, 0, 100<<16);
SendMessage(hTrack, TBM_SETPOS, TRUE, track); hSpin = GetDlgItem(hDlg, IDC_SPIN1); hBuddy = GetDlgItem(hDlg, IDC_SP1);
SendMessage(hSpin, UDM_SETBUDDY, (WPARAM)hBuddy, 0);
SendMessage(hSpin, UDM_SETRANGE, 0, 100);
SendMessage(hSpin, UDM_SETPOS, 0, spin); hProgress = GetDlgItem(hDlg, IDC_PROGRESS1);
SendMessage(hProgress, PBM_SETRANGE, 0, 100<<16);
SendMessage(hProgress, PBM_SETSTEP, 1, 0);
SendMessage(hProgress, PBM_SETPOS, t, 0);
SetTimer(hDlg, 1, 100, NULL); return TRUE; case WM_TIMER : if (++t > 99) t = 0;
SendMessage(hProgress, PBM_SETPOS, t, 0); return TRUE; case WM_HSCROLL: track = LOWORD(SendMessage(hTrack, TBM_GETPOS, 0, 0));
SetDlgItemInt(hDlg, IDC_TR1, track, 0); return TRUE;

Глава 3

134
case WM_COMMAND: switch(LOWORD(wParam))
{ case IDOK : progress = t;
::track = track; spin = SendMessage(hSpin, UDM_GETPOS, 0, 0);
InvalidateRect(GetParent(hDlg),NULL,1); case IDCANCEL:
KillTimer(hDlg,1);
EndDialog(hDlg, 0); return TRUE; default: return FALSE;
} default: return FALSE;
} return FALSE;
}
Окно редактирования

Для ввода данных чаще всего используется элемент управления — окно редакти-
рования (Edit Box Control). Управление окном осуществляется передачей ему со- общений или специализированными функциями диалогового окна:

GetDlgItemText(hDlg, IDC_EDIT, text, length)
— возвращает в
TCHAR
мас- сив text не более length символов окна редактирования;

SetDlgItemText(hDlg, IDC_EDIT, text)
— заполняет окно редактирования содержимым
TCHAR
-строки text
;

GetDlgItemInt(hDlg, IDC_EDIT, lpTranslated, bSigned)
— функция возвра- щает целое число, в которое преобразуется содержимое окна редактирования. lpTranslated
— указатель переменной типа
BOOL
, которая устанавливается в
TRUE
при успешном преобразовании числа, и
FALSE
— в противном случае. Ес- ли bSigned равно
TRUE
, преобразуется число со знаком иначе, число рассматри- вается как беззнаковое;

SetDlgItemInt(hDlg, IDC_EDIT1, Value, bSigned)
— устанавливается значе- ние переменной целого типа
Value в окне редактирования. Если bSigned равно
TRUE
, рассматривается число со знаком.
Для демонстрации возможностей этого окна создадим тестовую программу (лис- тинг 3.5), вид ее окна приведен на рис. 3.8. В диалоговом окне осуществляется ввод текстовых строк в элемент управления список, а после закрытия диалогового окна данные выводятся в главном окне. Предусмотрена возможность удаления строки списка.

Окна и элементы управления

135
Дополнительно выведем в нижней части главного окна строку состояния (строку статуса), где предусмотрим отображение количества строк списка.

Рис.
3.8.
Демонстрационная программа "Окно редактирования"
Листинг 3.5. Окно редактирования и список

#include
#include
#include
INT_PTR CALLBACK Dialog1(HWND, UINT, WPARAM, LPARAM); typedef std::basic_string, std::allocator > String; std::vector v;
HWND hWndStatusBar; //Дескриптор строки состояния
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa- ram)
{
PAINTSTRUCT ps;
HDC hdc;
TEXTMETRIC tm; static int cy, sx, sy; int y;
RECT rt; std::vector::iterator it; switch (message)
{ case WM_CREATE: hdc = GetDC(hWnd);
GetTextMetrics(hdc,&tm); cy = tm.tmHeight + tm.tmExternalLeading;

Глава 3

136
ReleaseDC(hWnd, hdc); hWndStatusBar = CreateStatusWindow(WS_CHILD | WS_VISIBLE |
WS_CLIPSIBLINGS | CCS_BOTTOM | SBARS_SIZEGRIP,_T("Ready"), hWnd, 1); break; case WM_SIZE: sx = LOWORD(lParam); sy = HIWORD(lParam);
GetWindowRect(hWndStatusBar, &rt); y = rt.bottom-rt.top;
MoveWindow(hWndStatusBar, 0, sy - y, sx, sy, TRUE); break; case WM_COMMAND: switch (LOWORD(wParam))
{ case ID_DIALOG_READLISTBOX:
DialogBox(hInst, MAKEINTRESOURCE(IDD_READ), hWnd, Dialog1); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); for (y = 0, it = v.begin(); it != v.end(); ++it, y += cy)
TextOut(hdc, 0, y, it->data(), it->size());
EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} return 0;
}
//////////////////////////////////////////////////////////////////////
INT_PTR CALLBACK Dialog1(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{ static HWND hList, hEdit;
TCHAR text[256]; int i, k; switch (message)
{ case WM_INITDIALOG: hList = GetDlgItem(hDlg, IDC_LIST1);

Окна и элементы управления

137
hEdit = GetDlgItem(hDlg, IDC_EDIT1); return TRUE; case WM_COMMAND: switch (LOWORD(wParam))
{ case IDC_ADD:
GetDlgItemText(hDlg, IDC_EDIT1, text, 256);
SetDlgItemText(hDlg, IDC_EDIT1, _T(""));
SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)text);
SetFocus(hEdit); return TRUE; case IDC_DEL: k = SendMessage(hList, LB_GETCURSEL, 0, 0);
SendMessage(hList, LB_DELETESTRING , (WPARAM)k, 0); return TRUE; case ID_OK: v.clear(); k = SendMessage(hList, LB_GETCOUNT, 0, 0); for (i = 0; i < k; i++)
{
SendMessage(hList, LB_GETTEXT, (WPARAM)i, (LPARAM)text); v.push_back(text);
}
InvalidateRect(GetParent(hDlg), NULL, 1);
_stprintf(text,_T("Список: %d строк"), k);
SendMessage(hWndStatusBar, WM_SETTEXT, 0, (LPARAM)text); case WM_DESTROY: EndDialog(hDlg, LOWORD(wParam)); return TRUE;
} default: return FALSE;
} return FALSE;
}
В качестве контейнера для хранения текстовых строк, получаемых из списка, ис- пользуем контейнер vector для типа данных
String
, производного от шаблонного класса basic_string для типа
TCHAR
(см. листинг 1.3).
В заголовке программы добавим файлы включений:
#include
#include
Опишем на глобальном уровне вектор, явно указывая область видимости: std::vector v;

Глава 3

138
Итератор вектора нет смысла описывать на глобальном уровне, поэтому опишем его в функции главного окна: std::vector::iterator it;
Нужно позаботиться о выводе результатов в главное окно, поэтому в сообщении
WM_CREATE
найдем высоту строки текущего шрифта при помощи функции
GetTextMetrics()
, как мы это делали в листинге 1.13.
Переменную tm типа
TEXTMETRIC и статическую переменную cy
,
необходимую для хранения высоты шрифта, предварительно опишем в оконной функции.
Диалоговое окно будем вызывать через главное меню, где создадим пункт с иден- тификатором
ID_DIALOG_READLISTBOX
Dialog1
— имя оконной функции диалога.
Именно в этой функции и реализуется вся логика работы с элементами управления.
На поверхности диалогового окна разместим следующие элементы:

поле ввода (EditBox) с идентификатором
IDC_EDIT1
;

список (ListBox) с идентификатором
IDC_LIST1
;

кнопку
IDC_ADD
;

кнопку
IDC_DEL
;

кнопку
ID_OK
;

иконку
IDC_STATIC
с изображением
IDI_TRASH
;

битовый образ с изображением
IDB_MOUSE
Последние два элемента мы ввели, чтобы "оживить" диалоговое окно, а картинки нашли в имеющейся коллекции и импортировали в созданный проект (см. рис. 3.2).
Разместим их в элементе управления Picture Control.
Начнем рассмотрение функции диалогового окна
Dialog1()
В сообщении
WM_INITDIALOG определим дескрипторы окна редактирования и списка: hList = GetDlgItem(hDlg, IDC_LIST1); hEdit = GetDlgItem(hDlg, IDC_EDIT1);
Эти переменные описаны в заголовке функции: static HWND hList, hEdit;
Теперь обработаем реакцию на нажатие кнопки с изображением "
>>"
Функцией
GetDlgItemText() читаем содержимое окна редактирования в массив text и функцией
SetDlgItemText() очищаем окно, посылая "пустую" строку.
Посылая списку сообщение
LB_ADDSTRING
, добавим к нему строку text
. Теперь ус- тановим фокус ввода обратно на окно редактирования для последующего ввода:
SetFocus(hEdit);
П
Р И МЕ Ч А Н И Е

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

Окна и элементы управления

139
Обработаем реакцию на нажатие кнопки "
<<"
для удаления выделенной в списке строки. Посылая списку сообщение
LB_GETCURSEL
, получим индекс выделенного элемента, а сообщением
LB_DELETESTRING
удалим элемент с найденным индексом.
Нажатием на кнопку OK (идентификатор которой заменим на
ID_OK
, и установим в
False свойство
Default Button
, чтобы избежать стандартной реакции на нажатие клавиши
)
извлечем из списка введенные строки и поместим их в контей- нер vector
, описанный на глобальном уровне.
Очистим содержимое контейнера: v.clear();
Найдем размер списка, передавая ему сообщение
LB_GETCOUNT
: k = SendMessage(hList, LB_GETCOUNT, 0, 0);
В цикле читаем последовательно содержимое списка, посылая ему сообщение
LB_GETTEXT
, где третьим параметром служит индекс извлекаемого элемента, а чет- вертый параметр — массив text для хранения строки текста. Прочитав строку тек- ста, помещаем ее в контейнер методом push_back()
TCHAR
-строка автоматически преобразуется к типу
String for (i = 0; i < k; i++)
{
SendMessage(hList, LB_GETTEXT, (WPARAM)i, (LPARAM)text); v.push_back(text);
}
П
Р И МЕ Ч А Н И Е

В этой задаче применение контейнера для хранения строк не является необходимым, поскольку количество строк в списке известно, и можно было бы выделить массив ти- па String
Функцией
InvalidateRect() инициируем перерисовку главного окна программы, где дескриптор главного окна получим обращением к функции
GetParent(hDlg)
Мы не поставили оператор return перед следующим оператором case намеренно, поскольку выполнение кода будет продолжаться, произойдет переход на операто- ры закрытия диалогового окна и выхода из функции:
EndDialog(hDlg, LOWORD(wParam)); return TRUE; что и требовалось.
Сообщение с кодом
WM_DESTROY будет генерироваться при нажатии на кнопку за- вершения приложения системного меню. В этом случае приложение завершится без сохранения данных списка и перерисовки главного окна.
Мы поместили на диалоговом окне еще два элемента Picture Control, оставив для них идентификатор по умолчанию
IDC_STATIC
, поскольку нет необходимости дос- тупа к ним. Для первого элемента выбрали тип
Icon
, а в качестве изображения вы- брали идентификатор
IDI_TRASH
импортированной иконки, которую подобрали в специализированной библиотеке.
Для второго элемента выберем тип
Bitmap
, а в качестве битового образа —
IDB_MOUSE
. Этот идентификатор мы получим, импортируя bmp-файл с растровым

Глава 3

140
изображением. Для полноты картины заменим и иконку приложения. Для этого импортируем еще одну иконку из найденной коллекции и присвоим ей идентифи- катор
IDI_FLGRUS
Теперь осталось изменить стиль окна, для чего отредактируем лишь одно поле класса окна: wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_FLGRUS));
Вот и все, теперь в качестве иконки приложения будет использоваться найденное нами изображение.
Строка состояния

Для того чтобы придать приложению, представленному в листинге 3.5, более со- временный вид, добавим в окно строку состояния. Эта строка располагается обыч- но в нижней части окна и выводит справочную информацию о состоянии приложе- ния. Выведем здесь количество строк списка.
Поскольку окно состояния является общим элементом управления, необходимо добавить файл включений commctrl.h и, соответственно, библиотечный файл comctl32.lib
Дескриптор окна состояния опишем на глобальном уровне:
HWND hWndStatusBar; и создадим окно при обработке сообщения
WM_CREATE
главного окна функцией
CreateStatusWindow()
:
HWND WINAPI CreateStatusWindowW(LONG style, LPCWSTR lpszText, HWND hwndParent, UINT wID); где:

style
— стиль окна;

lpszText
— текст по умолчанию;

hwndParent
— дескриптор родительского окна;

wID
— идентификатор окна.
К сожалению, окно состояния не может самостоятельно подстроиться под размер окна при его изменении, поэтому необходимо написать дополнительный код при обработке сообщения
WM_SIZE
:
GetWindowRect(hWndStatusBar, &rt); y = rt.bottom-rt.top;
MoveWindow(hWndStatusBar, 0, sy - y, sx, sy, TRUE);
Теперь нужно решить вопрос — где мы будем выводить информацию в строку со- стояния? Проще всего это сделать при обработке сообщения о нажатии кнопки OK диалогового окна. Для этого мы сформируем строку вывода text
:
_stprintf(text,_T("Список: %d строк"), k);
и выведем эту строку в окно состояния, послав ему сообщение
WM_SETTEXT
:
SendMessage(hWndStatusBar, WM_SETTEXT, 0, (LPARAM)text);

Окна и элементы управления

141
Простой текстовый редактор на элементе управления
Edit Box Control
Мы рассмотрели использование окна редактирования Edit Box для ввода строки текста, однако этот элемент управления имеет гораздо больше возможностей.
В качестве демонстрационного примера построим на его основе простой текстовый редактор. Как мы вскоре убедимся, все операции по вводу и редактированию тек- ста элемент Edit Box берет на себя, нам остается лишь реализовать внешний ин- терфейс. Следует оговориться, что элемент управления Edit Box использует внут- реннюю память для хранения текста, а это приводит к ограничениям на размер редактируемого текста в 32 767 символов (
0x7fff
).
П
Р И МЕ Ч А Н И Е

В операционной системе
Windows NT и выше размер буфера
Edit Box может быть увеличен посылкой ему сообщения EM_LIMITTEXT
до 0x7FFFFFFE
байтов.
В качестве основы используем стандартный проект Win32. Добавим три пункта меню: New, Open, Save, панель инструментов с кнопками, соответствующими этим пунктам меню, а также строку состояния (см. листинг 3.6). Будем создавать уни- версальный проект, однако, поскольку приложение ориентировано на работу с тек- стовыми файлами в однобайтной кодировке, будем явно указывать тип char*
для входных и выходных массивов данных.
Листинг 3.6. Текстовый редактор с элементом управления
Edit Box
#include
#include
#include
TBBUTTON tbb[] =
{{STD_FILENEW, ID_FILE_NEW, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0, 0},
{STD_FILEOPEN, ID_FILE_OPEN,TBSTATE_ENABLED,TBSTYLE_BUTTON, 0, 0, 0, 0},
{STD_FILESAVE, ID_FILE_SAVE,TBSTATE_ENABLED,TBSTYLE_BUTTON, 0, 0, 0, 0}
};
//////////////////////////////////////////////////////////////////////
VOID StatusOut(HWND hStatus, int count, TCHAR *str)
{
TCHAR text[256];
_stprintf(text,_T("Строк: %d"), count);
SendMessage(hStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)text);
SendMessage(hStatus, SB_SETTEXT, (WPARAM)1, (LPARAM)str);
}
//////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{ static OPENFILENAME file;

Глава 3

142
static int n, sx, sy; static HWND hEdit, hWndToolBar, hWndStatusBar;
RECT rt; int m, k, aWidths[2]; static TCHAR name[256]; char szText[0x7fff]; std::ifstream in; std::ofstream out; switch (message)
{ case WM_CREATE: hWndToolBar = CreateToolbarEx(hWnd, WS_CHILD|WS_VISIBLE|CCS_TOP,
2, 0, HINST_COMMCTRL, IDB_STD_SMALL_COLOR, tbb, 3, 0, 0, 0, 0, sizeof(TBBUTTON)); hEdit = CreateWindow(WC_EDIT,NULL,WS_CHILD|WS_VISIBLE|WS_HSCROLL|
WS_VSCROLL|ES_LEFT|ES_MULTILINE|ES_AUTOHSCROLL|ES_AUTOVSCROLL,
0, 0, 0, 0, hWnd, (HMENU) 1, hInst, NULL); file.lStructSize
= sizeof(OPENFILENAME); file.hInstance
= hInst; file.lpstrFilter
= _T("Text\0 *.txt\0Все файлы\0 *.*"); file.lpstrFile
= name; file.nMaxFile
= 256; file.lpstrInitialDir = _T(".\\"); file.lpstrDefExt
= _T("txt"); hWndStatusBar = CreateWindow(STATUSCLASSNAME, NULL, WS_CHILD |

WS_VISIBLE, 0, 0, 0, 0, hWnd, NULL, hInst, NULL); break; case WM_SIZE: sx = LOWORD(lParam); sy = HIWORD(lParam); aWidths[0] = 100; aWidths[1] = sx;
GetWindowRect(hWndToolBar, &rt); m = rt.bottom - rt.top;
SendMessage(hWndToolBar, TB_AUTOSIZE, 0, 0);
GetWindowRect(hWndStatusBar, &rt); k = rt.bottom - rt.top;
MoveWindow(hWndStatusBar, 0, sy - k, sx, sy, TRUE);
SendMessage(hWndStatusBar, SB_SETPARTS, (WPARAM)2, (LPARAM)aWidths);
StatusOut(hWndStatusBar, n, name);
MoveWindow(hEdit, 0, m, sx, sy - m - k, TRUE);
UpdateWindow(hEdit);
SetFocus(hEdit); return 0;

Окна и элементы управления

143
case WM_COMMAND: switch (LOWORD(wParam))
{ case ID_FILE_NEW: szText[0] = '\0';
SetWindowTextA(hEdit, szText);
StatusOut(hWndStatusBar, 0, _T("")); break; case ID_FILE_OPEN: file.lpstrTitle = _T("Открыть файл для чтения"); file.Flags = OFN_HIDEREADONLY; if (!GetOpenFileName(&file)) return 1; in.open(name, std::ios::binary); in.read(szText, 0x7fff); if ((m = in.gcount()) == 0x7fff)
{
MessageBox(hWnd, _T("Слишком большой файл"),

_T("Edit"),MB_OK | MB_ICONSTOP); in.close(); return 0;
} szText[m] = '\0'; in.close();
SetWindowTextA(hEdit, szText); n = SendMessage(hEdit, EM_GETLINECOUNT, 0, 0);
StatusOut(hWndStatusBar, n, name); break; case ID_FILE_SAVE: file.lpstrTitle = _T("Открыть файл для записи"); file.Flags = OFN_NOTESTFILECREATE | OFN_HIDEREADONLY; if (!GetSaveFileName(&file)) return 1; out.open(name, std::ios::binary); m = GetWindowTextA(hEdit, szText, 0x7fff); out.write(szText, m); out.close(); n = SendMessage(hEdit, EM_GETLINECOUNT, 0, 0);
StatusOut(hWndStatusBar, n, name); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} break;

Глава 3

144
case WM_DESTROY:
PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} return 0;
}
Нам понадобятся файлы включений: commdlg.h
, commctrl.h
, fstream
. Доступ к стандартной области имен открывать не будем, а опишем переменные с явным указанием области видимости.
Как и в программе просмотра файлов, рассмотренной в главе 2 (см. листинг 2.3), опишем в массиве
TBBUTTON
3 кнопки инструментальной панели. Саму панель ин- струментов создадим при обработке сообщения
WM_CREATE
и получим ее дескрип- тор hwndToolBar
: hwndToolBar = CreateToolbarEx(hWnd, WS_CHILD|WS_VISIBLE|CCS_TOP, 2, 0,
HINST_COMMCTRL,IDB_STD_SMALL_COLOR,tbb, 3, 0, 0, 0, 0,sizeof(TBBUTTON));
Корректировку размера панели инструментов вставим в обработчик сообщения
WM_SIZE
, посылая ей сообщение
TB_AUTOSIZE
:
SendMessage(hwndToolBar, TB_AUTOSIZE, 0, 0);
Здесь же создадим элемент управления
Edit Box
: hEdit = CreateWindow(WC_EDIT, NULL, WS_CHILD|WS_VISIBLE|WS_HSCROLL|
WS_VSCROLL|ES_LEFT|ES_MULTILINE|ES_AUTOHSCROLL|ES_AUTOVSCROLL,
0, 0, 0, 0, hWnd, (HMENU) 1, hInst, NULL);
В параметре Класс окна функции
CreateWindow() указываем
WC_EDIT

предопределенный в системе идентификатор окна Edit Box. Заголовка у окна нет, поэтому второй параметр
NULL
Стиль окна будет складываться из следующих компонент:

WS_CHILD
— дочернее окно;

WS_VISIBLE
— окно отображается при создании;

WS_HSCROLL
— имеет горизонтальную полосу скроллинга;

WS_VSCROLL
— имеет вертикальную полосу скроллинга;

ES_LEFT
— выравнивание текста влево;

ES_MULTILINE
— многострочное окно редактирования;

ES_AUTOHSCROLL
— автоматическая прокрутка текста при вводе строки;

ES_AUTOVSCROLL
— автоматическая прокрутка текста, когда окно заполнено.
Все четыре параметра расположения и размера окна зададим равным
0
, а реальные размеры определим позднее в сообщении
WM_SIZE
Далее укажем дескриптор родительского окна hWnd
. Следующий параметр — ука- затель меню — в данном контексте служит для задания идентификатора окна: при-

Окна и элементы управления

145
своим ему номер
1
. Укажем следующим параметром дескриптор приложения — hInst и последний параметр —
NULL
, поскольку дополнительные параметры от- сутствуют.
Окно создано, но мы хотели бы наложить его на главное окно программы, размеры которого нужно определять в сообщении
WM_SIZE
. Сделаем это функцией:
MoveWindow(hEdit, 0, m, sx, sy - m - k, TRUE);
Функция
MoveWindow() позволяет изменить положение
(0, m)
и размер окна
(sx, sy

m - k)
. Поскольку окно редактирования объявлено как дочернее, то распола- гается оно в клиентской области главного окна и начало его нужно поместить в левом верхнем углу клиентской области. Но там располагается панель инструмен- тов, высоту которой мы определим, обратившись к функции
GetWindowRect()
:
GetWindowRect(hwndToolBar,& rt); m = rt.bottom - rt.top;
Таким образом, левый верхний угол окна редактирования нужно сместить вниз по оси y на m
. Высоту окна следует уменьшить на вертикальный размер панели инст- рументов и строки состояния, которая также располагается в клиентской области главного окна: sy – m - k
Строку состояния создадим с нулевыми размерами функцией
CreateWindow() в со- общении
WM_CREATE
: hWndStatusBar = CreateWindow(STATUSCLASSNAME, NULL, WS_CHILD |
WS_VISIBLE, 0, 0, 0, 0, hWnd, NULL, hInst, NULL);
П
Р И МЕ Ч А Н И Е

Идентификатор строки состояния STATUSCLASSNAME
определен в файле включений как: "msctls_statusbar32"
Реальный же размер зададим в сообщении
WM_SIZE
:
MoveWindow(hWndStatusBar, 0, sy - k, sx, sy, TRUE);
где sx, sy
— ширина и высота главного окна, а k
— высота окна состояния.
Воспользуемся некоторыми особенностями этого окна управления. Имеется воз- можность разделить строку состояния на несколько частей и выводить информа- цию в каждую часть отдельно. Для этого необходимо, послав сообщение
SB_SETPARTS
, указать в wParam количество частей, а в lParam их правые границы, заданные массивом aWidths
:
SendMessage(hWndStatusBar, SB_SETPARTS, (WPARAM)2, (LPARAM)aWidths);
Поделим строку состояния на две части, установив фиксированный размер первой части, а для второй части отдадим остаток строки: aWidths[0] = 100; aWidths[1] = sx;
Вывод в строку состояния организуем в локальной функции
StatusOut()
, куда пе- редаем количество строк текста count и имя файла str
Сформируем вывод в первую часть строки состояния функцией
_stprintf()
и от- правим данные, послав окну сообщение
SB_SETTEXT
. Номер части указывается

Глава 3

146
в
WPARAM
сообщения. Во вторую часть поместим имя файла str
, полученное из диа- логового окна открытия файла.
После установки всех размеров необходимо обратиться к функции:
UpdateWindow(hEdit); иначе окно не успеет перерисоваться.
Что еще важно, так это обеспечить получение фокуса ввода окном редактирования:
SetFocus(hEdit);
Собственно, что касается окна редактирования, то оно уже должно работать — можно вводить текст и редактировать его, доступны скроллинг и контекстное меню.
Оставшийся код необходим для организации чтения файла в окно редактирования и записи в файл отредактированного текста.
Как и в задаче, представленной листингом 2.3, в сообщении о создании окна запол- ним структуру
OPENFILENAME
. И лишь два поля этой структуры: lpstrTitle и
Flags отличающихся при чтении и записи, мы заполним при обработке команд чтения и записи файла. Диалог выбора файла реализуется функцией
GetOpenFileName()
Но здесь имеется одна особенность — дело в том, что окно редактирования имеет собственную память, и нам нет необходимости вводить контейнер для хранения текста, вместо этого нужно поместить прочитанный файл в окно редактирования.
Нужно учесть еще и ограничение — максимальный размер текста в окне редакти- рования 32К.
П
Р И МЕ Ч А Н И Е

Размер буфера окна редактирования
Edit Box может быть увеличен посылкой ему со- общения EM_SETLIMITTEXT
И еще один момент — строки в окне редактирования разделяются парой символов "\r\n"
так же, как и в файле, и это не случайно.
Можно, конечно, организовать чтение файла построчно и добавлять эти строки в окно редактирования, однако проще прочитать файл как двоичный в буфер szText и оттуда отправить весь текст в окно, так мы и поступим.
Итак, суммируя все сказанное: открываем файл как двоичный и читаем блоком не более
0x7fff символов. in.open(name, std::ios::binary); in.read(szText, 0x7fff);
Если прочитали ровно
0x7fff символов, то файл имеет больший размер и не может быть помещен в окно редактирования. Мы выводим предупреждающее сообщение и прекращаем чтение: if ((m = in.gcount()) == 0x7fff)
{
MessageBox(hWnd,_T("Слишком большой файл"),_T("Edit"),MB_OK|MB_ICONSTOP); in.close(); return 0;
}

Окна и элементы управления

147
Если же размер файла меньше критического, добавим признак завершения строки '\0'
, поскольку метод read() возвращает массив символов, а нам нужна С-строка: szText[m] = '\0';
После чего закрываем файл.
Теперь поместим текст в виде строки в окно редактирования. Это делается функ- цией:
SetWindowText(hEdit, szText);
Осталось только подсчитать количество текстовых строк, передавая окну редакто- ра сообщение
EM_GETLINECOUNT
, и обновить строку состояния.
Для сохранения файла, при обработке пункта меню Save, нужно решить обратную задачу. Так же открываем двоичный файл для записи, но метод для записи блока требует задания его размера, поэтому при копировании текста из окна редактиро- вания в буфер функцией
GetWindowText() сохраняем его размер m
в байтах. После чего записываем буфер в файл. out.open(name, std::ios::binary); m = GetWindowText(hEdit, szText, 0x7fff); out.write(szText, m); out.close();
Вновь определяем количество строк текста и обновляем строку состояния.
Еще проще выглядит обработка команды меню New. Мы сделаем буфер текста "пустым", просто присвоив первому байту массива символ '\0'
. Теперь осталось послать эту "пустую" строку в окно редактирования, и окно очистится: szText[0] = '\0';
SetWindowText(hEdit, szText);
Теперь нужно очистить строку состояния. Вот и все! Вид работающего приложе- ния представлен на рис. 3.9. Вся логика ввода и редактирования текста, а также скроллинг и контекстное меню уже реализованы в окне редактирования, и нам ос- талось лишь пожинать плоды выполненной работы.

Рис.
3.9.
Простейший редактор текстов с элементом управления
Edit Box Control

Глава 3

148
Немодальные окна

До сих пор мы имели дело с модальными окнами, которые после создания стано- вятся активными, и пока они открыты, управление не может быть передано друго- му окну приложения. Немодальные же окна работают принципиально иначе — по- сле создания они могут как потерять управление, так и получить его вновь.
Примером такого окна может служить диалоговое окно поиска Найти и заменить
(Find and Replace) в текстовом редакторе Word.
Для демонстрации работы немодального окна построим приложение с диалоговым окном выбора цвета фона главного окна, состоящего из трех линеек скроллинга, для базовых цветов: красного, зеленого, синего (рис. 3.10).
В качестве основы используем стандартную заготовку, куда добавим пункт меню
Color, при обработке которого создадим диалоговое окно, но, поскольку нам нуж- но немодальное окно, создавать окно будем функцией
CreateDialog()
: hDlgColor = CreateDialog(hInst,MAKEINTRESOURCE(IDD_COLOR),hWnd,Dialog);
Функция имеет тот же набор параметров, что и
DialogBox()
, но возвращает дескриптор окна, который мы опишем на глобальном уровне. На самом деле это макрос, который преобразуется в функцию
CreateDialogParam()
П
Р И МЕ Ч А Н И Е

При создании диалогового окна нужно установить для не- го свойство Visible. Иначе окно, созданное функцией
CreateDialog()
, не будет отображаться.
Поскольку немодальное окно может потерять управление, то цикл обработки сообщений головной функции
WinMain()
должен определить источник сообщения, кото- рое может исходить либо от главного, либо немодального окна. Для этого добавим оператор if в цикл обработки сообщений: while (GetMessage(&msg, NULL, 0, 0))
{ if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
if (!IsDialogMessage(hDlgColor, &msg))
{

TranslateMessage(&msg);

DispatchMessage(&msg);
}
}
Функция
IsDialogMessage()
определяет, предназначено ли сообщение для указан- ного диалогового окна и, если это так, обрабатывает сообщение, возвращая
TRUE
, иначе обработка сообщения происходит традиционным образом оконной функцией главного окна. Так происходит "диспетчеризация" сообщений и, например, щелчок

Рис.
3.10.
Немодальное диалоговое окно

Окна и элементы управления

149
мыши на диалоговом окне приведет к обработке этого сообщения функцией диало- гового окна, а такой же щелчок в главном окне будет обрабатываться функцией главного окна. В связи с этим принципиально важно, чтобы для всех сообщений,
обрабатываемых в оконной функции немодального диалога, возвращаемое значе-
ние было
TRUE
, а для всех прочих сообщений —
FALSE
.
Далее приведем листинг 3.7 с текстом функции окна немодального диалогового окна и отметим, что закрываться немодальное окно должно функцией
DestroyWindow()
Листинг 3.7. Оконная функция немодального диалогового окна

INT_PTR CALLBACK Dialog(HWND, UINT, WPARAM, LPARAM);
/////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa- ram)
{ switch (message)
{ case WM_COMMAND: switch (LOWORD(wParam))
{ case ID_DIALOG_COLOR: hDlgColor = CreateDialog(hInst, MAKEINTRESOURCE(IDD_COLOR), hWnd, Dialog); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} return 0;
}
//////////////////////////////////////////////////////////////////////
INT_PTR CALLBACK Dialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{ static int ID_SCROLL[3] = {IDC_RED, IDC_GREEN, IDC_BLUE}; static int ID[3] = {IDC_R, IDC_G, IDC_B}; static HWND hWnd, hScroll[3];
//{hRed, hGreen, hBlue} static int color[3] = {255, 255, 255}; //{red, green, blue}
HBRUSH hBrush; int index; switch (message)
{

Глава 3

150
case WM_INITDIALOG: for (index = 0; index < 3; index++)
{ hScroll[index] = GetDlgItem(hDlg, ID_SCROLL[index]);
SetScrollRange(hScroll[index], SB_CTL, 0, 255, FALSE);
SetScrollPos (hScroll[index], SB_CTL, color[index], TRUE);
SetDlgItemInt(hDlg, ID[index], color[index], 0);
} return TRUE; case WM_VSCROLL : for (index = 0; index < 3; index++) if ((HWND)lParam == hScroll[index]) break;
SetFocus(hScroll[index]); switch(LOWORD(wParam))
{ case SB_LINEUP : color[index]--; break; case SB_LINEDOWN : color[index]++; break; case SB_PAGEUP : color[index] -= 16; break; case SB_PAGEDOWN : color[index] += 16; break; case SB_THUMBTRACK: case SB_THUMBPOSITION : color[index] = HIWORD(wParam); break;
} color[index] = max(0, min(color[index], 255)); if (color[index] != GetScrollPos(hScroll[index], SB_CTL))
{
SetDlgItemInt(hDlg,ID[index],color[index],0);
SetScrollPos(hScroll[index], SB_CTL, color[index], TRUE); hBrush = CreateSolidBrush(RGB(color[0],color[1],color[2])); hWnd = GetParent(hDlg);
DeleteObject((HBRUSH)GetClassLong(hWnd, GCL_HBRBACKGROUND));
SetClassLong(hWnd, GCL_HBRBACKGROUND, (LONG)hBrush);
InvalidateRect(hWnd, NULL, TRUE);
} return TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDCANCEL) {DestroyWindow(hDlg); return TRUE;} return FALSE;
} return FALSE;
}
Рассмотрим оконную функцию диалогового окна. Поскольку диалоговое окно со- держит 3 линейки скроллинга и 3 статических элемента для отображения состоя-

Окна и элементы управления

151
ния скроллинга, то нам удобнее описать эти элементы массивами, присвоив их элементам значение соответствующих идентификаторов: static int ID_SCROLL[3] = {IDC_RED, IDC_GREEN, IDC_BLUE}; static int ID[3] = {IDC_R, IDC_G, IDC_B};
Так же опишем массив дескрипторов для линеек скроллинга и массив для хранения трех базовых цветов: static HWND hScroll[3];

//{hRed, hGreen, hBlue} static int color[3] = {255, 255, 255};
//{red, green, blue}
Опишем дескриптор окна hWnd и кисти hBrush
, а также переменную целого типа index
Далее организуем обработку сообщений.
При инициализации диалогового окна в цикле определяем дескрипторы линеек скроллинга: hScroll[index] = GetDlgItem(hDlg, ID_SCROLL[index]);
Устанавливаем диапазон скроллинга
[0;255]
:
SetScrollRange(hScroll[index], SB_CTL, 0, 255, FALSE); и начальную позицию в
255
, поскольку начальный цвет окна — белый, соответст- вует значению
RGB(255, 255, 255)
SetScrollPos (hScroll[index], SB_CTL, color[index], TRUE);
После чего выведем значение в статическом элементе:
SetDlgItemInt(hDlg, ID[index], color[index], 0);
Теперь рассмотрим сообщение
WM_VSCROLL
,
генерируемое при воздействии на ли- нейку скроллинга. Вначале нужно определить, от какой линейки пришло сообще- ние. Сделаем это в цикле, сравнивая дескриптор элемента, породившего сообще- ние, и передаваемого в lParam
, с дескрипторами линеек скроллинга: for (index = 0; index < 3; index++) if ((HWND)lParam == hScroll[index]) break;
При выходе из цикла переменная index будет соответствовать индексу линейки скроллинга, которая сгенерировала сообщение.
Установим фокус ввода на выбранную линейку:
SetFocus(hScroll[index]);
Это не обязательно, но позволит нам использовать для управления клавиатуру.
Обработку сообщений скроллинга осуществим стандартным образом так же, как в листинге 2.3, только вместо переменной будем оперировать элементом массива color[index]
:
Убедившись, что состояние скроллинга изменилось, установим новое значение в статическом элементе:
SetDlgItemInt(hDlg, ID[index], color[index],0); а также новую позицию движка линейки:
SetScrollPos(hScroll[index], SB_CTL, color[index], TRUE);

Глава 3

152
Теперь создаем новую кисть: hBrush = CreateSolidBrush(RGB(color[0],color[1],color[2])); и уничтожаем старую. Дескриптор старой кисти получим обращением к функции
GetClassLong()
:
DWORD WINAPI GetClassLong(HWND hWnd, int nIndex); если передать в качестве параметра
GCL_HBRBACKGROUND
:
DeleteObject((HWND)GetClassLong(hWnd, GCL_HBRBACKGROUND));
Уничтожить старую кисть необходимо, чтобы не "замусорить" память.
П
Р И МЕ Ч А Н И Е

Здесь нам понадобился дескриптор главного окна hWnd. Можно перенести его опре- деление на глобальный уровень или воспользоваться функцией GetParent()
Осталось установить новое значение параметра класса окна функцией
SetClassLong()
, эта функция, напротив, позволит изменить свойства класса окна, зарегистрированного в системе:
DWORD WINAPI SetClassLong(HWND hWnd, int nIndex, LONG dwNewLong); передавая ей тот же параметр
GCL_HBRBACKGROUND
и дескриптор новой кисти.
SetClassLong(hWnd, GCL_HBRBACKGROUND, (LONG)hBrush);
После чего объявляем окно недействительным для перерисовки фона:
InvalidateRect(hWnd, NULL, TRUE);
П
Р И МЕ Ч А Н И Е

Функция GetClassLong()
позволяет получить, а SetClassLong() установить большин- ство параметров класса окна, которые определяются значением передаваемого индекса:
GCL_MENUNAME,GCL_HBRBACKGROUND,GCL_HCURSOR,GCL_HICON,GCL_HMODULE,GCL_
CBWNDEXTRA,GCL_CBCLSEXTRA,GCL_WNDPROC,GCL_STYLE
Нам осталось обработать сообщение о закрытии окна. Мы убрали кнопку закрытия диалогового окна и воспользовались кнопкой системного меню, которая приводит к тому же результату.
Все остальные сообщения оконная функция диалога игнорирует, возвращая
FALSE
Вот собственно и все: запустив программу на выполнение, мы можем регулировать цвет фона, передвигая движки линеек скроллинга для трех базовых цветов.
Стандартное диалоговое окно выбора цвета

Картина будет неполной, если мы не рассмотрим стандартное диалоговое окно вы- бора цвета, реализованное в Windows. Прототипы функции
ChooseColor()
и струк- туры
CHOOSECOLOR
, необходимых для создания диалогового окна, как и для всех других диалоговых окон, описаны в файле включений commdlg.h
, который нужно добавить в заголовке программы.
Для создания диалогового окна создадим пункт меню StdColor с идентификатором
ID_STDCOLOR
. При обработке этого пункта меню вызовем диалоговое окно выбора

Окна и элементы управления

153
цвета, которое изображено на рис. 3.11, а код оконной функции, вызывающий это окно, в листинге 3.8.

Рис.
3.11.
Стандартное диалоговое окно выбора цвета
Листинг 3.8. Оконная функция задачи выбора цвета фона с помощью стандартного
диалогового окна

#include
COLORREF stdColor = RGB(255,255,255);
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa- ram)
{ static CHOOSECOLOR ccs; static COLORREF acrCustClr[16]; static HBRUSH hBrush; switch (message)
{ case WM_CREATE: ccs.lStructSize = sizeof(CHOOSECOLOR); ccs.hwndOwner = hWnd; ccs.rgbResult = stdColor; ccs.Flags = CC_RGBINIT | CC_FULLOPEN; ccs.lpCustColors = (LPDWORD)acrCustClr; break; case WM_COMMAND: switch (LOWORD(wParam))
{ case ID_STDCOLOR:

Глава 3

154
if (ChooseColor(&ccs))
{
stdColor = ccs.rgbResult;
if (hBrush) DeleteObject(hBrush);
hBrush = CreateSolidBrush(stdColor);

SetClassLong(hWnd, GCL_HBRBACKGROUND, (LONG)hBrush);

InvalidateRect(hWnd, NULL, TRUE);
} break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} return 0;
}
Для создания диалогового окна выбора цвета необходимо описать переменную ти- па структуры
CHOOSECOLOR
, массив acrCustClr типа
COLORREF для хранения 16 до- полнительных цветов, а также переменную color
, которую окно будет использо- вать для задания начального цвета.
П
Р И МЕ Ч А Н И Е

В диалоговом окне выбора цвета имеется возможность кроме основного цвета задать еще 16 дополнительных цветов. Причем даже если они не нужны, массив acrCustClr должен быть определен.
Мы задали начальное значение stdColor = RGB(255, 255, 255)
, чтобы начать вы- бор с белого цвета. static CHOOSECOLOR ccs; static COLORREF acrCustClr[16];
Здесь же опишем дескриптор кисти. static HBRUSH hBrush;
Все переменные класса памяти static
, чтобы они не потеряли значения при выхо- де из оконной функции.
Необходимые поля структуры
CHOOSECOLOR заполняются при обработке сообщения
WM_CREATE
ccs.lStructSize = sizeof(CHOOSECOLOR);
//Задается размер структуры ccs.hwndOwner = hWnd;
//
Дескриптор родительского окна ccs.rgbResult = stdColor;
//Начальный цвет диалога ccs.Flags = CC_RGBINIT | CC_FULLOPEN;

Окна и элементы управления

155
Флаг работы
CC_RGBINIT заставляет диалоговое окно использовать цвет, указан- ный в rgbResult
, как начальный цветовой выбор; флаг
CC_FULLOPEN
— отображает дополнительные средства управления для создания дополнительных цветов в мас- сиве acrCustClr ccs.lpCustColors = (LPDWORD)acrCustClr;
Диалоговое окно выбора цвета вызывается функцией
ChooseColor()
, аргументом которой является адрес структуры
CHOOSECOLOR
. При удачном результате работы функции она возвращает
TRUE
, а выбранный цвет мы получаем из поля ccs.rgbResult
Теперь, чтобы не оставлять "мусор" в памяти, уничтожим старую кисть: if (hBrush) DeleteObject(hBrush);
Оператор if используем, чтобы обойти функцию
DeleteObject() при первом об- ращении к диалогу, когда дескриптор hBrush равен
NULL
Создаем новую кисть: hBrush = CreateSolidBrush(stdColor); и используем ее в качестве фонового цвета окна:
SetClassLong(hWnd, GCL_HBRBACKGROUND, (LONG)hBrush);
Осталось только объявить окно недействительным для смены фона:
InvalidateRect(hWnd, NULL, TRUE); и задача будет решена.
Вопросы к главе

1.
Техника создания окна, аргументы функции
CreateWindow()
2.
Чем отличаются дочерние и всплывающие окна?
3.
Назначение функции
UpdateWindow()
?
4.
Создание диалогового окна, функции
DialogBox()
и
CreateDialog()
5.
Стандартные и общие элементы управления. В чем заключается особенность общих элементов управления?
6.
Как осуществляется обмен данными с элементами управления?
7.
Приятельские окна для спина.
8.
Полоса прокрутки — свойство окна и элемент управления.
9.
Как происходит передача управления от немодального окна?
10.
Стандартное диалоговое окно выбора цвета, функция
ChooseColor()

Глава 3

156
Задания

для самостоятельной работы

1.
Написать программу, изображающую шахматную доску, где каждая клетка бу- дет дочернем окном, которое меняет цвет при щелчке левой кнопкой мыши на его поверхности, а при нажатии на правую кнопку появляется всплывающее окно с координатами клетки в шахматной нотации. Всплывающее окно можно создать на элементе управления
STATIC
2.
Модифицировать программу построения графика (листинг 3.2), предусмотрев блокировку пункта меню xy-graph до тех пор, пока не будет загружен файл с данными. После загрузки файла блокировку снять.
3.
В программе построения графика предусмотреть вывод координат точки во всплывающем окне после нажатия правой кнопки мыши. При отпускании кла- виши мыши окно убрать.
Указание: воспользуйтесь элементом управления
STATIC
. В случае перекрытия курсора мыши всплывающим окном воспользуйтесь стандартным приемом "захвата мыши".
4.
Предусмотрите возможность интерактивного редактирования графика путем "буксировки" точек данных левой кнопкой мыши. После закрытия окна графи- ка сохраните отредактированные данные.
5.
Написать тестовую программу для работы со спином при помощи "приятель- ского" окна Edit Box.
6.
Написать программу для построения круговой диаграммы в отдельном всплы- вающем окне. Реализовать диалоговое окно для настройки цветов заполнения.
7.
Для предыдущей задачи организовать ввод исходных данных для построения круговой диаграммы в диалоговом окне.
8.
Добавить к окну программы просмотра текстовых файлов (листинг 2.3) строку состояния, вывести в нее количество строк текста, максимальную длину строки и полное имя файла.
9.
С помощью немодального диалогового окна реализовать функцию поиска сло- ва для программы просмотра текстовых файлов (листинг 2.3). Если слово най- дено, обеспечить прокрутку текста до необходимой позиции и выделить най- денное слово цветом.
10.
Решить предыдущую задачу, используя элемент управления Find.
1   ...   5   6   7   8   9   10   11   12   ...   15


База данных защищена авторским правом ©nethash.ru 2019
обратиться к администрации

войти | регистрация
    Главная страница


загрузить материал