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



Pdf просмотр
страница7/15
Дата28.11.2016
Размер8.29 Mb.
Просмотров3128
Скачиваний1
1   2   3   4   5   6   7   8   9   10   ...   15
Глава 3

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


Кроме главного окна, приложение может создавать и другие окна: перекрываемые
WS_OVERLAPPED
,
всплывающие
WS_POPUP
, дочерние
WS_CHILD
. Каждое окно должно
иметь свою оконную функцию, куда передаются посылаемые операционной сис-
темой сообщения.
Начнем рассмотрение с создания дочерних окон, особенностью которых является то, что они располагаются в клиентской области родительского окна и автоматиче- ски уничтожаются при его закрытии.
Для создания окна используется функция
CreateWindow()
или ее расширение
Crea- teWindowEx()
:
HWND WINAPI CreateWindowExW(DWORD dwExStyle, LPCWSTR lpClassName,
LPCWSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance,
LPVOID lpParam);
Функции отличаются лишь первым параметром расширенных стилей dwExStyle
Если посмотреть файл включений winuser.h
, то можно увидеть, что все вызовы функции
CreateWindow()
заменяются ее расширенной версией, где первый пара- метр имеет нулевое значение.
П
Р И МЕ Ч А Н И Е

Параметр hMenu используется для задания уникального идентификатора дочернего окна child_ID
В этом случае приходится задавать явное преобразование типа
(HMENU)child_ID
В главе 1 мы уже рассматривали значение аргументов этой функции при анализе "скелета" Windows-приложения. Вспомним последовательность шагов при созда- нии окна:
1.
Создается переменная типа "Класс окна"
WNDCLASSEX
2.
Регистрируется класс окна функцией
RegisterClassEx()
3.
Вызывается функция
CreateWindowEx() для создания окна.
4.
При помощи функции
ShowWindow()
окно отображается.
5.
Поскольку отображение окна происходит асинхронно, то обычно используют функцию
UpdateWindow()
для принудительной прорисовки окна.

Глава 3

104
П
Р И МЕ Ч А Н И Е

Сейчас, как правило, используют расширенный класс окна WNDCLASSEX вместо
WNDCLASS и, соответственно, расширенную функцию регистрации класса окна
RegisterClassEx()
Рассмотрим процесс создания дочерних окон на примере реализации нескольких учебных задач.
Дочерние окна

Построим приложение, имитирующее широко известную игру "крестики-нолики", где вторым партнером будет выступать компьютер (рис. 3.1). В качестве заготовки используем стандартный Win32-проект. Добавим один пункт меню New для запус- ка новой игры. Текст оконной функции приложения приведен в листинге 3.1.

Рис.
3.1.
Крестики
- нолики
Основная идея, которая реализуется в этой программе, заключается в том, что вме- сто рисования прямоугольников и вычисления затем позиции нажатия левой кнопки мыши, мы создадим 9 дочерних окон и будем обрабатывать сообщение о щелчке мышью в соответствующем окне.
Листинг 3.1. Крестики
-
нолики

