Часто используемые сообщения управления окнами 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Часто используемые сообщения управления окнами



Типичное окно реагирует не только на WM_PAINT, но и на многие другие сообщения. Некоторые наиболее часто используемые сообщения перечислены ниже.

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

WM_DESTROY. Это сообщение посылается в оконную процедуру окна, которое уже удалено с экрана и вскоре будет уничтожено.

WM_CLOSE. Сообщение посылается в окно как признак того, что оно должно быть закрыто. При обработке по умолчанию в DefWindowProc вызывается DestroyWindow. Приложение может, например, вывести окно подтверждения выхода и вызвать DestroyWindow только если пользователь подтвердит закрытие окна.

WM_QUIT. Это сообщение является требованием завершения приложения и обычно является последним сообщением, которое получает главное окно приложения. При его получении функция GetMessage возвращает FALSE, что в большинстве приложений приводит к завершению цикла обработки сообщений. WM_QUIT генерируется в результате вызова функции PostQuitMessage.

WM_QUERYENDSESSION. Сообщение уведомляет приложение о том, что сеанс работы Windows будет завершен. В ответ приложение может вернуть FALSE, чтобы предотвратить закрытие Windows. После обработки WM_QUERYENDSESSION Windows посылает всем приложениям сообщение WM_ENDSESSION с результатами обработки сообщения WM_QUERYENDSESSION.

WM_ENDSESSION. Сообщение посылается всем приложениям после обработки сообщения WM_QUERYENDSESSION. Оно уведомляет приложения, что Windows будет закрыта или что процесс закрытия был прерван. Если закрытие состоится, то оно может произойти в любой момент после того, как сообщение WM_ENDSESSION будет обработано всеми приложениями. Поэтому важно, чтобы приложения завершали все свои действия для обеспечения безопасного завершения работы.

WM_ACTIVATE. Сообщение уведомляет окно верхнего уровня о том, что оно станет активным или неактивным. При смене активного окна это сообщение сначала посылается окну, которое будет неактивным, а потом окну, которое станет активным.

WM_SHOWWINDOW. Это сообщение извещает окно о том, что оно будет скрыто или показано на экране. Окно м.б. скрыто путем вызова функции ShowWindow или в результате перекрытия другим развернутым окном.

WM_ENABLE. Посылается окну, когда оно разрешается или запрещается. Окно может быть разрешено или запрещено с помощью функции EnableWindow. В запрещенном состоянии окно не получает сообщений мыши и клавиатуры.

WM_MOVE. Извещает окно об изменении его местоположения на экране.

WM_SIZE. Сообщение WM_SIZE уведомляет окно об изменении его размеров.

WM_SETFOCUS. Это сообщение извещает окно о том, что оно получило клавиатурный фокус ввода. Приложение может в ответ на это сообщение включить клавиатурный курсор.

WM_KILLFOCUS. Уведомляет окно о потере клавиатурного фокуса ввода. Если приложение включало курсор, то при обработке WM_KILLFOCUS его надо выключить.

WM_GETTEXT. Сообщение посылается окну как запрос на копирование текста окна в буфер. У большинства окон текст окна – это его заголовок. Для элементов управления вроде кнопок, строк ввода, статического текста и т.п. текст окна – это текст, отображаемый в элементе управления. Это сообщение обычно обрабатывается процедурой по умолчанию DefWindowProc.

WM_SETTEXT. Это сообщение требует, чтобы окно запомнило текст, переданный в буфере, в качестве своего текста. При обработке WM_SETTEXT функцией DefWindowProc выполняется запоминание и отображение текста окна.

 

Приложение с несколькими циклами обработки сообщений

В рассмотренных ранее примерах (т.е., трех версиях hello.cpp) в приложении был только один цикл обработки сообщений. В первой версии hello.cpp он был скрыт в системной функции MessageBox.

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

Аналогичным образом, вы можете реализовать второй (или третий, или четвертый) цикл обработки сообщений тогда, когда на некотором этапе выполнения вашего приложения требуется обрабатывать сообщения иным способом, нежели это делается при нормальном функционировании.

