Существует два способа передачи сообщений

Тема: Работа с элементами управления в Windows-программах

Стандартные дочерние окна управления имеют вид кнопок, флажков, окон редактирования, списков, комбинированных списков, строк текста и полос прокрутки. Приложению нет необходимости беспокоиться о логике обработки мыши этими окнами, или о логике их отрисовки. Все это делается в Windows, а все, что остается приложению – это обрабатывать сообщение WM_COMMAND, которыми дочерние окна информируют оконную процедуру о различных событиях.

Сообщения дочерних окон родительскому окну

Если орган управления изменяет свое состояние, то функция родительского окна получает сообщение WM_COMMAND. Вместе с сообщением оконная процедура получает и дополнительную информацию. Эти дополнительные параметры имеют следующий смысл:

LOWORD(wParam) - идентификатор дочернего окна (тип UINT);

HIWORD(wParam) - код уведомления (тип UINT);

lParam - дескриптор дочернего окна (тип HWND).

  • Идентификатор дочернего окна – это значение, передаваемое функции CreateWindow, когда создается рабочее окно.
  • Дескриптор дочернего окна – это значение, которое Windows возвращает при вызове функции CreateWindow.
  • Код уведомления – это дополнительный код, который дочернее окно использует для того, чтобы сообщить родительскому окну более точные сведения о сообщении.

Константы, идентифицирующие различные коды уведомления, определены в заголовочных файлах Windows и имеют соответственно следующие префиксы: BN_- “button”, EN_- “edit”, LBN_- “listbox”, CBN_- “combobox” и SB_- “scrollbar”.

Рассмотрим в качестве примера фрагмент оконной процедуры родительского окна, в котором происходит обработка нажатия на дочернее окно-кнопку с идентификатором ID_button:

case WM_COMMAND:

{ UINT idCtl=LOWORD(wParam); // идентификатор дочернего окна

UINT code=HIWORD(wParam); // код уведомления

HWND hChild=(HWND)lParam; // дескриптор дочернего окна

if(idCtl==ID_button&&code==BN_CLICKED)

{

// кнопка была нажата

CloseWindow(hWnd); // закрыть окно-родителя

}

}; return 0;

Сообщения родительского окна дочерним окнам

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

  • Передать можно как обычное оконное сообщение (с префиксом WM_), так и специфические для каждого типа элемента управления

Константы, идентифицирующие различные сообщения для дочерних окон управления, определены в заголовочных файлах Windows и имеют соответственно следующие префиксы: BM_- “button”, EM_- “edit”, LB_- “listbox”, CB_- “combobox”. Для работы с окнами класса “scrollbar” применяются специальные Set/Get-функции WinAPI.

Существует два способа передачи сообщений.

  • Первый способ передачи сообщений - запись сообщения в очередь приложения. Он основан на использовании функции PostMessage. Эта функция помещает сообщение в очередь сообщений для окна, указанного в параметрах, и сразу возвращает управление. Записанное при помощи функции PostMessage сообщение будет выбрано и обработано в цикле обработки сообщений.
  • Второй способ передачи сообщений - непосредственная передача сообщения функции окна, минуя очередь сообщений. Этот метод реализуется функцией SendMessage. В отличие от функции PostMessage функция SendMessage вызывает функцию окна и возвращает управление только после возврата из функции окна, которому передано сообщение.

Каждое дочернее окно имеет дескриптор окна (тип HWND) и идентификатор окна (тип UINT), которые являются уникальными среди других. Знание одного из этих элементов позволяет приложению получить другой.

  • Если известен дескриптор hWndChild дочернего окна, то можно получитьего идентификатор:

UINT id=GetWindowLong(hWndChild,GWL_ID);

  • Также можно использовать другую функцию (хотя часть “Dlg” имени функции относится к окну диалога, на самом деле это функция общего назначения):

UINT id=GetDlgCtrlID(hWndChild);

  • Зная идентификатор id дочернего окна, можно получить его дескриптор:

HWND hWndChild=GetDlgItem(hWndParent,id);

Рассмотрим пример сообщения родительского окна hWnd дочернему окну редактирования класса “edit”. Окну редактирования c дескриптором hWndEdit передается сообщение об установке максимального количества вводимых символов (5 символов):