LRESULT CALLBACK ChildProc(HWND, UINT, WPARAM, LPARAM);
TCHAR ChildClassName[MAX_LOADSTRING] = _T("WinChild");
ATOM MyRegisterChildClass()
{
WNDCLASSEX wcex
= { 0 }; wcex.cbSize
= sizeof(WNDCLASSEX); wcex.lpfnWndProc = ChildProc; wcex.hInstance
= hInst; wcex.hCursor
= LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

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

105
wcex.lpszClassName = ChildClassName; return RegisterClassEx(&wcex);
} static HFONT newFont; static HWND hChild[9]; unsigned char k[9] = { 0 }; char text[] = { ' ', 'X', '0' };
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa- ram)
{ int i; static int sx, sy; switch (message)
{ case WM_CREATE:
MyRegisterChildClass(); for (i = 0; i < 9; i++) hChild[i] = CreateWindow(ChildClassName, NULL, WS_CHILD |
WS_DLGFRAME | WS_VISIBLE, 0, 0, 0, 0, hWnd, NULL, hInst, NULL); break; case WM_SIZE: if (wParam == SIZE_MINIMIZED) break; //Кнопка свертывания окна sx = LOWORD(lParam)/3;
//Ширина дочернего окна sy = HIWORD(lParam)/3;
//Высота дочернего окна if (newFont) DeleteObject(newFont); newFont = CreateFont(min(sx,sy), 0, 0, 0, FW_NORMAL, 0, 0, 0,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, _T("Arial")); for (i = 0; i < 9; i++)
{ MoveWindow(hChild[i],(i%3)*sx, (i/3)*sy, sx, sy, TRUE);
UpdateWindow(hChild[i]);
} break; case WM_COMMAND: switch (LOWORD(wParam))
{ case ID_NEW: for (i = 0; i < 9; i++)
{ k[i] = 0;
InvalidateRect(hChild[i], NULL, 1);
UpdateWindow(hChild[i]);
}

Глава 3

106
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;
}
/////////////////////////////////////////////////////////////////////
LRESULT CALLBACK ChildProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa- ram)
{
PAINTSTRUCT ps;
HDC hdc;
RECT rt; int i, s; char *ch; switch (message)
{ case WM_LBUTTONDOWN : for (i = 0; hWnd != hChild[i]; i++); if (k[i]) break; else k[i] = 1;
InvalidateRect(hWnd, NULL, 1);
UpdateWindow(hWnd); srand(lParam); for(i = s = 0; i< 9; i++) if (k[i]) s++; if(s == 9) MessageBox(hWnd, _T("Для следующего сеанса выбирайте\
New"), _T("Конец игры"), MB_OK | MB_ICONQUESTION); else
{ while(true)
{ s = rand()*9/(RAND_MAX+1); if (k[s]) continue; k[s] = 2;
InvalidateRect(hChild[s], NULL, 1);
UpdateWindow(hChild[s]); break;
}
} break; case WM_PAINT:

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

107
for (i = 0; hWnd != hChild[i]; i++); if(k[i])
{ ch = text+k[i]; hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rt);
SelectObject(hdc, newFont);
DrawTextA(hdc,ch,1,&rt,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
EndPaint(hWnd, &ps);
} //Фоновая закраска окна else DefWindowProc(hWnd, message, wParam, lParam); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} return 0;
}
При созднии главного окна приложения в сообщении
WM_CREATE
создадим
9 дочерних окон. Для всех дочерних окон будем использовать один класс окна и, соответственно, одну оконную функцию
ChildProc
. В локальной функции
MyRegisterChildClass()
определим класс окна. Поскольку многие поля перемен- ной нужно оставить со значением по умолчанию, обнулим всю структуру при опи- сании и заполним лишь необходимые поля. После чего зарегистрируем класс окна с именем "WinChild"
Так как при обработке сообщения
WM_CREATE
главное окно приложения еще не прорисовано, дочерние окна создаем нулевого размера. Укажем стиль
WS_VISIBLE
для того, чтобы окно было отображаемым. Стиль рамки
WS_DLGFRAME
запретит из- менение размеров окна при буксировке его границ.
Определим на глобальном уровне массив типа
HWND дескрипторов дочерних окон hChild[9]
, дескриптор шрифта newFont
, а также статический массив k[9] типа unsigned char
, где мы будем хранить признак заполнения окна:

0 — окно не занято;

1 — пользователь щелкнул левой кнопкой мыши по этому окну,
'Х'
;

2 — компьютер выбрал это окно для вывода,
'0'
Для корректировки размера дочерних окон используем обработчик сообщения
WM_SIZE
, поскольку это сообщение генерируется при любом изменении размера главного окна.
Если сообщение связано с кнопкой минимации и wParam == SIZE_MINIMIZED
, пере- рисовывать окно нет необходимости, и мы заканчиваем обработку сообщения опе- ратором break
Ширину и высоту главного окна получим из параметра младшего и старшего слова lParam
. Размеры дочерних окон установим в
1/3 от полученного размера.

Глава 3

108
Создадим шрифт, определив его размер по размеру созданного окна. Дескриптор шрифта определен на глобальном уровне, он будет доступен и в оконной функции дочернего окна. Однако при изменении размера окна мы создаем новый шрифт, предварительно удаляя текущий, если он уже определен.
В цикле по переменной i
нарисуем 9 окон функцией
MoveWindow()
:
BOOL WINAPI MoveWindow(HWND hWnd, int x, int y, int nWidth, int nHeight, BOOL bRepaint);
Функция переопределяет позицию вывода окна
(x,y)
и его размеры nWidth, nHeight
. Если последний параметр отличен от
0
, генерируется сообщение
WM_PAINT
для перерисовки окна.
Координаты левого верхнего угла окна определяются формулами: x = (i%3)*sx; y = (i/3)*sy;
Действительно, в клиентской области главного окна координату x можно вычис- лить как остаток от деления i
на 3, умноженный на ширину дочернего окна; а ко- ординату y
как целую часть от деления i
на 3, умноженную на высоту дочернего окна.
Здесь, однако, необходимо использовать функцию
UpdateWindow()
, иначе окна не успеют перерисоваться.
Для того чтобы иметь возможность запустить новый сеанс игры, добавим в меню программы пункт New с идентификатором
ID_NEW
. В обработчике этой команды очистим массив k
и перерисуем все дочерние окна.
Основная же логика задачи сосредоточена в оконной функции дочерних окон. Мы будем использовать одну функцию для всех 9 окон, а различать их будем по деск- рипторам, которые сохранили в массиве hChild на глобальном уровне.
При обработке сообщения
WM_LBUTTONDOWN
сначала мы должны определить индекс ок- на, пославшего сообщение. Это можно сделать, организовав в цикле перебор дескрип- торов дочерних окон и сравнив их с дескриптором окна, пославшего сообщение: for (i = 0; hWnd != hChild[i]; i++);
По завершении цикла переменная i и будет искомым индексом.
Если значение элемента массива k[i]
отлично от нуля, мы игнорируем эту опера- цию, прекращая обработку сообщения оператором break
. Если же окно свободно, присваиваем элементу массива k[i]
значение
1
После этого нужно перерисовать дочернее окно последовательным вызовом функ- ций
InvalidateRect()
и
UpdateWindow()
Сейчас компьютер должен выбрать окно, куда он выведет ответный "нолик". Для этого мы используем функцию генерации случайного числа. Однако функция будет генерировать одну и ту же последовательность случайных чисел, что нас вряд ли устроит. Для того чтобы внести некоторое разнообразие в выборе ответа, мы ввели функцию srand()
, которая задает различные числовые последовательности, но их выбор опять же зависит от ее аргумента, в качестве которого мы взяли lParam
,

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

109
составленный из координат курсора мыши. Так что, щелкая мышью в разных час- тях окна, мы получим разные последовательности случайных чисел. Прием, конеч- но, примитивный, но действенный.
Однако предварительно проверим, не все ли окна уже заняты? Просто подсчитаем количество ненулевых значений массива k
и, если это количество равно 9, выведем окно сообщения, что игра завершена.
В противном случае в бесконечном цикле while(true) {. . .}
генерируем слу- чайное число в диапазоне от 0 до 8: s = rand()*9/(RAND_MAX+1);
Мы учли, что функция rand()
возвращает число от
0
до
RAND_MAX = 0x7fff
Проверяем, не занято ли окно с индексом s
? Если не занято, присваиваем элементу массива k[s] значение
2
и перерисовываем это окно. Если же окно уже занято, то выполняем следующий шаг цикла, и так далее, пока не попадем на свободное окно.
А такое окно обязательно существует, поскольку мы проверили, что занятых окон меньше 9.
Теперь осталось реализовать обработку сообщения
WM_PAINT
Начнем с определения индекса активного окна. Опять проверим, не является ли окно пустым? В этом случае ничего выводить не нужно, но для очистки окна луч- ше воспользоваться обработчиком сообщений по умолчанию
DefWindowProc()
, который закрасит окно фоновой кистью.
Если окно не является пустым, определим символ для вывода в окно. Для этого ус- тановим указатель ch на символ 'X'
, если k[i] == 1
; или на символ '0'
, если k[i] == 2
. Мы специально ввели в массив text первый символ пробела, чтобы упростить выражение: ch = text+k[i];
Определим дескриптор контекста устройства
BeginPaint()
, вычислим ограничи- вающий прямоугольник окна функцией
GetClientRect() и выберем в качестве текущего шрифт newFont
Функцией
DrawTextA()
выведем один символ в центр окна.
Для этого укажем флаги форматирования текста:
DT_SINGLELINE|DT_CENTER|
DT_VCENTER
. Функция
EndPaint()
завершает обработку сообщения
WM_PAINT
Всплывающие окна

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

Глава 3

110
и строит стандартный xy-график. В качестве основы используем программу чтения текстового файла (см. листинг 2.3). Для упрощения кода перенесем описание век- тора v
и итератора it на глобальный уровень.
На глобальном уровне введем имя нового класса окна и опишем прототип оконной функции построения графика:
TCHAR WinClassGraphName[MAX_LOADSTRING] = _T("ChildClass");
LRESULT CALLBACK WndGraph(HWND, UINT, WPARAM, LPARAM);
Для вывода графика добавим пункт меню xy-график с идентификатором
ID_LINE
Обработчик этой команды разместим в функции главного окна: case ID_LINE : if (IsWindow(hGraph)) break;
RegisterGraphClass(); hGraph = CreateWindow(WinClassGraphName, _T("xy-график"),
WS_SYSMENU | WS_POPUP | WS_VISIBLE | WS_THICKFRAME | WS_CAPTION, sx/4, sy/4, min(sx, sy), min(sx, sy), hWnd,0, hInst, NULL); break;
Стили окна обеспечат вывод всплывающего окна
WS_ POPUP
, ограниченного тонкой рамкой
WS_THICKFRAME
, с заголовком
WS_CAPTION и системным меню
WS_SYSMENU
, отображаемое сразу после создания; стиль
WS_VISIBLE
Дескриптор опишем в оконной функции главного окна: static HWND hGraph;
Для предотвращения повторного создания окна предусмотрим проверку его суще- ствования: if (IsWindow(hGraph)) break;
Положение всплывающего окна определяется в системе координат рабочего стола.
Выберем такие параметры: sx/4, sy/4, min(sx, sy), min(sx, sy)
П
Р И МЕ Ч А Н И Е

Для окна графика можно было бы выбрать стиль дочернего окна WS_CHILD. В этом случае, однако, необходим дополнительный стиль WS_EX_NOPARENTNOTIFY, чтобы предотвратить завершение приложения при закрытии дочернего окна.
Регистрацию класса окна выделим в отдельную функцию:
ATOM RegisterGraphClass()
{
WNDCLASSEX wcgraph
= {0}; wcgraph.cbSize
= sizeof(WNDCLASSEX); wcgraph.style
= CS_HREDRAW | CS_VREDRAW; wcgraph.lpfnWndProc
= WndGraph; wcgraph.hInstance
= hInst; wcgraph.hCursor
= LoadCursor(NULL, IDC_CROSS); wcgraph.hbrBackground
= (HBRUSH) (COLOR_WINDOW+1);

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

111
wcgraph.lpszClassName
= WinClassGraphName; wcgraph.hIconSm
= LoadIcon(hInst,MAKEINTRESOURCE(IDI_ICON1)); return RegisterClassEx(&wcgraph);
}
П
Р И МЕ Ч А Н И Е

Мы сделали присваивание wcgraph = {0}
, чтобы не заполнять нулевые поля струк- туры
WNDCLASSEX
Для графического окна используем курсор
IDC_CROSS
и новую пиктограмму, кото- рую мы импортировали в наш проект. Это можно сделать в контекстном меню Add
Resource… окна Resource View (рис. 3.2).

Рис.
3.2.
Диалоговое окно
Add Resource
По умолчанию новой иконке присваивается идентификатор
IDI_ICON1
Рассмотрим оконную функцию всплывающего окна (листинг 3.2).
Листинг 3.2. Оконная функция построения
xy-
графика

const int scaleX = 8;
//Метки по оси x const int scaleY = 4;
//Метки по оси y const int indent = 25;
//Отступ для вывода меток оси х struct DOUDLE_POINT { double x, y; }; const int GRAPHSIZE = 1200; const int GRAPHWIDTH = 1000;
LRESULT CALLBACK WndGraph(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa- ram)
{
PAINTSTRUCT ps;
HDC hdc; static HPEN hline;

Глава 3

112
static HBRUSH hrect;
RECT rt; static int n, sx, sy, kx, ky; static double max_x, max_y, min_x, min_y; int i, x, y; static POINT *pt;
TCHAR s[20];
DOUDLE_POINT t; double z, hx, hy; static DOUDLE_POINT *xy; switch (message)
{ case WM_CREATE: if((n = v.size()) == 0)
{
MessageBox(hWnd, _T("Загрузите файл"), _T("Нет данных"),

MB_OK | MB_ICONHAND);
DestroyWindow(hWnd); return 1;
} pt = new POINT[n]; xy = new DOUDLE_POINT[n]; for (it = v.begin(), i = 0; i < n; i++, it++)
{ if(sscanf(it->c_str(),"%lf %lf",&t.x, &t.y) != 2)
{
MessageBoxA(hWnd, it->c_str(), "Ошибка данных", MB_OK|

MB_ICONHAND);
DestroyWindow(hWnd); return 1;
} xy[i] = t;
} max_x = min_x = xy[0].x; max_y = min_y = xy[0].y; for (i = 1; i < n; i++)
{ if (max_x < xy[i].x) max_x = xy[i].x; else if (min_x > xy[i].x) min_x = xy[i].x; if (max_y < xy[i].y) max_y = xy[i].y; else if (min_y > xy[i].y) min_y = xy[i].y;
} hline = CreatePen(PS_SOLID, 6, RGB(0, 0, 255)); hrect = CreateSolidBrush(RGB(255,0,0));

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

113
hx = max_x - min_x; hy = max_y - min_y; for (i = 0; i < n; i++)
{ pt[i].x = int((xy[i].x - min_x)*GRAPHWIDTH/hx + 0.5); pt[i].y = int((xy[i].y - min_y)*GRAPHWIDTH/hy + 0.5);
} break; case WM_SIZE: sx = LOWORD(lParam); sy = HIWORD(lParam); break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); hx = (max_x - min_x)/scaleX; hy = (max_y - min_y)/scaleY;
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, GRAPHSIZE, -GRAPHSIZE, NULL);
SetViewportExtEx(hdc, sx, sy, NULL);
SetViewportOrgEx(hdc, 2*indent, sy-indent, NULL);
SetTextAlign(hdc, TA_RIGHT | TA_TOP); for (z = min_x, i = 0; i <= scaleX; z += hx, i++)
{ x = int((z - min_x)*GRAPHWIDTH/(max_x - min_x) + 0.5);
_stprintf(s, _T("%.1lf"), z);
TextOut(hdc, x, 0, s, _tcslen(s));
MoveToEx(hdc, x, -10, NULL);
LineTo(hdc, x, 10);
}
MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, GRAPHWIDTH, 0);
SetTextAlign(hdc, TA_RIGHT | TA_BOTTOM); for (z = min_y, i = 0; i <= scaleY; z += hy, i++)
{ y = int((z - min_y)*GRAPHWIDTH/(max_y - min_y) + 0.5);
_stprintf(s, _T("%.1lf"), z);
TextOut(hdc, 0, y, s, _tcslen(s));
MoveToEx(hdc, -10, y, NULL);
LineTo(hdc, 10, y);
}
MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, 0, GRAPHWIDTH);
SelectObject(hdc, hline);

