Метрику шрифта, выбранного в контекст отображения hdc, определяют с помощью функции GetTextMetrics:
BOOL GetTextMetrics(HDC hdc, LPTEXTMETRIC lptm);
Параметр lptm указывает на структуру типа TEXTMETRIC, в которую нужно записать метрики шрифта. Все размеры записываются в логических единицах контекста отображения hdc. В случае успешного выполнения функция возвращает ненулевое значение.
Структура TEXTMETRIC предназначена для описания базовой информации о метриках шрифта и описана следующим образом:
typedef struct {
LONG tmHeight;
LONG tmAscent;
LONG tmDescent;
LONG tmInternalLeading;
LONG tmExtemalLeading;
LONG tmAveCharWidth;
LONG tmMaxCharWidth;
LONG tmWeight;
LONG tmOverhang;
LONG tmDigitizedAspectX;
LONG tmDigitizedAspectY;
BCHAR tmFirstChar;
BCHAR tmLastChar;
BCHAR tmDefaultChar;
BCHAR tmBreakChar;
BYTE tmItalic;
BYTE tmUnderlined;
BYTE tmStruckOut;
BYTE tmPitchAndFamily;
BYTE tmCharSet;
} TEXTMETRIC;
Назначение полей этой структуры:
1. tmHeight – общая высота букв, равна tmAscent+tmDescent.
2. tmAscent – часть высоты букв от базовой линии с учетом таких элементов, как тильда в букве "Й".
3. tmDescent – часть высоты букв ниже базовой линии.
4. tmInternalLeading – высота таких выступающих элементов, как тильда в букве "Й", и может быть равна нулю.
|
|
5. tmExtemalLeading – высота межстрочного интервала, рекомендуемая разработчиком шрифта, может быть приравнена нулю.
6. tmAveCharWidth – средняя ширина строчных букв, равна ширине латинской буквы "х".
7. tmMaxCharWidth – ширина самой широкой буквы.
8. tmWeight – жирность шрифта, может находиться в пределах от 0 до 1000, как и для логического шрифта.
9. tmOverhang – величина изменения ширины символов при построении наклонного или полужирного шрифта из нормального шрифта.
10. tmDigitizedAspectX – разрешение устройства отображения по горизонтали.
11. tmDigitizedAspectY – разрешение устройства отображения по вертикали.
12. tmFirstChar – код первого символа в шрифте.
13. tmLastChar – код последнего символа в шрифте.
14. tmDefaultChar – код символа, заменяющего любой отсутствующий в шрифте символ.
15. tmBreakChar – код символа переноса слов с одной строки на другую при выравнивании текста.
16. tmItalic ¹ 0 – означает наклонный шрифт.
17. tmUnderlined ¹ 0 – означает подчеркнутый шрифт.
18. tmStruckOut ¹ 0 – означает перечеркнутый шрифт.
19. tmPitchAndFamily – код семейства шрифта. Четыре младших бита этого кода комбинацией следующих констант определяют информацию о шаге и типе шрифта:
Константа | Пояснение |
TMPF_FIXED_PITCH | Если этот бит установлен, то шрифт с переменным шагом, иначе – с постоянным |
TMPF_VECTOR | Если этот бит установлен, то шрифт векторного типа |
TMPF_TRUETYPE | Если этот бит установлен, то шрифт типа TrueType |
TMPF_DEVICE | Если этот бит установлен, шрифт определен устройством |
Четыре старших бита кода tmPitchAndFamily описывают семейство шрифта, так же как и для логического шрифта.
|
|
20. tmCharSet – код используемого набора символов, такой же, как и для логического шрифта.
Задача. В рабочей области окна вывести таблицу. В заголовке перечислить названия функций, а в строках таблицы отобразить значения этих функций. Причем вывод таблицы ограничить количеством столбцов и строк, которые полностью помещаются в рабочей области. Следующее приложение решает эту задачу.
Листинг 3.7. Вывод таблицы в окно.
#include <windows.h>
#include <math.h>
#include <tchar.h>
BOOL RegClass(WNDPROC, LPCTSTR, UINT);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE hInstance;
TCHAR szClass[] = TEXT("TableClass");
//Структура столбца данных
typedef struct
{
TCHAR str[15]; //Поле имени столбца
double val[50]; //Массив данных столбца
} COLUMN; //Имя типа
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
HWND hwnd;
hInstance = hInst;
if (!RegClass(WndProc, szClass, COLOR_WINDOW))
return FALSE;
hwnd = CreateWindow(szClass, TEXT("ТАБЛИЦА"), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0,0, hInstance, NULL);
if (!hwnd) return FALSE;
while(GetMessage(&msg, 0, 0, 0)) DispatchMessage(&msg);
return msg.wParam;
}
BOOL RegClass(WNDPROC Proc, LPCTSTR szName, UINT brBackground)
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.cbClsExtra=wc.cbWndExtra=0;
wc.lpfnWndProc = Proc;
wc.hInstance = hInstance;
wc.lpszClassName = szName;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(brBackground+1);
wc.lpszMenuName = NULL;
return (RegisterClass(&wc)!= 0);
}
BOOL DrawLine(HDC hdc, int x0, int y0, int x, int y)
{
MoveToEx(hdc, x0, y0, NULL);
return LineTo(hdc, x, y);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
//Описываем массив из 10 столбцов
static COLUMN cols[10];
static int cx, cy, cxChar, cyChar;
switch (msg)
{
case WM_SIZE:
{ cx=LOWORD(lParam); cy=HIWORD(lParam); return 0; }
case WM_CREATE:
{
//Заполняем заголовки столбцов
_tcscpy(cols[0].str, TEXT("i"));
_tcscpy(cols[1].str, TEXT("10*sin(i/10.)"));
_tcscpy(cols[2].str, TEXT("20*sin(i/20.)"));
_tcscpy(cols[3].str, TEXT("30*sin(i/30.)"));
_tcscpy(cols[4].str, TEXT("40*sin(i/40.)"));
_tcscpy(cols[5].str, TEXT("50*sin(i/50.)"));
_tcscpy(cols[6].str, TEXT("60*sin(i/60.)"));
_tcscpy(cols[7].str, TEXT("70*sin(i/70.)"));
_tcscpy(cols[8].str, TEXT("80*sin(i/80.)"));
_tcscpy(cols[9].str, TEXT("90*sin(i/90.)"));
//Заполняем массивы данных столбцов
for (int i=0; i<50; i++)
{
//В первом столбце будут номера строк
cols[0].val[i]=i;
//В остальных столбцах будут данные
for (int j=1; j<10; j++)
cols[j].val[i]=10*sin(i/10./j);
}
//Определяем средние высоту и ширину символов
{
TEXTMETRIC tm;
HDC hdc=GetDC(hwnd);
//Определяем метрики текста
GetTextMetrics(hdc, &tm);
ReleaseDC(hwnd, hdc);
//Количество пикселей в высоте символа
cyChar = tm.tmHeight + tm.tmExternalLeading;
//Количество пикселей в средней ширине
cxChar = tm.tmAveCharWidth+1;
}
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc=BeginPaint(hwnd, &ps);
TCHAR str[20]={0};
//Установим начальные параметры для вывода
int left=cxChar, //Левый край
top=cyChar/2, //Верх
dx=cxChar, //Пробел по оси X
dy=cyChar/4, //Пробел по оси Y
hy=cyChar+dy+dy, //Высота для строки
right=cx-cxChar, //Правый край
bottom=cy-cyChar, //Низ
bounds[10], //Массив ширин столбцов
i=0, j=0;
//Заполняем массив ширинами столбцов
while (j<10)
{
// Для j-ro столбца выбираем ширину
bounds[j] = _tcslen(cols[j].str);
for (i=0; i<50; i++)
{
_sntprintf(str, 7, _T("%f"), cols[j].val[i]);
//_gcvt(cols[j].val[i], 7, str);
int ss = _tcslen(str);
if (bounds[j]<ss) bounds[j]=ss;
}
if (bounds[j]<=3) bounds[j]=4;
if (bounds[j]>10) bounds[j]-=2;
bounds[j] = cxChar*(bounds[j]);
j++;
}
//Определяем максимальное количество столбцов,
//которое помещается на ширине рабочей области
int dd=left, maxcol=0;
while (maxcol<10)
{
if (dd+bounds[maxcol]>right) break;
dd += bounds[maxcol];
maxcol++;
}
//right теперь указывает на правый край таблицы
right=dd;
//Подгоняем ширину окна к количеству столбцов
{
RECT rc;
GetWindowRect(hwnd, &rc);
MoveWindow(hwnd, rc.left, rc.top, //Изменяем только ширину окна
rc.right-rc.left-(cx-right)+dx, rc.bottom-rc.top, TRUE);
}
//////НАЧАЛО ВЫВОДА ТАБЛИЦЫ//////
//Устанавливаем режим выравнивания
SetTextAlign(hdc, TA_RIGHT);
//////Начало вывода шапки таблицы//////
int y=top; //Текущая координата по оси Y
//Горизонтальная линия на всю ширину
DrawLine(hdc, left, y, right, y);
//Заголовки столбцов
int x=left; //Текущая координата по оси X
//Вертикальная линия слева от столбца
DrawLine(hdc, x, y, x, y+hy);
for (j=0; j<maxcol; j++)
{
//Устанавливаем x на правой границе столбца
x+=bounds[j];
TextOut(hdc, x-dx, y+dy, cols[j].str, _tcslen(cols[j].str));
//Вертикальная линия справа от столбца
DrawLine(hdc, x, y, x, y+hy);
|
|
}
//Горизонтальная линия на всю ширину
y += hy;
DrawLine(hdc, left, y, right, y);
//////Конец вывода шапки таблицы//////
//////Начало вывода данных таблицы//////
i = 0;//Счетчик номера строки данных
while (i<50 && y<bottom)
{
//Вертикальная линия слева от столбца
x=left;
DrawLine(hdc, x, y, x, y+hy);
for (j=0; j<maxcol; j++)
{
x+=bounds[j];
//Преобразуем целое число в строку
if (j==0) _itot((int)cols[j].val[i], str, 10);
//Преобразуем вещественное число в строку
else _sntprintf(str, 7, _T("%f"), cols[j].val[i]);
//_gcvt(cols[j].val[i], 7, str);
TextOut(hdc, x-dx, y+dy, str, _tcslen(str));
//Вертикальная линия справа от столбца
DrawLine(hdc, x, y, x, y+hy);
}
//Горизонтальная линия на всю ширину
y += hy;
DrawLine(hdc, left, y, right, y);
i++;
}
//////Конец вывода данных таблицы//////
//////КОНЕЦ ВЫВОДА ТАБЛИЦЫ//////
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY: { PostQuitMessage(0); return 0; }
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Рассмотрим основные шаги решения данной задачи.
1. Таблицу сначала создают в памяти.
1.1. Описывают тип структуры столбца данных:
typedef struct
{ char str[15]; //Поле имени столбца
double val[50]; //Массив данных столбца
} COLUMN; //Имя типа
1.2. Описывают массив cols из 10 столбцов этого типа:
static COLUMN cols[10];
1.3. Заполняют этот массив некоторыми значениями. Сначала заполняются заголовки столбцов:
strcpy(cols[0].str, "i");
strcpy(cols[1].str, "10*sin(i/10.)");
...
strcpy(cols[9].str, "90*sin(i/90.)");
1.4. Затем заполняют массивы данных столбцов:
for (int i=0; i<50; i++)
{
cols[0].val[i]=i;
for (int j=1; j<10; j++)
cols[j].val[i]=10*sin(i/10./j);
}
При этом в первый столбец записывают номера строк, а в другие столбцы записывают значения функций.
2. Определяют геометрические параметры вывода таблицы в рабочую область.
2.1. Определяют среднюю высоту и ширину символов установленного шрифта:
TEXTMETRIC tm;
HDC hdc=GetDC(hwnd);
//Определяем метрики текста
GetTextMetrics(hdc, &tm);
ReleaseDC(hwnd, hdc);
//Количество пикселей в высоте символа
cyChar = tm.tmHeight + tm.tmExternalLeading;
//Количество пикселей в средней ширине
cxChar=tm.tmAveCharWidth+1;
Метрики шрифта определяют на этапе создания окна потому, что используют шрифт по умолчанию. Иначе метрики нужно определять после выбора шрифта в контекст отображения.
2.2. Устанавливают границы вывода таблицы. Здесь left указывает на левую, top – на верхнюю, right – на правую, bottom – на нижнюю границу таблицы в рабочей области. Параметр dx равен величине пробела по оси X, a dy – по оси Y, hy задает высоту строки таблицы. Также описывают переменные bounds[10], i и j.
|
|
2.3. Массив bounds заполняют значениями ширины столбцов:
while (j<10)
{
bounds[j]=strlen(cols[j].str);
for (i=0; i<50; i++)
{
_gcvt(cols[j].val[i], 7, str);
int ss=strlen(str);
if (bounds0]<ss) bounds[j]=ss;
}
if (bounds[j]<=3) bounds[j]=4;
if (bounds[j]>10) bounds[j]-=2;
bounds[j] = cxChar*(bounds[j]);
j++;
}
Для j-го столбца вычисляют количество символов заголовка:
bounds[j]=strlen(cols[j].str);
Далее определяют максимально необходимое количество символов для вывода в этот столбец:
_gcvt(cols[j].val[i], 7, str);
int ss=strien(str);
if (bounds[j]<ss) bounds[j]=ss;
После определения необходимого количества символов вносят незначительные коррекции. Например, в столбце выделяют место для вывода не менее четырех символов:
if (bounds[j]<=3) bounds[j]=4;
А в случае большого (более 10) количества символов уменьшают количество выделяемых позиций:
if (bounds[j]>10) bounds[j]-=2;
Вычисляют ширину столбца в пикселях:
Bounds[j] = cxChar*bounds[j];
2.4. Определяют максимальное количество maxcol столбцов, которое помещается в установленных границах:
int dd=left, maxcol=0;
while (maxcol<10)
{
if (dd+bounds[maxcol]>right) break;
dd += bounds[maxcol];
maxcol++;
}
2.5. Значение right переносят на правую границу столбца maxcol:
right=dd;
2.6. Уменьшают ширину окна так, чтобы она была достаточна для вывода только maxcol столбцов:
RECT rc;
GetWindowRect(hwnd, &rc);
MoveWindow(hwnd, rc.left, rc.top,
rc.right-rc.left-(cx-right)+dx, rc.bottom-rc.top, TRUE);
3. Выводят таблицу.
3.1. Устанавливают режим выравнивания по правой границе:
SetTextAlign(hdp, TA_RIGHT);
Обратите внимание, что в этом режиме горизонтальная координата вывода текста указывает на точку, где завершается вывод правого конца выводимой строки.
3.2. Рисуют заголовки столбцов таблицы.
В первую очередь рисуют горизонтальную линию на всю ширину таблицы (от левой границы – left до правой границы – right):
DrawLine(hdc, left, y, right, y);
Для рисования заголовков столбцов вспомогательную координату по оси X устанавливают на левой границе (x = left). После этого рисуют вертикальную линию на левой границе таблицы высотой hy:
DrawLine(hdc, х, у, х, y+hy);
Вывод заголовка j-го столбца содержит следующие шаги:
3.2.1. Устанавливают х на правой границе столбца:
x+=bounds[j];
3.2.2. Выводят текст заголовка j-го столбца:
TextOut(hdc, x-dx, y+dy, cols[j].str, strlen(cols[j].str));
При этом координата x-dx указывает на правую границу j-го столбца минус величина пробела по оси X.
3.2.3. Рисуют вертикальную линию справа от j-го столбца:
DrawLine(hdc, х, у, х, y+hy);
После вывода всех maxcol заголовков рисуют горизонтальную линию на всю ширину таблицы:
y+=hy;
DrawLine(hdc, left, у, right, у);
Этим завершают вывод заголовков таблицы.
3.3. Выводят данные таблицы. Этот шаг практически ничем не отличается от шага 3.2. Здесь добавляют счетчик номера строки данных и операторы преобразования числовых данных в строковые. Кроме этого, вывод ограничивают 50 строками и значением bottom нижней границы вывода таблицы в рабочей области.