static UINT ID_edit=3;

static HWND hWndEdit;

. . .

hWndEdit=CreateWindow("edit",NULL,

WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS| WS_BORDER|ES_LEFT,

x,y,width,height,hWnd,(HMENU)ID_edit,hInst,NULL);

. . .

SendMessage(hWndEdit,EM_LIMITTEXT,5,0L);

. . .

Дочерние окна и фокус ввода

Дочерние элементы управления получают фокус при щелчке мыши на них. Когда дочерние окна управления получают фокус ввода, родительское окно теряет его; весь ввод с клавиатуры направляется теперь не на родительское окно, а на дочернее окно управления.

  • Такая ситуация создает очевидную проблему: приложение теряет контроль над обработкой сообщений от клавиатуры.

Примером является то, что перекрывающееся окно не имеет возможности обеспечить переход пользователя от элемента управления к элементу управления с помощью клавиш <Tab> и <Shift+Tab>.

Решение этой проблемы состоит в использовании приема, называемого созданием подкласса окна(window subclassing, разбиение на подклассы, установка новой оконной процедуры).

  • Оконная процедура дочерних окон управления находится в недрах Windows. Однако, можно получить адрес этой оконной процедуры с помощью вызова GetWindowLong, в котором в качестве параметра используется идентификатор GWL_WNDPROC.

Вызывая функцию SetWindowLong можно не только получить адрес оконной процедуры дочернего окна управления, но и установить новую оконную процедуру.

  • Это очень мощный прием, он позволяет “влезть” в существующие внутриWindows оконные процедуры, обработать некоторые сообщения специфическим для приложения способом, а все остальные сообщения оставить прежней оконной процедуре.

Так, например, новые оконные процедуры дочерних окон управления при получении сообщений о нажатии клавиш <Tab> и <Shift+Tab> могут просто передать фокус ввода функцией SetFocusследующему (или предыдущему) дочернему окну управления. Для обработки же остальных сообщений они могут вызвать старые оконные процедуры соответствующих окон управления с помощью функции CallWindowProc.

Кнопки различных стилей (класс button)

Родительское окно будет получать от кнопки сообщение WM_COMMAND с кодом уведомления BN_CLICKED. Этим сообщением кнопка информирует родительское окно о том, что с ней что-то сделали. Для обработки сообщения оконная функция родительского окна может содержать код следующего вида:

case WM_COMMAND:

{ UINT idCtl=LOWORD(wParam); // идентификатор дочер. окна

UINT code=HIWORD(wParam); // код уведомления

HWND hChild=(HWND)lParam; // дескриптор дочер. окна

if(idCtrl==ID_button&&code==BN_CLICKED)

{

// сообщение о том, что нажата кнопка

// с идентификатором ID_button

. . .

}

}; return 0;

Стиль кнопки Внешний вид Описание
BS_3STATE Существует два способа передачи сообщений - student2.ru Переключатель, который может находится в одном из трех состояний: включенном (квадратик перечеркнут), выключенном (квадратик не перечеркнут), неактивном (квадратик отображается серым цветом)
BS_AUTO3STATE Существует два способа передачи сообщений - student2.ru Аналогично стилю BS_3STATE, но внешний вид кнопки изменяется автоматически при ее переключении
BS_AUTOCHECKBOX Существует два способа передачи сообщений - student2.ru Переключатель, который может находиться в одном из двух состояний: включенном или выключенном. Внешний вид кнопки изменяется автоматически при ее переключении
BS_AUTORADIOBUTTON Существует два способа передачи сообщений - student2.ru Переключатель, который может находиться в одном из двух состояний: включенном (внутри окружности имеется жирная черная точка) или выключенном (окружность не закрашена). Внешний вид кнопки изменяется автоматически при ее переключении
BS_CHECKBOX Существует два способа передачи сообщений - student2.ru Переключатель, который может находиться в одном из двух состояний: включенном или выключенном.
BS_DEFPUSHBUTTON Существует два способа передачи сообщений - student2.ru Стандартная кнопка с толстой рамкой вокруг
BS_GROUPBOX Существует два способа передачи сообщений - student2.ru Прямоугольная область, внутри которой могут находиться другие кнопки. Обычно используется в диалоговых панелях. Этот орган управления не воспринимает сообщения от мыши или клавиатуры
BS_LEFTTEXT Существует два способа передачи сообщений - student2.ru Этот стиль указывается вместе с другими и означает, что текст, расположенный около кнопки, должен находиться слева, а не справа от кнопки
BS_OWNERDRAW Внешний вид определяется родительским окном Внешний вид кнопки определяется родительским окном, которое само рисует кнопку во включенном, выключенном или неактивном состоянии
BS_PUSHBUTTON Существует два способа передачи сообщений - student2.ru Стандартная кнопка без рамки
BS_RADIOBUTTON Существует два способа передачи сообщений - student2.ru Переключатель, который может находиться в одном из двух состояний: включенном или выключенном.
BS_USERBUTTON Внешний вид определяется родительским окном Устаревший стиль, аналогичный по назначению стилю BS_OWNERDRAW. Не рекомендуется к использованию. Этот стиль не описан в документации SDK для Windows версии 3.1, но определен в файле windows.h

