Читаем Исчерпывающее руководство по написанию всплывающих подсказок онлайн бесплатно
Исчерпывающее руководство по написанию всплывающих подсказок
Компьютеры и Интернет / Программирование, программы, базы данных / Программирование / Книги по ITВсплывающие подсказки (ToolTips) облегчают использование приложений. Если вам неясно назначение кнопки на панели инструментов (ToolBar), вы просто наводите на нее курсор мыши и ждете появления подсказки. Конечно, с помощью строки состояния можно получить больше информации, но в случае с всплывающими подсказками вам не приходится опускать взгляд вниз окна.
Существуют также другие виды подсказок: TitleTips – для расширения заголовков элементов управления "список" и "древовидный список", и DataTips – для получения дополнительной информации о данных в окне, и всплывающие подсказки для Web-страниц. Эта статья является подробным учебником по использованию подсказок в ваших приложениях, начиная от добавления простых подсказок средствами MFC до написания своих собственных подсказок. По пути я покажу, как добавлять подсказки к вашим Web-страницам, включая всплывающие подсказки для простого ActiveX-элемента "кнопка". Но прежде чем приступать к обсуждению деталей, давайте посмотрим, какую поддержку всплывающих подсказок предоставляют классы MFC.
Библиотека MFC располагает двумя классами для поддержки всплывающих подсказок: CToolTipCtrl и CWnd. CToolTipCtrl инкапсулирует функциональность стандартного элемента управления ToolTip (из библиотеки элементов управления общего назначения – Common Controls DLL) и может, таким образом, использоваться для создания и управления элементом подсказки напрямую. Один элемент ToolTip может поддерживать много инструментов (tools), которые представляют собой прямоугольники в окне, и могут быть (а могут и не быть) дочерними окнами. Один инструмент также может заполнять все окно. Информация об инструменте в некоторых случаях передается в структуре TOOLINFO со следующими полями: хэндл окна, содержащего инструмент, ID или хэндл окна самого инструмента, координаты инструмента (прямоугольник), и информация о тексте для этого инструмента. Один из самых важных методов – это CToolTipCtrl::RelayEvent, который используется для ретрансляции (relay) сообщений мыши элементу ToolTip для обработки. Передача сообщений мыши элементу необходима для того, чтобы ToolTip смог определить момент, когда следует показать или скрыть подсказку. К сожалению, CToolTipCtrl не полностью инкапсулирует функциональность элемента ToolTip. К примеру, CToolTipCtrl::SetDelayTime не поддерживает все допустимые интервалы задержки. Иногда мне приходилось напрямую использовать сообщения и уведомления Windows® из-за подобных ограничений.[1] Имена всех сообщений (messages) элемента ToolTip начинаются с префикса "TTM_", а имена всех уведомлений (notifications) – с префикса "TTN_". Далее я буду много использовать этот класс, поэтому пока что не стану заострять на нем внимание.
Не так давно Microsoft расширила DLL, содержащую элемент ToolTip (comctl32.dll), с выпуском Microsoft® Internet Explorer 4.0 (IE 4.0). Статья в MSJ из двух частей – "Предварительный обзор библиотеки элементов управления общего назначения для Microsoft Internet Explorer" (первая часть которой была опубликована в октябре 1996 года) – прекрасно описывает новые возможности библиотеки. В эти возможности входят пользовательская отрисовка подсказок (owner-draw), многострочные подсказки, подсказки произвольного цвета, а также поддержка подсказок, перемещающихся за мышью. Появилось сообщение TTM_GETDELAYTIME для получения различных значений интервалов задержки и сообщение TTM_POP для скрытия элемента ToolTip. Увы, на тот момент, когда я пишу эти строки, Microsoft еще не добавила поддержку новых возможностей в CToolTipCtrl. Поэтому для примеров в этой статье я вынужден использовать CWnd::SendMessage. (В выходящем скоро Visual Studio файл commctl.h должен содержать все необходимые объявления – ред.)
Класс CWnd представляет базовую поддержку добавления подсказок к окну. Рисунок 1 показывает методы CWnd для поддержки подсказок. CWnd::EnableToolTips разрешает или запрещает подсказки для окна, и должна быть вызвана до вызова других методов. Нужно заметить, что в работе CWnd::EnableToolTips есть недостаток: когда вы передаете CWnd::EnableToolTips значение FALSE, этот метод вызывает еще один метод, который посылает сообщение для деактивации элемента ToolTip. Когда же вы вызываете CWnd::EnableToolTip со значением TRUE, он не активирует ToolTip заново.
Рис.1. Поддержка подсказок классом CWnd
| Метод | Описание |
|---|---|
| BOOL EnableToolTips(BOOL bEnable) | Разрешает или запрещает подсказки для окна |
| virtual int CWnd::OnToolHitTest(CPoint point, TOOLINFO* pTI ) const | Вызывается библиотекой, чтобы определить, не находится ли курсор мыши над инструментом, имеющим подсказку |
| void FilterToolTipMessage(MSG* pMsg) | Проверяет, относится ли сообщение к выводу подсказок |
| static void PASCAL CancelToolTips(BOOL bKeys) | Прячет подсказку, если она показана на экране |
CWnd::OnToolHitTest вызывается непосредственно библиотекой, и вы можете переопределить эту функцию для реализации собственного алгоритма определения контура инструмента. Первый аргумент, point, является координатами курсора в клиентских координатах. Используйте его для сравнения позиции курсора с координатами ваших инструментов (или кнопок). Второй параметр – это уже упоминавшаяся структура TOOLINFO. Далее я покажу, как переопределять функцию CWnd::OnToolHitTest.
CWnd::FilterToolTipMessage обычно вызывается за вас функцией CWnd::PreTranslateMessage. Вы можете вызвать CWnd::FilterToolTipMessage напрямую (обычно из переопределенной PreTranslateMessage), если CWnd::PreTranslateMessage у вас не вызывается. Позже я покажу, как это делается. CWnd::CancelToolTips прячет показанный элемент ToolTip. Параметр bKeys устанавливается в TRUE, чтобы прятать подсказку по нажатию клавиши. Важно осознавать, что, несмотря на статичность функции-члена CWnd::CancelToolTips, она воздействует только на элементы ToolTip, созданные классом CWnd. Другими словами, она не влияет на объекты CToolTipCtrl, которые вы создаете в собственном коде.
На самом деле CWnd реализует подсказки скрытым созданием и манипулированием объектом CToolTipCtrl. CWnd сохраняет указатель на элемент ToolTip в поле m_pToolTip скрытой структуры AFX_THREAD_STATE. Эта структура используется библиотекой MFC для хранения локальной информации потока. CWnd не предоставляет документированного прямого доступа к этому элементу.
Microsoft упростила добавление подсказок к кнопкам на панелях инструментов. Если вы используете AppWizard, этот процесс происходит автоматически. При генерации вашего приложения с помощью AppWizard щелкните флажок "Docking toolbar". После генерации приложения в классе CMainFrame будет присутствовать переменная m_wndToolBar класса CToolBar, которая инициализируется в методе CMainFrame::OnCreate. В класс CToolBar встроена поддержка элементов ToolTip. AppWizard добавляет в файл ресурсов строки, которые CToolBar использует как подсказки для кнопок панели инструментов.
Изменить строки подсказок после генерации приложения просто – найдите панель инструментов в списке ресурсов, откройте двойным щелчком по любой из кнопок на панели диалог свойств кнопки панели инструментов (Toolbar Button Properties) и отредактируйте строку "Prompt" после символа "\n". Например, на рис.2, текстом всплывающей подсказки является "Open". Строка до символа "\n" является текстом, который появляется в строке состояния при наведении на кнопку.
Как я уже упоминал, текст подсказки хранится в таблице строк. ID строки с текстом подсказки равен ID соответствующей кнопки на панели инструментов. Для рис.2 ID строки будет ID_FILE_OPEN. Добавлять подсказки к панели инструментов так просто, что ошибиться практически негде. Единственной проблемой в моей практике было случайное перезаписывание строк в таблице из другого участка кода.
Вы, вероятно, видели диалоги с подсказками для каждого элемента управления на них. Это очень удобно, если предназначение элемента неочевидно из контекста. Статья Q141758 в базе знаний (Knowledge Base) подробно описывает, как реализовать подсказки в MFC-диалогах, поэтому здесь я лишь кратко перечислю основные пункты. Для MFC версии 4.0 и выше, вам придется выполнить следующие шаги (предполагаем, что диалоговое окно в вашем приложении уже существует):
• Добавить private или protected переменную типа CToolTipCtrl в класс вашего диалога.
• Добавить в класс управляющую переменную (control member variable), для каждого элемента, у которого будет подсказка. Это можно сделать с помощью ClassWizard (на закладке Member Variable).
• Переопределить CDialog::OnInitDialog и вызвать в нем CToolTipCtrl::Create. Затем вызвать CToolTipCtrl::AddTool для каждого элемента с подсказкой, передавая адрес управляющей переменной и текст подсказки в качестве параметров.
• Переопределить CDialog::PreTranslateMessage и вызвать в ней CToolTipCtrl::RelayEvent для каждого сообщения, передаваемого в функцию. Это нужно для того, чтобы элемент ToolTip получал все необходимые сообщения мыши.
В MFC версий ниже 4.0 из CDialog::DoModal не вызывается CDialog::PreTranslateMessage, поэтому придется выполнить дополнительные действия для передачи сообщений мыши элементу ToolTip. Нужно переопределить CWinApp::ProcessMessageFilter и в нем передавать сообщения элементу ToolTip. CWinApp::ProcessMessageFilter вызывается хуковой функцией MFC для реакции на определенные сообщения Windows. За деталями, а также рабочими примерами, обратитесь к статье в базе знаний.
Демонстрационный проект WebButton
Как и в приложениях, подсказки на Web-страницах могут быть очень полезны. Подсказки могут быть использованы в двух очевидных случаях: для картинок и элементов ActiveX. Я написал пример элемента "кнопка", чтобы продемонстрировать, как просто добавлять подсказки к элементам ActiveX. На рис.3 показаны и элемент ActiveX – кнопка с улыбающейся рожицей, и картинка – небольшое "художество" прямо под кнопкой.
Добавить подсказку к картинке проще простого, потому что эта функциональность встроена в язык HTML (см. рис.4). Эта строка:
![]()
задает имя и размер картинки. Подстрока "Image ToolTip" и есть текст подсказки, которая появляется при подведении курсора мыши к картинке. Также возможно задать для картинки несколько "активных зон" (HotSpots) и определить несколько подсказок, но это выходит за рамки статьи. Я только хочу показать, насколько просто добавлять подсказки на языке HTML.
Рис.4. HTML-код для подсказки
![]()
Sub WebButton1_Click
MsgBox "WebButton was clicked"
end sub
-->
Элемент ActiveX представляет собой кнопку с подсказкой. Для чего мне возиться с созданием кнопки, когда можно добавить на Web-страницу трехмерную картинку, которая будет выглядеть как кнопка? На это есть две причины. Во-первых, кнопка выглядит реалистичнее – она нажимается и отжимается по щелчку пользователя, как и положено настоящей кнопке. Во-вторых, я хотел показать добавление подсказок к элементам ActiveX, а кнопка – это простейший элемент, который я мог использовать в демонстрационных целях.
Для генерации кода я использовал AppWizard. Я установил флажок "Activate when visible" и отключил все остальные флажки. В опции "Which window class, if any, should this control subclass?" я выбрал BUTTON. AppWizard генерирует массу дополнительного кода, не относящегося к данной статье. В основном, я остановлюсь на коде, добавленном мной в класс CWebButtonCtrl (см. рис.5). Давайте для начала взглянем на пару переменных класса. CWebButtonCtrl::m_bToolTipEnabled устанавливается в TRUE, если подсказки разрешены. В CWebButtonCtrl::m_strToolTipText хранится текст подсказки. Я добавил обе переменные через ClassWizard и они представляют OLE-свойства, автоматические обновляемые библиотекой MFC при их изменении.
Рис.5. CWebButtonCtrl
// WebButtonCtl.cpp : Implementation of the CWebButtonCtrl OLE control class.
/////////////////////////////////////////////////////////////////////////////
// CWebButtonCtrl::RelayToolTipEvent – Pass mouse messages to ToolTip
void CWebButtonCtrl::RelayToolTipEvent(const MSG* pMsg) {
MSG MsgCopy;
::memcpy(&MsgCopy, pMsg, sizeof(MSG));
FilterToolTipMessage(&MsgCopy);
}
int CWebButtonCtrl::OnToolHitTest(CPoint point, TOOLINFO* pTI) const {
if (m_bToolTipEnabled && pTI != NULL && pTI->cbSize >= sizeof(TOOLINFO)) {
// setup the TOOLINFO structure
pTI->hwnd = m_hWnd;
pTI->uId = 0;
pTI->uFlags = 0;
GetClientRect(&(pTI->rect));
pTI->lpszText = LPSTR_TEXTCALLBACK;
}
return (m_bToolTipEnabled ? 1 : –1);
}
/////////////////////////////////////////////////////////////////////////////
// CWebButtonCtrl message handlers
int CWebButtonCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) {
if (COleControl::OnCreate(lpCreateStruct) == –1) {
return –1;
}
if (m_Bitmap.LoadBitmap(IDB_WEBBUTTON)) {