Глава 3

114
Polyline(hdc, pt, n); for ( i = 0; i < n; i++)
{
SetRect(&rt, pt[i].x-8, pt[i].y-8, pt[i].x+8, pt[i].y+8);
FillRect(hdc, &rt, hrect);
}
EndPaint(hWnd, &ps); break; case WM_DESTROY:
DeleteObject(hline);
DeleteObject(hrect); delete[] pt; delete[] xy; break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} return 0;
}
Создадим текстовый файл с данными и построим график, изображенный на рис. 3.3.
Предполагается, что точки графика упорядочены по возрастанию координаты x.

Рис.
3.3.
Построение
xy- графика
Константы, которые мы описали перед телом функции, служат для задания количе- ства меток по осям координат, а также для выделения места для подписей осей. Мы выделили 25 логических единиц для подписи оси x и 50 единиц для оси y. Две кон- станты
GRAPHSIZE
и
GRAPHWIDTH
определяют логические размеры окна и графика.

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

115
Начнем рассмотрение кода с сообщения
WM_CREATE
. Вначале проверим — загруже- ны ли данные в память? if((n = v.size()) == 0) . . .
Если данные не загружены и n равно
0
, выводим сообщение в диалоговом окне и закрываем окно hGraph
П
Р И МЕ Ч А Н И Е

