Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Обработка сообщений WM_PAINT
Прежде чем как продолжить чтение, снова запустите программу, приведенную в предыдущем разделе, и введите несколько символов. Затем минимизируйте и снова распахните окно. Вы увидите, что в восстановленном окне ничего не отображается. И в том случае, если окно перекрывается другим окном, а затем вновь становится активным, последний введенный символ не отображается. Причина этого проста: Windows, как правило, не запоминает содержимое окна (число окон зависит от приложений). Таким образом, все заботы по перерисовке содержимого окна возлагаются на Вашу программу. Для того чтобы программа знала, когда ей следует это делать, Windows каждый раз, когда необходимо перерисовать окно, посылает ей сообщение WM_PAINT. – Это сообщение посылается программе также при создании и первом отображении окна. Получив это сообщение, программа должна перерисовать содержимое окна. В этом разделе мы добавим в оконную функцию оператор case, обрабатывающий сообщение WM_PAINT. Замечание. В силу различных технических причин при перемещении, а иногда и при изменении размеров окон их содержимое сохраняется и перерисовывается системой. Однако это не касается тех случаев, когда окно минимизируется или перекрывается другим окном, а затем восстанавливается в исходное состояние. Прежде чем объяснять, как обрабатывается WM_PAINT, будет полезно рассказать, почему Windows не перерисовывает окно автоматически. В большинстве случаев перерисовать окно проще программе, нежели Windows, поскольку именно программа, а не Windows, должна «знать» о содержимом окна и способах его перерисовки. И хотя достоинства этого подхода являются спорными, в данном случае нужно просто принять его, поскольку не похоже, что он будет меняться. Первым шагом в обработке сообщения WM_PAINT будет добавление соответствующего case в оператор switch функции окна, как показано ниже:
// Обработка запроса на перерисовку окна case WM_PAINT: hdc=BeginPaint(hwnd, & paintstruct); // Получить DC TextOut(hdc, 1, 1, str, strlen(str)); // Вывести буфер EndPaint(hwnd, & paintstruct); // Освободить DC break;
Прежде всего заметьте, что контекст устройства создается при помощи вызова BeginPaint(), а не GetDC(). По различным причинам при обработке сообщения WM_PAINT контекст устройства необходимо получать при помощи функции BeginPaint(), которая имеет следующий прототип: HDC BeginPaint(HWND hwnd, LPPAINTSTRICT lpPS);
Второй параметр является указателем на структуру PAINTSTRUCT, которая определяется следующим образом:
typedef struct tagPAINTSTRUCT { HDC hdc; // Дескриптор DC BOOL fErase; // Истина, если перерисовывается окно RECT rcPaint; // Координаты области перерисовки BOOL fRestore; // Зарезервировано BOOL flncUpdate; // Зарезервировано BYTE rgbReserved[32]; // Зарезервировано } PAINTSTRUCT;
Тип RECT – это структура, описывающая прямоугольную область:
typedef struct tagRECT { LONG left, top; // Верхний левый угол LONG right, bottom; // Правый нижний угол } RECT;
В структуре PAINTSTRUCT поле rcPaint задает координаты прямоугольной области в окне, которая должна быть перерисована. В данном случае нас не интересует содержимое структуры PAINTSTRUCT; можно предположить, что рабочая область окна будет перерисовываться целиком.
Пример 3-2. Ниже приводится полный текст программы обработки сообщения WM_PAINT:
// Обработка сообщений WM_PAINT #include < Windows.h> #include < String.h> #include < Stdio.h> LRESULT CALLBACK WindowFunc(HWND, UINT, WPARAM, LPARAM); char szWinName[]=" МоеОкно"; // Имя класса окна char str[80]=" Пример"; // Буфер для строки вывода int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst, LPSTR lpszArgs, int nWinMode) { HWND hwnd; MSG msg; WNDCLASS wcl; // Определить класс окна wcl.hInstance=hThisInst; // Дескриптор приложения wcl.lpszClassName=szWinName; // Имя класса окна wcl.lpfnWndProc=WindowFunc; // Функция окна wcl.style=0; // Стиль по умолчанию wcl.hIcon=LoadIcon(NULL, IDI_APPLICATION); // Иконка wcl.hCursor=LoadCursor(NULL, IDC_ARROW); // Курсор wcl.lpszMenuName=NULL; // Без меню wcl.cbClsExtra=0; // Без дополнительной информации wcl.cbWndExtra=0; wcl.hbrBackground= (HBRUSH)GetStockObject(WHITE_BRUSH); //Белый фон if(! RegisterClass(& wcl)) // Регистрируем класс окна return 0; hwnd=CreateWindow(szWinName, // Создать окно " Обработка сообщений WM_PAINT", WS_OVERLAPPEDWINDOW, // Стиль окна CW_USEDEFAULT, // x-координата CW_USEDEFAULT, // y-координата CW_USEDEFAULT, // Ширина CW_USEDEFAULT, // Высота HWND_DESKTOP, // Нет родител. окна NULL, // Нет меню hThisInst, // Дескриптор приложения NULL); // Нет дополнит. аргументов ShowWindow (hwnd, nWinMode); // Показать окно UpdateWindow (hwnd); // и перерисовать
while(GetMessage(& msg, NULL, 0, 0)) // Запустить цикл { // обработки сообщений TranslateMessage(& msg); // Разреш. исп. клавиатуры DispatchMessage (& msg); // Вернуть управл. Windows } return msg.wParam; }
// Следующая функция вызывается операционной // системой Windows и получает в качестве // параметров сообщения из очереди сообщений // данного приложения LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT paintstruct; switch(message) { case WM_CHAR: // Обработка нажатия клавиши hdc=GetDC(hwnd); // Для получ. контекста устр-ва TextOut(hdc, 1, 1, " ", 4); // Стереть символ sprintf(str, " %c", (char)wParam); // Запись симв. TextOut (hdc, 1, 1, str, strlen(str)); // Вывод ReleaseDC (hwnd, hdc); // Освободить контекст break; case WM_PAINT: // Перерисовка рабочей области hdc=BeginPaint(hwnd, & paintstruct); // Получить DC TextOut(hdc, 1, 1, str, strlen(str)); //Вывести буфер EndPaint(hwnd, & paintstruct); // Освободить DC break; case WM_DESTROY: // Завершение программы PostQuitMessage (0); break; default: // Все сообщения, не обрабатываемые в // данной функции, направляются на обработку // по умолчанию return DefWindowProc(hwnd, message, wParam, lParam); } return 0; }
Прежде чем продолжить чтение, скомпилируйте и повторно запустите эту программу. Попробуйте ввести несколько символов, затем минимизируйте и снова раскройте окно. Вы увидите, что каждый раз при перерисовке окна последний введенный символ также перерисовывается. Заметьте, что внешний массив str инициализируется как слово " Пример", и эта строка отображается в начале работы программы. Так происходит потому, что при создании окна оно получает сообщение WM_PAINT. Далее, если окно не свертывать, графический «остаток» этого слова будет виден на экране. В нашем случае программа обработки WM_PAINT довольно простая, во многих же реальных программах она может быть весьма сложной, поскольку большинство окон содержат значительно больше отображаемой информации. Зная механизм перерисовки содержимого окна, Вы должны всегда использовать его в своих программах. В реальных программах перерисовка информации производится, как правило, одним из трех способов. Во-первых, программа может выводить информацию, получаемую в результате каких-либо вычислений. Это проще всего, когда не требуется взаимодействие с пользователем. Во-вторых, в некоторых случаях Вам может понадобиться запоминать последовательность событий и «проигрывать» их при необходимости перерисовывать окно. Наконец, программа может поддерживать виртуальный экран, который будет просто копироваться в окно при перерисовке. Это наиболее общий метод (и он реализован далее в этой книге). Выбор подхода полностью зависит от конкретного приложения. В некоторых примерах, приведенных в книге, способ перерисовки окна не определен, поскольку это требует увеличения программы и мешает концентрировать внимание на теме примера. Однако Ваши собственные программы должны будут перерисовывать свои окна для того, чтобы отвечать всем требованиям, предъявляемым к приложениям Windows.
3.4. Обработка сообщений «мыши»
Поскольку Windows широко использует ввод мыши, все Windows-программы должны обрабатывать сообщения мыши. Имеется несколько типов таких сообщений. В этом разделе используются два наиболее часто употребляемых сообщения мыши – WM_LBUTTONDOWN и WM_RBUTTONDOWN, которые поступают при нажатии соответственно левой и правой кнопок мыши. Для начала в оператор switch оконной функции необходимо добавить обработку этих сообщений так, как показано ниже:
case WM_RBUTTONDOWN: // Нажата правая кнопка мыши hdc = GetDC(hwnd); // Получить DC strcpy(str, " Нажата ПРАВАЯ кнопка"); TextOut(hdc, LOWORD(lParam), HIWORD(lParam), str, strlen(str)); ReleaseDC(hwnd, hdc); // Освободить DC break; case WM_LBUTTONDOWN: // Нажата левая кнопка мыши hdc = GetDC(hwnd); // Получить DC strcpy(str, " Нажата ЛЕВАЯ кнопка"); TextOut(hdc, LOWORD(lParam), HIWORD(lParam), str, strlen(str)); ReleaseDC(hwnd, hdc); // Освободить DC break;
При нажатии кнопки мыши координаты текущей позиции курсора x и y передаются соответственно в LOWORD(lParam) и HIWORD(lParam). Программа, обрабатывающая сообщения мыши, использует эти координаты для вывода на экран соответствующей текстовой строки. То есть каждый раз при нажатии кнопки мыши сообщение об этом будет выведено в текущей позиции курсора.
Пример 3-3. Далее следует полная программа обработки сообщений мыши.
// Обработка сообщений мыши #include < Windows.h> #include < String.h> #include < Stdio.h> LRESULT CALLBACK WindowFunc(HWND, UINT, WPARAM, LPARAM); char szWinName[]=" МоеОкно"; // Имя класса окна char str[80]=" Пример"; // Буфер для строки вывода int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst, LPSTR lpszArgs, int nWinMode) { HWND hwnd; MSG msg; WNDCLASS wcl; // Определить класс окна wcl.hInstance=hThisInst; // Дескриптор приложения wcl.lpszClassName=szWinName; // Имя класса окна wcl.lpfnWndProc=WindowFunc; // Функция окна wcl.style=0; // Стиль по умолчанию wcl.hIcon=LoadIcon(NULL, IDI_APPLICATION); // Иконка wcl.hCursor=LoadCursor(NULL, IDC_ARROW); // Курсор wcl.lpszMenuName=NULL; // Без меню wcl.cbClsExtra=0; // Без дополнительной информации wcl.cbWndExtra=0; wcl.hbrBackground= (HBRUSH)GetStockObject(WHITE_BRUSH); //Белый фон if(! RegisterClass(& wcl)) // Регистрируем класс окна return 0; hwnd=CreateWindow(szWinName, // Создать окно " Обработка сообщений мыши", WS_OVERLAPPEDWINDOW, // Стиль окна CW_USEDEFAULT, // x-координата CW_USEDEFAULT, // y-координата CW_USEDEFAULT, // Ширина CW_USEDEFAULT, // Высота HWND_DESKTOP, // Нет родител. окна NULL, // Нет меню hThisInst, // Дескриптор приложения NULL); // Нет дополнит. аргументов ShowWindow (hwnd, nWinMode); // Показать окно UpdateWindow (hwnd); // и перерисовать
while(GetMessage(& msg, NULL, 0, 0)) // Запустить цикл { // обработки сообщений TranslateMessage(& msg); // Разреш. исп. клавиатуры DispatchMessage (& msg); // Вернуть управл. Windows } return msg.wParam; } // Следующая функция вызывается операционной // системой Windows и получает в качестве // параметров сообщения из очереди сообщений // данного приложения LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT paintstruct; switch(message) { case WM_CHAR: // Обработка нажатия клавиши hdc=GetDC(hwnd); // Для получ. контекста устр-ва TextOut(hdc, 1, 1, " ", 4); // Стереть символ sprintf(str, " %c", (char)wParam); // Запись симв. TextOut(hdc, 1, 1, str, strlen(str)); // Вывод ReleaseDC(hwnd, hdc); // Освободить контекст break; case WM_PAINT: // Перерисовка рабочей области hdc=BeginPaint(hwnd, & paintstruct); // Получить DC TextOut(hdc, 1, 1, str, strlen(str)); //Вывести буфер EndPaint(hwnd, & paintstruct); // Освободить DC break; case WM_RBUTTONDOWN: // Нажата правая кнопка мыши hdc = GetDC(hwnd); // Получить DC strcpy(str, " Нажата ПРАВАЯ кнопка"); TextOut(hdc, LOWORD(lParam), HIWORD(lParam), str, strlen(str)); ReleaseDC(hwnd, hdc); // Освободить DC break; case WM_LBUTTONDOWN: // Нажата левая кнопка мыши hdc = GetDC(hwnd); // Получить DC strcpy(str, " Нажата ЛЕВАЯ кнопка"); TextOut(hdc, LOWORD(lParam), HIWORD(lParam), str, strlen(str)); ReleaseDC(hwnd, hdc); // Освободить DC break; case WM_DESTROY: // Завершение программы PostQuitMessage(0); break; default: // Все сообщения, не обрабатываемые в // данной функции, направляются на обработку // по умолчанию return DefWindowProc(hwnd, message, wParam, lParam); } return 0; }
Результаты работы этой программы показаны на рис. 3.2. Более подробно о сообщениях «мыши». Параметр wParam сообщений WM_LBUTTONDOWN и WM_RBUTTONDOWN содержит дополнительную информацию, представляющую собой комбинацию следующих флагов:
MK_CONTROL MK_SHIFT MK_RBUTTON MK_LBUTTON MK_MBUTTON
Если в момент нажатия кнопки мыши была нажата клавиша [Ctrl], wParam будет содержать флаг MK_CONTROL. Если же в этот момент была нажата клавиша [Shift], wParam будет содержать флаг MK_SHIFT. Флаги MK_LBUTTON, MK_RBUTTON и MK_MBUTTON устанавливаются, если были нажаты соответственно левая, правая и средняя кнопки мыши. Параметр wParam может содержать комбинацию (т.е. одновременно более одного) этих флагов. Вы можете поэкспериментировать с сообщениями мыши и убедиться в этом.
|