Лабораторная работа 8. Программа COLORS1 Для рассмотрения некоторых аспектов использования статических дочерних окон и полос прокрутки, а также для более глубокого изучения цветов, мы разработаем программу COLORS1. Программа COLORS1 выводит на экран три полосы прокрутки в левой половине рабочей области, помеченные "Red", "Green" и "Blue". Цвет правой половины рабочей области образуется путем сочетания трех исходных цветов, значения которых определяются положением бегунков полос прокрутки. Численные значения этих трех исходных цветов выводятся на экран под тремя полосами прокрутки. Программа COLORS1 включает в работу свои дочерние окна. В программе используется 10 дочерних окон управления: три полосы прокрутки, 6 окон статического текста, и один статический прямоугольник. Программа COLORS1 обрабатывает сообщения WM_CTLCOLORSCROLLBAR для закрашивания внутренних участков трех полос прокрутки в красный, зеленый и голубой цвета, а также сообщения WM_CTLCOLORSTATIC для окрашивания статического текста. Вы можете изменять состояние полос прокрутки либо с помощью мыши, либо с помощью клавиатуры. Вы можете использовать программу COLORS1 как инструмент для экспериментов с цветами и выбора наиболее привлекательного (или, если вам так хочется, самого некрасивого) цвета для ваших Windows-программ. Программа COLORS1 не обрабатывает сообщений WM_PAINT. Фактически вся работа в программе COLORS1 выполняется дочерними окнами. Цвет правой половины рабочей области окна фактически является цветом фона окна. Статическое дочернее окно со стилем SS_WHITERECT занимает левую половину рабочей области. Три полосы прокрутки являются дочерними окнами управления стиля SBS_VERT. Эти полосы прокрутки расположены в верхней части дочернего окна SS_WHITERECT. Еще шесть статических дочерних окон стиля SS_CENTER (выравнивание по центру) обеспечивают индикацию названий цветов и их значений. Программа COLORS1 строит обычное перекрывающееся окно и десять дочерних окон в функции WinMain при помощи функции CreateWindow. Окна SS_WHITERECT и SS_CENTER используют класс окна "static"; в трех полосах прокрутки используется класс окна "scrollbar". Первоначально параметры, определяющие положение х и у, ширину и высоту окна функции CreateWindow устанавливаются в 0, поскольку они зависят от размера рабочей области, который еще не известен. Оконная процедура программы COLORS1 с помощью вызова функции MoveWindow изменяет размеры всех десяти дочерних окон, когда получает сообщение WM_SIZE. Поэтому, когда бы вы ни изменили размер окна программы COLORS1, пропорционально меняются и размеры полос прокрутки. Когда оконная процедура WndProc получает сообщение WM_VSCROLL, то старшим словом параметра lParam является описатель дочернего окна. Для получения идентификатора дочернего окна можно использовать функцию GetWindowLong: i = GetWindowLong(lParam, GWW_ID); Мы можем задать идентификаторы трех полос прокрутки как три последовательных числа 0, 1 и 2, поэтому WndProc может определить, какая из полос является источником сообщения. Поскольку описатели дочерних окон при создании этих окон были сохранены в массиве, WndProc может обработать сообщение полосы прокрутки и установить новое положение соответствующей полосы прокрутки с помощью вызова функции SetScrollPos: SetScrollPos(hwndScrol[i], SB_CTL, color[i], TRUE); WndProc также изменяет текст дочернего окна под полосой прокрутки: SetWindowText(hwndValue[i], _itot(color[i], szbuffer, 10)); Интерфейс клавиатуры, поддерживаемый автоматически Полосы прокрутки — элементы управления также могут обрабатывать сообщения от клавиатуры, но только в том случае, если они имеют фокус ввода. В следующей таблице показано, как нажатия клавиш управления курсором преобразуются в сообщения полос прокрутки: Фактически, сообщения полос прокрутки SB_TOP и SB_BOTTOM могут вырабатываться только с помощью клавиатуры. Если вы хотите, чтобы полоса прокрутки управления получила фокус ввода, когда на полосе прокрутки происходит щелчок мыши, то вы должны включить идентификатор WS_TABSTOP в параметр стиля окна при вызове функции CreateWindow. Если полоса прокрутки имеет фокус ввода, то бегунок полосы прокрутки становится похож на мигающий серый блочок. Чтобы полностью обеспечить интерфейс клавиатуры для полос прокрутки, требуется затратить несколько больше усилий. Во-первых, оконная процедура WndProc должна специально передать полосе прокрутки фокус ввода. Она это делает, обрабатывая сообщение WM_SETFOCUS, которое получает родительское окно при получении фокуса ввода. WndProc просто устанавливает фокус ввода на одну из полос прокрутки: SetFocus(hwndScrol[iFocus]) Но кроме этого нужно иметь возможность как-то переходить от одной полосы прокрутки к другой, предпочтительнее с помощью клавиши <Tab>. Это более трудно, поскольку, раз полоса прокрутки имеет фокус ввода, она обрабатывает все нажатия клавиш. Но полоса прокрутки отслеживает только клавиши управления курсором; клавиша <Tab> ею игнорируется. Для решения этой задачи существует прием, который называется "введение новой оконной процедуры" (window subclassing). Мы будем пользоваться этим приемом, чтобы в программе COLORS1 получить возможность с помощью клавиши <Tab> переходить с одной полосы прокрутки на другую. Введение новой оконной процедуры Оконная процедура для полос прокрутки — элементов управления находится где-то внутри Windows. Однако, вы можете получить адрес этой оконной процедуры с помощью вызова функции GetWindowLong, в которой в качестве параметра используется идентификатор GWL_WNDPROC. Более того, вызывая функцию SetWindowLong, вы можете задать для полос прокрутки новую оконную процедуру. Это очень мощный прием, который называется "введение новой оконной процедуры". Он позволяет вам "влезть" в существующие внутри Windows оконные процедуры, обработать некоторые сообщения внутри вашей собственной программы, а все остальные сообщения оставить прежней оконной процедуре. Оконная процедура, которая в программе COLORS1 предварительно обрабатывает сообщения полос прокрутки, называется ScrollProc; она находится в конце программы COLORS1. Поскольку ScrollProc является функцией программы COLORS1, которая вызывается операционной системой Windows, то она должна определяться как функция обратного вызова (CALLBACK). Для каждой из трех полос прокрутки в программе COLORS1, для установки адреса новой оконной процедуры, а также для получения адреса существующей оконной процедуры полосы прокрутки, используется функция SetWindowLong : fnOldScr[i] =(WNDPROC) SetWindowLong(hwndScrol[i], GWL_WNDPROC,(LONG) ScrollProc)); Теперь функция ScrollProc получает все сообщения, которые Windows посылает оконной процедуре полосы прокрутки для трех полос прокрутки программы COLORS1 (но, конечно, не для полос прокрутки других программ). Оконная процедура ScrollProc, при получении сообщения о нажатии клавиши <Tab> или <Shift>+<Tab>, просто передает фокус ввода следующей (или предыдущей) полосе прокрутки. С помощью функции CallWindowProc она вызывает прежнюю оконную процедуру полосы прокрутки. Закрашивание фона Когда программа COLORS1 определяет свой класс окна, она задает для своей рабочей области сплошную черную кисть: wndclass.hbrBackground = CreateSolidBrush(0L); Если вы изменяете установки полос прокрутки программы COLORS1, то программа должна создать новую кисть и поместить в структуру класса окна новый описатель кисти. Точно также, как мы получали адрес прежней и вводили новую оконную процедуру полос прокрутки с помощью функций GetWindowLong и SetWindowLong, мы можем получить и ввести новый описатель этой кисти с помощью функций GetClassWord и SetClassWord. Вы можете создать новую кисть, ввести ее описатель в структуру класса окна, а затем удалить старую кисть: DeleteObject((HBRUSH)SetClassLong(hwnd,GCL_HBRBACKGROUND, (LONG)CreateSolidBrush(RGB(color[0], color[1], color[2]))); Следующий раз Windows при перерисовке фона окна будет пользоваться новой кистью. Чтобы заставить Windows обновить фон, мы делаем недействительной правую половину рабочей области: InvalidateRect(hwnd, &rcColor, TRUE); Использования в качестве третьего параметра значения TRUE (не равно 0) показывает, что перед рисованием мы хотим обновить фон. Функция InvalidateRect заставляет Windows поместить сообщение WM_PAINT в очередь сообщений оконной процедуры. Поскольку сообщения WM_PAINT имеют низкий приоритет, то такое сообщение, если вы еще перемещаете полосу прокрутки с помощью мыши или клавиш управления курсором, не будет обработано немедленно. В противном случае, если вы хотите, чтобы окно обновилось сразу после того, как его цвет был изменен, в программу, после вызова функции InvalidateRect необходимо добавить следующую инструкцию: UpdateWindow(hwnd); Но это может затормозить обработку сообщений клавиатуры и мыши. Функция WndProc программы COLORS1 не обрабатывает сообщения WM_PAINT, а передает его в DefWindowProc. Заданный в Windows по умолчанию процесс обработки сообщений WM_PAINT заключается просто в вызовах функций BeginPaint и EndPaint, которые делают окно действительным. Поскольку мы задали при вызове функции InvalidateRect, что фон должен быть обновлен, то вызов функции BeginPaint заставляет Windows выработать сообщение WM_ERASEBKGND (обновление фона). WndProc игнорирует и это сообщение тоже. Windows обрабатывает его, обновляя фон рабочей области с помощью кисти, заданной в классе окна. Считается хорошим стилем программирования удалять созданные ресурсы. Поэтому при обработке сообщения WM_DESTROY функция DeleteObject вызывается снова: DeleteObject((HBRUSH)SetClassLong(hwnd, GCL_HBRBACKGROUND,(LONG) GetStockObject(WHITE_BRUSH))); Окрашивание полос прокрутки и статического текста В программе COLORS1 внутренние участки трех полос прокрутки и текст шести текстовых полей окрашиваются красным, зеленым и голубым цветами. Окрашивание полос прокрутки осуществляется путем обработки сообщений WM_CTLCOLORSCROLLBAR. В WndProc мы для кистей определяем статический массив трех описателей: static HBRUSH hBrush[3]; При обработке сообщения WM_CREATE мы создаем три кисти: for(i = 0; i < 3; i++) hBrush[i] = CreateSolidBrush(crPrim[i]);\ где в массиве crPrim хранятся RGB-значения трех первичных цветов. При обработке сообщений WM_CTLCOLORSCROLLBAR, возвращаемым значением оконной процедуры является одна из этих трех кистей: case WM_CTLCOLORSCROLLBAR: i = GetWindowLong((HWND) lParam, GWW_ID); return(LRESULT) hBrush[i]; При обработке сообщения WM_DESTROY эти три кисти должны быть удалены: for(i = 0; i < 3; DeleteObject(hBrush[i++])); Аналогичным образом, путем обработки сообщения WM_CTLCOLORSTATIC и вызова функции SetTextColor, окрашивается текст в статических текстовых полях. Фон текста устанавливается функцией SetBkColor с системным цветом COLOR_BTNHIGHLIGHT. Это приводит к тому, что фон текста становится таким же, как цвет статического прямоугольника окна управления, который находится позади полос прокрутки и текста. Для статических дочерних текстовых окон управления этот цвет фона относится только к прямоугольнику позади каждого символа строки, а не ко всей ширине окна управления. Для того, чтобы это реализовать, оконная процедура должна также возвращать описатель кисти цвета COLOR_BTNHIGHLIGHT. Эта кисть называется hBrushStatic и создается при обработке сообщения WM_CREATE, а удаляется при обработке сообщения WM_DESTROY. Создав, при обработке сообщения WM_CREATE, кисть на основе цвета COLOR_BTNHIGHLIGHT, и пользуясь ею на протяжении работы программы, мы создали себе маленькую проблему. Если во время работы программы цвет COLOR_BTNHIGHLIGHT изменяется, то изменится и цвет статического прямоугольника, а также цвет его текстового фона, однако весь фон текстового окна управления останется прежним — COLOR_BTNHIGHLIGHT. Для решения этой проблемы в программе COLORS1 обрабатывается сообщение WM_SYSCOLORCHANGE путем простого повторного создания кисти hBrushStatic, использующей новый цвет.