Родительские окна могут посылать следующие сообщения кнопкам:

  • BM_GETCHECK и BM_SETCHECK - для установки и снятия меток типа “включено/выключено” флажков-переключателей и радио-переключателей;
  • BM_GETSTATE и BM_SETSTATE - для установки состояния “нажата/отпущена” всех типов кнопок;
  • BM_SETSTYLE - для изменения стиля любой кнопки после ее создания.

Следует заметить, что

  • Только нажимаемые кнопки и кнопки, определяемые пользователем, посылают своему родительскому окну сообщение WM_CTLCOLORBTN.
  • Кроме того, только кнопки, определяемые пользователем, реагируют на обработку сообщения родительским окном, используя кисть для закрашивания фона.

А это совершенно бесполезно, поскольку за рисование кнопок, определяемых пользователем, и так отвечает родительское окно.

Нажимаемые кнопки

  • Нажимаемые кнопки (push buttons) представляют собой прямоугольник, внутри которого находится текст, заданный в параметре текста окна функции CreateWindow.

Нажимаемые кнопки управления используются в основном для запуска немедленного действиябез сохранения какой-либо индикации кнопки типа “включено/выключено”. Эти два типа нажимаемых кнопок управления имеют стили, которые называются BS_PUSHBUTTONи BS_DEFPUSHBUTTON (символы “DEF” означают “по умолчанию – default”).

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

Когда курсор мыши находится на нажимаемой кнопке и левая клавиша мыши нажата, то кнопка перерисовывается так, чтобы выглядеть нажатой. 0

Отпускание клавиши мыши, когда курсор мыши находится на нажимаемой кнопке, приводит к восстановлению облика кнопки и посылке родительскому окну сообщения WM_COMMAND с кодом нотификации BN_CLICKED.

Приложение может имитировать нажатие кнопки, посылая окну сообщение BM_SETSTATE. Следующий оператор приводит к “нажатию” кнопки:

SendMessage(hWndButton,BM_SETSTATE,1,0l); // wParam=1 – нажата

Следующий вызов заставляет кнопку вернуться к своему нормальному состоянию:

SendMessage(hWndButton,BM_SETSTATE,0,0l); // wParam=0 – отпущена

Также можно послать нажимаемой кнопке сообщение BM_GETSTATE. Дочерняя кнопка управления возвращает текущее состояние – TRUE, если кнопка нажата и FALSE (или 0), если она в обычном состоянии:

int press= SendMessage(hWndButton,BM_GETSTATE,0,0l);

if(press) { /* кнопка нажата */ }

else { /* кнопка в нормальном состоянии */ }

Замечание.Поскольку нажимаемая кнопка не сохраняет информацию о своем положении типа “включено/выключено”, сообщения BM_GETCHECK и BM_SETCHECK не используются.

Флажки-переключатели

  • Флажки (check boxes) представляют собой маленькие квадратные окна с размещенным обычно справа от окна текстом (если при создании кнопки используется стиль BS_LEFTTEXT, то текст окажется слева).