В качестве примера рассмотрим рисование в режиме захвата мыши. В 4-й версии hello.cpp обеспечивается рисование от руки с помощью мыши. Т.е. пользователь может сам написать "Hello, World!" мышью (рис. 2.4).

Приложение обрабатывает события мыши. По нажатию левой кнопки в клиентской области окна происходит захват мыши. Пока мышь захвачена, ее сообщения передаются напрямую в окно, выполнившее захват. Сообщения информируют окно о каждом перемещении мыши. Т.о. приложение может выполнять рисование, соединяя отрезками текущее и предыдущее местоположения указателя мыши.

Освобождение мыши в нашем приложении происходит, когда пользователь отпускает левую кнопку мыши.

В программе 2.4 есть два цикла обработки сообщений, в которых вызывается функция GetMessage. Главный цикл расположен в WinMain, а дополнительный размещен в функции DrawHello.

 

Рис. 2.4. Графическая версия приложения "Hello, World!".

 

#include <windows.h>

 

void AddSegmentAtMessagePos(HDC hDC, HWND hwnd, BOOL bDraw)

{

DWORD dwPos;

POINTS points;

POINT point;

 

dwPos = GetMessagePos();

points = MAKEPOINTS(dwPos);

point.x = points.x;

point.y = points.y;

ScreenToClient(hwnd, &point);

DPtoLP(hDC, &point, 1);

 

if (bDraw)

LineTo(hDC, point.x, point.y);

else

MoveToEx(hDC, point.x, point.y, NULL);

}

 

void DrawHello(HWND hwnd)

{

if (GetCapture()!= NULL)

return;

 

HDC hDC = GetDC(hwnd);

if (hDC!= NULL)

{

SetCapture(hwnd);

AddSegmentAtMessagePos(hDC, hwnd, FALSE);

 

MSG msg;

while(GetMessage(&msg, NULL, 0, 0))

{

if (GetCapture()!= hwnd)

break;

 

switch (msg.message)

{

case WM_MOUSEMOVE:

AddSegmentAtMessagePos(hDC, hwnd, TRUE);

break;

case WM_LBUTTONUP:

goto ExitLoop;

default:

DispatchMessage(&msg);

}

}

 

ExitLoop:

ReleaseCapture();

ReleaseDC(hwnd, hDC);

}

}

 

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg,

WPARAM wParam, LPARAM lParam)

{

switch(uMsg)

{

case WM_LBUTTONDOWN: DrawHello(hwnd); break;

case WM_DESTROY: PostQuitMessage(0); break;

default: return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return 0;

}

 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPSTR d3, int nCmdShow)

{

if (hPrevInstance == NULL)

{

WNDCLASS wndClass;

memset(&wndClass, 0, sizeof(wndClass));

wndClass.style = CS_HREDRAW | CS_VREDRAW;

wndClass.lpfnWndProc = WndProc;

wndClass.hInstance = hInstance;

wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);

wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

wndClass.lpszClassName = "HELLO";

if (!RegisterClass(&wndClass))

return FALSE;

}

 

HWND hwnd;

hwnd = CreateWindow("HELLO", "HELLO", WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,

NULL, NULL, hInstance, NULL);

ShowWindow(hwnd, nCmdShow);

UpdateWindow(hwnd);

 

MSG msg;

while (GetMessage(&msg, NULL, 0, 0))

DispatchMessage(&msg);

 

return msg.wParam;

}

Программа 2.4. Графическая версия приложения "Hello, World!".

 

В предыдущей версии функция DrawHello просто выводила текстовую строку "Hello, World!" в контекст устройства, связанный с окном приложения. В новой версии эта функция устроена сложнее. Сначала она проверяет, не захвачена ли мышь каким-либо окном. Затем функция получает дескриптор контекста устройства, связанного с клиентской областью главного окна приложения. Затем выполняется захват мыши с помощью функции SetCapture. В режиме захвата мыши Windows будет посылать сообщения мыши непосредственно в окно, выполнившее захват.

Функция DrawHello также вызывает вспомогательную функцию AddSegmentAtMessagePos, которая в зависимости от своего третьего логического параметра, или перемещает текущую позицию рисования в точку указателя мыши из последнего сообщения, или рисует в эту точку отрезок из текущей позиции. Чтобы узнать координаты указателя мыши от последнего сообщения, применяется функция GetMessagePos. Функция AddSegmentAtMessagePos выполняет преобразование координат из экранной системы координат, в которой заданы координаты указателя мыши, в логическую систему координат окна, в которой выполняется рисование.