Можно было бы решить эту проблему иначе. При создании окна сделать пункт меню построения графика
xy-graph неактивным:
EnableMenuItem(GetMenu(hWnd),ID_LINE, MF_DISABLED);
После загрузки файла вернуть активность пункту меню:
EnableMenuItem(GetMenu(hWnd),ID_LINE, MF_ENABLED);
Если же данные имеются, выделяем память под массив типа
DOUDLE_POINT
, где бу- дем хранить данные графика в виде чисел с плавающей точкой типа double
, и мас- сив
POINT
, где координаты точек хранятся в логических единицах. Дело в том, что функция
Polyline()
, которую мы будем использовать для вывода графика, требует аргумента типа
POINT*
После чего извлекаем числовые данные из контейнера, где они хранятся в виде строк типа string
Поскольку число строк текста нам известно, читаем их в цикле, и функцией sscanf() извлекаем по два числа из строки. Одна из причин, почему мы восполь- зовались этой функцией, заключается в том, что она возвращает количество преоб- разованных данных. Если же в строке оказалось менее двух чисел, выведем сооб- щение об ошибке в данных и закроем окно hGraph
Здесь же определим диапазон для x и y-координат графика, т. е. минимальное и мак- симальное значения. Создадим перо для построения графика и кисть для выделе- ния точек. После чего заполним массив логических координат точек графика.
При обработке сообщения
WM_SIZE
найдем размеры дочернего окна sx и sy
(размер окна может быть изменен пользователем, и график должен быть перестроен).
Заранее предполагаем, что график будет строиться в логическом прямоугольнике размером 1000 1000. Размер окна определим 1200 1200, начало координат помес- тим в левом нижнем углу окна, выполнив смещение по оси x на 50, а по оси y на
25 единиц. Ось x направлена вправо, а ось y — вверх.
Формула преобразования: min min лог лог max min max min
1000;
1000.
i
i
i
i
x
x
y
y
x
y
x
x
y
y
Для округления до целого добавим
0.5
и укажем явное преобразование типа.
Вывод графика осуществляется при обработке сообщения
WM_PAINT