Флажки, как правило, действуют как двухпозиционные переключатели: один щелчок вызывает появление контрольной метки (состояние “включено”); другой щелчок приводит к исчезновению этой метки (состояние “выключено”).

  • В приложениях флажки обычно объединяются, что дает пользователю возможность установить опции.Двумя наиболее используемыми стилями для флажков являются BS_CHECKBOX и BS_AUTOCHECKBOX.

При использовании стиля BS_CHECKBOX приложение само должно устанавливать контрольную метку, посылая сообщение BM_SETCHECK. В этом случае обработка родительским окномсообщения WM_COMMAND с кодом нотификации BN_CLICKEDот флажка с идентификатором ID_button могла бы быть следующей:

case WM_COMMAND:

{ UINT idCtl=LOWORD(wParam); // идентификатор дочер. окна

UINT code=HIWORD(wParam); // код уведомления

HWND hChild=(HWND)lParam; // дескриптор дочер. окна

if(idCtrl==ID_button&&code==BN_CLICKED)

{

int cur_label= SendMessage(hWndButton,BM_GETCHECK,0,0l);

SendMessage(hChild,BM_SETCHECK,(WPARAM)(!cur_label),0l);

}

}; return 0;

При стиле BS_AUTOCHECKBOX флажок самостоятельно включает и выключает контрольную метку, и оконная процедура родительского окна может игнорировать сообщения WM_COMMAND.

Если приложению необходимо инициализировать флажок меткой (установить состояние “включено”), то ему следует послать сообщение BM_SETCHECK c параметром wParam, равным 1 (значение 0 снимает с флажка метку):

SendMessage(hWndButton,BM_SETCHECK,1,0l);

Если необходимо узнать текущее состояние флажка, то для этого можно послать сообщение BM_GETCHECK:

int iCheck=SendMessage(hWndButton,BM_GETCHECK,0,0l);

Полученное значение равно TRUE (не равно 0), если флажок отмечен (“включен”) или FALSE (или 0), если не отмечен.

Двумя другими стилями флажков являются BS_3STATE и BS_AUTO3STATE. Как показывают их имена, эти стили могут отображать третье состояние – серый цвет внутри окна флажка – которое имеет место, когда такому флажку посылается сообщение BM_SETCHECK с параметром, равным 2.

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

Радио-переключатели

  • Радио-переключатели (radio buttons, радио-кнопки) похожи на флажки, но их форма не квадратная, а круглая. Жирная точка внутри флажка показывает, что переключатель отмечен.

Радио-кнопка может иметь стиль окна BS_RADIOBUTTON или BS_AUTORADIOBUTTON, но последний используется только в окнах диалога. В окнах диалога группы радио-переключателей, как правило, используются для индикации нескольких взаимоисключающих опций.В отличие от флажков, если повторно щелкнуть на радио-кнопке, то ее состояние не изменится.

При получении сообщения WM_COMMAND с кодом нотификации BN_CLICKED от радио-переключателя с идентификатором ID_button, необходимо отобразить его отметку, отправив сообщение BM_SETCHECK с параметром wParam, равным 1. Для остальных переключателей этой группы можно отключить контрольную метку, послав сообщение BM_SETCHECK с параметром wParam, равным 0:

case WM_COMMAND:

{ UINT idCtl=LOWORD(wParam); // идентификатор дочер. окна

UINT code=HIWORD(wParam); // код уведомления

HWND hChild=(HWND)lParam; // дескриптор дочер. окна

if(idCtrl==ID_button&&code==BN_CLICKED)

{

SendMessage(hChild,BM_SETCHECK,1,0l);

// для всех остальных радиопереключателей группы - снять метки

SendMessage(hWndOtherButton,BM_SETCHECK,0,0l);

. . .

}

}; return 0;

Статические поля (класс static)

  • Статическое дочернее окно управления - это окно, создаваемое на базе предопределенного класса ”static”.

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

  • Все сообщения от мыши через “прозрачное” окно статического дочернего окна попадают в родительское окно.

Для создания статического дочернего окна необходимо использовать функцию CreateWindow. В качестве первого параметра следует указать класс окна “static”, например:

static UINT ID_static=2;

static HWND hWndStatic;

. . .

hWndStatic=CreateWindow("static",NULL,

WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS| SS_GRAYRECT,

x,y,width,height,

hWndParent,(HMENU)ID_static,hInst,NULL);

Наши рекомендации