После вызова AddSegmentAtMessagePos функция DrawHello входит в цикл обработки сообщений. Пока мышь захвачена, мы ожидаем особого поведения от приложения, а именно, что траектория мыши будет отображаться внутри окна отрезками. Это обеспечивается функцией AddSegmentAtMessagePos, которая вызывается с третьим параметром TRUE при получении каждого сообщения WM_MOUSEMOVE.

Этот цикл обработки сообщений завершается, когда отпускается левая кнопка мыши, или когда приложение теряет захват мыши по какой-то другой причине. Тогда функция DrawHello возвращает управление и возобновляется выполнение главного цикла обработки сообщений в функции WinMain.

Действительно ли необходимы в этом приложении два цикла обработки сообщений? Вполне возможно обрабатывать сообщения WM_MOUSEMOVE в оконной процедуре, получая их в результате диспетчеризации из главного цикла обработки сообщений. Но предложенная структура программы делает исходный текст более понятным и позволяет избежать излишне громоздкой оконной процедуры.

 

Резюме

Каждое Windows-приложение строится на основе цикла обработки сообщений. В нем выполняется вызов функции для получения сообщения (GetMessage или PeekMessage), и последующая диспетчеризация сообщения в соответствующую оконную процедуру с помощью DispatchMessage.

Оконные процедуры приписываются оконным классам в момент регистрации с помощью функции RegisterClass. Типичная оконная процедура содержит оператор switch с ветвями case для всех сообщений, которые интересуют данное приложение. Остальные сообщения передаются оконной процедуре "по умолчанию" с помощью вызова функции DefWindowProc (или DefDlgProc для диалоговых окон).

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

 

Упражнения.

1) В Visual C++ заведите новый проект типа Win32 Application и добавьте в него исходный файл с текстом программа 2.1. Скомпилируйте и запустите ее.

2) Повторите действия, аналогичные заданию 1), для остальных версий приложения.

3) Во 2-й версии "Hello, World!" попробуйте вместо стандартного класса BUTTON ("Кнопка") создать окно класса STATIC ("Статический текст").

4) Посмотрите разделы справочной системы по функциям API, встречающимся в тексте приложений "Hello, World!".

5) Разместите комментарии, поясняющие отдельные части 4-й версии "Hello, World!". Например, комментарии должны отмечать такие элементы программы, как цикл обработки сообщений, регистрация оконного класса или пояснять смысл отдельных функций API (назначение неизвестных функций API выясните в справочной системе).

6) Выясните, сохраняется ли содержимое окна 3-й и 4-й версии "Hello, World!" при перемещении окна приложения по экрану или при изменении его размеров. Объясните, что происходит.

7) Добавьте в 4-ю версию "Hello, World!" массив для хранения координат точек рисунка (в качестве типа элементов массива удобно использовать структуру из API POINT). Обеспечьте рисование содержимого окна при обработке сообщения WM_PAINT. Теперь при изменении размеров окно не должно очищаться.

Перед написанием функции-обработчика сообщения WM_PAINT проанализируйте содержимое функции DrawHello из п.3 и AddSegmentAtMessagePos из п.4.

8) Добавьте в одну из программ обработчик события WM_CLOSE, который бы запрашивал у пользователя подтверждение о выходе из программы. Для организации запроса можно использовать стандартное окно сообщения:

MessageBox(hwnd, "Вы хотите завершить работу программы?",

"Завершение работы", MB_YESNO | MB_ICONQUESTION)

Этот вызов создает окно сообщения двумя кнопками " Да " и " Нет " и пиктограммой в виде вопросительного знака. В зависимости от нажатой кнопки, функция вернет IDYES или IDNO. Приложение должно завершать работу только при нажатии пользователем кнопки Да.

9) Изучите англо-русский словарь терминов по теме 2-й лекции (см. CD-ROM).



Поделиться:


Последнее изменение этой страницы: 2016-08-01; просмотров: 276; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.141.30.162 (0.045 с.)