Глава 3

116
Установим логическую систему координат 1200 1200 на всю клиентскую часть окна:
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, GRAPHSIZE, -GRAPHSIZE, NULL);
SetViewportExtEx(hdc, sx, sy, NULL);
Ось x — вправо, а y — вверх. Начало координат определим относительно исходной системы координат (см. рис. 3.3).
SetViewportOrgEx(hdc, 2*indent, sy-indent, NULL);
Выведем подписи оси x, т. е. 8 числовых меток (как определено в константе scaleX
), от минимального до максимального значения x-координаты.
При выводе горизонтальных меток хотелось бы, чтобы числовое значение своим правым краем стояло на позиции метки, но по умолчанию текст выравнивается влево. Решить эту проблему можно, установив выравнивание текста от правой верхней точки при помощи функции
SetTextAlign()
Теперь весь вывод будет осуществляться, исходя из заданных параметров align
:
TA_RIGHT | TA_TOP
Для преобразования числового значения в текстовый вид здесь так же можно вос- пользоваться функцией sprintf()
. Зададим формат вывода "%.1lf"
, что означает
1 знак после десятичной точки для числа с плавающей точкой типа double (lf)
, размер поля вывода по фактической длине строки.
После вывода очередного числа ставим вертикальную риску длиной 20 логических единиц, а линию оси выведем после завершения цикла.
Подписи оси y сделаем аналогично, только для вывода числовых значений устано- вим выравнивание по нижнему правому краю:
TA_RIGHT | TA_BOTTOM
Выбираем синее перо hline и строим ломаную линию функцией
Polyline()

После чего в цикле "заливаем" красной кистью hrect все точки квадратами со сто- роной в 16 единиц.
Нам осталось лишь освободить занятые ресурсы: при обработке сообщения
WM_DESTROY
— удалить перо, кисть и освободить память, занятую массивами pt и xy

Каталог: uploads
uploads -> Научно-исследовательская работа Путешествие в мир компьютера Севастьянов Вадим
uploads -> «деревянная игрушка коми пермяцкого народа»
uploads -> Викторина «Я хочу здоровым быть»
uploads -> «чем великобритания интересна для россии?» Великобритания входит в число крупнейших мировых держав
uploads -> Персональные компьютеры, история создания и развития
uploads -> Подросток и компьютерные игры
uploads -> Руководство пользователя 2 Заключение 12
uploads -> Сборник Из опыта проектной деятельности учащихся гимназии №524 в 2012-2013 учебном году Санкт-Петербург 2013


Поделитесь с Вашими друзьями:
1   2   3   4   5   6   7   8   9   10   ...   15


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

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


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