| « Поставить закладку » « Сделать стартовой » | |||
|
|||
|
Глава 11. Вводим элементы пользовательского интерфейса
Глава 11.
Начиная с этой главы, мы с вами начнем разработку нового приложения — простейшего графического редактора, которое позволит нам познакомиться со следующими вопросами: 1. Создание панелей инструментов и работа с ними. 2. Создание строки состояния и работа с ней. 3. Динамическое изменение курсора. 4. Вычерчивание геометрических фигур с помощью мыши. 5. Работа с меню. Итак, прежде всего необходим каркас приложения. Те из вас, кто работает с Visual C++ 6.0, должны создать стандартное MFC-приложение, отказавшись от поддержки архитектуры "документ/представление". Для тех же читателей, которые используют Visual C++ 5.0, я подготовил специального мастера — MyWizard4, чтобы не начинать все практически с нуля. Примечание Внешний вид приложений, созданных стандартным мастером Visual C++ 6.0 и моим, несколько отличается друг от друга, но это никак не повлияет на работу приложения Прежде всего посмотрите на состав проекта (рис. 11.1) и убедитесь, что основные изменения, по сравнению с нашим предыдущим приложением, коснулись ресурсов. Добавилось меню и панель инструментов. Вопросы, связанные с меню, мы частично (на необходимом на данный момент уровне) уже рассматривали и будем еще развивать эту тему в дальнейшем. Сейчас же займемся панелями инструментов. Работа с панелями инструментов Любое современное профессионально написанное приложение имеет в своем главном окне как минимум два дополнительных инструментальных средства, значительно облегчающих работу — это панель инструментов (Toolbar) и строка состояния (Statusbar). И хотя в библиотеке MFC для этих, а также некоторых 'других, элементов давно реализованы соответствующие классы, только 32-разрядные операционные системы Windows 95/98 и Windows NT обеспечивают со своей стороны поддержку этих элементов пользовательского интерфейса. На самом деле создать панель управления не сложнее, чем обыкновенное окно, тем более используя классы MFC. Давайте подробнее познакомимся с этими удобными и полезными компонентами приложения.
Рис. 11.1. В приложении появились новые ресурсы Для того чтобы можно было работать с панелью инструментов, ее необходимо прежде всего создать. Сделать это очень просто (особенно, если вы умеете рисовать). Щелкните правой кнопкой мыши на папке Toolbar в Resource View, а затем выберите из контекстного меню Insert Toolbar (Вставить панель инструментов), и Visual C++ автоматически создаст новую панель инструментов. Выведите на экран окно свойств панели инструментов и в поле ID введите какой-либо содержательный идентификатор (рис. 11.2).
Рис. 11.2. Свойства панели инструментов Теперь осталось заполнить панель кнопками. Сделать это несложно — выделите "пустую" кнопку и творите. Вот что получилось у меня (рис. 11.3). После того как кнопка "нарисована", необходимо присвоить ей идентификатор. Для этого дважды щелкните на кнопке, чтобы вывести окно свойств, и в нем введите необходимую информацию, например, так, как это показано на рис. 11.4. Вот и все. Новая панель инструментов создана, и каждой кнопке присвоен идентификатор, интерпретируемый как команда — совершенно аналогично команде меню.
Рис. 11.3. Пример панели инструментов рисования
Рис. 11.4. Свойства кнопки панели инструментов Теперь необходимо добавить код, который создаст объект; Windows для панели инструментов. Как вы помните, панель инструментов Лучше всего ассоциировать с классом CMainFrame. Библиотека MFC для панелей инструментов предлагает два класса, являющихся производными от класса CControlBar (рис. 11.5). Мы кратко рассмотрим их в следующем разделе.
Рис. 11.5. Иерархия объектов инструментов
Этот класс определяет поведение панели инструментов (toolbar) — панели управления, отображающей полосу растровых кнопок (все они должны быть одного размера — стандартно 23x22 пиксела) и разделителей. Кнопки активизируют команды, т. е. нажатие на кнопку панели инструментов подобно выбору элемента меню. Так же, как и элементы меню, кнопки панели инструментов могут действовать аналогично обычным кнопкам, переключателям и флажкам. Как и любая другая, панель инструментов либо привязана к какой-либо стороне родительского фрейма, либо является плавающей. В последнем случае пользователь может изменять ее размер. Класс CToolBar поддерживает и работу с всплывающей подсказкой — маленьким окном, содержащим текстовую строку. Для изображения всех кнопок панели инструментов создается один битовый массив, который должен содержать один и только один образ для каждой кнопки. Обычно порядок расположения образов совпадает с тем, как они отображаются на экране. Все образы должны иметь один и тот же размер (обычно 16x15 пикселов) и располагаться рядом друг с другом без промежутков. Изменение, изображения кнопок панели инструментов в зависимости от их состояния и стиля (нажатая, заблокированная, неопределенная, отжатая и т. п.) формируется автоматически благодаря механизму ON_UPDATE_COMMAND_UI: во время цикла простоя (idle time) панель инструментов вызывает обработчик команды ON_UPDATE_COMMAND_UI с идентификаторами команд для всех своих кнопок (исключая разделители). Для панелей инструментов класс CCmdUI, обеспечивающий правильное функционирование обработчика, поддерживает следующие функции: CCmdUI::Enable, CCmdUI::SetCheck и CCmdUI::SetRadio. Теперь, когда у вас сложилось общее представление о панели инструментов, мы, как обычно, рассмотрим основные функциональные возможности, предоставляемые классом CToolBar. Поскольку панель инструментов — это окно, то для ее создания необходимо проделать стандартные шаги: 1. Создать объект класса (в классе CMainFrame)
Конструктор класса
создает объект класса и устанавливает размеры по умолчанию: размер кнопок — 23x22, размер рисунков на них (образов) — 16x15, высота верхней и нижней рамки — 3 пиксела. 2. На базе предопределенного оконного класса TOOLBARCLASSNAME создать окно Windows. Сделать это лучше всего в обработчике OnCreate сообщения WM_CREATE класса CMainFrame: if (!m_wndPaneBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_SIZE_FIXED | CBRSJTOP | CBRSJTOOLTIPS) | | !m_wndPaneBar.LoadToolBar(IDR_PANEBAR)) TRACED("Ошибка при создании панели инструментов рисованияn")); return -1; // при создании произошла ошибка Примечание Пусть вас не пугают слова "... создать на базе предопределенного оконного класса TOOLBARCLASSNAME...". Они приведены только для информации — ничего специально делать не надо. Фактически мы создаем новое окно, а о том, как это делается, мы говорили уже достаточно. Для создания окна Windows и присоединения его к объекту предназначены две функции класса CToolBar.
CBRS_TOP
CBRS_BOTTOM
CBRS_NOALIGN
CBRS_TOOLTIPS
CBRS_SIZE_DYNAMIC
CBRS_SIZE_FIXED
CBRS_FLOATING
CBRS_FLYBY
CBRS_HIDE_INPLACE
Помимо вышеперечисленных можно задавать также стили окон Windows и общие стили для всех панелей управления: CBRS_ALIGN_TOP
CBRS_ALIGN_BOTTOM
CBRS_ALIGN_LEFT
CBRS_ALIGN_RIGHT
CBRS_ALIGN_ANY
CBRS_BORDER_TOP
CBRS_BORDER_BOTTOM
CBRS_ BORDER _LEFT
CBRS_BORDER_RIGHT
CBRS_FLOAT_MULTI
CCS_ADJUSTABLE
CCS_BOTTOM
CCS_TOP
CCS_NODIVIDER
CCS_NOHILITE
CCS_NOMOVEY
CCS_NOPARENTALIGN
CCS_NORESIZE
TBSTYLE_BUTTON
TBSTYLE_SEP
TBSTYLE_CHECK
TBSTYLE_GROUP
TBSTYLE_CHECKGROUP
TBSTYLE_DROPDOWN
TBSTYLE_AUTOSIZE
TBSTYLE_NOPREFIX
TBSTYLE_TOOLTIPS
TBSTYLE_WRAPABLE
TBSTYLE_ALTDRAG
TBSTYLE_FLAT
TBSTYLE_LIST
TBSTYLE_CUSTOMERASE
TBSTYLE_REGISTERDROP
TBSTYLE_TRANSPARENT
TBSTYLE_EX_DRAWDDARROWS
3. Теперь осталось только загрузить из ресурсов образы кнопок. Для этого в классе CToolBar предусмотрены две возможности: через вызов функции LoadToolBar и путем загрузки образов кнопок при помощи функции LoadBitmap с привязкой их к соответствующим командам вызовом функции SetButtons. Рассмотрим эти функции более подробно. Примечание Visual C++, начиная с версии 4.x, поддерживает ресурс типа TOOLBAR, где задаются не только вид кнопок, но также осуществляется автоматическая привязка к ним команд. Поэтому функция LoadBitmap приводится скорее для справки, чем для реального использования.
и
и
Пример использования этих функций вы найдете в приведенном фрагменте кода. Теперь посмотрим, как к изображениям на кнопках можно добавить текстовые строки:
Рассмотрим функции, использованные в этом фрагменте.
После того как панель инструментов создана, необходимо ее где-то расположить. Рассмотрим фрагмент кода:
Как видите, код небольшой и достаточно простой. Поэтому мне осталось привести только некоторые пояснения по использованным в нем функциям. Начну с метода, который класс CToolBar унаследовал от своего базового класса CControlBar.
Поскольку возможность быть привязанной к родительскому фрейму зависит не только от "желания" самой панели управления, но и от ее родителя — чаще всего фрейма главного окна, здесь же рассмотрим функции класса CFrameWnd, которые отвечают за работу с панелями управления.
AFX_IDW_DOCKBAR_TOP
AFX_IDW_DOCKBAR_BOTTOM
AFX_IDW_DOCKBAR_LEFT
AFX_IDW_DOCKBAR_RIGHT
Если параметр nDockBarlD равен нулю, то сторона может быть любой из числа предварительно определенных при помощи двух функций: CControlBar: EnableDocking и CFrameWnd::EnableDocking. Параметр IpRect определяет в координатах экрана, где вне рабочей области фрейма будет привязано окно, содержащее плавающую панель элементов управления.
Еще две функции, которые мы использовали для изменения стиля панели инструментов, выглядят следующим образом.
TBBS_BUTTON
TBBS_SEPARATOR
TBBS_CHECKBOX
TBBS_GROUP
TBBS_CHECKGROUP
TBBS_WRAP
Как уже говорилось, класс CToolBar реализует основные функциональные возможности для всех панелей управления. Более специфические и интересные возможности реализованы в его производных классах. Но прежде чем переходить к их рассмотрению, отметим еще один важный момент: в том случае, если кнопка панели управления не имеет обработчиков для UPDATE_COMMAND_UI или COMMAND, то она автоматически блокируется фреймом и выводится серым цветом. Последняя функция класса CToolBar, которую мы рассмотрим, позволяет получить ссылку на объект CToolBarCtrl:
Для использования преимуществ общего элемента управления Toolbar можно пойти двумя путями: либо непосредственно создать окно CToolBarCtrl, либо создать панель инструментов на основе класса CToolBar, а затем просто получить доступ к элементам объекта CToolBarCtrl (рис. 11.6), вызвав функцию этого класса CToolBar::CToolBarCtrl. Чаще всего используют именно второй способ.
Рис. 11.6. Место класса CToolbarCtrl в иерархии библиотеки MFC Любая кнопка панели инструментов имеет ассоциированную с ней структуру TBBUTTON, которая содержит полную информацию об этой кнопке. Определение структуры находится в файле <commctrl.h>:
TBSTATE_CHECKED
TBSTYLE_CHECKED
TBSTATE_ENABLED
TBSTATE_HIDDEN
TBSTATE_IDETERMINATE
TBSTATE_PRESSED
TBSTATE_WRAP
TBSTATE_ELLIPSES
TBSTATE_MARKED
Полную информацию о любой кнопке панели инструментов можно получить при помощи функции GetButton. Достаточно большая группа функций позволяет установить новое или узнать текущее состояние определенной кнопки данного элемента управления. Их действие настолько очевидно, что мы ограничимся только их перечислением: IsButtonEnabled, IsButtonChecked, IsButtonPressed, Is Button Hidden, Is Button Indeterminate, GetState, SetState, EnableButton, CheckButton, PressButton, HideButton, Indeterminate. Мы использовали доступ к объекту класса CToolBarCtrl для определения числа кнопок панели инструментов:
Теперь необходимо добавить код, который будет отображать панель инструментов на экране. Для этого создадим элемент меню, как мы это делали в главе 8. Но сначала нужно решить, кто будет отвечать за отображение панели инструментов. Можно возложить эту задачу на дочернее окно, а можно на фрейм главного окна. Для ответа на этот вопрос вспомним, что мы решили каждому компоненту программы поручить свою задачу. Поэтому включим соответствующий пункт в меню, ассоциированное именно с фреймом главного окна. 1. Для добавления кода обработчика команды меню выведем на экран окно мастера ClassWizard (рис. 11.7), выполнив команду View | ClassWizard...
Рис. 11.7. Окно мастера ClassWizard 2. В комбинированном списке Class name выберите класс CMainFrame, с которым мы будем связывать созданную панель инструментов рисования. 3. В списке ObjectID мастера ClassWizard выделите идентификатор созданного элемента меню IDM_VIEWPANE, при этом изменится содержимое списка Messages. Выделите в этом списке COMMAND и нажмите кнопку Add Function. Мастер автоматически создаст для вас пустой каркас обработчика сообщения, в который мы добавим код, позволяющий отображать и скрывать панель инструментов:
BOOL bVisible = ;(m_wndPaneBar.GetStyle() & WS_VISIBLE) != 0);
Единственная неизвестная нам функция выглядит следующим образом:
Остальной код не должен вызвать дополнительных вопросов. Теперь нужно отметить состояние, для чего необходимо определить обработчик сообщения:
BOOL bVisible = ((m_wndPaneBar.GetStyle() & WS_VISIBLE) != 0);
Ну вот, теперь можно поиграть с созданной панелью инструментов — отображать ее и прятать, переводить в плавающее состояние и привязывать к любому краю главного фрейма. Мы не можем только получить доступа к ее кнопкам, т. к. они заблокированы. Чтобы изменить такое положение, необходимо добавить код, выполняемый при нажатии на кнопку. К сожалению, ClassWizard нам помочь не может, поскольку вставлять обработчик для каждой кнопки слишком расточительно. Придется реализовывать это вручную. Добавим в карту сообщений класса CMainFrame следующие строки:
Добавим строки также в класс CMainFrame:
Теперь осталось только реализовать, собственно, обработчик команд, ассоциированных с каждой кнопкой панели инструментов: void CMainFrame::OnPane(UINT nID) { // nID — идентификатор команды, ассоциированной с кнопкой m_nlndex = nID — ID_PANEERASE; // Порядковый номер кнопки // Запоминаем идентификатор нажатой кнопки m_wndChild.m_nToolNum = nID; // Сбрасываем все кнопки for(int i = ID_PANEERASE; i <= ID_PANEOVAL; i++) m_vmdPaneBar.GetTociBarCtrlt).CheckButton(i, FALSE); // Определяем нажатую кнопку BOOL bCheck = m_wndPaneBar.GetToolBarCtrl().IsButtonChecked(nlD); // "Нажимаем" ее m_wndPaneBar.GetTooIBarCtri ().CheckButton(nID, IbCheck); ... } void CMainFrame: :OnrJpdate?ane ;ССжШ1* pCmdUI) // Нажимаем или отпускаем кнопку pCmdUI->SetChecki(UINTi (ID_?ANE3RASE + nlndex) = pCmdUI->m_nID); } Что еще сказать по поводу работы с кнопками панели инструментов? Вроде бы все понятно и так. Поэтому коснусь только изменения формы курсора мыши при нажатии на некоторую кнопку, поскольку мы планируем разработать графический редактор. Прежде всего создадим необходимые курсоры. Рассмотрим этот процесс на одном примере: 1. Раскройте вкладку Resource View и создайте новый пустой курсор. 2. Нарисуйте внешний вид этого курсора (рис. 11.8).
Рис. 11.8. Создаем курсор 3. Нажмите кнопку Hot, а затем на ту точку образа курсора, которая будет определять его нулевые, т. е. точку (0, 0), координаты. 4. Раскройте окно свойств курсора и в поле ID введите содержательный идентификатор, например, IDC_CUR_PAINT (рис. 11.9). 5. Аналогичную операцию проделайте для всех остальных курсоров. Примечание Идентификаторы курсоров необходимо располагать в том же порядке, что и кнопки панели инструментов. Это не требование, а всего лишь пожелание. Если это условие не выполнить, то в приводимые фрагменты кода приложения необходимо будет внести определенные изменения.
Рис. 11.9. Свойства курсоров Теперь, когда необходимые курсоры созданы, внесем некоторые добавления в рассмотренный ранее код. Но начнем с описания функции API.
GCL_CBCLSEXTRA
GCL_CBWNDEXTRA
GCL_HBRBACKGROUND
GCL_HCURSOR
GCL_HICON
GCL_HMODULE
GCL_MENUNAME
GCL_STYLE
GCL_WNDPROC
При успешном выполнении функция возвращает значение указанного слова из структуры класса окна или нуль — при ошибке. Дополнительную информацию об ошибке можно получить, вызвав функцию API GetLastError. Обратную задачу — получение информации из структуры класса окна — выполняет функция
Примечание Получить значение дескриптора hWnd для требуемого окна можно с помощью функции CWnd::GetSafeWnd. Мне осталось только привести код:
void CMainFrame::OnViewPaneBar() { // Проверяем "видимость" панели инструментов BOOL bVisibl.e = ( (m_wndPaneBar.GetStyle () & WS_VISIBLE) != 0) ; // Проверяем "видимость" панели инструментов и устанавливаем либо // текущий для выбранной кнопки курсор, либо "стандартную стрелку" if(Invisible) ::SetClassLong(m_wndChiid.GetSafeHwnd(), GCL_HCURSOR, (LONG)AfxGetApp()->LoadCursor(cursors[m_nlndex])); else ::SetClassLong(m_wndChild.GetSafeHwnd(), GCL_HCURSOR, (LONG)AfxGetApp()->LoadStandardCursor(IDC_ARROW)); } void CMainFrame::OnPane(UINT nID) { // nID — идентификатор команды, ассоциированной с кнопкой m_nlndex = nID — ID_PANEERASE; // Порядковый номер кнопки // Устанавливаем соответствующий кнопке курсор ::SetClassLong(m_wndChild.GetSafeHwnd(), GCL_HCURSOR, (LONG)AfxGetApp()->LoadCursor(cursors[mjilndex])) ; } Примечание В окончательном варианте приложения в основную панель инструментов управления я добавил раскрывающийся список (элемент Combobox). Но эту часть мы рассмотрим, когда познакомимся с элементами управления. Чтобы закончить с панелями управления, рассмотрим, каким образом можно работать со строкой состояния. Для нее библиотека MFC также содержит два класса: CStatusBar и CStatusBarCtrl.
Строка состояния (statusbar) — это панель управления, отображающая полосу, которую можно разделить на несколько областей для раздельного вывода в них текста или графической информации. Наиболее часто области используются как строка сообщений (например, для вывода расширенной информации об элементах меню или кнопках панели инструментов) и для отображения индикаторов состояния (например, индикаторов CAPS LOCK, NUM LOCK и INSERT). В качестве стандартного шрифта для строки состояния используется шрифт "MS Sans Serif высотой 10 пунктов. Изменение отображения строки состояния осуществляется автоматически благодаря механизму ON_UPDATE_COMMAND_UI: во время цикла простоя (idle time) она вызывает обработчик команды ON_UPDATE_COMMAND_UI с идентификаторами строк областей индикации. Для строки состояния класс CCmdUI, обеспечивающий правильное функционирование обработчика, поддерживает следующие функции: CCmdUI::Enable и CCmdUI::SetText. Строка состояния может работать в двух режимах — упрощенном и стандартном. В первом режиме разделение этого окна на несколько областей невозможно и выводить в него можно только текстовую информацию. В стандартном режиме приложение может разбить окно строки состояния на несколько областей для раздельного вывода в них текстовой или графической информации. По умолчанию принимается, что первая область — "эластичная": ее длина определяется длиной области, не используемой всеми другими областями, которые к тому же выравнены по правому краю. Переход из одного режима в другой осуществляется при помощи сообщения SB_SIMPLE. Все настройки обычно проводятся перед первым отображением строки состояния на экране.
Класс CStatusBar отвечает в библиотеке MFC за реализацию строки состояния, для создания которой, как обычно, необходимы все те же два шага: создать объект класса и создать окно Windows. Рассмотрим их: 1. В нашем примере объект создается в классе CMainFrame:
2. Теперь необходимо создать массив идентификаторов областей строки состояния:
3. Само окно (строки состояния) создается главным фреймом в ответ на сообщение WM_CREATE:
Для создания строки состояния используется функция:
По умолчанию строка состояния располагается в нижней части родительского окна и имеет стиль SBARS_SIZEGRIP. Однако при желании вы можете расположить его и в верхней части, использовав стиль CBRS ТОР. Очевидно, что этот стиль нельзя комбинировать со стилем SBARS_SIZEGRIP, т. к., во-первых, такой элемент управления будет выглядеть очень странно, а во-вторых, он не будет работать так, как ожидает пользователь.
Рис. 11.10. Строка состояния, созданная со стилем SBARS_SIZEGRIP Помимо функции Create, в Visual C++ 6.0 реализована еще одна, которая аналогична такой же функции класса CToolBar.
После того, как объект Windows "Строка состояния" создан, необходимо разбить ее на области. Используем для этого функцию
Массив идентификаторов мы уже определили, но, кроме этого, нужны еще соответствующие строковые ресурсы (с целью экономии места привожу фрагмент из файла ресурсов, однако задавать их проще с помощью редактора ресурсов, выбрав создание String Table):
Естественно, существуют и предопределенные идентификаторы. Вот они:
Поскольку мы решили отображать в строке состояния текущее время и индикатор времени простоя, то необходимо каким-нибудь способом передать строке состояния эту информацию. Сделать это можно с помощью функции
Осталось только получить индекс по известному идентификатору, что можно сделать с помощью метода
Последняя функция класса CStatusBar, которую мы рассмотрим, служит для получения ссылки на объект CStatusBarCtrl:
Это позволит вам воспользоваться функциональными возможностями общего элемента управления Statusbar, включающим, в частности, настройку строки состояния. В заключение осталось сказать, каким образом строка состояния обрабатывает команду ON_UPDATE_COMMAND_UI. Как обычно, обработчик этой команды вызывается во время цикла простоя последовательно для всех областей строки состояния. Доступными командами класса CCrndUI, отвечающего за обработку, являются Enable и SetText. Первая из них блокирует или разблокирует соответствующую область, при этом у заблокированных областей выключается отображение текста. Вторая функция позволяет изменить текст, который выводится в текущей области. Следует иметь в виду, что при выводе текста не происходит автоматического изменения размеров области. // Каждый раз меняем символ в области строки состояния. // Эта функция при отсутствии каких-либо действий // вызывается примерно раз в секунду, а при манипуляциях мышью - // практически постоянно void CMainFrame::OnUpdateIndicatorIdle(CCmdUI* pCmdUI) { // Обратите внимание, что переменная bldle — статическая static BOOL bldle = FALSE; // Выводим текст pCmdUI->SetText(bldle ? " ": "x95"); bldle = !bldle; } Примечание Идентификаторы строковых ресурсов не отображаются в окне мастера ClassWizard. Поэтому все необходимые действия следует производить вручную. Как это сделать, я уже показывал. Практический пример вывода информации в строку состояния мы рассмотрим после знакомства с таймером. А мы на этом заканчиваем рассмотрение наиболее часто используемых панелей управления — панели инструментов и строки состояния — и переходим к достаточно полезному устройству — таймеру.
Таймер в Windows является устройством, которое периодически извещает приложение об истечении заданного интервала времени. Происходит это путем посылки сообщения WM_TIMER. Таймер присоединяется к приложению при помощи специальной функции основного оконного класса CWnd:
Установить таймер лучше всего в обработчике сообщения WM_CREATE:
Остановить поток сообщений WM_TIMER можно в любой момент, вызвав функцию
В любом случае перед завершением работы приложения необходимо уничтожить все активные таймеры. И если вы не сделали этого раньше, то используйте сообщение WM_DESTROY, предварительно создав соответствующий обработчик:
Теперь приведу некоторые пояснения. Начнем с первого параметра функции SetTimer. Как вы уже поняли, можно определить несколько таймеров, каждый из которых будет "срабатывать" в "свое" время. Так, например, если необходимо определить два таймера, то это можно сделать следующим образом:
Система Windows не хранит в очереди сообщений несколько сообщений WM_TIMER, а объединяет все такие сообщения в одно. Для того чтобы их разделить, требуется соответствующим образом организовать обработчик сообщения. Создать его очень просто с помощью уже знакомого мастера ClassWizard, в поле Messages которого необходимо выделить сообщение WM_TIMER и нажать кнопку Add Function (рис. 11.11). Код, помещаемый в этот обработчик, может быть примерно таким:
Рис. 11.11. Определяем обработчик системного сообщения WM_TIMER Примечание Если необходимо установить новый интервал времени для существующего таймера, то необходимо сначала уничтожить его, а потом определить снова. Теперь то, что касается второго параметра. Таймер в Windows имеет ту же разрешающую способность — 54,925 миллисекунды, что и встроенный таймер PC. Поэтому существуют следующие ограничения:
Учитывайте эти обстоятельства при работе с таймером. При том способе, который использовали мы, сообщения WM_TIMER посылаются в обычную оконную процедуру, где его можно обработать. Третий параметр функции SetTimer позволяет переадресовать такие сообщения в специальную функцию, которая называется функцией "обратного вызова" и вызывается Windows. Она должна иметь определенные параметры:
HWND hWnd, // Дескриптор окна, для которого вызывается SetTimer
На этом мы закончим рассмотрение таймера — полученных сведений достаточно, чтобы отобразить в строке состояния текущее время. void CMainFrame: :OnTimer (UINT nIDEvent} { // Отображаем текущее время в строке состояния" m_wndStatusBar.SetPaneText( m_wndStatusBar.CommandToIndex(ID_INDICATOR_TIME), //Получаем индекс CTime::GetCurrentTime().Format("%H:%M:%S")); // Получаем текущее // время с помощью // класса CTime // Передаем дальнейшую обработку в библиотеку CMDIFrameWnd::OnTimer(nIDEvent); } Итак, что мы имеем на данный момент? Прежде всего дочернее окно, в которое можно организовать вывод. Затем панель инструментов рисования, позволяющая выбрать необходимый инструмент, что сигнализируется нажатой кнопкой и изменением формы курсора (рис. 11.12).
Рис. 11.12. Мы ввели панели инструментов и строку состояния Осталось воспользоваться созданными элементами и научиться рисовать различные фигуры. Для этого нам понадобится организовать графический вывод и научиться работать с мышью. Определенный навык по рисованию в Windows мы уже приобрели, поэтому познакомимся с еще одним устройством — мышью.
Прежде всего поставим задачу: в зависимости от нажатой кнопки на панели инструментов рисования вычертить соответствующую фигуру в дочернем окне. Всю подготовительную работу для этого мы уже проделали. Поэтому сейчас рассмотрим основные шаги, необходимые для динамического рисования: 1. Когда пользователь щелкает левой кнопкой мыши, приложение должно запомнить координаты х и у из сообщения WM_LBUTTONDOWN. 2. Когда пользователь двигает мышь по экрану, не отпуская левую кнопку, приложение рисует соответствующую фигуру всякий раз, когда получает сообщение WM_MOUSEMOVE. Причем перед тем, как изобразить новую фигуру, необходимо стереть предыдущую. Размеры новой фигуры вычисляются посредством комбинирования запомненных координат, полученных в сообщении WM_LBUTTONDOWN, с текущими, передающимися в сообщении WM_MOUSEMOVE. 3. Когда пользователь отпускает левую кнопку мыши, генерируя тем самым сообщение WM_LBUTTONUP, приложение должно сформировать окончательное изображение соответствующей фигуры. Очевидно, что описанный процесс довольно сильно упрощен, однако главное пока — это прочувствовать основные шаги. Еще раз внимательно прочитайте все необходимые этапы и переходите к деталям. Начнем с кода обработчика сообщения WM_LBUTTONDOWN. Примечание Надеюсь, что нет необходимости пояснять, где именно мы собираемся обрабатывать сообщения от мыши.
|