Вывод текста на экран и устройство в 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" данного курса.