Пользователи, работающие с Windows-программами, привыкли, что в строке состояния отображается информация о назначении выбранного ими пункта меню. Меню посылает сообщение WM_MENUSELECT, когда пользователь осуществляет навигацию по его пунктам с помощью мыши или клавиатуры. Поэтому для поддержки отображения справочной информации необходимо обрабатывать это сообщение.
Когда оконная процедура получает сообщение WM_MENUSELECT, из его параметров можно извлечь следующую информацию:
Младшее слово параметра wParam содержит идентификатор пункта меню, если это пункт-команда, или индекс подменю (если это пункт-подменю).
Старшее слово параметра wParam содержит один из флагов, характеризующих статус выбранного пункта меню. Из всех возможных флагов сейчас нас будет интересовать только флаг MF_P0PUP, означающий, что данный пункт открывает всплывающее меню или подменю.
Параметр IParam содержит дескриптор меню, которому принадлежит выбранный пункт.
Win32 API содержит функцию MenuHelp, предназначенную для упрощения обработки сообщений WM_MENUSELECT и отображения текста в строке состояния. Технология использования этой функции описана в [1]. Однако, на мой взгляд, зта технология довольно громоздка и неудобна. Здесь предлагается альтернативная технология, в которой функция MenuHelp не применяется.
Ранее мы уже убедились, что бывает очень удобно использовать ресурс таблицы строк при отображении окон подсказок для кнопок панели инструментов. Простота решения базировалась на совпадении идентификаторов кнопок и идентификаторов строк.
Хотелось бы использовать аналогичную идею и для решения рассматриваемой проблемы. Однако если пункты-команды в меню имеют уникальные идентификаторы и мы можем использовать эти же идентификаторы для строк в таблице строк, то с пунктами-подменю ситуация гораздо сложней. В пределах меню каждого уровня пункты-подменю нумеруются начиная с нуля, поэтому ни о какой уникальности не может быть и речи.
Идея решения, предлагаемая здесь, базируется на некоторой договоренности о системе идентификации таких пунктов меню. Сразу уточним, что мы рассматриваем только те меню, которые имеют не более трех уровней. Нулевой уровень — это меню верхнего уровня, подменю в нем имеет первый уровень, а подменю в меню первого уровня получает второй уровень. Большинство Windows-программ удовлетворяет этому ограничению.
Так же как и панель инструментов, строка состояния занимает часть клиентской области главного окна приложения. К сожалению, Windows не корректирует автоматически размеры и начало координат клиентской области при появлении панели инструментов и строки состояния. В приложении ToolBar мы вычисляли прямоугольник, ограничивающий видимую часть клиентской области, с учетом высоты панели инструментов tbHeight. Точно так же при использовании строки состояния необходимо учитывать уменьшение высоты видимой части клиентской области на величину, равную высоте строки состояния.
Оконная процедура дочернего окна строки состояния автоматически устанавливает начальную позицию и размеры этого элемента управления. Ширина строки состояния равна ширине клиентской области родительского окна. Высота строки состояния устанавливается на основе метрик шрифта, выбранного по умолчанию в контекст устройства элемента управления.
В дальнейшем оконная процедура строки состояния автоматически регулирует ее позицию и ширину всякий раз, когда получает сообщение WM_SLZE. Из этого вытекает, что при каждом изменении размеров родительского окна, то есть при получении сообщения WM_SIZE, оконная процедура WndProc должна отправить строке состояния такое же сообщение, передав текущие значения параметров wParam и IParam:
SendMessage (hwndStatusBar, WMJ5IZE, wParam, IParam):
Если высота строки состояния по умолчанию вас не устраивает, вы можете ее изменить, послав сообщение SB_SETMINHEIGHT:
SendMessageChwndStatusBar, SB_SETMINHEIGHT. minHeight, 0); где minHeight — минимальная высота окна строки состояния в пикселах. На самом деле Windows устанавливает высоту окна строки состояния, равной minHeight + + 2wVB, где wVB — ширина вертикальной рамки окна.
Возможность увеличить высоту строки состояния бывает полезной, если на ней размещены другие дочерние окна элементов управления (см. ниже приложение ProgressBar).
Для отображения текста в строке состояния посылается сообщение SB_SETTEXT:
SendMessage(hwndStatusBar, SB_SETTEXT. wParam, (LPARAM)szText); где szText — указатель на С-строку, a wParam задает номер поля в строке состояния и графический стиль для этого поля.
Значение параметра wParam может быть задано в виде объединения iPart | uType.
Если строка состояния работает в стандартном режиме, то iPart содержит номер поля. Если используется простой режим, то параметр iPart должен иметь значение 255.
Если строка состояния используется в стандартном режиме, то вы должны разделить ее на отдельные поля при помощи отсылки сообщения SB_SETPARTS:
SendMessage(hwndStatusBar, SB_SETPARTS, nParts. (LPARAM)aWidths);
Здесь параметр nParts задает число полей, a aWidths — адрес целочисленного массива, каждый элемент которого определяет позицию (в клиентских координатах) правой границы соответствующего поля. Если элемент массива aWidths равен -1, то границей соответствующего поля считается правая граница строки состояния.
Обычно в каждое поле строки состояния выводится отдельное текстовое сообщение. Но вы можете также разместить в любом поле другой элемент управления, например индикатор процесса (Progress bar). При таком размещении полезно знать клиентские координаты этого поля, которые могут быть получены при помощи отправки сообщения SB_GETRECT:
SendMessage(hwndStatusBar. SB_GETRECT. iPart. (LPARAM)&rect); где iPart — номер поля, отсчитываемый от нуля, a rect — структура типа RECT, принимающая координаты поля.
Вернемся к нашей последней программе, в которой комбинированный список, встроенный в панель инструментов, не имел всплывающей подсказки. Попробуем устранить этот недостаток.
Одновременно заменим хорошо послуживший нам класс KWnd на его модификацию — класс KWndEx. Цель замены — обеспечить регистрацию оконных классов для элементов управления общего пользования в конструкторе класса, чтобы освободить программиста от этой рутинной операции. Кроме того, в файлах KWnd Ex. h, KWndEx.cpp будут размещены также интерфейс и реализация функций общего применения.
В состав функций общего применения войдут следующие функции: перегруженная функция ShiftWindow, предназначенная для сдвига и модификации размеров окон (эта функция использовалась ранее в листинге 7.5);
функция AddTooltip, добавляющая окно подсказки к указанному окну;
функция TRACE, которую можно использовать для отладочного вывода в окно Output (аналогично макросу TRACE в библиотеке MFC).
Все во имя программиста, все для блага программиста!
Создайте новый проект с именем ToolTip. Скопируйте из папки проекта ComboInToolbar в папку проекта ToolTip файлы с расширениями .срр, .h и .гс, скорректировав их имена заменой подстроки ComboInToolbar на ToolTip. Скопируйте также файл toolbarl.bmp. Измените имена файлов Kwnd.h, Kwnd.cpp на KwndEx.h и KwndEx.cpp соответственно. Добавьте все перечисленные файлы в состав проекта. Также к настройкам проекта на вкладке Link надо добавить библиотеку comctl32.lib.
Откройте вкладку ResourceView в окне Workspace. В списке ресурсов откройте папку String table и вызовите редактор таблицы строк двойным щелчком мыши на элементе String table.
Обратите внимание на то, что потребовалось указать две строки, чтобы элемент управления Combo box получил все необходимые подсказки. Первый текст подсказки будет отображаться при наведении курсора мыши на стрелку, используемую для открывания списка, второй текст подсказки — при наведении курсора мыши на окно редактирования.
В отличие от встроенного на панель инструментов элемента управления Tooltip, автономный элемент управления Tooltip создается в явном виде как окно предопределенного оконного класса T00LTIPS_CLASS при помощи функции CreateWindowEx. В дальнейшем изложении, употребляя термин «окно подсказки», мы будем говорить именно об автономном элементе управления Tooltip.
Элемент управления Tooltip всегда имеет стили WS_EX_TOPMOST и WS_POPUP, независимо от того, указаны ли они при вызове функции CreateWindowEx. Дополнительно можно использовать стиль TTS_NОРREFIX, означающий, что символ амперсанда (&) не является префиксом для обозначения «горячей» клавиши в меню и выводится в строке подсказки наравне с другими символами. Также можно использовать стиль TTS_ALWAYSTIP, означающий, что окно подсказки появляется при наведении курсора мыши на инструмент даже тогда, когда окно-владелец подсказки неактивно.
Напомним, что элемент управления Tooltip может хранить список «горячих» областей или инструментов {tool). Каждый инструмент — это некоторая прямоугольная область в обслуживаемом окне (в пределе — все окно).
Панель инструментов поддерживает только кнопки, поэтому для расположения на ее поверхности какого-либо другого элемента управления следует создать дочернее окно. Наиболее часто на панель инструментов добавляются комбинированные списки (combo box).
Добавление дочернего окна элемента управления на панель инструментов связано с решением трех проблем: а) резервирование места для дочернего окна; б) обработка сообщений от дочернего окна; в) поддержка подсказки для дочернего окна.
Резервирование места под встроенный элемент управления несложно обеспечить, поместив кнопку-разделитель требуемой ширины. Напомним, что фактическая ширина кнопки-разделителя задается присваиванием нужного значения полю tbb[i].iBitmap. Обычно это значение подбирается экспериментально, исходя из желательной ширины встраиваемого элемента управления.
Уведомительные сообщения от встроенного элемента управления поступают в виде сообщений WM_COMMAND в родительское окно панели инструментов. Так как оконная процедура панели инструментов (спрятанная в недрах Windows) эти сообщения не обрабатывает, она передает их родительскому окну панели инструментов, то есть главному окну приложения. Поэтому в блоке обработки сообщения WM_COMMAND оконной процедуры WndProc необходимо предусмотреть обработку этих уведомительных сообщений.
Мы уже знаем, что панель инструментов поддерживает окна подсказок для всех своих кнопок. Но на встроенные элементы управления эта поддержка не распространяется. В следующем разделе будет показано, как решить эту проблему с помощью автономных элементов управления Tooltip.
Рассмотрим технику добавления комбинированного списка на панель инструментов на примере разработки приложения ComboInTooLbar, которое является модификацией приложения ToolBar. Цель модификации — добавить возможность выбора толщины пера, которым обводится контур рисуемой фигуры (до сих пор использовалось перо по умолчанию толщиной в 1 пиксел).
Создайте новый проект с именем ComboInTooLbar. Затем скопируйте из папки проекта ToolBar (см. листинг 8.1) в папку проекта ComboInTooLbar файлы с расширениями .срр, .h и .гс, скорректировав их имена заменой подстроки ToolBar на ComboInTooLbar. Скопируйте также файл toolbarl.bmp. Добавьте эти файлы в состав проекта. Добавьте к настройкам проекта на вкладке Link библиотеку comctl32.lib.
Откройте вкладку ResourceView в окне Workspace и вызовите редактор панелей инструментов двойным щелчком мыши на элементе IDR_T00LBAR1. Добавьте в начале панели инструментов две кнопки без изображений с идентификаторами ID_SEP.
Ранее говорилось, что кнопки панели инструментов помимо растровых изображений могут содержать и текстовые метки. Элемент управления Toolbar имеет встроенный список текстовых строк, который можно заполнять, отправляя элементу сообщения TB_ADDSTRING. Кроме того, при инициализации массива tbb необходимо присвоить полю tbb[i].iString соответствующий индекс (удобнее всего, если этот индекс будет совпадать с индексом изображения).
Чтобы посмотреть, как все это работает, сделаем небольшую доработку предыдущей программы. Создайте новый проект с именем ToolBarWithText. Скопируйте из папки проекта ToolBar (см. листинг 8.1) в папку проекта ToolBarWithText файлы с расширениями .срр, .h и .гс, скорректировав их имена заменой подстроки ToolBar на ToolBarWithText. Скопируйте также файл toolbarl.bmp. Добавьте эти файлы в состав проекта. Также добавьте к настройкам проекта на вкладке Link библиотеку comctl32.lib.
Окно подсказки (Tooltip) — это маленькое окно, содержащее текст подсказки. Обычно подсказка всплывает, когда курсор мыши оказывается в «горячей» (hot) области, обслуживаемой данным элементом управления, задерживаясь в ней на некоторое время.
Элемент управления Tooltip хранит список «горячих» областей, которые для него ассоциируются с инструментами (tool). Каждая «горячая» область определяется как некоторая прямоугольная область в обслуживаемом окне или относится к окну целиком.
Существует две категории окон подсказки: автономные элементы управления Tooltip, создаваемые при помощи функции
CreateWindowEx как окна предопределенного оконного класса ТО О LTI PS_C LASS;
элементы управления Tooltip, встроенные в панель инструментов.
Техника работы с ними различна. Первую категорию мы рассмотрим позже. Сейчас нас интересуют подсказки, встроенные в панель инструментов.
Если при создании панели инструментов указан стиль TBSTYLE_TOOLTIPS, то каждая кнопка, добавляемая в панель инструментов, регистрирует инструмент подсказки. Когда подсказка становится активной, она посылает сообщение WM_N0TIFY с кодом уведомления WM_NEEDTEXT. Панель инструментов передает это сообщение своему родительскому окну, которое должно его обработать, если необходимо отобразить на экране текст подсказки.