
- •Часть IV
- •Глава 25
- •224 Глава 25
- •226 Глава 25
- •Глава 26
- •230 ____Глава 26
- •232 Глава 26
- •234 Глава 26
- •236 Глава 26
- •238 Глава 26
- •240 Глава 26
- •242 Глава 26
- •Глава 27
- •Virtual void InitMainWindow();//Замещаем функцию InitMainWindow
- •246 Глава 27
- •248 Глава 27
- •250 Глава 27
- •252 . Глава27
- •Глава 28 Диалоговые окна
- •256 Глава 28
- •258 Глава 28
- •260 Глава 28
- •262 Глава 28
- •264 Глава 28
- •266 Глава 28
- •268 Глава 28
- •Глава 29
- •270 Глава 29
- •272 Глава 29
- •274 Глава 29
- •276 Глава 29
- •278 Глава 29
- •280 Глава 29
- •282 Глава 29
230 ____Глава 26
нем Ps), используемую системой Windows при перерисовке изображения (см. гл. 8). Функций-членов в классе TPaintDC всего две: конструктор TPaintDC() и деструктор ~TPaintDC(). Как показано на рис. 26.2, в конструкторе вызывается функция API Windows BeginPaint(), которая заполняет структуру Ps и возвращает контекст устройства; в деструкторе вызывается функция API Windows EndPaint(), освобождающая контекст устройства. Таким образом, при создании объекта класса TPaintDC автоматически выполняются необходимые инициализирующие действия, а при его уничтожении - необходимые завершающие действия. Стоит еще отметить, что в деструктор ~TPaintDC() входит также вызов функции Restore-Objects(), выполняющей выбор в контекст устройства исходных инструментов. Таким образом, в OWL-программе после окончания работы с созданными и выбранными в контекст устройств инструментами, нет необходимости выполнять относительно громоздкую процедуру выбора назад в контекст всех исходных дескрипторов, так это действие автоматически выполняется в деструкторе класса.
Поступление в окно приложения сообщения WM_PAINT приводит к вызову защищенной функции класса TWindow EvPaint(). Поскольку сообщение WM_PAINT приходит в наше окно, с которым ассоциируется объект MyWin (являющийся потомком класса TWindow и наследующий его открытые и защищенные члены), то функция EvPaint() вызывается именно для этого объекта. Определение функции EvPaint() (которое можно найти в файле SOURCE\OWL\WINDOW.CPP), за вычетом некоторых несущественных пока деталей, выглядит следующим образом:
void TWindow::EvPaint(){ TPaintDC dc(*this);
TRect& rect=*(TRect*)&dc.Ps.rcPaint; Paint(dc,dc.Ps.fErase,rect);
}
В первом предложении вызывается конструктор класса TPaintDC, который создает объект этого класса с именем dc. Имя этого объекта используется в дальнейших предложениях приведенного фрагмента, а также и в тексте нашей программы. Прототип конструктора класса TPaintDC имеет следующий вид:
TPaintDC (HWND);
В качестве аргумента конструктора выступает дескриптор окна типа HWND. Однако в функции EvPaint() в качестве фактического аргумента конструктора использовано обозначение *this, что означает указатель на текущий объект со снятой ссылкой, т.е. сам текущий объект (этот объект в нашей программе не имеет имени, так как создан не по имени, а с помощью указателя MyWin). Каким образом целый объект *MyWin преобразуется в конкретное данное типа HWND? Это делается с помощью оператора преобразования типа, включенного в класс TWindow, который в OWL 2.5 (пакет Borland C++ 4.5) выглядит следующим образом:
TWindow::operator HWND() const {return HWindow;}
Как было показано в гл. 22, операторы такого рода позволяют преобразовывать пользовательские классы в скалярные данные базовых типов; в данном случае задаются правила преобразования класса TWindow в скалярную переменную HWindow типа HWND. Конструктор TPaintDC требует в качестве аргумента переменную типа HWND, однако в функции EvPaint() он вызывается с указанием параметра типа "объект класса TWindow". Следовательно, компилятор должен преобразовать объект TWindow в переменную типа HWND. Правила такого преобразования, задаваемые оператором operator, требуют подстановки вместо объекта класса TWindow данного-члена того же класса HWindow. Таким образом, конструктор TPaintDC получает в качестве параметра данное HWindow, представляющее собой дескриптор окна, которое он затем использует при вызове функции BeginPaint().
В OWL 5.0 (пакет Borland C++ 5.0) описанная процедура выглядит сложнее, хотя ее смысл остается в точности тем же. Отличие заключается в том, что в классе TWindow оператором typedef вводится новый тип данных THandle, в точности эквивалентный HWND: typedef HWND THandle;
и, соответственно, переменная класса, представляющая дескриптор окна (она здесь имеет имя не HWindow, a Handle), объявлена типа THandle:
THandle Handle;
Оператор преобразования типа также написан для типа THandle: TWindow::operator THandle() const {return GetHandle();}
Функция GetHandle() того же класса возвращает дескриптор окна Handle: TWindow::THandle TWindow::GetHandle() const {return Handle;}
В итоге конструктор TPaintDC получает в качестве параметра дескриптор окна Handle, который затем передается в функцию BeginPaint().
Вернемся, однако, к обсуждению функции EvPaint(). Во втором предложении этой функции TRect& rect=*(TRect*)&dc.Ps.rcPaint;
Обработка сообщения WM_PAINT и интерфейс GDI 231
создается ссылочная переменная rect класса TRect. Эта переменная описывает прямоугольную область (в данном случае - область вырезки изображения) по ее четырем координатам. Как известно, область вырезки передается Windows в элемент rcPaint структуры PAINTSTRUCT, однако она имеет там тип RECT. Поскольку переменная rcPaint является элементом структурного объекта Ps типа PAINTSTRUCT, a Ps является данным-членом объекта dc (созданного конструктором), полное имя области вырезки будет dc.Ps.rcPaint. В приведенном выше предложении образуется адрес этой переменной (&dc.Ps.rcPaint), с помощью префикса преобразования типа (TRect*) преобразуется в указатель на класс TRect, затем с него снимается ссылка (знак *) и полученное значение присваивается ссылочной переменной rect. В итоге в rect поступает область вырезки.
В последнем предложении функции EvPaint()
Paint(dc,dc.Ps.fErase,rect);
вызывается функция TWmdow::Paint() с передачей ей трех параметров: образованного ранее объекта dc класса TPaintDC, члена fErase структуры Ps и области вырезки rect. Как уже отмечалось выше, функция Paint() является заглушкой, которую необходимо переопределять в производных классах, при этом замещающая ее функция производного класса получает все три описанные выше параметра и может с ними работать.
Заметим, что в прототипе функции Paint
virtual void Paint(TDC& dc, bool erase, TRect& rect);
первый аргумент указан типа TDC, а мы присваиваем ему значение типа TPaintDC. Поскольку, однако, класс TPaintDC является производным от TDC, такое преобразование указателей (и ссылочных переменных) является допустимым (см. гл. 24). Правда, при этом преобразовании объект dc усекается до содержимого базового класса TDC и из него исключаются члены, добавленные классом TPaintDC, конкретно, структура Ps и дескриптор окна Wnd, которые мы уже не можем использовать в функции Paint(). Видимо, разработчики полагали, что эти данные не понадобятся прикладной программе. Область же вырезки, которая может программе потребоваться, передается в функцию Paint() через параметр rect. Передается также и флаг стирания фона окна, который обычно имеет нулевое значение, задающее автоматическое перерисовывание фона окна программами Windows.
Итак, при поступлении в наше окно сообщения WM_PAINT, автоматически создается объект класса TPaintDC с конкретным именем dc, через который мы можем получить доступ ко всему набору графических функций класса TDC, и выполняется вызов функции API Windows BeginPaint(), которая заполняет структуру Ps. Наша задача теперь сводится к вызову любых требуемых функций GDI для объекта dc. В примере 26-1 для объекта dc вызывается единственная функция TextOut() (инкапсулированная в OWL) для вывода строки текста:
dc.TextOut(10,10,"Строка текста");//Вывод строки текста
В дальнейших примерах будут проиллюстрированы приемы работы с другими функциями GDI.
Как уже отмечалось, при выходе из функции нам нет необходимости заботиться об освобождении контекста устройства (функция API Windows EndPaint()), так как эту работу берет на себя класс TPaintDC.
На рис. 26.3. приведен результат работы приложения, рассматриваемого в этом разделе.
//Пример 26-2. Вывод геометрических фигур
//Файл 26-2.срр
#include <owl\framewin.h>
/*Отображаемые на графике даяние*/
int data[10]={0,20,15,36,50,45,50,70,85,100); /*Константы, описывающие размера изображения*/
const radius=4;//Радиус точек графика
const dx=2Q;//Шаг по X
const margins=10;//Поля графика
const X=dx*9; // Ширина графика из 10 точек
const Y=100;//Высота графика
const XBorder=X+2*margins;/7Высота рамки вокруг графика const YBorder=Y+2*margins;//Ширина рамки вокруг графика /*Объекты классов положения и размеров*/
TSize size(radius*2,radius*2);//Квадрат для рисования точек графика TPoint X0Y0(30,10);//Верхний левый угол рамки относительно окна приложения TPoint XmYm=X0Y0.OffsetBy(XBorder,YBorder);//Правый нижний угол рамки TRect border(X0Y0,XmYm);//Прямоугольник рамки TRect graph=border.InflatedBy(-margins,-margins);//Прямоугольник графика