Вывод текста на экран и устройство в Windows
12.10. Приложение № IV
Использование полос прокрутки для вывода текста
Полосы прокрутки широко используются в приложениях Windows для просмотра текста или изображения, не помещающегося в окне. Полосы прокрутки бывают горизонтальными и вертикальными. Горизонтальная и вертикальная полосы прокрутки посылают в функцию родительского окна сообщения WM_HSCROLL и WM_VSCROLL соответственно. Поэтому функция обработки сообщений родительского окна должна обрабатывать эти сообщения. Параметр WParam сообщений несёт информацию о действии, которое выполнялось над полосой прокрутки.
Создание полосы прокрутки
Во-первых, Вы можете создать полосу прокрутки с помощью функции CreateWindow, указав предопределённый класс окна "scrollbar". Этот способ аналогичен способу создания кнопок или статических органов управления. Этот способ подробно рассматривается в книге [39 ]. Во-вторых, при создании окна на базе своего собственного класса Вы можете указать, что окно должно иметь горизонтальную, вертикальную или обе полосы прокрутки. Этот способ мы и рассмотрим.
Второй способ создания полос чрезвычайно прост, но с его помощью можно создать только одну горизонтальную и одну вертикальную полосы прокрутки, расположенные по краям окна. Для того чтобы эта полосы прокрутки появились в окне, при регистрации класса окна в третьем параметре функции CreateWindow необходимо указать стиль окна WS_VSCROLL, или WS_HSCROLL, или оба стиля вместе.
Пример 12.1.
hwnd = CreateWindow( szClassName, szWindowTitle, // Стиль окна WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, NULL );
Сообщения от полосы прокрутки рассматриваются в таблице 12.6.
| Константы WParam сообщений для полсы прокрутки | |
|---|---|
| Флаг полосы просмотра | Описание |
| SB_LEFT | Сдвиг влево на начало документа (горизонтальная полоса прокрутки) |
| SB_TOP | Сдвиг вверх на начало документа (вертикальная полоса прокрутки) |
| SB_LINELEFT | Сдвиг влево на одну строку |
| SB_LINEUP | Сдвиг вверх на одну строку |
| SB_PAGELEFT | Сдвиг на одну страницу влево |
| SB_PAGEUP | Сдвиг на одну страницу вверх |
| SB_RIGHT | Сдвиг вправо в конец документа (горизонтальная полоса прокрутки) |
| SB_BOTTOM | Сдвиг вниз в конец документа (вертикальная полоса прокрутки) |
| SB_LINERIGHT | Сдвиг вправо на одну строку |
| SB_LINEDOWN | Сдвиг вниз на одну строку |
| SB_PAGERIGHT | Сдвиг на одну страницу вправо |
| SB_PAGEDOWN | Сдвиг на одну страницу вниз |
| SB_THUMBPOSITION | Сдвиг в абсолютную позицию. Текущая позиция определяется младшим словом параметра lParam |
| SB_ENDSCROLL | Сообщение приходит в момент, когда вы отпускаете клавишу мыши после работы с полосой просмотра. Это сообщение обычно игнорируется (передаётся функции DefWindowProc ) |
| SB_THUMBTRACK | Перемещение ползунка просмотра. Текущая позиция определяется младшим словом параметра lParam |
Инициализация полосы просмотра
Для полосы прокрутки определены понятия "текущая позиция" и "диапазон изменения значений позиции". При передвижении ползунка вдоль полосы прокрутки текущая позиция принимает дискретные значения внутри диапазона изменения значений позиции. Если ползунок находится в самом левом или самом верхнем положении, текущая позиция является минимальной. Если ползунок находится в самом правом или самом нижнем положении, текущая позиция является максимальной.
После того, как Вы создали полосу прокрутки, её необходимо проинициализировать, указав диапазон изменений значений позиции. Для этого необходимо вызвать функцию: SetScrollRange, описанную ниже:
| ФУНКЦИЯ | SETSCROLLRANGE |
| ОПРЕДЕЛЕНА В: | <windows.h> |
| СИНТАКСИС: |
void WINAPI SetScrollRange( hwnd, fnBar, nMin, nMax, fRedraw ), где
|
| НАЗНАЧЕНИЕ: | Функция устанавливает диапазон значений позиции для полосы прокрутки; |
| ОПИСАНИЕ: | |
| ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ: | Функция не возвращает значений; |
| ПЕРЕНОСИМОСТЬ: | Только Windows; |
Всегда можно узнать диапазон для полосы просмотра, вызвав функцию GetScrollRange:
| ФУНКЦИЯ | GETSCROLLRANGE |
| ОПРЕДЕЛЕНА В: | <windows.h> |
| СИНТАКСИС: |
void WINAPI SetScrollRange( hwnd, fnBar, lpnMin, lpnMax ), где
|
| НАЗНАЧЕНИЕ: | Функция выдаёт диапазон значений позиции для полосы прокрутки; |
| ОПИСАНИЕ: | |
| ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ: | Функция не возвращает значений; |
| ПЕРЕНОСИМОСТЬ: | Только Windows; |
Другие функции, управляющие режимом отображения полос прокрутки, смотри в книге [39].
| Значения флага fnBar функций работы с полосами прокрутки | |
|---|---|
| Значение | Описание |
| SB_CTL | Установка диапазона полосы прокрутки, созданной как орган управления класса "scrollbar". В этом случае параметр hwnd функции SetScrollRange и других должен содержать идентификатор органа управления, полученный при его создании функцией CreateWindow |
| SB_HORZ | Установка диапазона горизонтальной полосы прокрутки, при создании которой был использован стиль окна WS_HSCROLL. Параметр функции hwnd должен содержать идентификатор окна, имеющий эту полосу прокрутки. |
| SB_VERT | Установка диапазона вертикальной полосы прокрутки, при создании которой был использован стиль окна WS_VSCROLL. Параметр функции hwnd должен содержать идентификатор окна, имеющий эту полосу прокрутки. |
12.11. Приложение № V
Пример программы на основе библиотек WinAPI для вывода текста в рабочее окно с вертикальной полосой прокрутки.
Данная программа оптимизирована под среду разработки Borland C/C++ 3.1.
/* Файл TEXTOUTS.H с объявлениями функций и переменных*/
#ifndef MAXTEXTBUFFSIZE
#define MAXTEXTBUFFSIZE 80 // Максимальная ширина строки в символах
#endif
/* #ifndef MAXTEXTSTRINGS
#define MAXTEXTSTRINGS 24 // Максимальное число строк в окне
#endif */
#ifndef __WINDOWS_H
#include <windows.h>
#endif
#ifndef __TEXTOUTS_H
#define __TEXTOUTS_H
// Методы
// "Удержание" позиции прокрутки в "правильных" границах
void vnormalize( void );
void WINAPI setVScrollPos( int ); // Установка текущей позиции скроллинга
int WINAPI getVScrollPos(); // Выдача текущей позиции скроллинга
void WINAPI setMaxVScrol( int ); // Установка максимального количества строк
int WINAPI getMaxVScroll(); // Выдача максимального количества строк
void WINAPI TextMetricsInit( HDC ); // Установка параметров шрифтов
void WINAPI vPageUp( int ); // Перемещение рабочей области окна на экран вверх
void WINAPI vPageDown( int ); // Перемещение рабочей области окна на экран вниз
void WINAPI PrintHDC_scroll( HDC, const char * ); // Печать строки без переноса на новую строку
void WINAPI PrintLnHDC_scroll( HDC, const char * ); // Печать строки с переносом на новую строку
void WINAPI PrintInitHDC_scroll( void ); // Данная функция инициализирует позиции вывода текста при обработке прерывания WM_PAINT
#endif
/* Файл View0000.CPP */
// ------------------------------------
// Вывод текста в рабочую область окна
// с вертикальной полосой прокрутки
// ----------------------------------------
#define STRICT
#include <windows.h>
#include <mem.h>
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
char const szClassName[] = "ViewerAppClass";
char const szWindowTitle[] = "Viewer0000 Application";
// =====================================
// Функция WinMain
// =====================================
#pragma argsused
int PASCAL
WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow)
{
MSG msg; // структура для работы с сообщениями
HWND hwnd; // идентификатор главного окна приложени
if(!InitApp(hInstance))
return FALSE;
hwnd = CreateWindow(
szClassName, // имя класса окна
szWindowTitle, // заголовок окна
WS_OVERLAPPEDWINDOW | WS_VSCROLL, // стиль окна
CW_USEDEFAULT, // задаем размеры и расположение
CW_USEDEFAULT, // окна, принятые по умолчанию
CW_USEDEFAULT,
CW_USEDEFAULT,
0, // идентификатор родительского окна
0, // идентификатор меню
hInstance, // идентификатор приложени
NULL); // указатель на дополнительные
// параметры
if(!hwnd)
return FALSE;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg, 0, 0, 0))
{
DispatchMessage(&msg);
}
return msg.wParam;
}
// =====================================
// Функция InitApp
// =====================================
BOOL
InitApp(HINSTANCE hInstance)
{
ATOM aWndClass; // атом для кода возврата
WNDCLASS wc; // структура для регистрации
// класса окна
memset(&wc, 0, sizeof(wc));
// Определяем стиль класса окна, при
// использовании которого окно требует
// перерисовки в том случае, если
// изменилась его ширина или высота
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = (LPSTR)NULL;
wc.lpszClassName = (LPSTR)szClassName;
aWndClass = RegisterClass(&wc);
return (aWndClass != 0);
}
/* Файл WINPROC.CPP с примерами функций */
// =====================================
// Функция WndProc
// =====================================
#define STRICT
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "textouts.h"
#define MAXTEXTSTRINGS 20 // Переопределённое значение высоты экрана в строках
// Переменные
static int nVScrollPos; // Текущая позиция вертикальной полосы прокрутки
static int nMaxVScrolled; // Максимальное значение позиции
static int nxCurPos; // Текущая горизонтальная позиция вывода в окне
static int nyCurPos; // Текущая вертикальная позиция вывода в окне
static int cxChar; // Ширина символов
static int cyChar; // Высота строки с символами
static char otladka[80]; // Данные для отладки
LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static WORD cxClient, cyClient;
HDC hdc; // индекс контекста устройства
PAINTSTRUCT ps; // структура для рисовани
TEXTMETRIC tm; // Структура для записи метрик шрифта
switch (msg)
{
case WM_CREATE:
{
// Получаем контекст отображения,
// необходимый для определения метрик шрифта
hdc = GetDC(hwnd);
// Заполняем структуру информацией
// о метрике шрифта, выбранного в
// контекст отображени
GetTextMetrics( hdc, &tm );
// Запоминаем значение ширины для самого широкого символа
cxChar = tm.tmMaxCharWidth;
// Запоминаем значение высоты букв с учётом междустрочного интервала
cyChar = tm.tmHeight + tm.tmExternalLeading;
// Инициализируем текущую позицию вывода текста
nxCurPos = cxChar/2; // Текущая горизонтальная позиция вывода в окне
nyCurPos = 0; // Текущая вертикальная позиция вывода в окне
// Задаём начальное значение позиции
nVScrollPos = 0;
nMaxVScrolled = MAXTEXTSTRINGS;
// Освобождаем контекст
ReleaseDC(hwnd, hdc);
// Задаем диапазон изменения значений
SetScrollRange(hwnd, SB_VERT, 0, nMaxVScrolled, FALSE);
// Устанавливаем ползунок в начальную позицию
SetScrollPos(hwnd, SB_VERT, nVScrollPos, TRUE);
return 0;
}
// Определяем размеры внутренней области окна
case WM_SIZE:
{
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return 0;
}
// Сообщение от вертикальной полосы просмотра
case WM_VSCROLL:
{
switch(wParam)
{
case SB_TOP:
{
nVScrollPos = 0;
break;
}
case SB_BOTTOM:
{
nVScrollPos = nMaxVScrolled;
break;
}
case SB_LINEUP:
{
nVScrollPos -= 1;
break;
}
case SB_LINEDOWN:
{
nVScrollPos += 1;
break;
}
case SB_PAGEUP:
{
nVScrollPos -= cyClient / cyChar;
break;
}
case SB_PAGEDOWN:
{
nVScrollPos += cyClient / cyChar;
break;
}
case SB_THUMBPOSITION:
{
nVScrollPos = LOWORD(lParam);
break;
}
// Блокируем для того чтобы избежать
// мерцания содержимого окна при
// перемещении ползунка
case SB_THUMBTRACK:
{
return 0;
}
default:
break;
}
// Ограничиваем диапазон изменения значений
vnormalize();
// Устанавливаем ползунок в новое положение
SetScrollPos(hwnd, SB_VERT, nVScrollPos, TRUE);
// Обновляем окно
InvalidateRect(hwnd, NULL, TRUE);
return 0;
}
case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
/* Эти операторы используется для тестировани
sprintf( otladka, "nVScrollPos = %d\n", nVScrollPos );
MessageBox( hwnd, otladka, NULL, MB_OK ); */
// Инициализируем позицию вывода текста
PrintInitHDC_scroll();
// Выводим текст
PrintLnHDC_scroll(hdc, "Это первая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "Это следующая строка");
PrintLnHDC_scroll(hdc, "А это последняя строка");
EndPaint(hwnd, &ps);
return 0;
}
// Обеспечиваем управление полосой просмотра
// при помощи клавиатуры
case WM_KEYDOWN:
{
// В зависимости от кода клавиши функция окна
// посылает сама себе сообщения, которые
// обычно генерируются полосой просмотра
switch (wParam)
{
case VK_HOME:
{
SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0L);
break;
}
case VK_END:
{
SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0L);
break;
}
case VK_UP:
{
SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0L);
break;
}
case VK_DOWN:
{
SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0L);
break;
}
case VK_PRIOR:
{
SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0L);
break;
}
case VK_NEXT:
{
SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0L);
break;
}
}
return 0;
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
// "Удержание" позиции прокрутки в "правильных" границах
void vnormalize( void )
{
if(nVScrollPos < 0) nVScrollPos = 0;
if( nVScrollPos > nMaxVScrolled ) nVScrollPos = nMaxVScrolled;
return;
};
void WINAPI PrintHDC_scroll( HDC hdc, const char *str )
{
char buf[MAXTEXTBUFFSIZE];
char temp[MAXTEXTBUFFSIZE];
int i, y;
// Обрезание входной строки при превышении её длины.
// Это нужно, чтобы не было ошибки "переполнение буфера"
memset( temp, '\0', MAXTEXTBUFFSIZE );
strncpy( temp, str, MAXTEXTBUFFSIZE );
// Вычисляем начальную позицию для вывода
y = nyCurPos + cyChar * (- nVScrollPos);
// Подготавливаем в рабочем буфере
// и выводим в окно, начиная с текущей
// позиции название параметра
sprintf(buf, "%s", temp);
i = strlen(temp);
TextOut(hdc,
nxCurPos, y, buf, i);
// Увеличиваем текущую позицию по
// горизонтали на ширину символа
nxCurPos += cxChar*i;
return;
}
void WINAPI PrintLnHDC_scroll( HDC hdc, const char *str )
{
char buf[MAXTEXTBUFFSIZE], temp[MAXTEXTBUFFSIZE];
int i, y;
// Обрезание входной строки при превышении её длины.
// Это нужно, чтобы не было ошибки "переполнение буфера"
memset( temp, '\0', MAXTEXTBUFFSIZE );
strncpy( temp, str, MAXTEXTBUFFSIZE );
// Вычисляем начальную позицию для вывода
y = nyCurPos + cyChar * (- nVScrollPos);
// Подготавливаем в рабочем буфере
// и выводим в окно, начиная с текущей
// позиции название параметра
sprintf(buf, "%s", temp);
i = strlen(temp);
TextOut(hdc,
nxCurPos, y, buf, i);
// Увеличиваем текущую позицию по
// вертикали на высоту символа
// и переносим начало вывода на новую строку
nyCurPos += cyChar;
nxCurPos = cxChar/2;
return;
}
void WINAPI PrintInitHDC_scroll( )
{
/* Данная функция инициализирует позиции вывода текста при
обработке прерывания WM_PAINT перед собственно выводом
текста */
nyCurPos = 0; // Начальная позиция вывода в окно 0
nxCurPos = cxChar / 2; // Начальная горизонтальная позиция -- половина от ширины символа
return;
}
/* Файл описания модулей VIEW0000.DEF */
; =====================================
; Файл определения модуля VIEW0000
; =====================================
NAME VIEW0000
DESCRIPTION 'Приложение VIEW0000, (C) 2010, yudenisov'
EXETYPE windows
STUB 'winstub.exe'
STACKSIZE 5120
HEAPSIZE 4096
CODE preload moveable discardable
DATA preload moveable multiple
Листинг
.
Замечания к текстам примеров
Вывод текста в рабочую область экрана осуществляется только при обработке прерывания WM_PAINT. Собственно перед выводом текста необходимо провести так называемую "инициализацию окна", вызвав функцию: PrintInitHDC_scroll без параметров. Эта функция устанавливает начальную позицию вывода в рабочей области окна при её перерисовке (отступ сверху - 1 интервал, отступ слева - 0,5 от ширины литеры указанного шрифта). Это необходимо сделать, поскольку при каждом приёме сообщения WM_PAINT окно перерисовывается заново.
Затем идут собственно функции вывода текста в окно PrintHDC_scroll и PrintLnHDC_scroll.
Первая из этих функций выводит текст в окно без перехода на другую строку. Новый вывод будет производиться в той же строке, начальная позиция которой будет равна "числу напечатанных символов" * "максимальную ширину литера шрифта". В Бейсике это эквивалентно командам:
TAB("позиция"); PRINT "строка";Вторая функция после вывода строки переносит новую позицию вывода в начало следующей строки таблицы - вертикальный сдвиг в 1 интервал и начальная горизонтальная позиция - половина ширины символа от рамки окна. Обе функции в качестве параметров воспринимают контекст устройства и выводимую на экран строку текста.
Обработка нажатия клавиш на клавиатуре и изменение позиции вертикальной полосы просмотра происходит отдельно от вывода текста. При этом изменяются только специальные переменные, содержащие дополнительную информацию для вывода текста. После этого происходит вызов сообщения: "WM_PAINT", и текст выводится в рабочую область окна. Остальные функции и параметры претерпели мало изменений относительно тех функций, описанных в приложениях №№ III - VI "Ввод-вывод с использованием WinAPI" данного курса.