Практически все приложения, имеющие оконный интерфейс, управляются посредством меню. Пользователь выбирает строки меню или, другими словами, команды, а приложение в ответ на это выполняет определенные действия, например, отображает диалоговые панели, выполняет вычисления и т. д.
Многие приложения также имеют панель управления - небольшую полоску, содержащую ряд кнопок. Как правило, эта полоска располагается ниже меню в главном окне приложения. Кнопки панелей управления дублируют некоторые, как правило наиболее часто используемые, строки меню. Их использование позволяет значительно ускорить и облегчить работу с приложением.
Меню и панели управления служат для передачи команд приложению. Напротив, панель состояния отражает текущее состояние приложения и может давать пользователю краткие подсказки, состоящие буквально из нескольких строк.
Все однооконные и многооконные приложения, созданные с использованием MFC AppWizard, по умолчанию имеют меню, панель управления и панель состояния.
В предыдущем томе из серии “Библиотека системного программиста”, посвященном использованию библиотеки классов MFC, и в первой главе этой книги, называющейся “Многооконное приложение“ мы уже рассказали об основных принципах устройства меню и панелей управления. Сейчас мы более подробно расскажем об этих наиболее важных элементах интерфейса приложения.
Самый простой и удобный способ создания меню приложения основан на использовании специального ресурса - шаблона меню. Если вы применяете для создания приложения средства MFC AppWizard, то однооконное приложение по умолчанию будет иметь один ресурс меню, а многооконное два.
Для создания и изменения меню приложения следует использовать редактор ресурсов Microsoft Visual C++. С помощью него вы разработаете меню буквально за несколько минут.
Редактор ресурсов позволяет для каждой строки меню определить ее название, идентификатор, текст подсказки, а также некоторые дополнительные характеристики. Но самым ценным является возможность, добавив к меню новые строки, сразу запустить MFC ClassWizard и определить методы приложения, которые будут использоваться для обработки командных сообщений от этих строк меню.
Чтобы добавить новую строку к меню, достаточно выполнить двойной щелчок левой кнопкой мыши по одному из пустых прямоугольников из точек, которые располагаются в конце главного меню (вы создадите еще одно меню верхнего уровня) и в низу каждого из меню (создается новая строка меню). При этом на экране появится диалоговая панель Menu Item Properties, которую вы должны заполнить (рис. 3.1).
Рис. 3.1. Редактор меню и диалоговая панель Menu Item Properties
Текст, который будет отображаться в строке меню, вы должны ввести в поле Caption. Затем в списке с полем редактирования ID надо выбрать идентификатор для этой строки меню. Если вы не заполните поле ID, редактор ресурсов самостоятельно создаст новый идентификатор на основе имени меню и строки.
В поле Prompt вы можете ввести текстовую строку, которая будет отображаться при выборе данного элемента меню в панели состояния приложения. Редактор ресурсов Microsoft Visual C++ создаст для введенного текста новый строковый ресурс и запишет его в файл ресурсов приложения, присвоив ему тот же идентификатор, что и меню. Так как строки меню и описывающий их текст, имеют одинаковые идентификаторы, то MFC сможет использовать их вместе. При выборе строки меню, MFC просто загружает строковый ресурс с идентичным идентификатором и отображает его в панели состояния.
Остальные переключатели диалоговой панели Menu Item Properties задают различные характеристики строк меню, отвечающие в первую очередь за их внешний вид. Изучите их самостоятельно, используя документацию Microsoft Visual C++.
Также легко можно просмотреть и изменить свойства уже существующих строк меню. Для этого надо выполнить по ним двойной щелчок левой кнопкой мыши. На экране появится уже описанная нами выше диалоговая панель Menu Item Properties, которая уже заполнена текущими параметрами строки меню.
Как и для других элементов пользовательского интерфейса, для управления меню в состав библиотеки классов MFC включен специальный класс - класс CMenu. Класс CMenu - один из самых незаметных классов библиотеки MFC. Ваше приложение может активно работать с меню, но все же в исходных текстах вы не найдете ни одного объекта этого класса.
В приложениях, созданных с помощью MFC AppWizard меню создается автоматически вместе с панелью управления и панелью состояния. Для этого достаточно указать при создании шаблона документа XE "шаблон документа" общий идентификатор этих ресурсов:
// Объявляем указатель на шаблон документа CSingleDocTemplate* pDocTemplate; // Создаем шаблон документа pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, // Идентификатор меню, панели // управления и пиктограммы RUNTIME_CLASS(CSingleDoc), // Класс документа RUNTIME_CLASS(CMainFrame), // Класс главного окна RUNTIME_CLASS(CSingleView)); // Класс окна просмотра
В случае многооконного приложения дополнительно указываются ресурсы, используемые когда все окна просмотра документов закрыты. Все эти ресурсы имеют один и тот же идентификатор. В примере, представленном ниже, это идентификатор IDR_MAINFRAME:
// Создаем главное окно многооконного приложения CMainFrame* pMainFrame = new CMainFrame; // Загружаем ресурсы с идентификатором IDR_MAINFRAME, // в том числе и меню if(!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE;
В приложениях MFC AppWizard, имеющих однооконный или многооконный интерфейс, меню создается и изменяется самой библиотекой MFC. Несмотря на это, вы также можете управлять меню. Самый простой способ заключается в обработке команд обновления от меню. Проблеме использования этих команд мы посвятили раздел “Класс CCmdUI”.
Даже когда приложение создано без использования средств MFC AppWizard, процедура создания меню остается очень простой и также может не задействовать объекты класса CMenu напрямую.
В таких приложениях, как правило, создается главное окно на основе класса CFrameWnd. Для этого сначала создается соответствующий объект класса CFrameWnd, а затем вызывается либо метод Create, либо метод LoadFrame, который в свою очередь уже создает само окно вместе с меню.
Метод Create создает и инициализирует окно, связанное с объектом CFrameWnd. В случае успешного завершения метод Create возвращает ненулевое значение, а в противном случае - ноль:
BOOL Create( LPCTSTR lpszClassName, // Класс окна LPCTSTR lpszWindowName, // Имя окна DWORD dwStyle = WS_OVERLAPPEDWINDOW, // Тип окна const RECT& rect = rectDefault, // Расположение окна CWnd* pParentWnd = NULL, // Родительское окно LPCTSTR lpszMenuName = NULL, // Меню DWORD dwExStyle = 0, // Дополнительные // характеристики окна CCreateContext* pContext = NULL ); // Используется для // организации механизма // документ/окно просмотра
Обязательно надо указать только два первых параметра метода Create. Первый параметр lpszClassName служит для задания класса окна. В качестве него можно также указать значение NULL, тогда по умолчанию будет использован класс, определенный для окон CFrameWnd. Второй параметр lpszWindowName указывает имя окна - оно будет отображаться в заголовке окна.
Остальные параметры метода необязательные. Если их не указать, будут использованы значения, принятые по умолчанию. Нас, однако, сейчас интересует только параметр lpszMenuName. Через него вы можете указать имя ресурса меню, которое будет создано для данного окна. По умолчанию редактор ресурсов Microsoft Visual C++ присваивает созданным в нем ресурсам, в том числе меню, числовые идентификаторы. Чтобы получить из такого идентификатора значение, совместимое по типу с параметром lpszMenuName, следует использовать макрокоманду MAKEINTRESOURCE.
Следующий пример показывает, как можно создать окно с меню:
CMultiMenuWindow::CMultiMenuWindow() { // Создаем окно приложения, соответствующее // данному объекту класса CMultiMenuWindow Create(NULL, "Multi Menu Sample", WS_OVERLAPPEDWINDOW, rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU)); }
Класс CMultiMenuWindow, конструктор которого представлен выше, наследован от базового класса CFrameWnd. Конструктор CMultiMenuWindow создает окно с заголовком Multi Menu Sample. Это окно имеет меню IDR_MENU. Меню с идентификатором IDR_MENU должно быть определено в файле ресурсов приложения.
Виртуальный метод LoadFrame позволяет динамически создавать окно, пользуясь информацией из файла ресурсов. В случае успешного завершения метод LoadFrame возвращает ненулевое значение, а в противном случае - ноль:
virtual BOOL LoadFrame( UINT nIDResource, DWORD dwDefaultStyle = WS_OVERLAPPEDWINDOW|FWS_ADDTOTITLE, CWnd* pParentWnd = NULL, CCreateContext* pContext = NULL );
Параметры метода LoadFrame практически идентичны параметрам метода Create, описанного ранее. Исключение составляет первый параметр - nIDResource. Он представляет идентификатор, общий для нескольких ресурсов, используемых при создании окна. К таким ресурсам относятся меню (будет использоваться как меню окна), строковый ресурс (заголовок окна), пиктограмма (отображается в случае минимизации окна) и таблица клавиш акселерации (используется для ускоренного выбора строк меню).
Вы можете создать меню и без использования методов Create или LoadFrame. Для этого вы должны будете создать объект класса CMenu XE "CMenu" и вызвать для него несколько методов.
Объект класса CMenu не является меню, он только представляет существующее меню. Вы можете создать объект класса CMenu как локальный, а после использования удалить. На меню это не повлияет:
{ CMenu myMenu; }
После объявления объекта класса CMenu вы можете загрузить меню из ресурсов приложения, воспользовавшись для этой цели методом LoadMenu. В случае успешного завершения метод LoadMenu возвратит ненулевое значение, и нуль в противном случае:
BOOL LoadMenu(LPCTSTR lpszResourceName); BOOL LoadMenu(UINT nIDResource);
Метод LoadMenu загрузит меню, заданное именем lpszResourceName или идентификатором nIDResource, и свяжет его с соответствующим объектом класса CMenu. Теперь вы можете использовать для управления загруженным меню другие методы класса CMenu.
После того как меню загружено, его можно “подключить” к окну. Для этого следует воспользоваться методом SetMenu входящим в класс CWnd.
В качестве параметра pMenu передайте методу SetMenu указатель на объект класса CMenu, представляющий меню. Если вы желаете просто удалить текущее меню, используемое окном, передайте методу SetMenu в качестве параметра значение NULL:
BOOL SetMenu(CMenu* pMenu);
В случае успешного завершения операции метод SetMenu вернет ненулевое значение. В противном случае SetMenu вернет ноль.
После того, как вы установили меню, вызвав метод SetMenu, и до того, как соответствующий объект CMenu будет удален, надо вызвать метод Detach класса CMenu. Этот метод разорвет связь между меню и соответствующим объектом класса CMenu, после чего последний может быть удален:
HMENU Detach();
Метод Detach возвращает в случае успешного завершения идентификатор меню, а в случае ошибки - значение NULL.
Сразу отметим, что если до установки меню окно уже имело меню, надо удалить его, воспользовавшись методом DestroyMenu класса CMenu. Если с меню, подлежащим удалению, не связан объект класса CMenu, вы можете обратиться к методу Attach:
BOOL Attach(HMENU hMenu);
Для этого создайте объект класса CMenu, а затем вызовите для него метод Attach, указав в качестве параметра hMenu идентификатор меню. Метод Attach возвращает в случае успешного завершения ненулевое значение, а в случае ошибки - ноль.
Чтобы определить идентификатор меню известного окна, можно воспользоваться методом GetMenu, определенным в классе CWnd. Этот метод возвращает указатель на объект типа CMenu:
CMenu* GetMenu() const;
Вы можете получить из него идентификатор меню, если обратитесь к элементу данных m_hMenu, входящему в класс CMenu.
Мы продемонстрируем различные методы создания и управления меню в приложении MultiMenu, а сейчас сделаем несколько замечаний относительно остальных методов класса CMenu.
Класс CMenu, наследованный от базового класса CObject, содержит все необходимые методы для создания и управления меню. Используя эти методы, вы можете добавлять к меню новые строки, удалять и изменять их. Специальные методы класса CMenu позволяют выделять отдельные строки меню и даже создавать элементы меню, содержащие не только текст но и изображение.
В MFC реализован специальный механизм для обновления таких объектов интерфейса пользователя как меню, панели управления и панели состояния. Этот механизм предусматривает передачу приложению команд обновления пользовательского интерфейса (update command user interface). Для обработки этих команд предназначена макрокоманда ON_UPDATE_COMMAND_UI, размещаемая в таблице сообщений класса.
Для каждой строки меню, для каждой кнопки панели управления и для каждого индикатора панели состояния передается отдельное сообщение.
Когда передаются команды обновления интерфейса пользователя? Многое зависит от самого обновляемого объекта.
В случае меню, команды обновления передаются в момент, когда пользователь открывает меню. Для каждой строки меню посылается отдельное сообщение.
Для кнопок панели управления и индикаторов панели состояния команды обновления передаются в период “бездействия” приложения, когда очередь сообщений приложения пуста.
В момент, когда пользователь открывает меню, приложению передаются команды обновления. В результате для всех строк меню, для которых в таблице сообщений приложения присутствуют макрокоманды ON_UPDATE_COMMAND_UI, вызываются соответствующие методы-обработчики. Они могут изменить состояние меню - заблокировать отдельные строки меню, выделить их символами · или Ö.
Если очередь сообщений приложения пуста, вызывается метод OnIdle главного класса приложения. В своем приложении вы можете переопределить метод OnIdle и выполнять с его помощью какую-либо фоновую работу.
Метод OnIdle определен в классе CWinApp и по умолчанию выполняет обновление пользовательского интерфейса - передает команды обновления для тех кнопок панелей управления и индикаторов панелей состояния, которые имеют в таблице сообщений приложения макрокоманду ON_UPDATE_COMMAND_UI. Макрокоманда ON_UPDATE_COMMAND_UI вызывает методы-обработчики, которые могут изменить состояние кнопок и индикаторов панелей управления и панелей состояния.
Как правило, ряд строк меню и кнопок панелей управления имеют одинаковые идентификаторы. В этом случае для обработки команд обновления строк меню и кнопок панели управления вызываются одни и те же методы.
Не только меню и панели управления обновляются с использованием механизма команд обновления. Точно также можно обновить и состояние кнопок и других органов управления диалоговых панелей.
Макрокоманда ON_UPDATE_COMMAND_UI предназначена для использования в таблицах сообщений приложения и имеет следующий формат:
ON_UPDATE_COMMAND_UI(id, memberFxn)
Параметр id определяет идентификатор строки меню, кнопки панели управления или индикатора панели состояния, для которых надо обработать команду обновления. Параметр memberFxn задает метод, выполняющий обновление.
Если один и тот же метод вызывается для обработки различных команд обновления, можно использовать другую макрокоманду - ON_UPDATE_COMMAND_UI_RANGE. Она вызывает метод memberFxn для обработки всех команд обновления, идентификаторы которых находятся в промежутке значений от id1 до id2:
ON_UPDATE_COMMAND_UI_RANGE(id1, id2, memberFxn)
Метод - обработчик команд обновления, который вызывается макрокомандами ON_UPDATE_COMMAND_UI и ON_UPDATE_COMMAND_UI_RANGE имеет следующий формат:
afx_msg void memberFxn(CCmdUI* pCmdUI);
Имя метода - обработчика обычно формируется из префикса OnUpdate и названия соответствующего объекта интерфейса пользователя - строки меню, кнопки панели управления или индикатора панели состояния.
В качестве параметра pCmdUI методу передается указатель на объект класса CCmdUI. Этот объект представляет элемент интерфейса пользователя (строку меню, кнопку панели управления…), для которого надо обработать команду обновления. Вызывая методы класса CCmdUI, вы можете легко изменять состояние соответствующего объекта интерфейса пользователя.
Более подробно самые важные методы класса CCmdUI мы рассмотрим в следующем разделе.
Если приложение подготовлено с использованием MFC AppWizard, то наилучшим способом создания обработчиков команд обновления является использование средств ClassWizard. Процедура создания обработчиков команд обновления от строк меню и кнопок панелей управления практически не отличается от процедуры создания обычных обработчиков командных сообщений.
Запустите ClassWizard. На экране появится диалоговая панель MFC ClassWizard. Выберите из нее страницу Message Maps (рис. 3.2). Теперь из списка Object IDs выберите идентификатор интересующей вас строки меню или кнопки панели управления. В списке Messages появятся две строки - COMMAND и ON_UPDATE_COMMAND_UI.
Строка COMMAND позволяет создать обработчик командных сообщений, а строка ON_UPDATE_COMMAND_UI - обработчик команд обновления. О том как создавать с помощью MFC ClassWizard методы для обработки командных сообщений, мы рассказывали в 24 томе серии “Библиотека системного программиста”, посвященном библиотеке классов MFC.
Чтобы создать обработчик для команды обновления, выберите из списка Messages строку ON_UPDATE_COMMAND_UI, а из списка Class name имя класса к которому будет добавлен новый метод. Нажмите кнопку Add Function. MFC ClassWizard предложит имя для нового метода. Вы можете согласиться с предложением ClassWizard или изменить имя метода по своему усмотрению. В частности, для нескольких разных строк меню или кнопок панели управления вы можете указать один и тот же метод обработчик.
Рис. 3.2. MFC ClassWizard
К сожалению, MFC ClassWizard не позволяет назначить один обработчик команд обновления нескольким объектам пользовательского интерфейса с помощью макрокоманды ON_UPDATE_COMMAND_UI_RANGE. Вместо одной макрокоманды ON_UPDATE_COMMAND_UI_RANGE MFC ClassWizard разместит в таблице сообщений необходимое количество макрокоманд ON_UPDATE_COMMAND_UI.
Еще одно неприятное ограничение MFC ClassWizard заключается в том, что он не дает возможности создать обработчики для команд обновления от индикаторов панели состояния. Такие обработчики вы должны будете добавлять к классам приложения вручную.
Важную роль в работе таких объектов интерфейса пользователя, как меню, панели управления и панели состояния, играет класс CCmdUI. Методы этого класса позволяют заблокировать отдельные элементы меню, панелей управления и панелей состояния, отметить их символами Ö или ·.
Класс CCmdUI один из немногих классов библиотеки MFC, который не имеет других базовых классов. Поэтому для объектов данного класса доступно относительно мало методов. Вы можете использовать только методы, определенные в самом классе CCmdUI.
Метод |
Описание |
Enable |
Устанавливает или снимает блокировку |
SetCheck |
Помечает строку меню символом Ö |
SetRadio |
Помечает строку меню символом · |
SetText |
Устанавливает текст. Обычно используется для изменения текста в индикаторах панели состояния |
Виртуальный метод Enable позволяет установить или снять блокировку с объекта интерфейса пользователя, представленного объектом класса CCmdUI. Метод имеет единственный параметр bOn. Если параметр bOn содержит значение TRUE или не указан совсем, то блокировка снимается. Если параметр bOn содержит значение FALSE, то блокировка устанавливается:
virtual void Enable(BOOL bOn = TRUE);
Заблокированные строки меню и кнопки панелей управления отображаются серым цветом и не могут быть использованы до момента снятия блокировки. В случае блокировки индикатора панели состояния его текст не будет отображаться.
Метод Enable также можно использовать для блокирования органов диалоговых панелей управления.
Виртуальный метод SetCheck можно использовать для изменения состояния строки меню и кнопок панели управления:
virtual void SetCheck(int nCheck = 1);
Если вы используете метод SetCheck для управления меню и задали в качестве параметра nCheck нулевое значение, то соответствующая строка меню выделяется символом Ö, если параметр nCheck не указан или равен 1, то выделение снимается.
В случае использования метода SetCheck для управления кнопкой панели управления, параметр nCheck задает новое состояние кнопки. Если параметр nCheck равен нулю, кнопка переходит в нажатое положение, если параметр nCheck не указан или равен единице - кнопка переходит в отжатое положение, а если параметр nCheck равен 2, кнопка принимает промежуточное состояние.
Вы можете использовать метод SetCheck для управления внешним видом индикаторов панелей состояния. Если параметр nCheck равен нулю, то рамка индикатора изменяется таким образом, что он будет располагается выше общего уровня панели состояния. Если параметр nCheck равен 1, тогда индикатор переходит в нормальное состояние.
Метод Enable также можно использовать для выбора положения переключателей в диалоговых панелей управления.
Виртуальный метод SetRadio, также как метод SetCheck, можно использовать для изменения состояния строки меню и кнопок панели управления:
virtual void SetRadio(BOOL bOn = TRUE);
Если вы используете метод SetRadio для управления меню и задали в качестве параметра bOn значение TRUE, то соответствующая строка меню выделяется символом ·, если параметр nCheck равен FALSE, то выделение снимается.
В случае использования метода SetRadio для управления кнопкой панели управления, параметр bOn задает новое состояние кнопки. Если параметр bOn равен FALSE, кнопка переходит в нажатое положение, если параметр bOn не указан или равен TRUE - кнопка переходит в отжатое положение.
Вы можете использовать метод SetRadio для управления внешним видом индикаторов панелей состояния. Если параметр bOn равен FALSE, рамка индикатора изменяется таким образом, что он будет располагается выше общего уровня панели состояния. Если параметр bOn равен TRUE, тогда индикатор переходит в нормальное состояние.
Метод Enable также можно использовать для выбора положения переключателей в диалоговых панелей управления.
Виртуальный метод SetText может быть использован для изменения текста, отображаемого в индикаторе панели состояния, в строке меню, в названии кнопок и некоторых органах диалоговых панелей управления. В качестве параметра lpszText надо указать текстовую строку, которую надо вывести:
virtual void SetText(LPCTSTR lpszText);
Следует отметить, что при использовании метода SetText для изменения текста в индикаторах панели состояния, вы должны отдельно позаботиться об изменении размера индикатора. Метод SetText не меняет размер индикатора, вы должны сами рассчитать ширину текста и изменить размер индикатора с помощью соответствующего метода. Более подробно об изменении параметров индикаторов панели состояния мы расскажем в разделе “Панель состояния”.
Помимо представленных методов, в состав класса входит и несколько элементов данных. Они позволяют определить идентификатор строки меню, кнопки панели управления или индикатора панели состояния, для которого вызван метод обработчик.
Метод |
Описание |
m_nID |
Идентификатор объекта, для которого вызвано сообщение |
m_nIndex |
Индекс объекта, для которого вызвано сообщение |
m_pMenu |
Указатель на меню. Если команда обновления передана не от меню, m_pOther содержит значение NULL |
m_pOther |
Указатель на панель состояния или панель управления для объекта которой выполняется обновление. Если команда обновления передана от меню, m_pOther содержит значение NULL |
Чтобы ускорить выбора строк из меню в приложениях используются таблицы клавиш акселераторов. Они задают соответствие комбинаций клавиш идентификаторам командных сообщений. Когда пользователь нажимает комбинацию клавиш, определенную в таблице акселераторов, приложению передается командное сообщение с соответствующим идентификатором. В принципе, можно определить комбинации клавиш акселерации, не только дублирующие строки меню, но и вызывающие передачу других командных сообщений.
Для создания и изменения таблиц акселераторов следует использовать редактор ресурсов Microsoft Visual C++. Он позволяет определить соответствие комбинаций клавиш и идентификаторов командных сообщений (рис. 3.3).
Рис. 3.3. Редактор таблицы клавиш ускорения
Для приложений имеющих оконный интерфейс, и созданных с использованием MFC AppWizard, таблица акселераторов создается автоматически. Таблица акселераторов загружается приложением, во время создания главного окна приложения методом LoadFrame. Мы уже рассматривали этот метод, который также используется для загрузки меню и ряда других ресурсов:
// Создаем главное окно многооконного приложения CMainFrame* pMainFrame = new CMainFrame; // Загружаем ресурсы с идентификатором IDR_MAINFRAME, // в том числе и таблицу акселераторов if(!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE;
Для многооконных приложений каждый тип документа может иметь собственную таблицу акселераторов. Эта таблица будет загружена автоматически вместе с меню (и некоторыми другими ресурсами), когда пользователь откроет окно просмотра документа данного типа.
Чтобы определить таблицу акселераторов для документов данного типа, надо просто включить ее в файл ресурсов приложения, присвоив ей идентификатор данного типа документов:
CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_MULTITYPE, RUNTIME_CLASS(CMultiDoc), RUNTIME_CLASS(CChildFrame), RUNTIME_CLASS(CMultiView)); AddDocTemplate(pDocTemplate);
Если приложение создается без использования средств MFC AppWizard и модели документ - окно просмотра, вы можете загрузить таблицу акселераторов, с помощью метода LoadAccelTable, входящего в состав класса CFrameWnd:
BOOL LoadAccelTable(LPCTSTR lpszResourceName);
В качестве параметра lpszResourceName следует указать имя ресурса таблицы акселераторов. Если таблица акселераторов вместо строкового имени имеет числовой идентификатор, то вы должны воспользоваться макрокомандой MAKEINTRESOURCE.
Как и многие другие методы классов MFC, метод LoadAccelTable возвращает в случае успешного завершения ненулевое значение и нуль в случае ошибки. Ошибка во время загрузки таблицы акселераторов может случиться, если вы неправильно укажите идентификатор (или имя) ресурса таблицы.
Создайте новый проект под названием MultiMenu. В качестве типа приложения выберите из списка Type строку Application. Настройте проект MultiMenu, указав что приложение будет работать с библиотекой классов MFC.
Наберите в редакторе исходный текст приложения и сохраните его в файле MultiMenu.cpp (листинг 3.1). Включите готовый файл MultiMenu.cpp в проект.
Листинг 3.1. Файл MultiMenu.cpp
//============================================================ // Приложение MultiMenu // (c) Frolov G.V., 1996 // E-mail: frolov@glas.apc.org //============================================================ // Включаемые файлы для MFC #include <afxwin.h> #include <afxext.h> #include <afxcmn.h> // Включаемый файл для ресурсов приложения и идентификаторов #include "resource.h" //============================================================ // Класс CMultiMenuApp - главный класс приложения //============================================================ class CMultiMenuApp : public CWinApp { public: // Мы будем переопределять метод InitInstance, // предназначенный для инициализации приложения virtual BOOL InitInstance(); }; // Создаем объект приложение класса CMultiMenuApp CMultiMenuApp MultiMenuApp; //============================================================ // Класс CMultiMenuWindow - представляет главное окно //============================================================ class CMultiMenuWindow : public CFrameWnd { protected: // Панель состояния CStatusBar m_wndStatusBar; // Флаг управляет строкой Prosess меню Mission BOOL bEnable; // Флаг управляет строкой Construction меню Mission BOOL bRadio; // Флаг управляет строкой Restrict меню Menu int nCheck; protected: // Метод для создания окна приложения и панели состояния afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); // Методы для обработки командных сообщений // от меню приложения afx_msg void CMultiMenuWindow::OnDisable(); afx_msg void CMultiMenuWindow::OnCommand(); afx_msg void CMultiMenuWindow::OnExit(); afx_msg void CMultiMenuWindow::OnConstruct(); afx_msg void CMultiMenuWindow::OnRestrictMenu(); afx_msg void CMultiMenuWindow::OnFullMenu(); // Методы для обновления меню afx_msg void OnUpdateProcess(CCmdUI* pCmdUI); afx_msg void OnUpdateConstruct(CCmdUI* pCmdUI); afx_msg void OnUpdateDisable(CCmdUI* pCmdUI); public: // Конструктор класса CMultiMenuWindow CMultiMenuWindow(); // Макрокоманда необходима, так как класс // CMultiMenuWindow обрабатывает сообщения DECLARE_MESSAGE_MAP() }; //============================================================ // Таблица сообщений класса CMultiMenuWindow //============================================================ BEGIN_MESSAGE_MAP(CMultiMenuWindow, CFrameWnd) // Макрокоманда вызывает метод OnCreate ON_WM_CREATE() // Макрокоманда вызывает метод OnContextMenu ON_WM_CONTEXTMENU() // Макрокоманды для обработки командных сообщений ON_COMMAND(ID_MENU_DISABLE, OnDisable) ON_COMMAND(ID_MISSION_CONSTRUCT, OnConstruct) ON_COMMAND(ID_FILE_EXIT, OnExit) ON_COMMAND(ID_MISSION_PROCESS, OnCommand) ON_COMMAND(ID_MENU_RESTRICT, OnRestrictMenu) ON_COMMAND(ID_MENU_FULL, OnFullMenu) // Обработчики сообщений ON_UPDATE_COMMAND_UI ON_UPDATE_COMMAND_UI(ID_MISSION_PROCESS, OnUpdateProcess) ON_UPDATE_COMMAND_UI(ID_MISSION_CONSTRUCT, OnUpdateConstruct) ON_UPDATE_COMMAND_UI(ID_MENU_DISABLE, OnUpdateDisable) END_MESSAGE_MAP() // Индикатор панели управления UINT indicator = ID_SEPARATOR; //============================================================ // Метод InitInstance класса CMultiMenuApp // Создает главное окно приложения и отображает его на экране //============================================================ BOOL CMultiMenuApp::InitInstance() { m_pMainWnd = new CMultiMenuWindow(); m_pMainWnd -> ShowWindow(m_nCmdShow); m_pMainWnd -> UpdateWindow(); return TRUE; } //============================================================ // Конструктор класса CMultiMenuWindow //============================================================ CMultiMenuWindow::CMultiMenuWindow() { // Создаем окно приложения, соответствующее // данному объекту класса CMultiMenuWindow Create(NULL, "Multi Menu Sample", WS_OVERLAPPEDWINDOW, rectDefault, NULL, MAKEINTRESOURCE(IDR_FULL_MENU)); // Загружаем таблицу клавиш акселерации LoadAccelTable(MAKEINTRESOURCE(IDR_ACCELERATOR)); // Инициализируем флаги bEnable = TRUE; bRadio = TRUE; nCheck = 0; } //============================================================ // Метод OnCreate класса CMultiMenuWindow // Вызывается во время создания окна приложения //============================================================ int CMultiMenuWindow::OnCreate(LPCREATESTRUCT lpCreateStruct) { // Вызываем метод OnCreate базового класса if(CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // Создаем панель состояния if(!m_wndStatusBar.Create(this)) { // Ошибка при создании панели состояния TRACE0("Failed to create status bar\n"); return -1; } // Отображаем индикаторы панели состояния if(!m_wndStatusBar.SetIndicators(&indicator,1)) { // Ошибка при установке индикатора TRACE0("Failed to set indicators\n"); return -1; } return 0; } //============================================================ // Метод OnDisable класса CMultiMenuWindow // Изменяем состояние флагов bEnable и nCheck //============================================================ void CMultiMenuWindow::OnDisable() { // Меняем значение bEnable с TRUE на FALSE и наоборот bEnable = !bEnable; // Меняем значение bEnable с 1 на 0 и наоборот nCheck = (nCheck == 1) ? 0 : 1; } //============================================================ // Метод OnRestrictMenu класса CMultiMenuWindow // Изменяем меню приложения с IDR_FULL_MENU на // IDR_RESTRICT_MENU //============================================================ void CMultiMenuWindow::OnRestrictMenu() { CMenu menuOld; // текущее меню CMenu menuRestrict; // новое меню CMenu* pMenu; // Получаем указатель на текущее меню pMenu = this->GetMenu(); // Связываем меню с объектом menuOld menuOld.Attach(pMenu->m_hMenu); // Удаляем меню menuOld.DestroyMenu(); // Загружаем меню IDR_RESTRICT_MENU menuRestrict.LoadMenu(IDR_RESTRICT_MENU); // Устанавливаем загруженное меню SetMenu(&menuRestrict); // Разрываем связь меню с объектом menuRestrict menuRestrict.Detach(); } //============================================================ // Метод OnFullMenu класса CMultiMenuWindow // Изменяем меню приложения с IDR_RESTRICT_MENU на // IDR_FULL_MENU //============================================================ void CMultiMenuWindow::OnFullMenu() { CMenu menuOld; CMenu menuRestrict; CMenu* pMenu; pMenu = this->GetMenu(); menuOld.Attach(pMenu->m_hMenu); menuOld.DestroyMenu(); menuRestrict.LoadMenu(IDR_FULL_MENU); SetMenu(&menuRestrict); menuRestrict.Detach(); } //============================================================ // Метод OnCommand класса CMultiMenuWindow //============================================================ void CMultiMenuWindow::OnCommand() { MessageBox("Command not implemented"); } //============================================================ // Метод OnConstruct класса CMultiMenuWindow // Изменяем состояние флага bRadio //============================================================ void CMultiMenuWindow::OnConstruct() { // Меняем значение bRadio с TRUE на FALSE и наоборот bRadio = !bRadio; } //============================================================ // Метод OnExit класса CMultiMenuWindow //============================================================ void CMultiMenuWindow::OnExit() { // Завершаем приложение DestroyWindow(); return; } //============================================================ // Метод OnUpdateProcess класса CMultiMenuWindow //============================================================ void CMultiMenuWindow::OnUpdateProcess(CCmdUI* pCmdUI) { // Блокируем или разблокируем строку Process меню Mission pCmdUI->Enable(bEnable); } //============================================================ // Метод OnUpdateConstruct класса CMultiMenuWindow //============================================================ void CMultiMenuWindow::OnUpdateConstruct(CCmdUI* pCmdUI) { // Устанавливаем или снимаем пометку // строки Construction меню Mission pCmdUI->SetRadio(bRadio); } //============================================================ // Метод OnUpdateDisable класса CMultiMenuWindow //============================================================ void CMultiMenuWindow::OnUpdateDisable(CCmdUI* pCmdUI) { // Устанавливаем или удаляем пометку // у строки Disable меню Menu pCmdUI->SetCheck(nCheck); }
Создайте новый файл ресурсов и включите его в проект под именем MultiMenu.rc. Включите в него два меню, присвоив им идентификаторы IDR_RESTRICT_MENU и IDR_FULL_MENU.
Введите строки этих меню в соответствии с представленным нами файлом ресурсов (листинг 3.2). Для всех строк меню введите их описания. Они будут записаны в файл ресурсов как строковые ресурсы, имеющие одинаковые идентификаторы со строками меню.
Добавьте в файл ресурсов строку Ready, выбрав для нее идентификатор AFX_IDS_IDLEMESSAGE. Эта строка будет отображаться в панели состояния во время “бездействия” приложения.
Включите в файл ресурсов таблицу акселераторов, состоящую из трех команд: ID_MENU_DISABLE, ID_MISSION_PROCESS и ID_FILE_EXIT. Присвойте им комбинации клавиш <Ctrl+D>, <Ctrl+P> и <Ctrl+E> соответственно.
Листинг 3.2. Файл MultiMenu.rc
//Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // Russian resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) #ifdef _WIN32 LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT #pragma code_page(1251) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Menu // IDR_RESTRICT_MENU MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "P&rocess\tCtrl+P", ID_MISSION_PROCESS MENUITEM SEPARATOR MENUITEM "E&xit\tCtrl+E", ID_FILE_EXIT END POPUP "&Menu" BEGIN MENUITEM "Full", ID_MENU_FULL MENUITEM "Disa&ble\tCtrl+D", ID_MENU_DISABLE END POPUP "&Help", HELP BEGIN MENUITEM "Help index", ID_HELP_HELPINDEX MENUITEM SEPARATOR MENUITEM "System info", ID_HELP_SYSTEMINFO END END IDR_FULL_MENU MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM SEPARATOR MENUITEM "E&xit\tCtrl+E", ID_FILE_EXIT END POPUP "&Menu" BEGIN MENUITEM "Restrict", ID_MENU_RESTRICT MENUITEM "Disa&ble\tCtrl+D", ID_MENU_DISABLE END POPUP "M&ission" BEGIN MENUITEM "P&rocess\tCtrl+P", ID_MISSION_PROCESS MENUITEM SEPARATOR MENUITEM "Construction", ID_MISSION_CONSTRUCT END POPUP "&Help", HELP BEGIN MENUITEM "Help index", ID_HELP_HELPINDEX MENUITEM "Context help", ID_HELP_CONTEXTHELP MENUITEM SEPARATOR MENUITEM "System info", ID_HELP_SYSTEMINFO END END ////////////////////////////////////////////////////////////// // // Accelerator // IDR_ACCELERATOR ACCELERATORS DISCARDABLE BEGIN "D", ID_MENU_DISABLE, VIRTKEY, CONTROL, NOINVERT "E", ID_FILE_EXIT, VIRTKEY, CONTROL, NOINVERT "P", ID_MISSION_PROCESS, VIRTKEY, CONTROL, NOINVERT END ////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE DISCARDABLE BEGIN AFX_IDS_IDLEMESSAGE "Ready" END STRINGTABLE DISCARDABLE BEGIN ID_FILE_EXIT "Exit application" ID_MISSION_PROCESS "Process" ID_HELP_HELPINDEX "Open help index" ID_HELP_CONTEXTHELP "Context help" END STRINGTABLE DISCARDABLE BEGIN ID_HELP_SYSTEMINFO "Display system info" ID_MISSION_CONSTRUCT "Construct" ID_MENU_RESTRICT "Restrict menu" ID_MENU_FULL "Display full menu" ID_MENU_DISABLE "Disable command Process from menu Mission" END #endif // Russian resources ////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
Идентификаторы ресурсов приложения MultiMenu определены в файле resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++. Исходный текст этого файла представлен в листинге 3.3.
Листинг 3.3. Файл resource.h
//{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by MultiMenu.rc // #define IDR_RESTRICT_MENU 106 #define IDR_FULL_MENU 107 #define IDR_ACCELERATOR 108 #define ID_FILE_EXIT 40009 #define ID_MISSION_PROCESS 40013 #define ID_HELP_HELPINDEX 40014 #define ID_HELP_CONTEXTHELP 40015 #define ID_HELP_SYSTEMINFO 40016 #define ID_MISSION_CONSTRUCT 40017 #define ID_MENU_RESTRICT 40019 #define ID_MENU_FULL 40020 #define ID_MENU_DISABLE 40025 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 110 #define _APS_NEXT_COMMAND_VALUE 40027 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
Постройте приложение MultiMenu и запустите его. На экране появится окно приложения с главным меню и панелью состояния (рис. 3.4).
В панели состояния расположен один индикатор. В нем отображается подсказка о выбранной строке меню приложения или системного меню, а если приложение “бездействует” - строка Ready.
Рис. 3.4. Приложение MultiMenu
Сразу после запуска приложения MultiMenu используется меню с идентификатором IDR_FULL_MENU. Если вы выберите из меню Menu строку Restrict, то меню приложения будет заменено на меню IDR_RESTRICT_MENU. Это сокращенный вариант меню IDR_FULL_MENU, в котором отсутствуют некоторые строки, а строка Process перенесена из меню Mission в меню File.
Различные строки меню IDR_FULL_MENU и IDR_RESTRICT_MENU иллюстрируют режимы отображения строк меню. Так, при выборе из меню Menu строки Disable, блокируется строка Process в меню Mission. Около строки Disable при этом отображается символ Ö (рис. 3.3). Чтобы снять блокировку, выберите строку Disable из меню Menu еще раз. Символ Ö также исчезнет.
При выборе строки Process из меню Mission на экране появляется сообщение. Когда строка Process заблокирована, выбрать ее невозможно.
Если вы выберите из меню Mission строку Construction, то она будет выделена символом ·. Повторный выбор этой строки снимает с нее выделение.
Чтобы завершить работу приложения MultiMenu, можно выбрать из меню File строку Exit или выбрать из системного меню приложения строку Close. Все остальные строки меню приложения не работают и заблокированы.
Для ускорения выбора некоторых команд из меню приложения можно воспользоваться клавишами акселерации. В программе определены три такие комбинации.
Комбинация клавиш |
Соответствующая строка меню |
<Ctrl+D> |
Строка Disable из меню Menu |
<Ctrl+P> |
Строка Process из меню Mission или из меню File (для сокращенного варианта меню) |
<Ctrl+E> |
Строка Exit из меню File |
В приложении MultiMenu определены два класса - главный класс приложения CStateApp и класс главного окна приложения CStateWindow.
Главный класс приложения CMultiMenuApp наследуется от базового класса CWinApp. Объект MultiMenuApp класса CMultiMenuApp объявлен как глобальный и создается сразу после запуска приложения.
В класс CMultiMenuApp входит только метод InitInstance. Он создает главное окно приложения, представленное классом CMultiMenuWindow, наследованным от класса CFrameWnd XE "CFrameWnd" .
Класс CMultiMenuWindow управляет главным окном приложения, создает меню, загружает панель управления а также обрабатывает сообщения, в том числе командные сообщения и команды обновления от меню.
Фактически все методы, определенные в классе CMultiMenuWindow, можно условно разделить на три группы. В первую группу попал только один метод OnCreate. Он обрабатывает сообщение WM_CREATE, поступающее в момент создания окна приложения. Вторая группа состоит из шести методов - OnDisable, OnCommand, OnExit, OnConstruct, OnRestrictMenu и OnFullMenu. Эти методы используются для обработки командных сообщений от меню приложения. И, наконец, третья группа методов включает три метода - OnUpdateProcess, OnUpdateConstruct и OnUpdateDisable, которые обрабатывают команды обновления от трех различных строк меню приложения.
В состав класса также входит несколько элементов данных. Это флаги bEnable, bRadio и nCheck, управляющие характеристиками трех строк меню, а также объект m_wndStatusBar класса CStatusBar, представляющий панель состояния нашего приложения.
Рассмотрим отдельные методы класса CMultiMenuWindow более подробно.
Конструктор класса CMultiMenuWindow используется для создания главного окна приложения, подключения меню, загрузки таблицы акселераторов и инициализации флагов.
Для создания окна приложения вызывается метод Create класса CFrameWnd. Обратите внимание, что метод Create создает окно с меню, которое имеет идентификатор IDR_MENU:
Create(NULL, "Status Bar Sample", WS_OVERLAPPEDWINDOW, rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));
Затем выполняется загрузка таблицы акселераторов IDR_ACCELERATOR с помощью метода LoadAccelTable класса CFrameWnd:
LoadAccelTable(MAKEINTRESOURCE(IDR_ACCELERATOR));
И, в конце, конструктор устанавливает значение флагов bEnable, bRadio и nCheck. Флагам bEnable и bRadio присваивается значение TRUE, а флагу nCheck - нулевое значение.
Таблица сообщений класса CMultiMenuWindow обрабатывает командные сообщения и команды обновления от меню приложения, а также содержит макрокоманду ON_WM_CREATE.
Макрокоманда ON_WM_CREATE вызывает метод OnCreate во время создания окна:
ON_WM_CREATE()
Для обработки командных сообщений от меню приложения в таблицу сообщений класса CMultiMenuWindow включены шесть макрокоманд ON_COMMAND. Они вызывают обработчики OnDisable, OnConstruct, OnCommand, OnFullMenu, OnRestrictMenu и OnMenuExit:
ON_COMMAND(ID_MENU_DISABLE, OnDisable) ON_COMMAND(ID_MISSION_CONSTRUCT, OnConstruct) ON_COMMAND(ID_FILE_EXIT, OnExit) ON_COMMAND(ID_MISSION_PROCESS, OnCommand) ON_COMMAND(ID_MENU_RESTRICT, OnRestrictMenu) ON_COMMAND(ID_MENU_FULL, OnFullMenu)
Для обработки команд обновления в таблицу сообщений класса включены три макрокоманды ON_UPDATE_COMMAND_UI. Они вызывают методы OnUpdateProcess, OnUpdateConstruct и OnUpdateDisable:
ON_UPDATE_COMMAND_UI(ID_MISSION_PROCESS, OnUpdateProcess) ON_UPDATE_COMMAND_UI(ID_MISSION_CONSTRUCT, OnUpdateConstruct) ON_UPDATE_COMMAND_UI(ID_MENU_DISABLE, OnUpdateDisable)
Соответствие методов, вызываемых макрокомандами ON_COMMAND и ON_UPDATE_COMMAND_UI, строкам меню приложения вы можете посмотреть в файле ресурсов приложения, представленном в листинге 3.3.
Метод OnCreate класса CMultiMenuWindow сначала вызывает метод OnCreate базового класса CFrameWnd, чтобы создать главное окно приложения:
if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;
Затем мы создаем панель состояния, указывая в качестве ее родительского окна главное окно приложения. Для этого мы вызываем метод Create объекта m_wndStatusBar, представляющего панель состояния, передавая ему в качестве параметра значение this. В данном случае это означает, что окно приложения является родительским окном для панели состояния:
if(!m_wndStatusBar.Create(this)) { // Ошибка при создании панели состояния TRACE0("Failed to create status bar\n"); return -1; }
После того, как панель состояния создана, отображаем на ней единственный индикатор, вызывая метод SetIndicators. В качестве первого параметра передаем методу SetIndicators идентификатор этого единственного индикатора панели состояния, записанный в переменной indicator. Второй параметр метода SetIndicators, равен единице. Он определяет, что индикатор в панели состояния будет только один:
if(!m_wndStatusBar.SetIndicators(&indicator,1)) { // Ошибка при установке индикатора TRACE0("Failed to set indicators\n"); return -1; }
Более подробно о принципах устройства панелей состояния мы расскажем в отдельном разделе, который носит название “Панель состояния”.
Когда пользователь выбирает из меню Menu строку Disable или нажимает комбинацию клавиш <Ctrl+D>, приложению поступает командное сообщение, которое имеет идентификатор ID_MENU_DISABLE. Для обработки этого сообщения вызывается метод OnDisable класса CMultiMenuWindow. Этот метод изменяет состояние флагов bEnable и nCheck.
Значение флага bEnable изменяется с TRUE на FALSE и наоборот, а значение флага bEnable с 1 на 0 и наоборот:
bEnable = !bEnable; nCheck = (nCheck == 1) ? 0 : 1;
Сам метод OnDisable не меняет состояния строк меню приложения, но изменение флагов bEnable и nCheck фиксируется обработчиком команд обновления меню.
Так, флаг bEnable управляет блокировкой строки Process меню Mission (для полного варианта меню) и строки Process меню File (для укороченного варианта меню). Флаг bEnable проверяется методом OnUpdateProcess, который является обработчиком команд обновления от этих строк меню.
Флаг nCheck управляет отображением символа Ö около строки Disable меню Menu. Флаг nCheck проверяется методом OnUpdateDisable, который является обработчиком команд обновления от этой строки меню.
Когда пользователь выбирает строку Process из меню File (для укороченного варианта меню) или из меню Mission (для полного варианта меню), или просто нажимает комбинацию клавиш <Ctrl+P>, приложению поступает командное сообщение, которое имеет идентификатор ID_MISSION_PROCESS. Для обработки этого сообщения вызывается метод CMultiMenuWindow класса CMultiMenuWindow. Данный метод отображает на экране сообщение Command not implemented.
Когда пользователь выбирает из меню Mission строку Construction, приложению поступает командное сообщение с идентификатором ID_MISSION_CONSTRUCT. Для обработки этого сообщения вызывается метод OnConstruct класса CMultiMenuWindow. Метод OnConstruct изменяет состояние флага bRadio, меняя значение bRadio с TRUE на FALSE и наоборот:
bRadio = !bRadio;
Флаг bRadio управляет отображением символа · около строки Construction меню Mission. Флаг bRadio проверяется методом OnUpdateConstruct, который является обработчиком команд обновления от этой строки меню.
Приложение MultiMenu имеет два меню, полное и укороченное. Вы можете выбирать, какое меню будет использоваться в данный момент, с помощью строки Restrict и Full меню Menu. Если в данный момент используется полный вариант меню, то чтобы заменить его укороченным вариантом, следует выбрать из меню Menu строку Restrict. Для обратной замены меню с укороченного варианта на полный, надо выбрать из меню Menu строку Full.
При выборе строк Restrict и Full приложению передаются командные сообщения с идентификаторами IDR_RESTRICT_MENU и IDR_FULL_MENU, соответственно. Для их обработки вызываются методы OnRestrictMenu и OnFullMenu. По сути, методы OnRestrictMenu и OnFullMenu практически идентичны. Отличие между ними заключается только в том, что метод OnRestrictMenu заменяет текущее меню укороченным вариантом меню (идентификатор меню IDR_RESTRICT_MENU), а метод OnFullMenu меняет текущее меню на полный вариант меню (идентификатор меню IDR_FULL_MENU).
Метод OnRestrictMenu работает следующим образом. Сначала он получает указатель на текущее меню окна приложения. Указатель на объект класса CMenu, представляющий это меню, записывается во временную переменную pMenu:
pMenu = this->GetMenu();
Затем текущее меню удаляется, для чего вызывается метод DestroyMenu:
pMenu->DestroyMenu();
Теперь загружается ресурс нового меню, имеющего идентификатор IDR_RESTRICT_MENU (или IDR_FULL_MENU для метода OnFullMenu):
CMenu menuRestrict; // Новое меню menuRestrict.LoadMenu(IDR_RESTRICT_MENU);
Загруженное меню подключается к окну приложения - вызывается метод SetMenu класса окна. В качестве параметра ему передается указатель на объект menuRestrict, представляющий новое меню:
SetMenu(&menuRestrict);
И, наконец, вызывается метод Detach, отпускающий меню в “свободное плавание”, то есть отсоединяющее его от объекта menuRestrict класса CMenu:
menuRestrict.Detach();
Команды обновления от строк Process меню File и Mission передаются для обработки методу OnUpdateProcess класса CMultiMenuWindow. Этот метод блокирует или снимает блокировку со строки Process в зависимости от значения флага bEnable:
pCmdUI->Enable(bEnable);
Команда обновления от строки Construction меню Mission передается для обработки методу OnUpdateConstruct класса CMultiMenuWindow. Этот метод устанавливает или снимаем отметку · со строки Construction в зависимости от значения флага bRadio:
pCmdUI->SetRadio(bRadio);
Команда обновления от строки Disable меню Menu передается для обработки методу OnUpdateDisable класса CMultiMenuWindow. Этот метод устанавливает или снимаем отметку Ö со строки Disable, в зависимости от значения флага nCheck:
pCmdUI->SetCheck(nCheck);
Пользователь может завершить приложение, выбрав из меню File строку Exit. В этом случае приложению передается командное сообщение с идентификатором ID_FILE_EXIT. Соответствующая макрокоманда ON_COMMAND из таблицы сообщений класса CStateWindow вызывает для обработки этого сообщения метод OnMenuExit:
ON_COMMAND(ID_WORK_EXIT, OnMenuExit)
Метод OnMenuExit завершает работу приложения, для чего вызывает метод DestroyWindow, определенный в классе CWnd, для главного окна приложения:
void CMultiMenuWindow::OnExit() { // Завершаем приложение DestroyWindow(); return; }
Новые операционные системы Windows 95 и Windows NT версии 4.0 и приложения, разработанные для них, значительно шире используют правую кнопку мыши, чем ранние версии Windows. Обычно при нажатии правой кнопки мыши на экране появляется временное меню, внешний вид которого зависит от выбранного объекта.
Современные приложения используют правую клавишу мыши для вывода контекстного меню. В диалоговой панели Component Gallery расположен компонент Pop-up Menu. Он позволяет подключить контекстное меню к любому окну приложения.
Если вы желаете подключить контекстное меню к вашему приложению, выберите в диалоговой Component Gallery панели компонент Pop-up Menu и нажмите кнопку Insert. На экране появится диалоговая панель Pop-up Menu. В списке Add pop-up menu to перечислены классы проекта, представляющие окна и диалоговые панели. К одному из них вы можете добавить контекстное меню. По умолчанию к проекту добавляется новое меню, состоящее из трех строк, которому присваивается идентификатор, состоящий из префикса CG_IDR_POPUP_, названия приложения и части названия класса окна, к которому добавлено меню.
Далее мы опишем добавление компонента Pop-up Menu к приложению Multi, рассмотренному в разделе “Приложение Multi”.
Загрузите в Microsoft Visual C++ проект Multi, откройте диалоговую панель Component Gallery, выберите компонент Pop-up Menu и нажмите кнопку Insert. На экране появится диалоговая панель Pop-up Menu (рис. 3.5). Выберите из списка Add pop-up menu to класс CMultiView.
По умолчанию к проекту добавляется новое меню, состоящее из трех строк, которому присваивается идентификатор CG_IDR_POPUP_MULTI_VIEW.
Рис. 3.5. Диалоговая панель Pop-up Menu
Название идентификатора контекстного меню отображается в поле Menu resource ID диалоговой панели Pop-up Menu. Вы можете заменить его по своему усмотрению.
Нажмите кнопку OK. Диалоговая панель Pop-up Menu закроется. В исходных текстах приложения будут выполнены все необходимые изменения, а к ресурсам добавиться новое меню с идентификатором CG_IDR_POPUP_MULTI_VIEW.
Редактор ресурсов Microsoft Visual C++ позволяет изменять шаблон контекстного меню по вашему усмотрению. Из него можно удалить строки, добавленные Component Gallery по умолчанию, и вставить строки нужные вам.
Когда вы построите проект и запустите приложение, то при нажатии на правую кнопку мыши будет открываться контекстное меню окна над которым расположен указатель мыши.
Рис. 3.6. Контекстное меню, которое использует компонент Pop-up Menu
В файл ресурсов будет добавлено определение контекстного меню CG_IDR_POPUP_MULTI_VIEW. Как видите, оно не отличается от меню, которые вы создавали или использовали ранее, за исключением того, что соответствующее меню верхнего уровня обозначено строкой _POPUP_ (рис. 3.6). Эта строка не будет отображаться в контекстном меню.
Если в приложении имеется несколько окон, то вы можете добавить к каждому окну свое контекстное меню. Для этого вставьте в проект компонент Pop-up Menu несколько раз, указывая в поле Add pop-up menu to различные классы окон. Конечно, каждое вставленное в проект меню может состоять из различного набора строк.
Например, если у вас многооконное приложение, то вы можете вставить компонент Pop-up Menu для главного окна приложения и для окна просмотра. Тогда если вы нажмете правую кнопку мыши в то время, когда указатель мыши находится в окне просмотра, то отображается одно контекстное меню, а если вы нажмете правую кнопку мыши когда ее указатель расположен вне окна просмотра - отображается другое контекстное меню.
Все изменения в программном коде приложения Multi, выполненные при вставке в него компонента Pop-up Menu, происходят только в классе окна, к которому добавляется контекстное меню. Компонент Pop-up Menu добавляет макрокоманду ON_WM_CONTEXTMENU к таблице сообщений класса CMultiView, а также встсавляет в класс CMultiView методы OnContextMenu и PreTranslateMessage.
В определении класса CMultiView добавляется только метод-обработчик OnContextMenu. Все остальные элементы класса не изменяются. После добавления к проекту Pop-up Menu класс CMultiView, определенный в файле MultiView.h будет выглядеть следующим образом:
class CMultiView : public CView { protected: // CG: Метод OnContextMenu добавлен компонентом Pop-up Menu afx_msg void OnContextMenu(CWnd*, CPoint point); CMultiView(); DECLARE_DYNCREATE(CMultiView) // Attributes public: virtual BOOL PreTranslateMessage(MSG* pMsg); CMultiDoc* GetDocument(); // Operations public: // Overrides //{{AFX_VIRTUAL(CMultiView) public: virtual void OnDraw(CDC* pDC); virtual BOOL PreCreateWindow(CREATESTRUCT& cs); protected: virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); //}}AFX_VIRTUAL // Implementation public: virtual ~CMultiView(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif protected: //{{AFX_MSG(CMultiView) //}}AFX_MSG DECLARE_MESSAGE_MAP() };
Остальные классы приложения остаются без изменения.
При добавлении контекстного меню к окну класса CMultiView, в таблицу сообщений класса CMultiView добавляется новая макрокоманда ON_WM_CONTEXTMENU:
////////////////////////////////////////////////////////////// // Таблица сообщений класса CMultiView // Объекты класса CMultiView создаются динамически IMPLEMENT_DYNCREATE(CMultiView, CView) // Таблица сообщений класса CMultiView. В нее добавлена // макрокоманда ON_WM_CONTEXTMENU BEGIN_MESSAGE_MAP(CMultiView, CView) ON_WM_CONTEXTMENU() //{{AFX_MSG_MAP(CMultiView) //}}AFX_MSG_MAP // Стандартные команды ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW,CView::OnFilePrintPreview) END_MESSAGE_MAP()
Когда пользователь нажимает правую кнопку мыши в окне, макрокоманда ON_WM_CONTEXTMENU вызывает метод-обработчик OnContextMenu из класса этого окна. Методу OnContextMenu передаются два параметра:
afx_msg void OnContextMenu(CWnd* pWnd, CPoint pos);
Параметр pWnd содержит указатель на объект класса CWnd. Он представляет окно, в котором находился указатель мыши, когда была нажата правая кнопка мыши. Это может быть окно класса к которому принадлежит таблица сообщений или его дочернее окно.
Параметр pos, представляющий объект класса CPoint, содержит координаты указателя мыши, зафиксированные в момент нажатия правой кнопки мыши.
Реализация метода OnContextMenu добавляется в файле MultiView.cpp:
////////////////////////////////////////////////////////////// // Метод OnContextMenu класса CMultiView // CG: Метод OnContextMenu добавлен компонентом Pop-up Menu void CMultiView::OnContextMenu(CWnd*, CPoint point) { // Объект menu будет представлять контекстное меню CMenu menu; // Загружаем меню CG_IDR_POPUP_MULTI_VIEW VERIFY(menu.LoadMenu(CG_IDR_POPUP_MULTI_VIEW)); // Получаем указатель на всплывающее меню CMenu* pPopup = menu.GetSubMenu(0); ASSERT(pPopup != NULL); // Получаем указатель на объект CWnd, представляющий окно // для которого надо отобразить контекстное меню CWnd* pWndPopupOwner = this; while (pWndPopupOwner->GetStyle() & WS_CHILD) pWndPopupOwner = pWndPopupOwner->GetParent(); // Отображаем контекстное меню pPopup->TrackPopupMenu( TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, pWndPopupOwner); }
Для вывода контекстного меню на экран используется метод TrackPopupMenu, входящий в класс CMenu. Контекстное меню можно открыть в любом месте экрана. Вне зависимости от расположения меню, все командные сообщения от него передаются одному определенному окну.
Параметры метода TrackPopupMenu задают расположение контекстного меню и выбирают для него окно, в которое будут передаваться командные сообщения:
BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = 0 );
Параметр nFlags представляет собой комбинацию атрибутов. Они определяют, как будет отображаться меню и какая кнопка мыши используется для выбора строк из этого меню.
Если в качестве nFlags указан атрибут TPM_CENTERALIGN, то контекстное меню отображается по центру относительно координаты, указанной параметром x. Если в параметре nFlags установлен атрибут TPM_LEFTALIGN, то параметр x определяет координату левой стороны меню, а если установлен атрибут TPM_RIGHTALIGN - правой.
Кроме атрибутов TPM_CENTERALIGN, TPM_LEFTALIGN или TPM_RIGHTALIGN в параметре nFlags можно установить атрибут TPM_LEFTBUTTON или TPM_RIGHTBUTTON. Атрибут TPM_LEFTBUTTON говорит, что выбор из меню осуществляется нажатием левой, а TPM_RIGHTBUTTON - правой кнопкой мыши.
Назначение параметра x зависит от атрибутов, установленных в параметре nFlags. Параметр y во всех случаях указывает расположение верхней стороны меню. Координаты x и y указываются в экранных координатах.
Параметр pWnd должен содержать указатель на объект класса CWnd, представляющий окно, в которое будут передаваться все командные сообщения от контекстного меню.
Контекстное меню закрывается, если вы нажмете на кнопку мыши вне меню. Параметр lpRect позволяет указать прямоугольник, внутри которого нажатие на кнопку мыши не будет вызывать закрытия меню. Если этот параметр равен NULL, меню закрывается, если пользователь нажал кнопку мыши в любом месте экрана вне меню.
В случае успешного завершения, метод TrackPopupMenu ненулевое значение, а в противном случае нуль.
Кроме добавления новой макрокоманды к таблице сообщений класса CMultiView и соответствующего метода-обработчика OnContextMenu, компонент Pop-up Menu добавляет метод PreTranslateMessage к классу CMultiView.
В него записывается программный код, который обнаруживает нажатие комбинации клавиш <Shift+F10> или специальной клавиши на клавиатуре с дополнительными клавишами Windows 95 и напрямую вызывает метод OnContextMenu:
////////////////////////////////////////////////////////////// // Метод PreTranslateMessage класса CMultiView BOOL CMultiView::PreTranslateMessage(MSG* pMsg) { // CG: Следующий блок добавлен компонентом Pop-up Menu { // Если нажата комбинация клавиш <Shift+F10> if((((pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN) && (pMsg->wParam == VK_F10) && (GetKeyState(VK_SHIFT) & ~1)) != 0) || // it's Shift+F10 OR Natural keyboard key (pMsg->message == WM_CONTEXTMENU)) { // Определяем экранные координаты клиентской части окна CRect rect; GetClientRect(rect); ClientToScreen(rect); // Записываем в объект point класса CPoint координаты // левого верхнего угла клиентской части окна, добавляя // к нему смещения в 5 пикселов по горизонтали и вертикали CPoint point = rect.TopLeft(); point.Offset(5, 5); // Отображаем контекстное меню в позиции point OnContextMenu(NULL, point); // Возвращаем значение TRUE, так как сообщение обработано return TRUE; } } // Вызываем метод PreTranslateMessage базового класса CView return CView::PreTranslateMessage(pMsg); }
Обычно, панель управления располагается в верхней части главного окна приложения. Она содержит ряд кнопок, дублирующих функции некоторых строк меню приложения. Так как кнопки располагаются непосредственно в окне приложения и всегда доступны, то нажать на кнопки можно быстрее, чем открыть меню и выбрать соответствующую строку.
Если приложение имеет сложную систему управления и большое меню, то создавать для каждой строки меню отдельную кнопку в панели управления не надо. В противном случае кнопки просто не поместятся в окне приложения.
Чтобы выйти из этого положения, создают несколько отдельных панелей управления, которые можно открывать или закрывать по своему усмотрению. Такая организация панелей управления используется очень часто. Достаточно взглянуть на любое офисное приложение Microsoft - Microsoft Word или Microsoft Excel, или даже на среду Microsoft Visual C++.
Начиная с версии 4.0 Microsoft Visual C++, панель управления является отдельным ресурсом, таким же как меню или шаблон диалоговой панели. Редактор ресурсов Microsoft Visual C++ позволяет создать новую панель управления или изменить уже существующую. В редакторе вы не только можете нарисовать новые кнопки, вы сразу можете присвоить им идентификаторы и вызвать ClassWizard, чтобы добавить программный код, вызываемый при нажатии на данную кнопку. В самом простом случае процедура добавления новой кнопки к уже существующей панели управления займет у вас всего несколько минут.
Создайте с помощью MFC AppWizard новый проект или откройте проект, созданный ранее, например, проект Multi. В окне Project Workspace откройте страницу ResourceView. Откройте папку ресурсов Toolbar. В ней располагается только один ресурс этого типа, имеющий идентификатор IDR_MAINFRAME. Загрузите этот ресурс в редактор ресурсов (рис. 3.7).
Рис. 3.7. Редактор ресурсов toolbar
В верхней части окна редактора ресурсов типа toolbar отображается редактируемая панель управления. С помощью мыши можно выбирать кнопки этой панели, которые вы желаете изменить. Редактирование изображения кнопок выполняется в нижней части окна редактора, разделенной на две области, в которых отображается кнопка в нормальном и увеличенном масштабе. Для редактирования предназначены инструменты, представленные в панели Graphics. Вы также можете задавать цвета из палитры Colors.
Редактор ресурсов позволяет присвоить идентификаторы отдельным кнопкам панели управления. Для этого сделайте двойной щелчок по изображению кнопки в верхней части окна редактора. На экране появится диалоговая панель Toolbar Button Properties (рис. 3.8).
Рис. 3.8. Диалоговая панель Toolbar Button Properties
Идентификатор кнопки вводится в списке ID. По умолчанию в списке ID отображается идентификатор, который кнопка имеет в данный момент. Вы можете заменить его другим идентификатором, выбрав его из списка или введя его вручную. Если кнопке, идентификатор еще не присвоен и поле ID пустое, то когда вы закроете панель Toolbar Button Properties, идентификатор будет создан автоматически.
В полях Width и Height выводятся, соответственно, ширина и высота кнопок панели управления в пикселах. Эти два значения относятся ко всем кнопкам панели, а не только к кнопке, выбранной для редактирования в данный момент.
В поле Prompt отображается текстовая строка, описывающая кнопку панели управления. Строка состоит из двух частей, разделенных символом \n. Первая часть содержит описание кнопки, отображаемое в панели состояния когда кнопка выбрана (или соответствующей строки меню, если в меню есть строка с таким же идентификатором). Во второй части строки находится короткое описание кнопки. Оно отображается в окне подсказки tooltips, если вы поместите указатель мыши над кнопкой и некоторое время подождете.
Каждая кнопка в панели управления и каждый разделитель имеют свой собственный индекс. Этот индекс соответствует порядковому номеру, под которым кнопка или разделитель отображаются на экране.
Каждый ресурс, представляющий панель управления в редакторе ресурсов Microsoft Visual C++, выступает как единое целое. Загрузив в редактор панель управления вы можете менять внешний вид кнопок, задавать их идентификаторы и строки описания, не открывая других ресурсов или дополнительных файлов.
Если вы просмотрите исходный текст файла ресурсов приложения, то обнаружите, что на самом деле ресурс панели управления состоит из двух, а если быть точнее, то из трех частей.
Первая часть описывает панель управления:
////////////////////////////////////////////////////////////// // Панель управления toolbar IDR_MAINFRAME TOOLBAR DISCARDABLE 16, 15 BEGIN BUTTON ID_FILE_NEW BUTTON ID_FILE_OPEN BUTTON ID_FILE_SAVE SEPARATOR BUTTON ID_EDIT_CUT BUTTON ID_EDIT_COPY BUTTON ID_EDIT_PASTE SEPARATOR BUTTON ID_FILE_PRINT BUTTON ID_APP_ABOUT BUTTON ID_TOOL_EXIT END
В нашем примере эта панель имеет идентификатор IDR_MAINFRAME. После идентификатора следует ключевое слово TOOLBAR, а затем дополнительные параметры, описывающие режим использования ресурса и два числа, определяющие размер кнопок панели.
Затем в блоке BEGIN - END идет описание каждой кнопки панели. После ключевого слова BUTTON, представляющего кнопку следует ее идентификатор. Между описаниями кнопок могут располагаться ключевые слова SEPARATOR XE "SEPARATOR" .
В нашем маленьком примере присутствуют два таких слова. Они означают, что между кнопками, разделенными строкой SEPARATOR, увеличено расстояние. За счет увеличения расстояния между отдельными кнопками достигается эффект разделения кнопок панели управления на три отдельные группы. Это, в свою очередь, улучшает восприятие приложения и делает его более удобным для пользователя.
Как видите, в первой части ресурса панели управления отсутствуют сами изображения кнопок. Они располагаются отдельно и представляют вторую часть ресурса. Все кнопки представлены одним изображением bitmap, имеющим тот же идентификатор, что и соответствующий ресурс TOOLBAR:
////////////////////////////////////////////////////////////// // Bitmap IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp"
Изображение кнопок нашей панели управления IDR_MAINFRAME хранится в файле Toolbar.bmp (рис. 3.9). Файл записан в каталоге RES основного каталога проекта. Все кнопки панели управления расположены последовательно, одна за другой. Порядок, в котором они расположены, соответствует порядку, в котором кнопки описаны в ресурсе TOOLBAR, и порядку в котором они будут отображаться на экране во время работы приложения.
Рис. 3.9. Файл Toolbar.bmp с изображением кнопок панели управления
Между отдельными изображениями кнопок отсутствуют промежутки, даже если в описании ресурса TOOLBAR XE "TOOLBAR" присутствуют разделители SEPARATOR.
Когда вы подготавливаете ресурс toolbar в редакторе ресурсов, то для каждой кнопки вы можете ввести описывающую ее текстовую строку. Эти строки как раз и представляют третью, необязательную часть ресурса toolbar. Хранятся они в стоковых ресурсах приложения.
Соответствие строковых ресурсов кнопкам панели управления достигается за счет присвоения им одинаковых идентификаторов. Так, для нашего примера вы можете найти в файле ресурсов следующие текстовые ресурсы (мы привели только выборку из строковых ресурсов приложения):
STRINGTABLE DISCARDABLE BEGIN ID_FILE_NEW "Create a new document\nNew" ID_FILE_OPEN "Open an existing document\nOpen" ID_FILE_SAVE "Save the active document\nSave" ID_FILE_PRINT "Print the active document\nPrint" ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy" ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" ID_EDIT_PASTE "Insert Clipboard contents\nPaste" ID_APP_ABOUT "Display program information, version number and copyright\nAbout" END
Надо сказать, что строки описания некоторых кнопок могут отсутствовать, или наоборот, использоваться еще и в других ресурсах, например, в описании ресурсов меню. Все кнопки стандартной панели управления, которая автоматически создается MFC AppWizard для однооконных и многооконных приложений, имеют соответствие в меню приложения.
Чтобы добавить в панели управления новую кнопку, переместите указатель мыши на крайне правую кнопку в панели управления, отображаемой в верхней части окна редактора ресурсов, и нажмите на левую кнопку мыши. Теперь вы можете нарисовать изображение, которое будет отображаться на новой кнопке.
Вместе с Microsoft Visual C++ поставляются несколько изображений кнопок панелей управления, которые можно использовать в разрабатываемых приложениях. Откройте файл ресурсов Common.res, записанный на компакт диске Microsoft Visual C++, и просмотрите записанные в нем ресурсы типа toolbar. Если вы обнаружите подходящие вам изображение, скопируйте его в обменный буфер Windows clipboard и вставьте в редактируемую панель управления. Для более полного описания файла Common.res обратитесь к разделу “Ресурсы Microsoft”.
Введите идентификатор новой кнопки панели управления. Для этого, выполните двойной щелчок по изображению этой кнопки в верхней части окна редактирования. В поле ID, открывшейся диалоговой панели Toolbar Button Properties, введите идентификатор ID_MY_BUTTON (рис. 3.8).
Теперь вы можете построить проект и запустить приложение. В панели управления появится новая кнопка, но она будет иметь серый цвет и будет заблокирована. Такая блокировка выполняется автоматически, если кнопка не имеет соответствующего обработчика сообщений.
Когда вы разрабатываете в редакторе ресурсов меню или диалоговую панель, то достаточно нажать правую кнопку мыши и откроется временное меню из которого можно запустить ClassWizard. В окне редактора панелей управления toolbar правая кнопка не работает, точнее она задействована для других целей.
Чтобы воспользоваться ClassWizard, сначала надо выбрать кнопку, к которой вы желаете привязать код, а затем нажать кнопку ClassWizard из панели Standard. ClassWizard откроет страницу Message Map и сразу предложит создать методы для выбранной кнопки (рис. 3.10).
Рис. 3.10. Диалоговая панель ClassWizard
По умолчанию, в списке Object IDs будет отображаться идентификатор кнопки, выбранной в редакторе панели управления - ID_MY_BUTTON. В списке Class name выбран класс CMainFrame - класс главного окна приложения. Вы можете выбрать и любой другой класс приложения, если желаете чтобы он обрабатывал команды от данной кнопки.
Теперь выберите из списка Messages идентификатор сообщения, подлежащего обработке. Вам доступны два варианта - COMMAND и UPDATE_COMMAND_UIё.
Выберите из списка Messages идентификатор COMMAND и нажмите кнопку Add Function. ClassWizard добавит к таблице сообщений класса CMainFrame новую макрокоманду для обработки команды от кнопки и запросит имя метода для обработки этой команды. По умолчанию будет предложено имя OnMyButton. Вы можете согласиться с предложением и нажать кнопку OK. ClassWizard добавит к классу CMainFrame метод OnMyButton.
Чтобы сразу перейти к редактированию данного метода, достаточно нажать на кнопку Edit Code. В окне редактирования появится файл с исходными текстами методов класса CMainFrame, а курсор будет установлен на метод OnMyButton:
////////////////////////////////////////////////////////////// // Метод OnMyButton класса CMainFrame void CMainFrame::OnMyButton() { // TODO: Здесь вы можете добавить собственный // программный код }
Как видите, метод OnMyButton не имеет параметров. Одно то, что он вызван, служит сигналом нажатия на кнопку ID_MY_BUTTON. Добавьте после комментария TODO код, который вы желаете выполнять по нажатию кнопки. Для нашего первого тестового примера достаточно добавить вызов всего одного метода MessageBox или функции AfxMessageBox XE "AfxMessageBox" :
////////////////////////////////////////////////////////////// // Метод OnMyButton класса CMainFrame void CMainFrame::OnMyButton() { // TODO: Здесь вы можете добавить собственный // программный код MessageBox("Button is pressed"); }
Постройте проект и запустите полученное приложение. Теперь, когда вы добавили обработчик для команд от кнопки с идентификатором ID_MY_BUTTON, она отображается в нормальном виде и вы можете на нее нажать.
Когда вы нажимаете на кнопку ID_MY_BUTTON, вызывается метод обработчик OnMyButton из класса CMainFrame. Он отображает на экране сообщение Button is pressed.
В состав библиотеки MFC включены два класса для работы с панелями управления - CToolBar и CDialogBar. Оба они наследуются от базового класса CControlBar, реализующего основные функции панелей управления. Кроме того, от базового класса CControlBar наследуется еще один класс - CStatusBar. Он предназначен для работы с панелями состояния и будет рассмотрен позже:
CToolBar <- | <- CControlBar <- CWnd <- CCmdTarget <- CObject CDialogBar <- | CStatusBar <- |
Класс CToolBar представляет панель управления, состоящую из ряда кнопок. При желании, вы, конечно, сможете разместить на панели управления и другие органы управления, например списки или поля редактирования, однако такая возможность требует дополнительного программирования.
Если требуется создать панель, содержащую различные органы управления, а не только кнопки, то гораздо удобнее воспользоваться классом CDialogBar. Класс CDialogBar, также наследованный от базового класса CControlBar, позволяет создать панель управления на основе хорошо известного вам шаблона диалоговой панели. Более подробно о возможностях использования класса CDialogBar мы расскажем несколько позже, а сейчас остановим свое внимание на классе CToolBar.
Кнопки панели управления могут работать как обычные кнопки, как переключатели и как переключатели с зависимой фиксацией. Тип кнопок панели управления выбирается методами класса CToolBar.
Чтобы создать панель управления, сначала необходимо определить объект класса CToolBar, который будет представлять данную панель. Если панель имеет много органов управления, можно наследовать от класса CToolBar новый класс, расширить его дополнительными методами, и использовать полученный класс вместо класса CToolBar.
Объект CToolBar обычно включают как элемент главного окна приложения. Так, например, в приложениях, созданных с помощью средств MFC AppWizard, панель управления объявляется как элемент класса CMainFrame, наследованного от класса CFrameWnd или CMDIFrameWnd (в зависимости от интерфейса приложения). Класс CMainFrame как раз представляет главное окно приложения, внутри которого расположены панели управления, окна просмотра и т. д.
Конструктор класса CToolBar не имеет параметров:
CToolBar();
После того как объект класса CToolBar создан, следует вызвать метод Create, который создает панель управления. В качестве параметров, методу Create указываются различные характеристики создаваемой панели:
BOOL Create( CWnd* pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_TOP, UINT nID = AFX_IDW_TOOLBAR );
Только первый параметр метода pParentWnd является обязательным. В нем надо указать идентификатор родительского окна для панели управления. В качестве такого окна обычно выступает главное окно приложения, элементом класса которого является объект класса CToolBar.
Второй параметр метода dwStyle не обязательный, но именно он определяет, как будет отображаться панель управления. В качестве параметра pParentWnd можно указать комбинацию флагов, представленных в следующей таблице.
Флаг |
Описание |
CBRS_BOTTOM |
Панель управления отображается в нижней части окна |
CBRS_FLOATING |
Панель управления отображается в отдельном окне |
CBRS_FLYBY |
Панель состояния отображает краткое описание выбранной кнопки. Конечно, для этого как минимум необходимо, чтобы окно, в котором располагается панель управления, также имело панель состояния и чтобы для кнопок панели были определены соответствующие строковые ресурсы |
CBRS_SIZE_DYNAMIC |
Размер панели управления можно изменять. При этом кнопки в панели управления перестраиваются в несколько рядов |
CBRS_SIZE_FIXED |
Панель состояния имеет фиксированную форму (размер) |
CBRS_TOOLTIPS |
Для кнопок панели управления отображаются их краткие описания в окнах tool tips |
CBRS_TOP |
Панель управления отображается в верхней части окна |
В качестве последнего параметра метода Create вы можете указать идентификатор, который будет присвоен панели управления. По умолчанию используется идентификатор AFX_IDW_TOOLBAR.
¨ Приложения, созданные MFC AppWizard, имеют меню View, содержащее строки Toolbar и Status bar. Строка Toolbar с идентификатором ID_VIEW_TOOLBAR позволяет закрывать и снова открывать панель управления. Обработка стандартного командного сообщения ID_VIEW_TOOLBAR выполняется методом OnUpdateControlBarMenu класса CFrameWnd. Сразу отметим, что метод OnUpdateControlBarMenu может управлять отображением панели управления только в том случае, если она имеет идентификатор AFX_IDW_TOOLBAR. Более подробно о методе OnUpdateControlBarMenu можно прочитать в разделе “Недокументированные возможности класса CMainFrame”.
Метод Create возвращает ненулевое значение в случае успешного создания панели или нуль в случае ошибки.
После того как вы создали панель управления, надо загрузить ресурс панели управления. Для этого предназначен метод LoadToolBar класса CToolBar. Метод LoadToolBar имеет две реализации:
BOOL LoadToolBar(LPCTSTR lpszResourceName); BOOL LoadToolBar(UINT nIDResource);
В качестве параметра lpszResourceName следует указать имя ресурса панели управления. Если вы знаете идентификатор ресурса панели управления, используйте второй прототип метода и укажите идентификатор в качестве параметра nIDResource.
Если загрузка ресурса прошла успешно, метод LoadToolBar возвращает ненулевое значение. В случае ошибки возвращается нуль.
В Microsoft Visual C++ версии 2.х и более ранних версиях ресурс типа toolbar отсутствует. Вместо этого ресурса в файле ресурсов приложения записывалось только изображение кнопок панели управления. А вместо метода LoadToolBar класса CToolBar использовались вызовы двух других методов этого же класса - LoadBitmap и SetButtons.
Метод LoadBitmap загружал из ресурсов приложения изображение кнопок панели управления, а метод SetButtons устанавливал соответствие каждой кнопке панели управления ее изображения и идентификатора. Так как при создании новых приложений лучше использовать новый метод для отображения панелей управления, предполагающий работу с ресурсами toolbar и методом LoadToolBar, то мы не будем останавливаться на методах LoadBitmap и SetButtons.
На этом создание панели управления можно считать оконченным. Теперь ваше приложение имеет полноценную панель управления. Однако стоит изучить еще несколько методов класса CToolBar и его базового класса CControlBar, которые помогут сделать панель управления еще более удобной для пользователя.
Во время создания панели управления вы можете указать ее характеристики через параметр dwStyle метода Create. Если вам потребовалось изменить эти характеристики уже во время работы приложения - используйте метод SetBarStyle. Метод SetBarStyle определен в классе CControlBar, который является базовым для класса CToolBar:
void SetBarStyle(DWORD dwStyle);
Параметр dwStyle задает новые характеристики панели управления. В качестве этого параметра можно использовать комбинацию флагов, представленных в следующей таблице.
Флаг |
Описание |
CBRS_ALIGN_TOP |
Панель управления можно пристыковать к верхней границе окна |
CBRS_ALIGN_BOTTOM |
Панель управления можно пристыковать к нижней границе окна |
CBRS_ALIGN_LEFT |
Панель управления можно пристыковать к левой границе окна |
CBRS_ALIGN_RIGHT |
Панель управления можно пристыковать к правой границе окна |
CBRS_ALIGN_ANY |
Панель управления можно пристыковать к любой границе окна |
CBRS_TOOLTIPS |
Для кнопок панели управления отображаются их краткие описания в окнах tool tips |
CBRS_FLYBY |
Панель состояния отображает краткое описание выбранной кнопки |
Чтобы определить текущие характеристики панели управления, используйте метод GetBarStyle класса CControlBar. Метод GetBarStyle возвращает комбинацию флагов. Подробное описание флагов смотрите выше, в описании метода SetBarStyle:
DWORD GetBarStyle();
Перед тем как продолжить изучение панелей управления, приведем приложение Bar, которое имеет панель управления, состоящую из нескольких кнопок.
Вы уже знаете, насколько просто добавить новые кнопки к панели управления, если приложение создано с помощью средств автоматизированной разработки MFC AppWizard. Однако приложение, подготовленное MFC AppWizard, достаточно многофункционально и состоит из нескольких сотен строк программного кода.
Чтобы подробнее разобраться с устройством и возможностями панелей управления, гораздо лучше взять маленькое приложение, программный код которого можно охватить буквально одним взглядом. Возьмем за основу приложение MFStart с одним единственным окном, представленное нами в первой книге серии “Библиотека системного программиста”, посвященной программированию в Microsoft Visual C++ с применением классов MFC.
¨ К сожалению, если вы не используете для создания приложения MFC AppWizard, то вам, скорее всего, не будут доступны и возможности ClassWizard. Сейчас нам придется пожертвовать удобством в разработке приложения ради простоты его исходных текстов.
Создайте новый проект под названием Bar. В качестве типа приложения выберите из списка Type строку Application. Выберите из меню Build строку Settings или нажмите комбинацию клавиш <Alt+F7>. На экране появится диалоговая панель Project Settings. В этой панели расположены несколько страниц, позволяющих настроить различные характеристики проекта.
Откройте страницу General. Выберите из списка Microsoft Foundation Classes строку Use MFC in a Shared Dll или строку Use MFC in a Static Library. Эта настройка указывает Microsoft Visual C++, что в приложении используются классы библиотеки MFC.
Наберите в редакторе исходный текст приложения и сохраните его в файле Bar.cpp (листинг 3.4). Затем включите этот файл в проект. Для простоты мы включили в один файл весь программный код приложения.
Листинг 3.4. Файл Bar.cpp
//============================================================ // Приложение Bar // (c) Frolov G.V., 1996 // E-mail: frolov@glas.apc.org //============================================================ // Исключаем редко используемые определения из // включаемых файлов #define VC_EXTRALEAN // Включаемый файл для MFC #include <afxwin.h> #include <afxext.h> #include <acmn.h> // Включаемый файл для ресурсов приложения #include "resource.h" //============================================================ // Класс CBarApp - главный класс приложения //============================================================ class CBarApp : public CWinApp { public: // Мы будем переопределять метод InitInstance, // предназначенный для инициализации приложения virtual BOOL InitInstance(); }; // Создаем объект приложение класса CBarApp CBarApp MyBarApp; //============================================================ // Класс CBarWindow - представляет главное окно //============================================================ class CBarWindow : public CFrameWnd { protected: // control bar embedded members CToolBar m_wndToolBar; protected: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); public: // Объявляем конструктор класса CBarWindow CBarWindow(); // Объявляем методы для обработки команд меню afx_msg void BarCommand(); // Макрокоманда необходима, так как класс // CBarWindow обрабатывает сообщения DECLARE_MESSAGE_MAP() }; //============================================================ // Метод BarCommand // Обрабатывает команду ID_TEST_BEEP //============================================================ void CBarWindow::BarCommand() { MessageBox("Command not implemented"); } //============================================================ // Таблица сообщений класса CBarWindow //============================================================ BEGIN_MESSAGE_MAP(CBarWindow, CFrameWnd) ON_WM_CREATE() ON_COMMAND(ID_FILE_OPEN, CBarWindow::BarCommand) ON_COMMAND(ID_FILE_SAVE, CBarWindow::BarCommand) ON_COMMAND(ID_TOOL_EXIT, CBarWindow::BarCommand) END_MESSAGE_MAP() //============================================================ // Метод InitInstance класса CBarApp //============================================================ BOOL CBarApp::InitInstance() { // Создаем объект класса CBarWindow m_pMainWnd = new CBarWindow(); // Отображаем окно на экране m_pMainWnd -> ShowWindow(m_nCmdShow); // Обновляем содержимое окна m_pMainWnd -> UpdateWindow(); return TRUE; } //============================================================ // Конструктор класса CBarWindow //============================================================ CBarWindow::CBarWindow() { // Создаем окно приложения, соответствующее // данному объекту класса CBarWindow Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault, NULL ); } //============================================================ // Метод OnCreate класса CBarWindow // Вызывается во время создания окна приложения //============================================================ int CBarWindow::OnCreate(LPCREATESTRUCT lpCreateStruct) { // Вызываем метод OnCreate базового класса if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // Создаем панель управления toolbar. В качестве // родительского окна указываем окно CBarWindow if (!m_wndToolBar.Create(this)) { // Ошибка при создании панели управления TRACE0("Failed to create toolbar\n"); return -1; } if (!m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { // Ошибка при загрузке ресурса панели управления TRACE0("Failed to load toolbar\n"); return -1; } return 0; }
Теперь необходимо создать ресурс панели управления toolbar. Вы можете сделать это несколькими способами - создать панель управления “с нуля” или скопировать уже готовую панель управления из другого приложения, например из приложения Multi, представленного в главе “Многооконное приложение”.
Перед тем как приступить к разработке панели управления, создайте файл ресурсов и включите его в проект. Для этого выберите из меню File строку New. Из открывшейся диалоговой панели New выберите строку Resource Script и нажмите на кнопку OK. Будет создан пустой файл ресурсов. Сохраните его в каталоге приложения под именем Bar.rc и включите в проект.
Выберите из меню Insert строку Resource, а затем из открывшейся диалоговой панели Insert Resource выберите строку Toolbar и нажмите на кнопку OK.
Запустится редактор ресурсов и в нем будет загружена новая панель управления. Создайте панель управления, постепенно добавляя к ней по одной кнопке. Для каждой кнопки нарисуйте ее изображение, а также присвойте ей уникальный идентификатор и строку описания (см. раздел “Редактор панели управления”).
При создании панелей управления руководствуйтесь информацией из файла ресурсов приложения Bar (листинг 3.5) и изображениями кнопок панелей управления (рис. 3.9).
Панель управления приложения Bar должна иметь идентификатор IDR_MAINFRAME. В ней следует расположить девять кнопок с идентификаторами ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_SAVE, ID_EDIT_CUT, ID_EDIT_COPY, ID_EDIT_PASTE, ID_FILE_PRINT, ID_APP_ABOUT и ID_TOOL_EXIT.
Между кнопками ID_FILE_SAVE и ID_EDIT_CUT, ID_EDIT_PASTE и ID_FILE_PRINT, вставьте разделители. Для каждой из кнопок вы также можете ввести их текстовые описания.
Сохраните измененный файл ресурсов.
Чтобы ускорить процесс разработки панели управления приложения Bar, можно взять за основу панели управления ресурс toolbar приложения Multi, созданного с использованием средств MFC AppWizard. Мы описывали приложение Multi в разделе “Многооконное приложение”.
Не закрывая проект Bar и файл ресурсов Bar.rc, откройте файл ресурсов приложения Multi. Выберите панель управления toolbar и запишите ее в буфер clipboard. Затем вставьте эту панель в файл ресурсов приложения Bar - Bar.rc.
Оставьте название панели toolbar, изображения кнопок, их идентификаторы и текстовые описания без изменения. Добавьте в конце панели еще одну кнопку, и присвойте ей идентификатор ID_TOOL_EXIT. Введите текстовое описание кнопки - строку Exit\nExit.
Сохраните измененный файл ресурсов приложения Bar и закройте файл ресурсов приложения Multi без изменений. Исходный текст файла ресурсов Bar.rc представлен в листинге 3.5.
Листинг 3.5. Файл Bar.rc
//Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // // Russian resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) #ifdef _WIN32 LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT #pragma code_page(1251) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED #endif // Russian resources ////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 ////////////////////////////////////////////////////////////// // // Toolbar // IDR_MAINFRAME TOOLBAR DISCARDABLE 16, 15 BEGIN BUTTON ID_FILE_NEW BUTTON ID_FILE_OPEN BUTTON ID_FILE_SAVE SEPARATOR BUTTON ID_EDIT_CUT BUTTON ID_EDIT_COPY BUTTON ID_EDIT_PASTE SEPARATOR BUTTON ID_FILE_PRINT BUTTON ID_APP_ABOUT BUTTON ID_TOOL_EXIT END ////////////////////////////////////////////////////////////// // // Bitmap // IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp" ////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE DISCARDABLE BEGIN ID_TOOL_EXIT "Exit\nExit" END #endif // English (U.S.) resources ////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // Generated from the TEXTINCLUDE 3 resource. // ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
Идентификаторы всех ресурсов приложения Bar определены в файле resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++ (листинг 3.6).
Листинг 3.6. Файл resource.h
//{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by Bar.rc // #define IDR_HAND_BAR 101 #define IDR_MAINFRAME 128 #define ID_TOOL_EXIT 32771 #define ID_BUTTON40001 40001 #define ID_BUTTON40002 40002 #define ID_BUTTON40003 40003 #define ID_BUTTON40004 40004 #define ID_BUTTON40005 40005 #define ID_FILE_NEW 0xE100 #define ID_FILE_OPEN 0xE101 #define ID_FILE_SAVE 0xE103 #define ID_FILE_PRINT 0xE107 #define ID_EDIT_COPY 0xE122 #define ID_EDIT_CUT 0xE123 #define ID_EDIT_PASTE 0xE125 #define ID_APP_ABOUT 0xE140 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40006 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
Изображения кнопок панели управления IDR_MAINFRAME располагаются в файле Toolbar.bmp (рис. 3.11). Файл Toolbar.bmp содержит только изображения кнопок, разделители SEPARATOR в нем не представлены.
Рис. 3.11. Файл Toolbar.bmp с изображением кнопок панели управления
Постройте приложение Bar и запустите его на выполнение. На экране появится главное окно приложения, содержащее панель управления (рис. 3.12). Панель управления нельзя переместить в другое место, она жестко зафиксирована в верхней части окна.
Рис. 3.12. Приложение Bar
Все кнопки, кроме трех, недоступны и отображаются серым цветом.
При нажатии на первые две доступные кнопки из панели управления на экране появляется сообщение о том, что данная команда недоступна. Если вы нажмете на последнюю кнопку в панели управления, то приложение завершит свою работу.
Обратите внимание на первые строки файла Bar.cpp. Они содержат директивы #include, которые включают в исходный текст два файла - afxwin.h и afxext.h:
// Включаемый файл для MFC #include <afxwin.h> #include <afxext.h> // Включаемый файл для ресурсов приложения #include "resource.h"
С файлом afxwin.h вы уже знакомы. В этом файле определены классы, методы, константы и другие структуры для библиотеки классов MFC. Кроме того, файл afxwin.h автоматически подключает другой включаемый файл - windows.h.
Файл afxext.h необходим, так как в нем описываются классы, используемые для создания панели управления, в том числе сам класс панели управления - CToolBar.
Кроме системных файлов afxwin.h и afxext.h, в исходный текст файла Bar.cpp включен файл resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++ и содержит определение различных идентификаторов приложения.
В приложении Bar определены два класса CBarApp и CBarWindow. Главный класс приложения CBarApp наследуется от базового класса CWinApp. Объект MyBarApp класса CBarApp объявлен как глобальный и создается сразу после запуска приложения.
В класс CBarApp входит единственный метод InitInstance. Метод InitInstance создает главное окно приложения, представленное классом CBarWindow, наследованным от класса CFrameWnd.
Взаимодействие главного класса приложения и главного класса окна приложения мы описывали в первой книге серии “Библиотека системного программиста”, посвященной программированию в Microsoft Visual C++ с использованием библиотеки классов MFC.
Обратите внимание, что в состав класса CBarWindow входит объект m_wndToolBar класса CToolBar. Именно этот объект и будет представлять панель управления. Включение объекта класса CToolBar в состав класса главного окна приложения вполне закономерно, так как панель управления, точно также как и меню, является атрибутом именно главного окна приложения:
class CBarWindow : public CFrameWnd { protected: CToolBar m_wndToolBar; // ... }
Объект m_wndToolBar, представляющий панель управления, объявлен как protected. Доступ к нему открыт только для методов класса CBarWindow. Если надо открыть доступ к панели управления из вне класса CBarWindow, тогда ключевое слово protected надо заменить на public.
В таблице сообщений класса CBarWindow находится макрокоманда ON_WM_CREATE. Поэтому в процессе создания главного окна приложения вызывается метод OnCreate. Мы используем метод OnCreate для создания панели управления.
Метод OnCreate класса CBarWindow сначала вызывает метод OnCreate базового класса CFrameWnd. Затем создается панель управления toolbar. Для этого вызывается метод Create объекта m_wndToolBar. В качестве указателя на родительское окно панели управления методу Create передается ключевое слово this, указывающее на текущий объект, то есть на главное окно приложения.
После создания панели управления вызывается метод LoadToolBar, загружающий панель управления с идентификатором IDR_MAINFRAME. Если вы запустите приложение под отладкой, то в случае возникновения ошибок при создании панели управления вызывается макрокоманда TRACE0 XE "TRACE0" . Она отображает сообщение об ошибке на странице Debug панели Output. Панель Output обычно располагается в нижней части окна Microsoft Visual C++.
Каждый раз, когда пользователь нажимает на кнопки в панели управления, в окно приложения, являющееся родительским окном панели управления, поступают командные сообщения. Идентификатор этих командных сообщений соответствует идентификатору нажатой кнопки.
Чтобы обработать командные сообщения от панели управления, в таблицу сообщений класса CBarWindow включены макрокоманды ON_COMMAND. В приложении Bar мы обрабатываем командные сообщения только от трех кнопок панели управления - ID_FILE_OPEN, ID_FILE_SAVE и ID_TOOL_EXIT:
//============================================================ // Таблица сообщений класса CBarWindow //============================================================ BEGIN_MESSAGE_MAP(CBarWindow, CFrameWnd) // Макрокоманда необходима для перехвата сообщения // WM_CREATE. Для обработки сообщения вызывается // метод OnCreate ON_WM_CREATE() // Обработка сообщений от кнопок панели управления ON_COMMAND(ID_FILE_OPEN, CBarWindow::BarCommand) ON_COMMAND(ID_FILE_SAVE, CBarWindow::BarCommand) ON_COMMAND(ID_TOOL_EXIT, CBarWindow::BarCommand) END_MESSAGE_MAP()
Чтобы не усложнять исходный текст приложения мы вызываем для обработки командных сообщений от кнопок панели управления один и тот же метод BarCommand. Метод BarCommand входит в состав класса CBarWindow. Единственное, что делает метод BarCommand - это выводит на экран сообщение, что данная команда не реализована.
Конечно, вы можете назначить для каждой кнопки панели управления свой собственный метод обработчик, выполняющий любые определенные вами действия.
Приложения, создаваемые с использованием средств MFC AppWizard, имеют только одну панель управления. Во многих случаях этого явно недостаточно. Сложные приложения, наподобие офисных приложений Microsoft и графических редакторов, имеют несколько таких панелей.
Панель управления приложения Bar нельзя переместить с одной границы окна к другой и нельзя разместить ее в отдельном окне, независимом от главного окна приложения. Единственное, что вы можете сделать, так это выбрать начальное положение панели управления, установив в момент вызова метода Create флаг CBRS_TOP или CBRS_BOTTOM.
Чтобы вы смогли перемещать панель управления с одной границы окна к другой надо:
1. Разрешить перемещение панели управления для окна, которое содержит панель управления. Для этого следует вызвать метод EnableDocking данного окна. Метод EnableDocking является элементом класса CFrameWnd
2. Разрешить такое перемещение для самой панели управления. Для этого следует вызвать метод EnableDocking панели управления. Метод EnableDocking является элементом класса CControlBar
3. Переместить панель управления к одной из сторон окна приложения или вывести ее в отдельном окне. Для этого необходимо вызвать метод DockControlBar или FloatControlBar данного окна приложения. Методы DockControlBar и FloatControlBar являются элементами класса CFrameWnd
Если в одном окне отображается несколько панелей управления, то вы должны вызвать для этого окна метод EnableDocking, а затем для каждой панели управления в отдельности вызвать методы EnableDocking и DockControlBar (или FloatControlBar).
¨ Если вы не выполните хотя бы один пункт из трех, перечисленных выше, панель управления будет жестко привязана к одной из границ окна, или возникнет ошибка во время работы приложения
Параметры методов CFrameWnd::EnableDocking, CControlBar::EnableDocking, а также CFrameWnd::DockControlBar и CFrameWnd::FloatControlBar определяют границы окна, к которым можно пристыковать панель управления. Рассмотрим эти методы более подробно.
Метод EnableDocking класса CFrameWnd разрешает пристыковку панели управления к определенным границам окна:
void EnableDocking(DWORD dwDockStyle);
Параметр dwDockStyle задает границы окна, к которым можно пристыковать панель управления. В качестве этого параметра надо использовать комбинации из флагов, перечисленных в следующей таблице.
Флаг |
Описание |
CBRS_ALIGN_TOP |
Панель управления можно пристыковать к верхней границе окна |
CBRS_ALIGN_BOTTOM |
Панель управления можно пристыковать к нижней границе окна |
CBRS_ALIGN_LEFT |
Панель управления можно пристыковать к левой границе окна |
CBRS_ALIGN_RIGHT |
Панель управления можно пристыковать к правой границе окна |
CBRS_ALIGN_ANY |
Панель управления можно пристыковать к любой границе окна |
Чтобы панель управления можно было перемещать с одной стороны окна к другой, надо вызвать метод EnableDocking для окна и для каждой панели управления (если их несколько).
Формат вызова метода EnableDocking класса CFrameWnd соответствует формату метода EnableDocking класса CControlBar. Однако набор флагов, которые можно указать в качестве параметра dwStyle, расширен:
void EnableDocking(DWORD dwStyle);
Флаг |
Описание |
CBRS_ALIGN_TOP |
Панель управления можно пристыковать к верхней границе окна |
CBRS_ALIGN_BOTTOM |
Панель управления можно пристыковать к нижней границе окна |
CBRS_ALIGN_LEFT |
Панель управления можно пристыковать к левой границе окна |
CBRS_ALIGN_RIGHT |
Панель управления можно пристыковать к правой границе окна |
CBRS_ALIGN_ANY |
Панель управления можно пристыковать к любой границе окна |
Если ни один из флагов не установлен, и параметр dwStyle равен нулю, то данная панель управления не может быть пристыкована ни к одной границе окна. В этом случае надо вызвать метод CFrameWnd::FloatControlBar и панель управления появится в отдельном мини-окне.
Панель управления можно пристыковать только к тем границам окна, которые одновременно выбраны методами CFrameWnd::EnableDocking и CControlBar::EnableDocking.
Чтобы пристыковать панель управления к границе окна, надо вызвать метод DockControlBar класса CFrameWnd:
void DockControlBar( CControlBar * pBar, UINT nDockBarID = 0, LPCRECT lpRect = NULL );
Панель управления, заданная параметром pBar, пристыковывается к границе окна, указанной параметром nDockBarID. В качестве nDockBarID можно использовать один или несколько флагов, перечисленных ниже.
Флаг |
Описание |
AFX_IDW_DOCKBAR_TOP |
Панель управления присоединяется к верхней границе окна |
AFX_IDW_DOCKBAR_BOTTOM |
Панель управления присоединяется к нижней границе окна |
AFX_IDW_DOCKBAR_LEFT |
Панель управления присоединяется к левой границе окна |
AFX_IDW_DOCKBAR_RIGHT |
Панель управления присоединяется к правой границе окна |
Если параметр nDockBarID равен нулю, то панель управления присоединяется к любой стороне окна.
Как видите, параметр nDockBarID может задавать несколько сторон окна одновременно. В этом случае панель управления присоединяется к той границе окна, которая одновременно указана методами CFrameWnd::EnableDocking и CControlBar::EnableDocking. Если таких границ несколько, то они выбираются в следующем порядке - сначала верхняя, а если к ней панель не может быть присоединена, тогда нижняя, левая, и правая стороны окна.
Последний параметр lpRect определяет, где именно будет отображаться панель управления. Положение панели задается в экранных координатах.
Если вам надо отобразить панель управления в отдельном мини-окне и не пристыковывать его к границам окна, то вместо метода DockControlBar вызовите метод FloatControlBar класса CFrameWnd:
CFrameWnd* FloatControlBar( CControlBar * pBar, CPoint point );
Панель управления, указанная параметром pBar, отображается в отдельном мини-окне. Расположение окна панели управления задается параметром point, который указывает координаты верхнего левого угла панели управления. Используются экранные координаты.
Метод FloatControlBar возвращает указатель на текущее окно.
По умолчанию, панель управления не имеет заголовка. Для установки и изменения текста заголовка используйте метод SetWindowText, определенный в классе CWnd:
void SetWindowText(LPCTSTR lpszString);
Параметр lpszString должен содержать указатель на объект класса CString или строку символов, закрытую двоичным нулем. В ней должен быть записан заголовок для панели управления.
Панель управления может иметь постоянную форму, которую пользователь не может изменить, или может быть динамически изменяемой. В последнем случае пользователь может менять форму панели управления с помощью мыши.
Можно изменить форму панели управления или нет, определяется методом Create класса CToolBar. Если при создании панели управления был установлен флаг CBRS_SIZE_DYNAMIC ее форму можно менять, а если был установлен флаг CBRS_SIZE_FIXED - нельзя.
В состав класса CToolBar входит метод SetButtonStyle. Этот метод позволяет определить режим работы кнопок панели управления, сгруппировать несколько кнопок вместе:
void SetButtonStyle( int nIndex, UINT nStyle );
Параметр nIndex выбирает индекс кнопки или разделителя в панели управления, а параметр nStyle позволяет установить новый режим работы для выбранной кнопки или разделителя. Индекс кнопки или разделителя соответствует ее порядковому номеру в панели управления.
В качестве параметра nStyle можно указать комбинацию из следующих флагов:
Флаг |
Режим кнопки или разделителя |
TBBS_BUTTON |
Стандартная кнопка |
TBBS_SEPARATOR |
Разделитель |
TBBS_CHECKBOX |
Переключатель |
TBBS_GROUP |
С данной кнопки начинается группа кнопок |
TBBS_CHECKGROUP |
С данной кнопки начинается группа переключателей |
TBBS_WRAPPED |
Этот флаг позволяет создать панель управления, в которой кнопки расположены в несколько рядов. Установите этот флаг для самых последних кнопок в каждом ряду. Кнопка, следующая за кнопкой с установленным флагом TBBS_WRAPPED, отображается в новом ряду |
¨ Заметим, что стиль TBBS_WRAPPED не описан в документации Microsoft Visual C++, но активно используется в примерах приложений и работает как положено
Перед тем, как изменить режим работы кнопки или указать группу кнопок, рекомендуется определить текущий режим кнопки. Для этого следует воспользоваться методом GetButtonStyle класса CToolBar:
UINT GetButtonStyle(int nIndex) const;
Метод возвращает комбинацию флагов, определяющих режим работы кнопки с индексом nIndex. Мы уже рассматривали эти флаги при описании метода SetButtonStyle класса CToolBar.
Вы можете определить индекс (порядковый номер) любой кнопки панели управления, если знаете ее идентификатор. Для этого предназначен метод CommandToIndex класса CToolBar. Он возвращает индекс кнопки, имеющей идентификатор nIDFind. Если вы укажите идентификатор несуществующей кнопки, тогда метод CommandToIndex возвращает значение -1:
int CommandToIndex(UINT nIDFind);
Обратную задачу выполняет метод GetItemID класса CToolBar. Этот метод возвращает идентификатор кнопки с индексом nIndex. Если в качестве параметра nIndex указать индекс разделителя, тогда метод GetItemID возвращает идентификатор ID_SEPARATOR:
UINT GetItemID(int nIndex) const;
Панель управления, созданная на основе класса CToolBar, состоит из одних только кнопок и разделителей. Стандартные средства для отображения в ней других органов управления, таких как поле редактирование или список, отсутствуют.
Однако, вы все же можете вывести в панели toolbar другие органы управления. Так как панель управления является ни чем иным как дочерним окном, то вы можете самостоятельно разместить в нем другие органы управления.
Для этого предлагается использовать следующий метод.
¨ В том месте панели управления toolbar, где вы желаете разместить дополнительный орган управления, вставьте разделитель
¨ Сразу после создания панели управления, измените размер разделителя вместо которого надо вставить другой орган управления. Присвойте ему другой идентификатор
¨ Создаем на месте разделителя нужный вам орган управления. Указываем, для него в качестве родительского окна - идентификатор панели управления
В состав класса CToolBar входит метод SetButtonInfo. Этот метод позволяет изменить внешний вид панели управления. Используя метод SetButtonInfo, можно изменить идентификатор, изображение, режим работы и размер разделителей кнопок панели управления:
void SetButtonInfo( int nIndex, UINT nID, UINT nStyle, int iImage );
Параметр nIndex метода SetButtonInfo определяет индекс кнопки или разделителя. Остальные параметры задают новые характеристики для этой кнопки.
Параметр nID позволяет задать новый идентификатор кнопки или разделителя.
Параметр nStyle определяет режимы работы данной кнопки и может содержать комбинацию флагов, которые уже были нами представлены при описании метода SetButtonStyle класса CToolBar.
Последний параметр метода позволяет изменить изображение кнопки. Новое изображение берется из ресурса панели управления. При этом используется изображение с порядковым номером, заданным параметром iImage.
Если вы вызвали метод SetButtonInfo и указали ему через параметр nIndex индекс разделителя, то назначение параметра iImage изменяется. В этом случае параметр iImage будет определять новую ширину разделителя.
Перед тем как изменять характеристики кнопок панели управления, вы можете воспользоваться методом GetButtonStyle класса CToolBar, чтобы узнать текущие характеристики кнопки:
void GetButtonInfo( int nIndex, UINT& nID, UINT& nStyle, int& iImage ) const;
В параметре nIndex надо указать индекс кнопки, о которой надо получить информацию. Через остальные три параметра методу GetButtonInfo передаются ссылки на переменные, в которые будут записаны характеристики кнопок.
Через параметр nID вы получите идентификатор кнопки, через параметр nStyle - режим ее работы, а через параметр iImage - индекс изображения кнопки в ресурсе панели управления.
Если метод GetButtonInfo вызывается для разделителя, то через параметр iImage вы получите не индекс изображения, а ширину разделителя в пикселах.
Когда вы создаете на панели управления дополнительный орган управления, например поле редактирования, вы должны указать координаты прямоугольной области которую он будет занимать. Для определения этих координат следует воспользоваться методом GetItemRect класса CToolBar:
virtual void GetItemRect(int nIndex, LPRECT lpRect);
Вызов метода GetItemRect заполняет структуру lpRect координатами прямоугольной области, занимаемой кнопкой или разделителем, с индексом nIndex.
Изучая пример приложения DOCKTOOL, поставляемого вместе с Microsoft Visual C++, мы обнаружили, что для отображения и удаления с экрана панелей управления используется метод OnBarCheck.
Метод вызывается из таблицы сообщений класса главного окна приложения CMainFrame. Для этого используется макрокоманда ON_COMMAND_EX. В случае прихода командных сообщений от строк меню, отвечающих за показ на экране панелей управления, вызывается метод OnBarCheck и ему в качестве параметра передается идентификатор соответствующей строки меню:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) // Другие макрокоманды таблицы сообщений ON_COMMAND_EX(IDW_BROWSE_BAR, OnBarCheck) ON_COMMAND_EX(IDW_DEBUG_BAR, OnBarCheck) ON_COMMAND_EX(IDW_EDIT_BAR, OnBarCheck) END_MESSAGE_MAP()
Таким образом, когда пользователь выбирает из меню View приложения строку с именем панели управления, выдается командное сообщение с идентификатором, соответствующим строке меню и самой панели управления (идентификаторы панелей управления идентичны идентификаторам соответствующих строк меню View). Командное сообщение обрабатывается таблицей сообщений класса CMainFrame. Для его обработки вызывается метод OnBarCheck, которому в качестве параметра передается идентификатор панели управления.
Если вы решите поискать описание метода OnBarCheck в справочной системе Microsoft Visual C++, вас ждет разочарование. Ни в класс CFrameWnd, ни в один из его базовых классов метод OnBarCheck не входит. Когда вы вернетесь к исходным текстам самого приложения, в них вы также не обнаружите определение этого метода.
Мы проявили настойчивость и смогли обнаружить метод OnBarCheck только в исходных текстах библиотеки классов MFC. Оказывается, несмотря на отсутствие описания метода OnBarCheck в документации библиотеки MFC, этот метод входит в хорошо известный вам класс CFrameWnd.
В файле Afxwin.h, в котором объявлен класс CFrameWnd, вы можете найти объявления входящих в него методов OnUpdateControlBarMenu и OnBarCheck:
class CFrameWnd : public CWnd { // ... // Command Handlers public: afx_msg void OnUpdateControlBarMenu(CCmdUI* pCmdUI); afx_msg BOOL OnBarCheck(UINT nID); }
Определения исходных текстов методов OnUpdateControlBarMenu и OnBarCheck содержатся в файле Winfrm.cpp.
В файле Winfrm.cpp также можно найти обращения к методам OnUpdateControlBarMenu и OnBarCheck в таблице сообщений класса CFrameWnd. Приведем соответствующий фрагмент этой таблицы:
BEGIN_MESSAGE_MAP(CFrameWnd, CWnd) // turning on and off standard frame gadgetry ON_UPDATE_COMMAND_UI(ID_VIEW_STATUS_BAR, OnUpdateControlBarMenu) ON_COMMAND_EX(ID_VIEW_STATUS_BAR, OnBarCheck) ON_UPDATE_COMMAND_UI(ID_VIEW_TOOLBAR, OnUpdateControlBarMenu) ON_COMMAND_EX(ID_VIEW_TOOLBAR, OnBarCheck) END_MESSAGE_MAP()
Две пары макрокоманд ON_UPDATE_COMMAND_UI и ON_COMMAND_EX вызывают методы OnUpdateControlBarMenu и OnBarCheck для обработки командных сообщений с идентификаторами ID_VIEW_STATUS_BAR и ID_VIEW_TOOLBAR. Командные сообщения с такими идентификаторами поступают при выборе строк Toolbar и Status Bar меню View.
Меню View, содержащее строки Toolbar и Status Bar, вставляется во все приложения с оконным интерфейсом, которые созданы с использованием средств MFC AppWizard.
Рассмотрим теперь сами методы OnBarCheck и OnUpdateControlBarMenu. Метод OnBarCheck класса CFrameWnd определен следующим образом:
////////////////////////////////////////////////////////////// // Метод OnBarCheck класса CFrameWnd BOOL CFrameWnd::OnBarCheck(UINT nID) { ASSERT(ID_VIEW_STATUS_BAR == AFX_IDW_STATUS_BAR); ASSERT(ID_VIEW_TOOLBAR == AFX_IDW_TOOLBAR); CControlBar* pBar = GetControlBar(nID); if (pBar != NULL) { ShowControlBar(pBar, (pBar->GetStyle() & WS_VISIBLE) == 0, FALSE); return TRUE; } return FALSE; }
Отладочная версия метода OnBarCheck класса CFrameWnd проверяет соответствие идентификаторов ID_VIEW_STATUS_BAR, AFX_IDW_STATUS_BAR и ID_VIEW_TOOLBAR, AFX_IDW_TOOLBAR. Отметим, что эти идентификаторы определены в файле Afxres.h следующим образом:
#define AFX_IDW_TOOLBAR 0xE800 #define AFX_IDW_STATUS_BAR 0xE801 #define ID_VIEW_TOOLBAR 0xE800 #define ID_VIEW_STATUS_BAR 0xE801
Метод GetControlBar класса CFrameWnd определяет указатель на объект класса CControlBar, который представляет панель управления или панель состояния с идентификатором nID. Идентификаторы строк меню ID_VIEW_TOOLBAR и ID_VIEW_STATUS_BAR соответствуют стандартным идентификаторам панели управления AFX_IDW_TOOLBAR и панели состояния AFX_IDW_STATUS_BAR.
При выборе из меню View строки Toolbar передается командное сообщение ID_VIEW_TOOLBAR, а при выборе строки Status bar - сообщение ID_VIEW_STATUS_BAR. Во время обработки этих сообщений, вызов метода GetControlBar определит объект класса CControlBar, соответствующий либо панели управления AFX_IDW_TOOLBAR, либо панели состояния AFX_IDW_STATUS_BAR.
Затем метод ShowControlBar отображает или закрывает соответствующую панель. Если панель была открыта, метод ShowControlBar скрывает ее и наоборот.
Аналогичным образом устроен метод OnUpdateControlBarMenu класса CFrameWnd, который обрабатывает команды обновления (по умолчанию, он обрабатывает команды обновления от строк Toolbar и Status bar меню View).
Метод OnUpdateControlBarMenu проверяет, отображается ли на экране панель управления или панель состояния с идентификатором, соответствующим идентификатору команды обновления. Если панель отображается, то строка меню отмечается символом Ö:
////////////////////////////////////////////////////////////// // Метод OnUpdateControlBarMenu класса CFrameWnd void CFrameWnd::OnUpdateControlBarMenu(CCmdUI* pCmdUI) { ASSERT(ID_VIEW_STATUS_BAR == AFX_IDW_STATUS_BAR); ASSERT(ID_VIEW_TOOLBAR == AFX_IDW_TOOLBAR); CControlBar* pBar = GetControlBar(pCmdUI->m_nID); if (pBar != NULL) { pCmdUI->SetCheck((pBar->GetStyle() & WS_VISIBLE) != 0); return; } pCmdUI->ContinueRouting(); }
В конце метода OnUpdateControlBarMenu класса CFrameWnd вызывается метод ContinueRouting класса CCmdUI, который направляет команду обновления для дальнейшей обработки другим классам MFC (см. раздел “Обработка командных сообщений”).
Создайте новый проект под названием MultiBar. В качестве типа приложения выберите из списка Type строку Application. Настройте проект MultiBar, точно также как вы настраивали проект Bar - укажите, что приложение будет работать с библиотекой классов MFC.
Наберите в редакторе исходный текст приложения и сохраните его в файле MultiBar.cpp (листинг 3.7). За основу вы можете взять файл Bar.cpp приложения Bar. Включите готовый файл MultiBar.cpp в проект.
Листинг 3.7. Файл MultiBar.cpp
//============================================================ // Приложение MultiBar // (c) Frolov G.V., 1996 // E-mail: frolov@glas.apc.org //============================================================ // Исключаем редко используемые определения из // включаемых файлов #define VC_EXTRALEAN // Включаемый файл для MFC #include <afxwin.h> #include <afxext.h> #include <afxcmn.h> // Включаемый файл для ресурсов приложения #include "resource.h" //============================================================ // Класс CMultiBarApp - главный класс приложения //============================================================ class CMultiBarApp : public CWinApp { public: // Мы будем переопределять метод InitInstance virtual BOOL InitInstance(); }; // Создаем объект приложение класса CMultiBarApp CMultiBarApp MultiBarApp; //============================================================ // Класс CExtendedBar - класс панели управления Extended //============================================================ class CExtendedBar : public CToolBar { public: // Дополнительные органы управления панели Extended CEdit m_edit; // текстовый редактор CComboBox m_combo_box; // список с текстовым редактором }; //============================================================ // Класс CMultiBarWindow - представляет главное окно //============================================================ class CMultiBarWindow : public CFrameWnd { // Определяем панели управления protected: // Панель управления Player CToolBar m_wndPlayerBar; // Панель управления Style CToolBar m_wndStyleBar; // Панель управления Extended CExtendedBar m_wndExtendedBar; protected: // Метод OnCreate используется для создания // панелей управления afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); public: // Объявляем конструктор класса CMultiBarWindow CMultiBarWindow(); // Объявляем методы для обработки команд от панелей // управления afx_msg void BarCommandOne(); afx_msg void BarCommandRange(UINT nID); afx_msg void AddStringToComboBox(); // Объявляем метод для обработки команд от меню View afx_msg BOOL ShowStyle(UINT nID); // Макрокоманда необходима, так как класс // CMultiBarWindow обрабатывает сообщения DECLARE_MESSAGE_MAP() }; //============================================================ // Метод BarCommandRange // Обрабатывает команды от панели управления //============================================================ void CMultiBarWindow::BarCommandRange( UINT nID ) { // Обработчик не выполняет никакой работы } //============================================================ // Метод BarCommand // Обрабатывает команды от кнопок панелей управления //============================================================ void CMultiBarWindow::BarCommandOne() { // Отображаем сообщение о том, что команда не реализована MessageBox("Command not implemented"); } //============================================================ // Метод AddStringToComboBox // Обрабатывает команду от кнопки Add панели // управления Extended //============================================================ void CMultiBarWindow::AddStringToComboBox() { // Получаем строку, введенную в текстовом редакторе m_edit char tmpStr[39]; m_wndExtendedBar.m_edit.GetLine(0, tmpStr,40); // Добавляем новую строку к списку m_combo_box m_wndExtendedBar.m_combo_box.AddString(tmpStr); } //============================================================ // Метод ShowStyle // Обрабатывает команды от меню View //============================================================ BOOL CMultiBarWindow::ShowStyle(UINT nID) { // Определяем указатель на панель управления, // соответствующую идентификатору nID CControlBar* pBar = GetControlBar(nID); // Определяем, отображается в данный момент панель // управления на экране или нет BOOL bShow = ((pBar -> GetStyle() & WS_VISIBLE) != 0); // Изменяем состояние панели управления: если панель // управления отображается на экране, удаляем ее с экрана, // если нет - отображаем ShowControlBar( pBar, !bShow, FALSE); return TRUE; } //============================================================ // Таблица сообщений класса CMultiBarWindow //============================================================ BEGIN_MESSAGE_MAP(CMultiBarWindow, CFrameWnd) // Макрокоманда вызывает метод OnCreate ON_WM_CREATE() // Обработчики команд от панели управления Player ON_COMMAND(ID_STOP, BarCommandOne) ON_COMMAND(ID_PLAY, BarCommandOne) ON_COMMAND(ID_PAUSE, BarCommandOne) ON_COMMAND_RANGE(ID_LEFT, ID_RIGHT, BarCommandRange) ON_COMMAND_RANGE(ID_TYPE, ID_WAVE, BarCommandRange) // Обработчики команд от панели управления Extended ON_COMMAND(ID_ADD, AddStringToComboBox) ON_COMMAND_RANGE(ID_FOTO, ID_DISK, BarCommandRange) // Обработчик команд от панели управления Style ON_COMMAND_RANGE(ID_UNDERLINE, ID_MARK_4, BarCommandRange) // Обработчики команд меню View ON_COMMAND_EX(ID_Style, ShowStyle) ON_COMMAND_EX(ID_Extended, ShowStyle) ON_COMMAND_EX(ID_Player, ShowStyle) END_MESSAGE_MAP() //============================================================ // Метод InitInstance класса CMultiBarApp // Создает главное окно приложения и отображает его на экране //============================================================ BOOL CMultiBarApp::InitInstance() { m_pMainWnd = new CMultiBarWindow(); m_pMainWnd -> ShowWindow(m_nCmdShow); m_pMainWnd -> UpdateWindow(); return TRUE; } //============================================================ // Конструктор класса CMultiBarWindow //============================================================ CMultiBarWindow::CMultiBarWindow() { // Создаем окно приложения, соответствующее // данному объекту класса CMultiBarWindow Create(NULL, "Multi Bar", WS_OVERLAPPEDWINDOW, rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU)); } //============================================================ // Метод OnCreate класса CMultiBarWindow // Используется для создания панелей управления //============================================================ int CMultiBarWindow::OnCreate(LPCREATESTRUCT lpCreateStruct) { // Вызываем метод OnCreate базового класса if(CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // Разрешаем присоединение панелей управления ко // всем сторонам окна CMultiBarWindow EnableDocking(CBRS_ALIGN_ANY); // Переменные для изменения стилей кнопок UINT nBarStyle; int nIndex; //============================================================ // Создаем и отображаем панель управления Player //============================================================ // Создаем панель управления toolbar if(!m_wndPlayerBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_SIZE_DYNAMIC | CBRS_BOTTOM | CBRS_TOOLTIPS, ID_Player)) { // Ошибка при создании панели управления TRACE0("Failed to create toolbar\n"); return -1; } // Загружаем ресурс панели управления Player if(!m_wndPlayerBar.LoadToolBar(IDR_PLAYER)) { // Ошибка при загрузке ресурса панели управления TRACE0("Failed to load toolbar\n"); return -1; } // Устанавливаем заголовок панели управления Player m_wndPlayerBar.SetWindowText("Player"); // Из кнопок с идентификаторами ID_TYPE, ID_CD_DRV и // ID_WAVE делаем трехпозиционный переключатель с зависимой // фиксацией. Устанавливаем для этих кнопок стиль // TBBS_CHECKGROUP nIndex = m_wndPlayerBar.CommandToIndex(ID_TYPE); nBarStyle = m_wndPlayerBar.GetButtonStyle(nIndex) | TBBS_CHECKGROUP; m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle); nIndex = m_wndPlayerBar.CommandToIndex(ID_CD_DRV); nBarStyle = m_wndPlayerBar.GetButtonStyle(nIndex) | TBBS_CHECKGROUP; m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle); nIndex = m_wndPlayerBar.CommandToIndex(ID_WAVE); nBarStyle = m_wndPlayerBar.GetButtonStyle(nIndex) | TBBS_CHECKGROUP; m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle); // Из кнопки с идентификатором ID_PAUSE делаем // переключатель. Устанавливаем для этой кнопки стиль // TBBS_CHECKBOX nIndex = m_wndPlayerBar.CommandToIndex(ID_PAUSE); nBarStyle = m_wndPlayerBar.GetButtonStyle(nIndex) | TBBS_CHECKBOX; m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle); // Разрешаем пристывковывать панель управления Player к // любой строке родительского окна m_wndPlayerBar.EnableDocking(CBRS_ALIGN_ANY ); // Пристывковываем панель управления Player к // родительскому окну DockControlBar(&m_wndPlayerBar); //============================================================ // Создаем и отображаем панель управления Style //============================================================ // Создаем панель управления toolbar if(!m_wndStyleBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_SIZE_FIXED | CBRS_TOP | CBRS_TOOLTIPS, ID_Style)) { // Ошибка при создании панели управления TRACE0("Failed to create toolbar\n"); return -1; } // Загружаем ресурс панели управления Style if(!m_wndStyleBar.LoadToolBar(IDR_STYLE)) { // Ошибка при загрузке ресурса панели управления TRACE0("Failed to load toolbar\n"); return -1; } // Устанавливаем заголовок панели управления Style m_wndStyleBar.SetWindowText("Style"); // Запрещаем пристывковывать панель управления Player к // родительскому окну m_wndStyleBar.EnableDocking(0); // Устанавливаем для кнопки ID_SUBSCRIPT стиль TBBS_WRAPPED nIndex = m_wndStyleBar.CommandToIndex(ID_SUBSCRIPT); nBarStyle = m_wndStyleBar.GetButtonStyle(nIndex) | TBBS_WRAPPED; m_wndStyleBar.SetButtonStyle(nIndex, nBarStyle); // Устанавливаем для кнопки ID_TEXT_JUSTIFY стиль // TBBS_WRAPPED nIndex = m_wndStyleBar.CommandToIndex(ID_TEXT_JUSTIFY); nBarStyle = m_wndStyleBar.GetButtonStyle(nIndex) | TBBS_WRAPPED; m_wndStyleBar.SetButtonStyle(nIndex, nBarStyle); // Отображаем панель управления Style в мини-окне. // Начальные координаты панели управления определяются // значением объекта pointStyleBar класса CPoint CPoint pointStyleBar(100, 100); FloatControlBar(&m_wndStyleBar, pointStyleBar); //============================================================ // Создаем и отображаем панель управления Extended //============================================================ // Создаем панель управления Extended if(!m_wndExtendedBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_SIZE_DYNAMIC | CBRS_TOP | CBRS_TOOLTIPS, ID_Extended)) { // Ошибка при создании панели управления TRACE0("Failed to create toolbar\n"); return -1; } // Загружаем ресурс панели управления Extended if(!m_wndExtendedBar.LoadToolBar(IDR_EXTENDED)) { // Ошибка при загрузке ресурса панели управления TRACE0("Failed to load toolbar\n"); return -1; } // Устанавливаем заголовок панели управления Extended m_wndExtendedBar.SetWindowText("Extended"); // Увеличиваем размер первого разделителя. // Этот разделитель имеет индекс 2 m_wndExtendedBar.SetButtonInfo(2, IDW_EDIT, TBBS_SEPARATOR, 130); // Определяем координаты прямоугольной области панели // управления, занимаемой разделителем CRect rectEdit; m_wndExtendedBar.GetItemRect(2, &rectEdit); // Делаем отступ с левой и правой стороны rectEdit.left += 6; rectEdit.right -= 6; // Размещаем на панели управления Extended, в области // rectEdit однострочный текстовый редактор if(!m_wndExtendedBar.m_edit.Create(WS_CHILD | ES_AUTOHSCROLL|WS_VISIBLE|WS_TABSTOP|WS_BORDER, rectEdit, &m_wndExtendedBar, IDW_EDIT)) { // Ошибка при создании текстового редактора TRACE0("Failed to create edit-box\n"); return FALSE; } // Увеличиваем размер второго разделителя. // Этот разделитель имеет индекс 4 m_wndExtendedBar.SetButtonInfo(4, IDW_COMBO, TBBS_SEPARATOR, 150); // Определяем координаты прямоугольной области панели // управления, занимаемой разделителем CRect rectComboBox; m_wndExtendedBar.GetItemRect(4, &rectComboBox); // Делаем отступ с левой и правой стороны rectComboBox.left += 6; rectComboBox.right -= 6; // Увеличиваем высоту прямоугольной области, чтобы в ней // можно было разместить список combo-box rectComboBox.bottom = rectComboBox.top + 80; // Размещаем на панели управления Extended, в области // rectComboBox список combo-box if(!m_wndExtendedBar.m_combo_box.Create( CBS_DROPDOWN | WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_AUTOHSCROLL | CBS_DISABLENOSCROLL, rectComboBox, &m_wndExtendedBar, IDW_COMBO)) { // Ошибка при создании списка TRACE0("Failed to create combo-box\n"); return FALSE; } // Добавляем три строки в список m_combo_box m_wndExtendedBar.m_combo_box.AddString("One"); m_wndExtendedBar.m_combo_box.AddString("Two"); m_wndExtendedBar.m_combo_box.AddString("Third"); // Разрешаем пристывковывать панель управления Extended к // любой из строн родительского окна m_wndExtendedBar.EnableDocking(CBRS_ALIGN_ANY ); // Пристывковываем панель управления Extended к // родительскому окну DockControlBar(&m_wndExtendedBar); return 0; }
Создайте новый файл ресурсов и включите его в проект под именем MultiBar.rc. Теперь надо создать три панели управления toolbar и включить их в файл ресурсов. Изображения кнопок панелей управления вы можете нарисовать самостоятельно или скопировать их из файла ресурсов Common.res, поставляемого вместе с Microsoft Visual C++. Более подробная информация о файле Common.res представлена в разделе “Ресурсы Microsoft”.
При создании панелей управления руководствуйтесь информацией из файла ресурсов приложения MultiBar (листинг 3.3) и изображениями кнопок панелей управления (рис. 3.3, 3.4, 3.5).
Первая панель управления должна иметь идентификатор IDR_PLAYER и содержать девять кнопок с идентификаторами ID_LEFT, ID_PLAY, ID_RIGHT, ID_STOP, ID_PAUSE, ID_EJECT, ID_TYPE, ID_CD_DRV и ID_WAVE. Между кнопками ID_RIGHT и ID_STOP, ID_PAUSE и ID_EJECT, ID_EJECT и ID_TYPE вставьте разделители. Для каждой из кнопок вы также можете ввести их текстовые описания.
Вторая панель управления должна иметь идентификатор IDR_STYLE и содержать двенадцать кнопок с идентификаторами ID_UNDERLINE, ID_2_UNDERLINE, ID_SUPERSCRIPT, ID_SUBSCRIPT, ID_TEXT_LEFT, ID_ID_TEXT_CENTER, ID_TEXT_RIGHT, ID_TEXT_JUSTIFY, ID_MARK_1, ID_MARK_2, ID_MARK_3 и ID_MARK_4. Для первых шести кнопок введите их текстовые описания.
Третья панель управления должна иметь идентификатор IDR_EXTENDED. Определите в ней четыре кнопки с идентификаторами ID_FOTO, ID_PRINTER, ID_ADD и ID_DISK. Между кнопками ID_PRINTER и ID_ADD, а также ID_ADD и ID_DISK вставьте разделители. Введите текстовые описания кнопок.
В файл ресурсов, кроме панелей управления toolbar, включите меню IDR_MENU с тремя строками Style, Extended и Player, которые имеют идентификаторы ID_Style, ID_Extended и ID_Player. Обратите внимание, что идентификаторы строк меню соответствуют идентификаторам панелей управления.
Файл ресурсов приложения MultiBar мы привели в листинге 3.8. В нем определены ресурсы панелей управления toolbar с идентификаторами IDR_PLAYER, IDR_STYLE и IDR_EXTENDED, ресурсы изображений этих панелей управления с соответствующими идентификаторами, меню приложения, а также несколько строковых ресурсов с описаниями кнопок панелей управления. Идентификаторы строковых ресурсов соответствуют идентификаторам кнопок панелей управления, которые они описывают.
Листинг 3.8. Файл MultiBar.rc
//Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // // Russian resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) #ifdef _WIN32 LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT #pragma code_page(1251) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Toolbar // IDR_PLAYER TOOLBAR DISCARDABLE 16, 15 BEGIN BUTTON ID_LEFT BUTTON ID_PLAY BUTTON ID_RIGHT SEPARATOR BUTTON ID_STOP BUTTON ID_PAUSE SEPARATOR BUTTON ID_EJECT SEPARATOR BUTTON ID_TYPE BUTTON ID_CD_DRV BUTTON ID_WAVE END IDR_STYLE TOOLBAR DISCARDABLE 16, 15 BEGIN BUTTON ID_UNDERLINE BUTTON ID_2_UNDERLINE BUTTON ID_SUPERSCRIPT BUTTON ID_SUBSCRIPT BUTTON ID_TEXT_LEFT BUTTON ID_ID_TEXT_CENTER BUTTON ID_TEXT_RIGHT BUTTON ID_TEXT_JUSTIFY BUTTON ID_MARK_1 BUTTON ID_MARK_2 BUTTON ID_MARK_3 BUTTON ID_MARK_4 END IDR_EXTENDED TOOLBAR DISCARDABLE 16, 15 BEGIN BUTTON ID_FOTO BUTTON ID_PRINTER SEPARATOR BUTTON ID_ADD SEPARATOR BUTTON ID_DISK END ////////////////////////////////////////////////////////////// // // Bitmap // IDR_PLAYER BITMAP DISCARDABLE "player.bmp" IDR_STYLE BITMAP DISCARDABLE "style.bmp" IDR_EXTENDED BITMAP DISCARDABLE "extended.bmp" ////////////////////////////////////////////////////////////// // // Menu // IDR_MENU MENU DISCARDABLE BEGIN POPUP "View" BEGIN MENUITEM "Style", ID_Style MENUITEM "Extended", ID_Extended MENUITEM "Player", ID_Player END END ////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE DISCARDABLE BEGIN ID_LEFT "Rewind to begin\nRewind to begin" ID_RIGHT "Rewind to end\nRewind to end" ID_PLAY "Play\nPlay" ID_STOP "Stop\nStop" ID_PAUSE "Pause\nPause" ID_EJECT "Eject\nEject" ID_TYPE "Type drive\nType" ID_CD_DRV "CD drive\nCD" ID_BUTTON40010 "Wave/nWave File" ID_WAVE "Wave file\nWAWE" ID_UNDERLINE "Underline\nUnderline" ID_2_UNDERLINE "Double underline\nDouble underline" ID_SUPERSCRIPT "Superscript\nSuperscript" ID_SUBSCRIPT "Subscript\nSubscript" END STRINGTABLE DISCARDABLE BEGIN ID_ADD "Add from edit-box item to combo- box\nAdd item to list" END STRINGTABLE DISCARDABLE BEGIN ID_TEXT_LEFT "Left text\nLeft" ID_ID_TEXT_CENTER "Center text\nCenter" ID_TEXT_RIGHT "Right text\nRight" ID_TEXT_JUSTIFY "Justify text\nJustify" ID_FOTO "Foto\nFoto" ID_PRINTER "Printer\nPrinter" ID_DISK "Disk\nDisk" END STRINGTABLE DISCARDABLE BEGIN IDW_EDIT "Edit\nEdit" IDW_COMBO "Combo box\nCombo box" END #endif // Russian resources ////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // Generated from the TEXTINCLUDE 3 resource. // ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
Изображения кнопок панелей управления располагаются в отдельных файлах player.bmp (рис. 3.14), style.bmp (рис. 3.15) и extended.bmp (рис. 3.13) в главном каталоге проекта. Файлы изображений панелей управления содержат только изображения кнопок. В них не представлены разделители и дополнительные органы управления.
В принципе, изображения панели управления можно редактировать в любом графическом редакторе, который работает с 16-цветными изображениями в формате BMP. Примером такого приложения является графический редактор Microsoft Paint, поставляемый вместе с операционными системами Windows 95 и Windows NT версии 4.0. Однако, лучше использовать редактор ресурсов среды Microsoft Visual C++, так как он не только разрешает редактировать существующие кнопки, но также позволяет добавлять новые кнопки вместе с соответствующими идентификаторами и строковыми ресурсами.
Рис. 3.13. Изображение кнопок панели управления Extended
Рис. 3.14. Изображение кнопок панели управления Player
Рис. 3.15. Изображение кнопок панели управления Style
Идентификаторы всех ресурсов приложения MultiBar и идентификаторы дополнительных органов управления панели Extended определены в файле resource.h. Этот файл автоматически создается редактором ресурсов Microsoft Visual C++.
Мы привели исходный текст файла resource.h в листинге 3.9.
Листинг 3.9. Файл resource.h
//{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by MultiBar.rc // #define IDW_EDIT 101 #define IDW_COMBO 102 #define IDR_PLAYER 103 #define IDR_STYLE 105 #define IDR_EXTENDED 107 #define IDR_MENU 109 #define ID_LEFT 40001 #define ID_RIGHT 40002 #define ID_PLAY 40003 #define ID_STOP 40004 #define ID_PAUSE 40005 #define ID_EJECT 40007 #define ID_TYPE 40008 #define ID_CD_DRV 40009 #define ID_WAVE 40011 #define ID_UNDERLINE 40012 #define ID_2_UNDERLINE 40013 #define ID_SUPERSCRIPT 40014 #define ID_SUBSCRIPT 40015 #define ID_TEXT_LEFT 40017 #define ID_ID_TEXT_CENTER 40018 #define ID_TEXT_RIGHT 40019 #define ID_TEXT_JUSTIFY 40020 #define ID_MARK_1 40021 #define ID_MARK_2 40022 #define ID_MARK_3 40023 #define ID_MARK_4 40024 #define ID_FOTO 40025 #define ID_PRINTER 40026 #define ID_DISK 40027 #define ID_Style 40029 #define ID_Extended 40030 #define ID_Buttons 40031 #define ID_Player 40031 #define ID_ADD 40032 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 110 #define _APS_NEXT_COMMAND_VALUE 40033 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 103 #endif #endif
Постройте приложение MultiBar и запустите его. На экране появится главное окно приложения MultiBar, в котором отображаются сразу три панели управления - Extended, Player и Slyle. Панели управления Extended и Player присоединены к верхней и нижней границам окна, а панель управления Slyle отображается в отдельном мини-окне (рис. 3.16).
Рис. 3.16. Приложение MultiBar
Приложение MultiBar имеет меню View, состоящее из названий панелей управления Extended, Player и Slyle. Выбирая из меню View названия панелей управления, вы можете убрать их с экрана и опять отобразить (закрыть и открыть).
Панели управления, которые отображаются в мини-окнах, также можно закрыть, если нажать на переключатель в верхнем правом углу окна. Чтобы снова открыть закрытую панель управления, используйте меню View.
Панель управления Player состоит из девяти кнопок, сгруппированных в четыре отдельные группы. Панель Player предназначена для управления музыкальным центром. Все кнопки имеют краткие подсказки.
При нажатии на кнопки панели управления Player на экране появляется сообщение, о том, что команда не поддерживается. Обратите внимание на кнопки Pause, Type, CD и WAVE.
Кнопка Pause работает как переключатель. Если вы на нее нажмете - она остается в нажатом положении до тех пор, пока вы не нажмете на нее еще один раз. Кнопки Type, CD и WAVE работают вместе как переключатель с зависимой фиксацией. Одновременно может быть нажата только одна из этих кнопок.
Панель управления Player можно присоединить к трем из четырех сторон главного окна приложения - к верхней, левой и нижней. К правой стороне окна панель Player не присоединяется (рис. 3.17). Вы также можете использовать для панели Player отдельное мини окно, форму которого можно изменять.
Рис. 3.17. Изменение формы панели управления Player
Панель Extended демонстрирует использование в панелях управления toolbar дополнительных органов управления - поля редактирования и списка combo-box. Сразу после запуска приложения MultiBar в списке combo-box содержатся три строки - First, Second и Third. В поле редактирования можно ввести новые строки для этого списка. После того, как вы наберете строку, нажмите кнопку . Введенная строка появится в конце списка combo-box.
На рисунке 3.18 мы показали внешний вид панели управления Extended с открытым списком combo-box, после того как в нее добавлена строка Fifth.
Рис. 3.18. Новая строка в списке combo-box
Панель управления Extended можно присоединить к любой стороне главного окна приложения или разместить в отдельном мини-окне. Когда панель управления Extended отображается в мини-окне, ее форму можно изменять. Ширина панели Extended не может быть меньше, чем самый широкий орган управления.
Панель управления Slyle отображается исключительно в отдельном мини-окне. Вы не сможете пристыковать ее ни к одной из сторон главного окна приложения MultiBar. Кнопки панели управления Slyle отображаются в три ряда по четыре кнопки в каждом ряду. В отличие от панелей управления Extended и Player, форма панели управления Slyle не изменяется.
Два верхних ряда кнопок в панели управления MultiBar имеют краткие подсказки. Если указатель мыши задержится над ними на некоторое время, то около него появится маленькое окно tooltip с кратким описанием соответствующей кнопки. Кнопки из нижнего ряда подсказок не имеют.
В приложении MultiBar определены три класса CMultiBarApp, CMultiBarWindow и CExtendedBar. Классы CMultiBarApp и CMultiBarWindow представляют основные классы приложения, класс CExtendedBar представляет одну из панелей управления и будет рассмотрен ниже.
Главный класс приложения CMultiBarApp наследуется от базового класса CWinApp. Объект MyMultiBarApp класса CMultiBarApp объявлен как глобальный и создается сразу после запуска приложения.
В класс CMultiBarApp входит только метод InitInstance. Он создает главное окно приложения, представленное классом CMultiBarWindow, наследованным от класса CFrameWnd.
В класс CMultiBarWindow входят три объекта - m_wndPlayerBar, m_wndStyleBar и m_wndExtendedBar, представляющие панели управления Player, Style и Extended:
class CMultiBarWindow : public CFrameWnd { // Определяем панели управления protected: // Панель управления Player CToolBar m_wndPlayerBar; // Панель управления Style CToolBar m_wndStyleBar; // Панель управления Extended CExtendedBar m_wndExtendedBar; // ... }
Панели управления Player и Style представлены объектами класса CToolBar. Панель управления Extended представлена объектом m_wndExtendedBar класса CExtendedBar. Класс CExtendedBar определен в нашем приложении. Он наследуется от базового класса CToolBar и дополняет его двумя элементами m_edit и m_combo_box. Эти элементы представляют текстовый редактор и список combo-box, которые будут размещены на панели управления:
class CExtendedBar : public CToolBar { public: // Дополнительные органы управления панели Extended CEdit m_edit; // текстовый редактор CComboBox m_combo_box; // список с текстовым редактором };
В таблице сообщений класса CMultiBarWindow, находится макрокоманда ON_WM_CREATE. Поэтому в процессе создания главного окна приложения вызывается метод OnCreate. Мы используем метод OnCreate для создания сразу трех панелей управления. Рассмотрим метод OnCreate более подробно.
Метод OnCreate класса CMultiBarWindow сначала вызывает метод OnCreate базового класса CFrameWnd.
Чтобы разрешить перемещение панелей управления, вызываем метод EnableDocking для главного окна приложения. Чтобы разрешить присоединение панелей управления ко всем сторонам окна, передаем методу EnableDocking значение CBRS_ALIGN_ANY XE "CBRS_ALIGN_ANY" :
// Разрешаем присоединение панелей управления ко // всем сторонам окна CMultiBarWindow EnableDocking(CBRS_ALIGN_ANY);
¨ Создание панели управления Player
Затем мы приступаем к созданию трех панелей управления. Сначала создается панель управления Player. В ней расположен ряд кнопок, три из которых объединены в переключатель с зависимой фиксацией и еще одна кнопка является переключателем.
Чтобы создать панель управления Player, вызывается метод Create объекта m_wndPlayerBar. Ему передаются набор флагов, определяющий характеристики панели управления. Флаг CBRS_SIZE_DYNAMIC определяет, что пользователь сможет менять форму панели управления Player. Флаг CBRS_BOTTOM задает начальное положение панели - вверху окна приложения. Флаг CBRS_TOOLTIPS разрешает отображение кратких подсказок для тех кнопок панели управления, которые имеют соответствующие строковые ресурсы:
if (!m_wndPlayerBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_SIZE_DYNAMIC | CBRS_BOTTOM | CBRS_TOOLTIPS, ID_Player)) { // Ошибка при создании панели управления TRACE0("Failed to create toolbar\n"); return -1; }
После создания панели управления загружаем ресурс IDR_PLAYER, описывающий кнопки панели управления:
if (!m_wndPlayerBar.LoadToolBar(IDR_PLAYER)) { // Ошибка при загрузке ресурса панели управления TRACE0("Failed to load toolbar\n"); return -1; }
Когда панель управления отображается в мини-окне, она имеет заголовок. Чтобы установить текст в этих заголовках, вызываем метод SetWindowText:
m_wndPlayerBar.SetWindowText("Player");
Теперь мы указываем, что кнопки панели управления с идентификаторами ID_TYPE, ID_CD_DRV и ID_WAVE составляют трехпозиционный переключатель с зависимой фиксацией. Для этого мы последовательно определяем стиль каждой из этих кнопок и добавляем к ним стиль TBBS_CHECKGROUP:
nIndex = m_wndPlayerBar.CommandToIndex(ID_TYPE); nBarStyle = m_wndPlayerBar.GetButtonStyle(nIndex) | TBBS_CHECKGROUP; m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle); nIndex = m_wndPlayerBar.CommandToIndex(ID_CD_DRV); nBarStyle = m_wndPlayerBar.GetButtonStyle(nIndex) | TBBS_CHECKGROUP; m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle); nIndex = m_wndPlayerBar.CommandToIndex(ID_WAVE); nBarStyle = m_wndPlayerBar.GetButtonStyle(nIndex) | TBBS_CHECKGROUP; m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle);
Далее кнопку с идентификатором ID_PAUSE мы превращаем в переключатель. Для этого определяем стиль этой кнопки и добавляем к нему стиль TBBS_CHECKBOX:
nIndex = m_wndPlayerBar.CommandToIndex(ID_PAUSE); nBarStyle = m_wndPlayerBar.GetButtonStyle(nIndex) | TBBS_CHECKBOX; m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle);
Когда стили всех кнопок панели управления установлены, разрешаем присоединять ее к любой стороне родительского окна. Для этого вызываем метод EnableDocking, передав ему в качестве параметра значение CBRS_ALIGN_ANY:
m_wndPlayerBar.EnableDocking(CBRS_ALIGN_ANY);
Последним шагом в процессе создания панели управления Player является вызов метода DockControlBar для окна приложения. Этот метод пристывковывает панель управления Player к родительскому окну:
DockControlBar(&m_wndPlayerBar);
¨ Создание панели управления Style
Панель управления Style состоит из двенадцати кнопок, расположенных в три ряда по четыре кнопки в каждом ряду.
Чтобы создать панель управления Style вызывается метод Create объекта m_wndStyleBar. Ему передается набор флагов, определяющий характеристики панели управления. Флаг CBRS_SIZE_FIXED указывает, что панель управления имеет фиксированную форму, которую пользователь не сможет изменить. Флаг CBRS_TOP задает начальное положение панели - вверху окна приложения. Флаг CBRS_TOOLTIPS разрешает отображение кратких подсказок для тех кнопок панели управления, которые имеют соответствующие строковые ресурсы.
Файл ресурсов содержит строки описания только для первых восьми кнопок панели Style. Последние четыре кнопки не имеют соответствующих строковых ресурсов, поэтому для них подсказка не отображается:
if (!m_wndStyleBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_SIZE_FIXED | CBRS_TOP | CBRS_TOOLTIPS, ID_Style)) { // Ошибка при создании панели управления TRACE0("Failed to create toolbar\n"); return -1; }
После создания панели управления загружаем ресурс IDR_STYLE, описывающий кнопки панели управления:
if (!m_wndStyleBar.LoadToolBar(IDR_STYLE)) { // Ошибка при загрузке ресурса панели управления TRACE0("Failed to load toolbar\n"); return -1; }
Когда панель управления создана, вызываем метод SetWindowText, чтобы установить текст в ее заголовке:
m_wndStyleBar.SetWindowText("Style");
Панель управления Style задумана нами как панель управления, которая все время отображается в отдельном мини-окне. Поэтому мы запрещаем пристывковывать панель управления Player к родительскому окну. Для этого вызываем метод EnableDocking, указав ему в качестве параметра нулевое значение:
m_wndStyleBar.EnableDocking(0);
Чтобы установить форму панели управления (разделить кнопки на несколько рядов) добавляем к стилям кнопок, завершающих каждый ряд, стиль TBBS_WRAPPED XE "TBBS_WRAPPED" :
nIndex = m_wndStyleBar.CommandToIndex(ID_SUBSCRIPT); nBarStyle = m_wndStyleBar.GetButtonStyle(nIndex) | TBBS_WRAPPED; m_wndStyleBar.SetButtonStyle(nIndex, nBarStyle); nIndex = m_wndStyleBar.CommandToIndex(ID_TEXT_JUSTIFY); nBarStyle = m_wndStyleBar.GetButtonStyle(nIndex) | TBBS_WRAPPED; m_wndStyleBar.SetButtonStyle(nIndex, nBarStyle);
Когда стили всех кнопок установлены, вызываем метод FloatControlBar главного окна приложения, чтобы вывести панель управления Style в отдельном мини-окне. В качестве координат, в которых отображается панель Style, произвольно выбираем точку (100,100):
CPoint pointStyleBar(100, 100); FloatControlBar(&m_wndStyleBar, pointStyleBar);
¨ Создание панели управления Extended
Панель управления Extended содержит дополнительные органы управления - текстовый редактор и список combo-box.
Чтобы создать панель управления Extended вызывается метод Create объекта m_wndExtendedBar. Ему передаются набор флагов, определяющий характеристики панели управления. Флаг CBRS_SIZE_DYNAMIC указывает, что пользователь может изменить форму панели управления. Флаг CBRS_TOP задает начальное положение панели - вверху окна приложения. Флаг CBRS_TOOLTIPS разрешает отображение кратких подсказок для тех кнопок панели управления, которые имеют соответствующие строковые ресурсы:
if(!m_wndExtendedBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_SIZE_DYNAMIC | CBRS_TOP | CBRS_TOOLTIPS, ID_Extended)) { // Ошибка при создании панели управления TRACE0("Failed to create toolbar\n"); return -1; }
После создания панели управления загружаем ресурс IDR_ EXTENDED, описывающий кнопки панели управления:
if(!m_wndExtendedBar.LoadToolBar(IDR_EXTENDED)) { // Ошибка при загрузке ресурса панели управления TRACE0("Failed to load toolbar\n"); return -1; }
Когда панель управления создана, вызываем метод SetWindowText, чтобы установить текст в ее заголовке:
m_wndExtendedBar.SetWindowText("Extended");
Теперь мы приступаем к созданию дополнительных органов управления - текстового редактора и списка combo-box. Эти органы управления размещаются в панелях управления на месте разделителей.
¨ Отображаем текстовый редактор
По умолчанию разделитель имеет слишком маленький размер, чтобы разместить на его месте какой-либо орган управления. Поэтому сначала мы увеличиваем его размер. Для этого используем метод SetButtonInfo:
m_wndExtendedBar.SetButtonInfo(2, IDW_EDIT, TBBS_SEPARATOR, 130);
Этот метод увеличивает размер первого разделителя, имеющего индекс 2, до 130 пикселов в ширину. Теперь надо определить координаты прямоугольной области разделителя в которых будет размещен текстовый редактор:
CRect rectEdit; m_wndExtendedBar.GetItemRect(2, &rectEdit);
Метод GetItemRect записывает в rectEdit координаты разделителя. Чтобы отделить текстовый редактор от соседних кнопок, уменьшаем ширину прямоугольника rectEdit, делая отступ по 6 пикселов с правой и с левой стороны:
rectEdit.left += 6; rectEdit.right -= 6;
Координаты прямоугольной области для текстового редактора вычислены и мы вызываем метод Create для текстового редактора m_edit, который, собственно, и размещает текстовый редактор на панели управления:
if(!m_wndExtendedBar.m_edit.Create(WS_CHILD | ES_AUTOHSCROLL|WS_VISIBLE|WS_TABSTOP|WS_BORDER, rectEdit, &m_wndExtendedBar, IDW_EDIT)) { // Ошибка при создании текстового редактора TRACE0("Failed to create edit-box\n"); return FALSE; }
Обратите внимаете на стили, указанные при создании текстового редактора. Наибольшее значение имеет стиль WS_CHILD, означающий, что текстовый редактор является дочерним окном панели управления. В принципе, стиль WS_CHILD можно не указывать. В этом случае он будет установлен автоматически в процессе создания текстового редактора.
Стиль ES_AUTOHSCROLL позволяет вводить в текстовом редакторе длинные строки. Если строка не помещается в окне редактора, она сдвигается.
Стиль WS_VISIBLE устанавливается, чтобы текстовый редактор появился на экране сразу после создания. Если его не указать, то текстовый редактор останется невидимым.
Мы установили для текстового редактора стиль WS_BORDER, чтобы лучше выделить его на фоне панели управления. Этот стиль отображает вокруг текстового редактора тонкую рамку.
Панель управления не позволяет использовать клавишу <Tab> для перемещения фокуса ввода между кнопками. Однако если вы размещаете на панели управления дополнительные органы управления, для них можно установить стиль WS_TABSTOP. Тогда вы получите возможность перемещать фокус ввода между ними, нажимая клавишу <Tab>. Мы установили стиль WS_TABSTOP для двух дополнительных органов управления - текстового редактора и списка combo-box.
¨ Отображаем список combo-box
Теперь, когда текстовый редактор появился в панели управления, мы повторяем проделанные шаги и отображаем список combo-box.
Увеличиваем размер второго разделителя панели управления, который имеет индекс 4 до 150 пикселов:
m_wndExtendedBar.SetButtonInfo(4, IDW_COMBO, TBBS_SEPARATOR, 150);
Определяем координаты прямоугольной области панели управления, занимаемой этим разделителем, и уменьшаем ее ширину на 6 пикселов с каждой стороны:
CRect rectComboBox; m_wndExtendedBar.GetItemRect(4, &rectComboBox); rectComboBox.left += 6; rectComboBox.right -= 6;
Список combo-box раскрывается вниз. Отводим для него дополнительно 80 пикселов. Если не увеличить вертикальные размеры прямоугольной области, предназначенной для размещения списка combo-box, то вы не сможете его открыть. Для этого просто не хватит высоты панели управления:
rectComboBox.bottom = rectComboBox.top + 80;
Для создания списка combo-box вызываем метод Create для объекта m_combo_box. Он размещает список в прямоугольной области rectComboBox:
if(!m_wndExtendedBar.m_combo_box.Create( CBS_DROPDOWN | WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_AUTOHSCROLL | CBS_DISABLENOSCROLL, rectComboBox, &m_wndExtendedBar, IDW_COMBO)) { // Ошибка при создании списока с полем редактирования TRACE0("Failed to create combo-box\n"); return FALSE; }
Как и при создании текстового редактора, мы передали методу Create несколько стилей, определяющих режим работы и характеристики списка combo-box.
Так как список размещается в панели управления, то он является его дочерним окном. Поэтому мы указали для него стиль WS_CHILD. Вы можете опустить стиль WS_CHILD. В этом случае он будет установлен автоматически в процессе создания списка combo-box.
Стиль WS_VISIBLE устанавливается, чтобы список появился на экране сразу после создания. Если его не указать, список останется невидимым.
Стиль WS_VSCROLL добавляет к списку combo-box вертикальную полосу просмотра, если в списке слишком много строк. Мы также добавили стиль CBS_DISABLENOSCROLL, означающий что вертикальная полоса просмотра отображается, даже если все строки помещаются в списке. В этом случае, однако, полоса просмотра отображается серым цветом и не доступна для использования.
Внешний вид списка combo-box определяется стилем CBS_DROPDOWN, который означает, что список будет открываться в случае нажатия на кнопку .
Мы также установили для списка стиль WS_TABSTOP, который позволяет использовать клавишу <Tab> для передачи ему фокуса ввода. Напомним, что этот стиль также установлен для текстового редактора.
Сразу после создания списка combo-box мы записываем в него три строки: One, Two и Third. Вы увидите эти строки если откроете список combo-box в панели управления приложения.
Для добавления новых строк к списку нами используется метод AddString класса CComboBox:
m_wndExtendedBar.m_combo_box.AddString("One"); m_wndExtendedBar.m_combo_box.AddString("Two"); m_wndExtendedBar.m_combo_box.AddString("Third");
Когда стили всех кнопок панели управления выбраны, разрешаем присоединять ее к любой стороне родительского окна. Для этого вызываем метод EnableDocking, передав ему в качестве параметра значение CBRS_ALIGN_ANY:
m_wndExtendedBar.EnableDocking(CBRS_ALIGN_ANY );
Последним шагом в процессе создания панели управления Extended является вызов метода DockControlBar для окна приложения. Этот метод пристывковывает панель управления Extended к родительскому окну:
DockControlBar(&m_wndExtendedBar);
Каждый раз когда пользователь нажимает на кнопки в панели управления или работает с дополнительными органами управления (текстовым редактором и списком combo-box), в окно приложения, являющееся родительским окном панели управления, поступают соответствующие сообщения.
Кнопки панели управления передают в родительское окно командные сообщения. Идентификатор этих командных сообщений соответствует идентификатору нажатой кнопки. В родительское окно панели управления также поступают сообщения от дополнительных органов управления - текстового редактора и списка combo-box. Коды извещения этих сообщений определяют их назначение.
Таблица сообщений класса CMultiBarWindow обрабатывает целый ряд сообщений от меню и панелей управления. Кроме них в таблице сообщений класса CMultiBarWindow располагается макрокоманда ON_WM_CREATE, которая вызывает метод OnCreate во время создания окна:
ON_WM_CREATE()
Для обработки командных сообщений от кнопок панелей Player и Style мы вызываем методы BarCommandOne и BarCommandRange, входящие в класс CMultiBarWindow.
Метод BarCommandOne отображает на экране сообщение о том, что данная команда не реализована - Command not implemented. Метод BarCommandRange не выполняет никаких действий:
// Обработчики команд от панели управления Player ON_COMMAND(ID_STOP, BarCommandOne) ON_COMMAND(ID_PLAY, BarCommandOne) ON_COMMAND(ID_PAUSE, BarCommandOne) ON_COMMAND_RANGE(ID_LEFT, ID_RIGHT, BarCommandRange) ON_COMMAND_RANGE(ID_TYPE, ID_WAVE, BarCommandRange) // Обработчик команд от панели управления Style ON_COMMAND_RANGE(ID_UNDERLINE, ID_MARK_4, BarCommandRange)
Для перехвата командных сообщений от панели управления Player и Style используются макрокоманды ON_COMMAND и ON_COMMAND_RANGE. Макрокоманда ON_COMMAND вызывает метод, указанный во втором параметре командного сообщения, соответствующего идентификатору, приведенному в первом параметре. Макрокоманда ON_COMMAND_RANGE. работает аналогично ON_COMMAND, но позволяет вызвать метод обработчик, указанный в третьем параметре, сразу для нескольких командных сообщений. Идентификаторы обрабатываемых сообщений находятся в промежутке значений, указанных первым и вторым параметрами макрокоманды.
Для обработки командных сообщений от кнопок панели управления Extended мы также используем метод BarCommandRange. Исключение составляет только кнопка ID_ADD. Сообщения от этой кнопки обрабатываются методом AddStringToComboBox класса CMultiBarWindow:
// Обработчики команд от панели управления Extended ON_COMMAND(ID_ADD, AddStringToComboBox) ON_COMMAND_RANGE(ID_FOTO, ID_DISK, BarCommandRange)
В таблице сообщений класса CMultiBarWindow также расположены макрокоманды для обработки командных сообщений от меню View. Для их обработки используется метод ShowStyle класса CMultiBarWindow:
// Обработчики команд меню View ON_COMMAND_EX(ID_Style, ShowStyle) ON_COMMAND_EX(ID_Extended, ShowStyle) ON_COMMAND_EX(ID_Player, ShowStyle)
Командные сообщения от всех строк меню View обрабатываются одним методом ShowStyle, однако его вызов осуществляется с помощью макрокоманды ON_COMMAND_EX. Эта макрокоманда вызывает для обработки командного сообщения с идентификатором, заданным первым параметром, метод указанный во втором параметре. В отличие от макрокоманды ON_COMMAND, макрокоманда ON_COMMAND_EX передает методу-обработчику идентификатор полученного командного сообщения.
Метод ShowStyle использует этот идентификатор, чтобы определить какая строка меню была выбрана.
Методы BarCommandOne и BarCommandRange определены в классе главного окна приложения CMultiBarWindow и используются для обработки сообщений от кнопок панелей управления.
Фактически методы BarCommandOne и BarCommandRange не выполняют никакой полезной работы. Единственное, что делает метод BarCommandOne, - это отображает на экране сообщение о том, что выбранная вами команда не реализована. Для этого мы используем метод MessageBox:
void CMultiBarWindow::BarCommandOne() { // Отображаем сообщение о том, что команда не реализована MessageBox("Command not implemented"); }
Метод BarCommandRange вообще не выполняет никакой полезной работы. Однако не спешите исключать его из программы. Если вы удалите методы BarCommandOne и BarCommandRange из программы и удалите соответствующие макрокоманды из таблицы сообщений класса окна, тогда все кнопки, для обработки сообщений от которых использовались эти методы, окажутся заблокированы. MFC определит, что сообщения от кнопок не обрабатываются и запретит их использование:
void CMultiBarWindow::BarCommandRange( UINT nID ) { // Обработчик не выполняет никакой работы }
Метод AddStringToComboBox класса CMultiBarWindow выполняет обработку командных сообщений от кнопки ID_ADD панели управления Extended. Для этого он сначала считывает строку, введенную в текстовом редакторе m_edit, а затем добавляет ее в список m_combo_box:
void CMultiBarWindow::AddStringToComboBox() { // Получаем строку, введенную в текстовом редакторе m_edit char tmpStr[39]; m_wndExtendedBar.m_edit.GetLine(0, tmpStr,40); // Добавляем новую строку к списку m_combo_box m_wndExtendedBar.m_combo_box.AddString(tmpStr); }
В том случае, если в панели управления приложения надо разместить много разнообразных органов управления, то значительно удобнее создать его на основе другого класса, имеющего название CDialogBar:
CDialogBar <- CControlBar <- CWnd <- CCmdTarget <- CObject
Этот класс, также как класс CToolBar, наследован от базового класса CControlBar. Класс CDialogBar позволяет создать диалоговую панель управления на основе шаблона диалоговой панели. В отличии от обычной диалоговой панели, диалоговая панель управления, созданная на основе класса CDialogBar, имеет все свойства панели управления. Она может быть присоединена к одной из границ окна или может отображаться в отдельном мини-окне.
Приложение может иметь несколько панелей управления, созданных на основе классов CToolBar и CDialogBar.
Непосредственно в состав класса CDialogBar входят всего два метода - это конструктор класса CDialogBar и метод Create.
Конструктор класса CDialogBar создает только соответствующий объект, но не саму панель управления:
CDialogBar();
Чтобы создать панель управления, следует вызвать метод Create и указать ему шаблон диалоговой панели, используемый для создания диалоговой панели управления.
Метод Create имеет два различных формата:
BOOL Create( CWnd* pParentWnd, LPCTSTR lpszTemplateName, UINT nStyle, UINT nID ); BOOL Create( CWnd* pParentWnd, UINT nIDTemplate, UINT nStyle, UINT nID );
Первый параметр CWnd задает окно, для которого создается диалоговая панель управления. Обычно в качестве такого окна выступает главное окно приложения.
Второй параметр метода Create указывает шаблон панели управления. В зависимости от того, как определена панель управления, в качестве второго параметра можно использовать либо идентификатор шаблона диалоговой панели nIDTemplate, либо его имя lpszTemplateName.
Третий параметр nStyle определяет начальное положение диалоговой панели управления в окне приложения. Вы можете указать следующие флаги: CBRS_TOP, CBRS_BOTTOM, CBRS_LEFT и CBRS_RIGHT. Они позволяют установить панель управления соответственно у верхней, нижней, левой или правой границы окна.
Форма диалоговой панели управления зависит от шаблона диалоговой панели и ее расположения в окне приложения. В случае если диалоговая панель управления отображается в верхней или нижней части окна (параметр nStyle равен CBRS_TOP или CBRS_BOTTOM), то ширина панели управления соответствует ширине окна CWnd, а высота - высоте шаблона диалоговой панели. Если же диалоговая панель управления отображается с левой или правой части окна (параметр nStyle равен CBRS_LEFT или CBRS_RIGHT), то ширина панели управления соответствует ширине шаблона диалоговой панели, а высота - высоте окна CWnd.
Последний параметр nID определяет идентификатор диалоговой панели управления.
В случае успешного создания диалоговой панели управления метод Create возвращает ненулевое значение. Если при создании панели управления обнаружены ошибки, например, вы указали несуществующий шаблон диалоговой панели, тогда метод Create возвращает нулевое значение.
Как и в случае разработки обычных диалоговый панелей, сначала следует подготовить шаблон диалоговой панели, которая будет использоваться в качестве панели управления. Для этого можно воспользоваться редактором ресурсов Microsoft Visual C++.
В шаблоне диалоговой панели следует установить только один стиль - WS_CHILD. Диалоговая панель управления будет выступать в качестве дочернего окна и не должна иметь ни заголовка ни рамки.
Готовый шаблон следует сохранить в файле ресурсов проекта. На этом подготовка шаблона диалоговой панели управления считается законченной и надо приступить к написанию программного кода для работы с нею.
В состав класса окна, в котором будет отображаться диалоговая панель управления, надо включить элемент данных класса CDialogBar. Этот элемент данных будет представлять диалоговую панель управления. Если диалоговая панель управления достаточно сложна и имеет много органов управления, то можно создать для нее отдельный класс на основе базового класса CDialogBar. Этот класс следует дополнить методами, обрабатывающими сообщения от органов управления панели. Таким образом вы сможете разгрузить таблицу сообщений главного окна приложения.
Затем надо переопределить метод OnCreate класса окна и добавить в нем вызов метода Create класса CDialogBar для объекта представляющего диалоговую панель управления.
Если вы добавляете диалоговую панель управления к приложению, созданному с использованием средств MFC AppWizard, то в нем уже определен метод OnCreate и вам только остается добавить в него соответствующий вызов метода Create.
Органы управления диалоговой панели передают сообщения непосредственно своему родительскому окну. Добавьте в таблицу сообщений класса окна соответствующие макрокоманды для получения сообщений и включите в класс окна методы для их обработки.
В следующем разделе мы реализуем только что описанный алгоритм создания диалоговой панели управления на примере приложения DialogBar.
Создайте новый проект под названием DialogBar. В качестве типа приложения выберите из списка Type строку Application. Настройте проект DialogBar, точно также как вы настраивали проекты Bar и MultiBar: укажите, что приложение будет работать с библиотекой классов MFC.
Наберите в редакторе исходный текст приложения и сохраните его в файле DialogBar.cpp (листинг 3.10). Чтобы ускорить набор исходного текста приложения, за основу вы можете взять файл Bar.cpp приложения Bar. Готовый файл DialogBar.cpp включите в проект.
Листинг 3.10. Файл DialogBar.cpp
//============================================================ // Приложение DialogBar // (c) Frolov G.V., 1996 // E-mail: frolov@glas.apc.org //============================================================ // Исключаем редко используемые определения из // включаемых файлов #define VC_EXTRALEAN // Включаемый файл для MFC #include <fxwin.h> #include <afxext.h> #include <afxcmn.h> // Включаемый файл для ресурсов приложения #include "resource.h" //============================================================ // Класс CDlgBarApp - главный класс приложения //============================================================ class CDlgBarApp : public CWinApp { public: // Мы будем переопределять метод InitInstance virtual BOOL InitInstance(); }; // Создаем объект приложение класса CDlgBarApp CDlgBarApp DlgBarApp; //============================================================ // Класс CDlgBarWindow - представляет главное окно //============================================================ class CDlgBarWindow : public CFrameWnd { // Определяем панель управления protected: // Панель управления на основе класса CDialogBar CDialogBar m_wndDialogBar; protected: // Метод OnCreate используется для создания диалоговой // панели управления afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); public: // Объявляем конструктор класса CDlgBarWindow CDlgBarWindow(); // Объявляем методы для обработки команд от диалоговой // панелей управления afx_msg BOOL DlgBarCommand(UINT nID); afx_msg void DlgBarCombo(); // Макрокоманда необходима, так как класс // CDlgBarWindow обрабатывает сообщения DECLARE_MESSAGE_MAP() }; //============================================================ // Метод BarCommand // Обрабатывает команды, выводит на экран сообщение //============================================================ BOOL CDlgBarWindow::DlgBarCommand(UINT nID) { CString szCommandAbout; // Загружаем текстовую строку с идентификатором, // соответствующим идентификатору поступившего командного // сообщения и выводим ее на экран if(szCommandAbout.LoadString(nID)) MessageBox(szCommandAbout); else { // Ошибка при загрузке строкового ресурса TRACE0("Failed to load string\n"); return -1; } return TRUE; } //============================================================ // Метод DlgBarCombo // Обрабатывает команды, выводит на экран сообщение //============================================================ void CDlgBarWindow::DlgBarCombo() { // Отображаем сообщение о том, что сделан выбор из списка MessageBox("Combo-box selection changed"); } //============================================================ // Таблица сообщений класса CDlgBarWindow //============================================================ BEGIN_MESSAGE_MAP(CDlgBarWindow, CFrameWnd) // Макрокоманда вызывает метод OnCreate ON_WM_CREATE() //============================================================ // Обработчики команд от диалоговой панели управления // Командные сообщения от кнопок Set и Clear ON_COMMAND_EX(IDC_BUTTON_SET, DlgBarCommand) ON_COMMAND_EX(IDC_BUTTON_CLEAR, DlgBarCommand) ON_COMMAND_EX(IDC_CHECK1, DlgBarCommand) // Командные сообщения от переключателя Alighn ON_COMMAND_EX(IDC_RADIO_LEFT, DlgBarCommand) ON_COMMAND_EX(IDC_RADIO_CENTER, DlgBarCommand) ON_COMMAND_EX(IDC_RADIO_RIGHT, DlgBarCommand) // Командные сообщения от списка combo-box ON_CBN_SELCHANGE( IDC_COMBO_COLOUR, DlgBarCombo) END_MESSAGE_MAP() //============================================================ // Метод InitInstance класса CDlgBarApp // Создает главное окно приложения и отображает его на экране //============================================================ BOOL CDlgBarApp::InitInstance() { m_pMainWnd = new CDlgBarWindow(); m_pMainWnd -> ShowWindow(m_nCmdShow); m_pMainWnd -> UpdateWindow(); return TRUE; } //============================================================ // Конструктор класса CDlgBarWindow //============================================================ CDlgBarWindow::CDlgBarWindow() { // Создаем окно приложения, соответствующее // данному объекту класса CDlgBarWindow Create(NULL, "Dialog Bar", WS_OVERLAPPEDWINDOW, rectDefault, NULL); } //============================================================ // Метод OnCreate класса CDlgBarWindow // Используется для создания панелей управления //============================================================ int CDlgBarWindow::OnCreate(LPCREATESTRUCT lpCreateStruct) { // Вызываем метод OnCreate базового класса if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; //============================================================ // Создаем диалоговую панель управления if (!m_wndDialogBar.Create(this, IDD_DIALOG_BAR, CBRS_TOP, IDD_DIALOG_BAR)) { // Ошибка при создании диалоговой панели управления TRACE0(>Failed to create dialog bar\n>); return -1; } return 0; }
Создайте новый файл ресурсов и включите его в проект под именем DialogBar.rc. Создайте шаблон диалоговой панели, которая будет использоваться в качестве диалоговой панели управления.
Откройте панель свойств редактируемой панели управления Dialog Properties. Откройте страницу General и присвойте диалоговой панели идентификатор IDD_DIALOG_BAR. Затем откройте страницу Styles. Выберите из списка Style стиль диалоговой панели Child. Укажите, что диалоговая панель не имеет рамки - из списка Border надо выбрать строку None. Все остальные переключатели из страницы Styles и из страниц More Styles и Extended Styles должны быть отключены.
Чтобы диалоговая панель управления не заняла у вас большую половину окна приложения, постарайтесь сделать ее как можно более узкой.
Разместите в шаблоне диалоговой панели несколько органов управления. Для нашего примера вам надо создать две кнопки Set и Clear с идентификаторами IDC_BUTTON_SET и IDC_BUTTON_CLEAR, три переключателя Left, Center и Right с зависимой фиксацией, имеющие идентификаторы IDC_RADIO_LEFT, IDC_RADIO_CENTER и IDC_RADIO_RIGHT, а также список combo-box с идентификатором IDC_COMBO_COLOUR. В список IDC_COMBO_COLOUR внесите несколько начальных значений. Мы записали в него названия различных цветов - Red, Blue, Green, Yellow, Black и White. Эти строки будут содержаться в списке сразу после отображения на экране диалоговой панели.
Добавьте в файл ресурсов строковые ресурсы, описывающие органы управления диалоговой панели. Они должны иметь идентификаторы, соответствующие идентификаторам органов управления.
Мы привели файл ресурсов, который у вас должен получиться в листинге 3.11.
Листинг 3.11. Файл DialogBar.rc
//Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // // Russian resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) #ifdef _WIN32 LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT #pragma code_page(1251) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Dialog // IDD_DIALOG_BAR DIALOG DISCARDABLE 0, 0, 227, 50 STYLE WS_CHILD FONT 8, "MS Sans Serif" BEGIN CONTROL "Left", IDC_RADIO_LEFT, "Button", BS_AUTORADIOBUTTON, 95, 10, 40,10 CONTROL "Center", IDC_RADIO_CENTER, "Button", BS_AUTORADIOBUTTON, 95, 19, 45, 11 CONTROL "Right", IDC_RADIO_RIGHT, "Button", BS_AUTORADIOBUTTON, 95, 30, 40, 9 GROUPBOX "Aligns",IDC_STATIC,85,0,80,45 PUSHBUTTON "Set",IDC_BUTTON_SET,175,5,45,15 PUSHBUTTON "Clear",IDC_BUTTON_CLEAR,175,30,45,15 COMBOBOX IDC_COMBO_COLOUR, 5, 5, 70, 50, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP END ////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN IDD_DIALOG_BAR, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 220 TOPMARGIN, 7 BOTTOMMARGIN, 43 END END #endif // APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Dialog Info // IDD_DIALOG_BAR DLGINIT BEGIN IDC_COMBO_COLOUR, 0x403, 4, 0 0x6552, 0x0064, IDC_COMBO_COLOUR, 0x403, 5, 0 0x6c42, 0x6575, "\000" IDC_COMBO_COLOUR, 0x403, 6, 0 0x7247, 0x6565, 0x006e, IDC_COMBO_COLOUR, 0x403, 7, 0 0x6559, 0x6c6c, 0x776f, "\000" IDC_COMBO_COLOUR, 0x403, 6, 0 0x6c42, 0x6361, 0x006b, IDC_COMBO_COLOUR, 0x403, 6, 0 0x6857, 0x7469, 0x0065, 0 END ////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE DISCARDABLE BEGIN IDC_RADIO_LEFT "Left radio box" IDC_RADIO_CENTER "Center radio box" IDC_RADIO_RIGHT "Right radio box" IDC_BUTTON_SET "Set button pressed" END STRINGTABLE DISCARDABLE BEGIN IDC_BUTTON_CLEAR "Clear button pressed" END #endif // Russian resources ////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // Generated from the TEXTINCLUDE 3 resource. // ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
Идентификаторы ресурсов приложения DialogBar и идентификаторы органов управления диалоговой панели IDD_DIALOG_BAR определены в файле resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++. Исходный текст файла представлен в листинге 3.12.
Листинг 3.12. Файл resource.h
//{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by DialogBar.rc // #define IDD_DIALOG_BAR 101 #define IDC_RADIO_LEFT 1004 #define IDC_RADIO_CENTER 1005 #define IDC_RADIO_RIGHT 1006 #define IDC_BUTTON_SET 1007 #define IDC_BUTTON_CLEAR 1008 #define IDC_COMBO_COLOUR 1012 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 104 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1014 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
Постройте приложение DialogBar и запустите его. На экране появится главное окно приложения, в верхней части которого отображается диалоговая панель управления (рис. 3.19).
Рис. 3.19. Приложение DialogBar
Поработайте с приложением DialogBar. Если вы будете нажимать на кнопки диалоговой панели управления, изменять положение переключателя или выбирать новые строки из списка combo-box, то на экране будут появляться короткие сообщения, описывающие выбранный орган управления.
В приложении DialogBar определены всего только два класса - это главный класс приложения CDlgBarApp и класс главного окна приложения CDlgBarWindow. Как и во многих других приложениях, посвященных использованию панелей управления, главное окно приложения по сути является его единственным окном. Однако мы сохранили за ним это почетное название, чтобы внести однообразие в описание приложений, созданных с использованием MFC AppWizard и без него.
Главный класс приложения CDlgBarApp наследуется от базового класса CWinApp. Объект DlgBarApp класса CDlgBarApp объявлен как глобальный и создается сразу после запуска приложения.
В класс CDlgBarApp входит только метод InitInstance. Он создает главное окно приложения, представленное классом CDlgBarWindow, наследованным от класса CFrameWnd. Мы не станем подробно рассматривать этот метод, так как он фактически идентичен одноименному методу приложений Bar и MultiBar, представленных выше.
Класс CDlgBarWindow управляет главным окном приложения, создает диалоговую панель управления, а также обрабатывает сообщения, поступающие от диалоговой панели управления:
class CDlgBarWindow : public CFrameWnd { // Определяем панель управления protected: // Панель управления на основе класса CDialogBar CDialogBar m_wndDialogBar; protected: // Метод OnCreate используется для создания диалоговой // панели управления afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); public: // Объявляем конструктор класса CDlgBarWindow CDlgBarWindow(); // Объявляем методы для обработки команд от диалоговой // панелей управления afx_msg BOOL DlgBarCommand(UINT nID); afx_msg void DlgBarCombo(); // Макрокоманда необходима, так как класс // CDlgBarWindow обрабатывает сообщения DECLARE_MESSAGE_MAP() };
Кроме ряда методов, в класс CDlgBarWindow входит элемент m_wndDialogBar класса CDialogBar. Этот элемент представляет диалоговую панель управления, которая будет отображаться в главном окне приложения.
Рассмотрим отдельные методы класса CDlgBarWindow более подробно.
Сейчас для нас наиболее важен метод OnCreate, который собственно, и создает диалоговую панель управления приложения DialogBar. В таблице сообщений класса CDlgBarWindow, находится макрокоманда ON_WM_CREATE. Поэтому в процессе создания главного окна приложения вызывается метод OnCreate.
Конструктор класса CDlgBarWindow используется для создания главного окна приложения. Мы рассказывали о процедуре создания главного окна приложения в томе 24 серии “Библиотека системного программиста”, посвященном библиотеке MFC, поэтому сейчас не будем на нем останавливаться более подробно.
Таблица сообщений класса CDlgBarWindow обрабатывает сообщения от диалоговой панели управления. В ней также располагается макрокоманда ON_WM_CREATE, которая вызывает метод OnCreate во время создания окна:
ON_WM_CREATE()
Когда пользователь работает с диалоговой панелью управления, в ее родительское окно поступают сообщения. От кнопок Set и Clear и переключателя Alighn поступают командные сообщения, которые обрабатываются при помощи макрокоманд ON_COMMAND_EX:
// Командные сообщения от кнопок Set и Clear ON_COMMAND_EX(IDC_BUTTON_SET, DlgBarCommand) ON_COMMAND_EX(IDC_BUTTON_CLEAR, DlgBarCommand) ON_COMMAND_EX(IDC_CHECK1, DlgBarCommand) // Командные сообщения от переключателя Alighn ON_COMMAND_EX(IDC_RADIO_LEFT, DlgBarCommand) ON_COMMAND_EX(IDC_RADIO_CENTER, DlgBarCommand) ON_COMMAND_EX(IDC_RADIO_RIGHT, DlgBarCommand)
Для обработки всех этих командных сообщений вызывается один и тот же метод DlgBarCommand, которому передается идентификатор кнопки или переключателя, вызвавшего сообщение.
От списка combo-box, расположенного в диалоговой панели управления, мы обрабатываем только одно сообщение с кодом извещения CBN_SELCHANGE. Это сообщение передается, когда пользователь выбирает из списка новую строку:
// Командные сообщения от списка combo-box ON_CBN_SELCHANGE( IDC_COMBO_COLOUR, DlgBarCombo)
Метод OnCreate класса CDlgBarWindow сначала вызывает метод OnCreate базового класса CFrameWnd:
if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;
Затем мы создаем диалоговую панель управления. Для этого вызываем метод Create для объекта m_wndDialogBar, входящего в класс CDlgBarWindow, и представляющего панель управления:
if (!m_wndDialogBar.Create(this, IDD_DIALOG_BAR, CBRS_TOP|CBRS_TOOLTIPS, IDD_DIALOG_BAR)) { TRACE0("Failed to create dialog bar\n"); return -1; } return 0; }
В качестве родительского окна для диалоговой панели управления мы указываем главное окно приложения. Ключевое слово this представляет указатель на текущий объект, то есть, в данном случае, окно CDlgBarWindow.
Второй параметр метода указывает идентификатор шаблона диалоговой панели, которая будет отображаться как диалоговая панель управления приложения. Мы указали идентификатор шаблона диалоговой панели IDD_DIALOG_BAR.
В качестве третьего параметра метода Create мы привели стиль CBRS_TOP. Стиль CBRS_TOP устанавливает расположение диалоговой панели управления в верхней части окна приложения.
Когда пользователь выбирает строку из списка combo-box в диалоговой панели управления, в ее родительское окно, которое в нашем случае является главным окном приложения, поступает сообщение. Для его обработки вызывается метод DlgBarCombo класса CDlgBarWindow.
Метод DlgBarCombo выводит на экран сообщение о том, что пользователь сделал выбор из списка combo-box:
void CDlgBarWindow::DlgBarCombo() { // Отображаем сообщение о том, что сделан выбор из списка MessageBox("Combo-box selection changed"); }
Командные сообщения от кнопок Set, Clear, и переключателя Alighn обрабатываются методом BarCommand класса CDlgBarWindow. В качестве параметра nID методу BarCommand передается идентификатор вызвавшего его органа управления:
BOOL CDlgBarWindow::DlgBarCommand(UINT nID) { //... }
В приложении мы определили для кнопок Set, Clear, и переключателей группы Alighn, описывающие их строковые ресурсы, которые имеют точно такие же идентификаторы. Поэтому когда вызывается метод DlgBarCommand и ему передается идентификатор кнопки или переключателя, мы загружаем строковый ресурс, который имеет точно такой же идентификатор и отображаем его на экране:
if(szCommandAbout.LoadString(nID)) MessageBox(szCommandAbout); else { // Ошибка при загрузке строкового ресурса TRACE0("Failed to load string\n"); return -1; }
Если вам надо добавить панель управления к приложению, созданному с использованием средств MFC AppWizard, вы должны выполнить точно такие же действия, какие были нами описаны в предыдущих разделах. Сначала надо создать шаблон диалоговой панели, затем добавить к классу окна, в котором будет отображаться диалоговая панель управления, элемент класса CDialogBar, и наконец, создать диалоговую панель управления, вызвав соответствующий метод Create.
Наибольший интерес представляет использование ClassWizard для добавления обработчиков сообщений от диалоговой панели управления. Откройте в редакторе ресурсов шаблон диалоговой панели управления и запустите ClassWizard.
ClassWizard обнаружит новый ресурс и предложит создать или выбрать для него управляющий класс (рис. 3.20). Сообщения от диалоговой панели управления передаются для обработки в ее родительское окно. Поэтому в качестве управляющего класса надо выбрать класс этого окна. Так как в большинстве случаев в качестве родительского окна для панелей управления, в том числе и для диалоговой панели управления, выступает главное окно приложения, надо выбрать класс CMainFrame.
Рис. 3.20. Диалоговая панель Adding a Class
Переведите переключатель диалоговой панели Adding a Class в положение Select an existing class и нажмите на кнопку OK. На экране появится диалоговая панель Select Class, содержащая список классов, определенных в приложении (рис. 3.21).
Рис. 3.21. Диалоговая панель Select Class
Выберите из списка Class list класс главного окна приложения CMainFrame и нажмите на кнопку Select. ClassWizard сообщит о том, что выбранный класс CMainFrame не относится к классам, которые предназначены для управления диалоговыми панелями (рис. 3.22). Для продолжения нажмите на кнопку Yes.
Рис. 3.22. Предупреждение
Теперь на экране появится окно ClassWizard, в котором вы можете назначить обработчики сообщений от диалоговой панели управления, также просто как для меню или панели управления toolbar.
Во всех примерах, которые мы привели выше, для создания панели управления мы вызывали метод Create класса CToolBar во время создания окна приложения. В принципе, панель управления можно создать и позже, когда окно уже отображается на экране.
Возьмите приложение Bar. Добавьте к нему меню Tools, состоящее из одной строки Show Bar. Определите метод OnShowBar, который будет вызываться для обработки команд от этой строки меню. Не забудьте вставить в таблицу сообщений класса окна соответствующую макрокоманду.
Перенесите код для создания панели управления из метода OnCreate в метод OnShowBar. Теперь панель управления будет создаваться только после того, как вы выберите из меню Tools строку Show Bar.
Постройте проект и запустите приложение. Вы заметите, что панель управления почему-то возникает не сразу после выбора строки Show Bar. Чтобы панель управления появилась, необходимо еще изменить размер окна приложения.
Оказывается, метод Create класса CToolBar устанавливает нулевой размер окна панели управления. Настоящий размер панели управления выбирается позже, в зависимости от ее характеристик, а также размеров и характеристик родительского окна.
Чтобы установить правильные размеры и расположение панели управления следует вызвать метод RecalcLayout. Метод RecalcLayout входит в класс CFrameWnd и вызывается автоматически, если вы используете методы CFrameWnd::ShowControlBar, CFrameWnd::OnIdleUpdateCmdUI, CFrameWnd::OnSize, CFrameWnd::FloatControlBar, CMDIChildWnd::Create, а также некоторые другие.
В нижней части большинства современных приложений Windows находится строка состояния программы, которая называется панелью состояния. В ней обычно выводится краткая контекстная подсказка для пользователя, содержание которой зависит от того, с каким элементом окна работает пользователь. В панели состояния также может отображаться описание текущего режима приложения, текущее время, объем свободного пространства на диске и т. д.
Когда вы разрабатываете приложение с оконным интерфейсом при помощи средств MFC AppWizard, вы можете указать, будет ли приложение иметь панель состояния. Этой возможностью управляет переключатель Initial status bar на одной из панелей создания приложения AppWizard. Внешний вид этой панели представлен на рисунке 3.3.
По умолчанию переключатель Initial status bar установлен, и все оконные приложения, построенные с использованием средств MFC AppWizard, имеют панель состояния. В ней отображается текущее состояние приложения или краткая подсказка для выбранных строк меню, а также текущее положение клавиш <Caps Lock>, <Scroll Lock> и <Num Lock>.
К сожалению, нет специальных ресурсов, предназначенных для разработки панелей состояния. Редактор ресурсов вам не поможет - панель состояния создается “вручную”. Каждый элемент панели состояния должен быть описан в специальном массиве.
Для управления панелями состояния в состав библиотеки MFC включен класс CStatusBar. Как и классы CToolBar и CDialogBar, предназначенные для работы с панелями управления, класс CStatusBar также наследуется от базового класса CControlBar:
CStatusBar <- CControlBar <- CWnd <- CCmdTarget <- CObject
Процесс создания панели состояния во многом схож с процессом создания панелей управления.
Сначала надо создать объект для управления панелью состояния. Обычно для этого включают объект класса CStatusBar непосредственно в класс окна приложения, в котором будет размещена панель состояния.
Конструктор класса CStatusBar не имеет параметров и выглядит так:
CStatusBar();
В некоторых случаях, вместо использования класса CStatusBar, от него предварительно наследуют дополнительный класс. В этом случае, для создания панели состояния используют именно этот класс.
Следующим шагом является создание самой панели состояния. Панель состояния создается вызовом метода Create класса CStatusBar XE "CStatusBar:Create" :
BOOL Create( CWnd* pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, UINT nID = AFX_IDW_STATUS_BAR );
Через первый параметр метода pParentWnd вы должны указать окно, для которого создается панель состояния.
Второй параметр dwStyle позволяет задать характеристики панели состояния, в том числе ее расположение внутри окна. Панель состояния является дочерним окном, поэтому в параметре dwStyle надо указать атрибут WS_CHILD. Если вы не укажите атрибут WS_CHILD, ничего страшного тоже не случится - этот атрибут установится автоматически во время вызова метода Create. А вот атрибут WS_VISIBLE более важен. Если его опустить, то панель состояния хотя и будет создана, но на экране не появится.
Вы можете разместить панель состояния либо в верху, либо внизу окна. По умолчанию панель состояния размещается в нижней части окна (используется атрибут CBRS_BOTTOM). Чтобы панель состояния была размещена вверху окна, укажите в качестве параметра dwStyle атрибут CBRS_TOP. Если вы используете параметр dwStyle, надо обязательно указать либо атрибут CBRS_BOTTOM, либо атрибут CBRS_TOP.
Последний параметр метода nID определяет идентификатор дочернего окна панели состояния. По умолчанию используется идентификатор AFX_IDW_STATUS_BAR.
¨ Приложения, созданные MFC AppWizard, имеют меню View, содержащее строки Toolbar и Status bar. Строка Status bar с идентификатором ID_VIEW_STATUS_BAR позволяет закрывать и снова открывать панель состояния. Обработка стандартного командного сообщения ID_VIEW_STATUS_BAR выполняется методом OnUpdateControlBarMenu класса CFrameWnd. Метод OnUpdateControlBarMenu может управлять отображением панели управления только в том случае, если она имеет идентификатор AFX_IDW_STATUS_BAR. Более подробно о методе OnUpdateControlBarMenu можно прочитать в разделе “Недокументированные возможности класса CMainFrame”.
В случае успешного создания панели состояния метод Create возвращает ненулевое значение. Если в ходе создания панели состояния обнаружены ошибки, тогда метод Create возвращает нулевое значение.
После того как панель состояния создана, необходимо определить ее внешний вид. Для достижения этой цели вызывают метод SetIndicators и ему передают массив идентификаторов, представляющих индикаторы панели состояния. Каждый элемент панели состояния (индикатор) должен иметь отдельный идентификатор.
Метод SetIndicators загружает строковые ресурсы, соответствующие идентификаторам индикаторов, и размещает их на панели состояния:
BOOL SetIndicators( const UINT* lpIDArray, int nIDCount );
Через параметр lpIDArray, методу SetIndicators, надо передать указатель на массив идентификаторов панели состояния. Общее количество индикаторов панели состояния, определенных в массиве lpIDArray, задается параметром nIDCount.
Обычно для определения количества элементов массива используют следующий код:
nIDCount = sizeof(indicators)/sizeof(UINT);
Переменная indicators представляет массив идентификаторов панели состояния.
В случае успешного завершения метод SetIndicators возвращает ненулевое значение. Если же будут обнаружены ошибки, тогда метод SetIndicators возвращает нулевое значение.
Когда индикаторы созданы, вы можете изменить некоторые их характеристики, воспользовавшись методом SetPaneInfo:
void SetPaneInfo( int nIndex, UINT nID, UINT nStyle, int cxWidth );
Параметр nIndex определяет порядковый номер индикатора в панели состояния или, другими словами, его индекс. Характеристики этого индикатора будут меняться.
Метод SetPaneInfo позволяет изменить расположение индикаторов на панели, или даже заменить существующий индикатор новым индикатором. Для этого можно указать новый идентификатор через параметр nID. Если вы не знаете идентификатор индикатора, тогда можете определить его с помощью метода GetItemID. Метод GetItemID возвращает идентификатор индикатора с индексом nIndex:
UINT GetItemID(int nIndex) const;
Обратная операция выполняется при помощи метода CommandToIndex. Метод CommandToIndex возвращает индекс индикатора, имеющего идентификатор nIDFind. Если идентификатор указан неверно, возвращается значение -1:
int CommandToIndex(UINT nIDFind) const;
После короткого отступления вернемся к рассказу о параметрах метода SetPaneInfo.
Внешний вид идентификатора, заданного параметрами nIndex и nID, определяется параметрами nStyle и cxWidth. В качестве nStyle можно указать один или несколько атрибутов, объединенных логической операцией ИЛИ.
Атрибут |
Описание |
SBPS_NOBORDERS |
Убрать трехмерную рамку вокруг индикатора |
SBPS_POPOUT |
Обычная рамка вокруг индикатора создает впечатление, что индикатор расположен в углублении. Если указать атрибут SBPS_POPOUT, рамка изменяется таким образом, что индикатор будет располагается выше общего уровня панели состояния |
SBPS_DISABLED |
Если указать этот атрибут, то в индикаторе не будет отображаться текст из соответствующего строкового ресурса |
SBPS_STRETCH |
Один из индикаторов панели состояния может менять свой размер в зависимости от размера окна. Атрибут SBPS_STRETCH предназначен для выбора этого индикатора |
SBPS_NORMAL |
Стандартный индикатор |
Параметр cxWidth определяет ширину индикатора. Когда вы создаете панель состояния и устанавливаете индикаторы, вызывая метод SetIndicators, размер индикаторов определяется автоматически исходя из ширины текста индикатора.
¨ Если первый элемент массива идентификаторов, переданного методу SetIndicators, содержит константу ID_SEPARATOR, то для первого индикатора панели состояния по умолчанию устанавливаются атрибуты SBPS_NOBORDERS и SBPS_STRETCH
Узнать текущие характеристики индикатора можно при помощи метода GetPaneInfo. Он позволяет определить идентификатор, стиль и ширину индикатора с индексом nIndex:
void GetPaneInfo( int nIndex, UINT& nID, UINT& nStyle, int& cxWidth ) const;
Идентификатор записывается в переменную, ссылка на которую передается через параметр nID, набор атрибутов, определяющих внешний вид индикатора - в переменную nStyle, а ширина - в переменную cxWidth.
Если вам требуется определить или установить только стиль индикатора в панели управления, то вместо методов GetPaneInfo и SetPaneInfo лучше использовать два других метода класса CStatusBar - метод GetPaneStyle и метод SetPaneStyle.
Для изменения текста в самом первом индикаторе панели состояния, который имеет идентификатор ID_SEPARATOR, можно воспользоваться методом SetWindowText XE "CWnd:SetWindowText" . Этот метод определен в классе CWnd и вы можете его использовать, так как класс панели состояния наследуется от класса CWnd. Строку, которую надо вывести в панели состояния, следует передать через параметр lpszString:
void SetWindowText(LPCTSTR lpszString);
Метод SetWindowText изменяет текст в панели состояния, передавая ему сообщение WM_SETTEXT. Самый первый индикатор панели состояния, который имеет идентификатор ID_SEPARATOR, отличается от остальных индикаторов панели состояния. В нем отображаются подсказки для выбранных строк меню, кнопок панели управления и в некоторых других случаях. Фактически, этот индикатор используется различными объектами библиотеки MFC для отображения своего состояния или кратких подсказок.
Объекты MFC устанавливают текст первого идентификатора, непосредственно передавая окну панели состояния сообщение WM_SETTEXT. Отсюда, кстати, следует не очень утешительный вывод: текст, который вы выводите методом SetWindowText, может быть изменен в любой момент без вашего ведома. Чтобы исправить такое положение вещей, можно наследовать от класса CStatusBar собственный класс, в котором определить обработчик для сообщения WM_SETTEXT. Этот обработчик, например, может полностью игнорировать сообщения WM_SETTEXT или обрабатывать их только в особых случаях.
Если вам надо изменить текст не только в самом первом индикаторе, но и в других индикаторах панели состояния, можно воспользоваться методом SetPaneText, который определен в классе CStatusBar:
BOOL SetPaneText( int nIndex, LPCTSTR lpszNewText, BOOL bUpdate = TRUE );
Метод SetPaneText выводит в индикаторе с индексом nIndex текст, указанный в параметре lpszNewText. Параметр bUpdate позволяет сразу обновить данный индикатор. Если bUpdate содержит значение TRUE, то индикатор будет перерисован.
Текст, который отображается в индикаторе в данный момент, можно определить при помощи метода GetPaneText, также входящего в класс CStatusBar.
Еще один метод изменения индикаторов панели состояния основан на обработке сообщения ON_UPDATE_COMMAND_UI. Во время, когда приложение простаивает, сообщение ON_UPDATE_COMMAND_UI передается приложению для всех индикаторов панели состояния. Напомним, что сообщение ON_UPDATE_COMMAND_UI также рассылается для всех строк меню и всех кнопок панелей управления приложения.
Имена для методов обработчиков сообщения ON_UPDATE_COMMAND_UI обычно составляются из строки OnUpdate и имени объекта, сообщения которого обрабатываются. Формат этих методов следующий:
afx_msg void OnUpdateCtrlName(CCmdUI* pCmdUI);
Обработчику сообщения ON_UPDATE_COMMAND_UI через параметр pCmdUI передается указатель на объект класса CCmdUI. Полученный объект класса CCmdUI представляет индикатор панели состояния.
¨ Сообщение ON_UPDATE_COMMAND_UI также используется и для обновления других объектов пользовательского интерфейса, например строк меню и кнопок панелей управления
Во время обработки сообщения ON_UPDATE_COMMAND_UI вы можете изменить текст в данном индикаторе, вызвав метод SetText класса CCmdUI. Новый текст для индикатора надо указать в качестве параметра lpszText метода SetText:
virtual void SetText(LPCTSTR lpszText);
Метод SetText не выполняет автоматическое изменение размера индикатора. Поэтому, вы должны сами следить за шириной отображаемого текста и в случае необходимости корректировать размер индикатора при помощи метода SetPaneInfo.
Во время обработки сообщения ON_UPDATE_COMMAND_UI можно не только изменить текст в индикаторе, можно полностью заблокировать индикатор. В заблокированном индикаторе текст не отображается. Чтобы выполнить блокировку индикатора, при обработке соответствующего сообщения надо вызвать метод Enable.
Метод Enable, также как метод SetText, определен в классе CCmdUI и имеет следующий прототип:
virtual void Enable(BOOL bOn = TRUE);
Единственный параметр метода bOn выбирает новое состояние индикатора. Если в качестве параметра bOn указать значение TRUE или вызвать метод Enable без параметра, индикатор переходит в нормальное состояние - текст внутри индикатора отображается. Если через параметр bOn передать значение FALSE, тогда выполняется блокировка индикатора.
Если во время работы приложения выполняется какой-либо длительный процесс, например загрузка или сохранение документа, тогда в панели состояния можно вывести линейный индикатор progress bar. С ее помощью вы легко сможете показать ход данного процесса.
Многие офисные приложения, и сама среда Microsoft Visual C++, использует панель состояния подобным образом. Так, например, когда вы сохраняете документ в текстовом процессоре Microsoft Word, то в панели состояния отображается линейный индикатор progress bar, отражающий процесс сохранения документа.
Методика размещения полосы progress bar на панели состояния достаточно проста. В тот момент, когда потребуется вывести полосу progress bar, просто создайте ее, указав в качестве родительского окна панель состояния. Координаты линейного индикатора progress bar желательно выбрать таким образом, чтобы он отображался на месте одного из индикаторов. Предварительно вы можете убрать рамку с этого индикатора и заблокировать его так, чтобы в нем не отображался текст.
Создайте новый проект под названием Status. В качестве типа приложения выберите из списка Type строку Application. Настройте проект Status, указав, что приложение будет работать с библиотекой классов MFC.
Наберите в редакторе исходный текст приложения и сохраните его в файле Status.cpp (листинг 3.13). Включите готовый файл DialogBar.cpp в проект.
Листинг 3.13. Файл Status.cpp
//============================================================ // Приложение Status // (c) Frolov G.V., 1996 // E-mail: frolov@glas.apc.org //============================================================ // Включаемые файлы для MFC #include <afxwin.h> #include <afxext.h> #include <afxcmn.h> // Включаемый файл для ресурсов приложения и идентификаторов #include "resource.h" //============================================================ // Класс CStateApp - главный класс приложения //============================================================ class CStateApp : public CWinApp { public: // Мы будем переопределять метод InitInstance, // предназначенный для инициализации приложения virtual BOOL InitInstance(); }; // Создаем объект приложение класса CStateApp CStateApp StateApp; //============================================================ // Класс CStateWindow - представляет главное окно //============================================================ class CStateWindow : public CFrameWnd { protected: CStatusBar m_wndStatusBar; // Панель состояния BOOL bIndicatorTEXT; // Флаг для управления // индикатором // ID_INDICATOR_TEXT protected: // Метод для создания окна приложения и панели состояния afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); public: // Объявляем конструктор класса CStateWindow CStateWindow(); // Объявляем методы для обработки команд меню afx_msg BOOL OnMenuDirectADD_SUB(UINT nID); afx_msg void OnMenuProcessBar(); afx_msg void OnMenuDisableADD_SUB(); afx_msg void OnMenuSwitchTEXT(); afx_msg void OnMenuExit(); // Метод для обработки команды ON_UPDATE_COMMAND_UI // от индикатора ID_INDICATOR_TEXT afx_msg void OnUpdateTEXT(CCmdUI* pCmdUI); // Макрокоманда необходима, так как класс // CStateWindow обрабатывает сообщения DECLARE_MESSAGE_MAP() }; //============================================================ // Таблица сообщений класса CStateWindow //============================================================ BEGIN_MESSAGE_MAP(CStateWindow, CFrameWnd) // Макрокоманда вызывает метод OnCreate ON_WM_CREATE() // Обработчик сообщения ON_UPDATE_COMMAND_UI ON_UPDATE_COMMAND_UI(ID_INDICATOR_TEXT, OnUpdateTEXT) // Обработчики команд меню Work ON_COMMAND(ID_WORK_PROCESS, OnMenuProcessBar) ON_COMMAND(ID_WORK_DISABLE_ADDSUB, OnMenuDisableADD_SUB) ON_COMMAND(ID_WORK_ON_SWITCH_TEXT, OnMenuSwitchTEXT) ON_COMMAND_EX(ID_WORK_DIRECT_ADD, OnMenuDirectADD_SUB) ON_COMMAND_EX(ID_WORK_DIRECT_SUB, OnMenuDirectADD_SUB) ON_COMMAND(ID_WORK_EXIT, OnMenuExit) END_MESSAGE_MAP() //============================================================ // Индикаторы панели управления. Порядок идентификаторов // соответствует порядку индикаторов в панели состояния // (до тех пор, пока он не изменен методом SetPaneInfo) //============================================================ static UINT indicators[] = { ID_SEPARATOR, // Самый первый индикатор ID_INDICATOR_OVR, // Индикатор OVR ID_INDICATOR_PROGRESS, // Резервирование места для // progress bar ID_INDICATOR_CAPS, // Индикатор клавиши <Caps Lock> ID_INDICATOR_NUM, // Индикатор клавиши <Num Lock> ID_INDICATOR_SCRL, // Индикатор клавиши <Scroll Lock> ID_INDICATOR_TEXT, // Индикатор TEXT/PIC ID_INDICATOR_ADD, // Индикатор ADD/SUB (начальное // состояние START) }; //============================================================ // Метод InitInstance класса CStateApp // Создает главное окно приложения и отображает его на экране //============================================================ BOOL CStateApp::InitInstance() { m_pMainWnd = new CStateWindow(); m_pMainWnd -> ShowWindow(m_nCmdShow); m_pMainWnd -> UpdateWindow(); return TRUE; } //============================================================ // Конструктор класса CStateWindow //============================================================ CStateWindow::CStateWindow() { // Создаем окно приложения, соответствующее // данному объекту класса CStateWindow Create(NULL, "Status Bar Sample", WS_OVERLAPPEDWINDOW, rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU)); // Устанавливаем значение флага bIndicatorTEXT bIndicatorTEXT = TRUE; } //============================================================ // Метод OnMenuProcessBar класса CStateWindow //============================================================ void CStateWindow::OnMenuProcessBar() { // Определяем координаты индикатора ID_INDICATOR_PROGRESS RECT rectProgress; m_wndStatusBar.GetItemRect( m_wndStatusBar.CommandToIndex(ID_INDICATOR_PROGRESS), &rectProgress); // Создаем полосу progress bar. Размещаем ее // на месте индикатора ID_INDICATOR_PROGRESS CProgressCtrl ctrlProgressBar; if(!ctrlProgressBar.Create(WS_CHILD | WS_VISIBLE, rectProgress, &m_wndStatusBar, 1)) { // Ошибка при создании progress bar TRACE0("Failed to create progress bar\n"); return; } // Устанавливаем границы для progress bar ctrlProgressBar.SetRange(0, 100); // Устанавливаем шаг приращения для progress bar ctrlProgressBar.SetStep(1); // Плавно увеличиваем положение progress bar for(int i=0;i<100;i++) { // Выполняем короткую задержку Sleep(10); // Выполняем шаг приращения progress bar ctrlProgressBar.StepIt(); } // По завершении, отображаем текст в самом первом // индикаторе панели состояния m_wndStatusBar.SetWindowText("Process completed"); } //============================================================ // Метод OnMenuDirectADD_SUB класса CStateWindow //============================================================ BOOL CStateWindow::OnMenuDirectADD_SUB(UINT nID) { // Определяем индекс индикатора ID_INDICATOR_ADD int nIndex = m_wndStatusBar.CommandToIndex(ID_INDICATOR_ADD); // Устанавливаем нормальный режим отображения индикатора m_wndStatusBar.SetPaneStyle(nIndex, SBPS_NORMAL); // Из меню Work выбрана строка Direct set ADD if(nID == ID_WORK_DIRECT_ADD) { // Выводим текст ADD m_wndStatusBar.SetPaneText(nIndex, "ADD"); } // Из меню Work выбрана строка Direct set SUB else if(nID == ID_WORK_DIRECT_SUB) { // Изменяем внешний вид индикатора m_wndStatusBar.SetPaneStyle(nIndex, SBPS_POPOUT); // Выводим текст SUB m_wndStatusBar.SetPaneText(nIndex, "SUB"); } return TRUE; } //============================================================ // Метод OnMenuDisableADD_SUB класса OnMenuDisableADD_SUB //============================================================ void CStateWindow::OnMenuDisableADD_SUB() { // Определяем индекс индикатора ID_INDICATOR_ADD int nIndex = m_wndStatusBar.CommandToIndex(ID_INDICATOR_ADD); // Блокируем индикатор m_wndStatusBar.SetPaneStyle(nIndex, SBPS_DISABLED); } //============================================================ // Метод OnUpdateTEXT класса CStateWindow //============================================================ void CStateWindow::OnMenuSwitchTEXT() { // Изменяем состояние флага bIndicatorTEXT, // который используется методом OnUpdateTEXT bIndicatorTEXT = !bIndicatorTEXT; } //============================================================ // Метод OnMenuExit класса CStateWindow //============================================================ void CStateWindow::OnMenuExit() { // Завершаем приложение DestroyWindow(); return; } //============================================================ // Метод OnCreate класса CStateWindow // Вызывается во время создания окна приложения //============================================================ int CStateWindow::OnCreate(LPCREATESTRUCT lpCreateStruct) { // Вызываем метод OnCreate базового класса if(CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // Создаем панель состояния if(!m_wndStatusBar.Create(this)) { // Ошибка при создании панели состояния TRACE0("Failed to create status bar\n"); return -1; } // Отображаем индикаторы панели состояния if(!m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { // Ошибка при установке индикаторов TRACE0("Failed to set indicators\n"); return -1; } // Устанавливаем характеристики индикатора // ID_INDICATOR_PROGRESS m_wndStatusBar.SetPaneInfo( m_wndStatusBar.CommandToIndex(ID_INDICATOR_PROGRESS), ID_INDICATOR_PROGRESS, SBPS_DISABLED | // Текст не отображается SBPS_NOBORDERS, // Рамка вокруг индикатора отсутствует 150 ); // Ширина индикатора 150 пикселов return 0; } //============================================================ // Метод OnUpdateTEXT класса CStateWindow // Обрабатывает сообщение ON_UPDATE_COMMAND_UI // от индикатора ID_INDICATOR_TEXT //============================================================ void CStateWindow::OnUpdateTEXT(CCmdUI* pCmdUI) { // В зависимости от состояния флага bIndicatorTEXT // отображаем в индикаторе ID_INDICATOR_TEXT // строку TEXT или PIC if(bIndicatorTEXT) pCmdUI->SetText("TEXT"); // отображаем строку TEXT else pCmdUI->SetText("PIC"); // отображаем строку PIC // Разрешаем отображение текста в индикаторе pCmdUI->Enable(); }
Создайте новый файл ресурсов и включите его в проект под именем Status.rc. Включите в него меню, присвоив ему идентификатор IDR_MENU. Введите строки меню IDR_MENU в соответствии с представленным нами файлом ресурсов (листинг 3.14). Для всех строк меню введите их описания. Они будут записаны в файл ресурсов как строковые ресурсы, имеющие одинаковые идентификаторы со строками меню.
Добавьте в файл ресурсов строку Ready, выбрав для нее идентификатор AFX_IDS_IDLEMESSAGE. Эта строка будет отображаться в панели состояния во время “бездействия” приложения.
Добавьте в файл ресурсов строки, представляющие индикаторы панели состояния: ID_INDICATOR_ADD, ID_INDICATOR_PROGRESS и ID_INDICATOR_TEXT.
Листинг 3.14. Файл Status.rc
//Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // Russian resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) #ifdef _WIN32 LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT #pragma code_page(1251) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Menu // IDR_MENU MENU DISCARDABLE BEGIN POPUP "Work" BEGIN MENUITEM "Process", ID_WORK_PROCESS MENUITEM "Direct set ADD", ID_WORK_DIRECT_ADD MENUITEM "Direct set SUB", ID_WORK_DIRECT_SUB MENUITEM "Disable ADD SUB", ID_WORK_DISABLE_ADDSUB MENUITEM "Switch TEXT", ID_WORK_ON_SWITCH_TEXT MENUITEM SEPARATOR MENUITEM "Exit", ID_WORK_EXIT END END ////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE DISCARDABLE BEGIN ID_INDICATOR_ADD "START" END STRINGTABLE DISCARDABLE BEGIN ID_INDICATOR_PROGRESS "neve display" ID_INDICATOR_TEXT "TEXT" END STRINGTABLE DISCARDABLE BEGIN ID_WORK_PROCESS "Display and play progress bar" ID_WORK_DIRECT_ADD "Set indicator ID_INDICATOR_ADD to ADD" ID_WORK_ON_SWITCH_TEXT "Switch text in indicator ID_INDICATOR_TEXT" ID_WORK_DIRECT_SUB "Set indicator ID_INDICATOR_ADD to SUB" ID_WORK_DISABLE_ADDSUB "Disable indicator ID_INDICATOR_ADD" ID_WORK_EXIT "Exit application" END STRINGTABLE DISCARDABLE BEGIN AFX_IDS_IDLEMESSAGE "Ready" END #endif // Russian resources ////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
Идентификаторы ресурсов приложения Status определены в файле resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++. Исходный текст файла resource.h представлен в листинге 3.15.
Листинг 3.15. Файл resource.h
//{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by Status.rc // #define ID_INDICATOR_ADD 1 #define IDR_MENU 101 #define ID_INDICATOR_PROGRESS 102 #define ID_INDICATOR_TEXT 103 #define ID_WORK_PROCESS 40001 #define ID_WORK_DIRECT_ADD 40006 #define ID_WORK_ON_SWITCH_TEXT 40007 #define ID_WORK_DIRECT_SUB 40008 #define ID_WORK_DISABLE_ADDSUB 40009 #define ID_WORK_EXIT 40010 #define ID_TIMER_CHECK 0xE001 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 106 #define _APS_NEXT_COMMAND_VALUE 40011 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 104 #endif #endif
Постройте приложение Status и запустите его. На экране появится главное окно приложения, в нижней части которого отображается панель состояния (рис. 3.23).
Рис. 3.23. Приложение Status
В панели состояния расположены несколько индикаторов. Самый первый индикатор, размер которого зависит от размера окна приложения, отображает подсказку о выбранной строке меню приложения или системного меню, а если приложение “бездействует” в нем отображается строка Ready.
Следующий индикатор OVR. В нашем приложении он не действует. Вы сами можете разработать программный код для управления этим индикатором.
Вслед за идентификатором OVR следует свободное пространство, занятое индикатором. Этот индикатор не отображается на панели и предназначен только для резервирования места под линейный индикатор progress bar.
Если вы выберите из меню Work строку Process, то на этом месте появится линейный индикатор, плавно меняющий свое значение. Мы используем линейный индикатор, чтобы отображать ход какого-нибудь длительного процесса.
После того, как линейный индикатор покажет окончание процесса, он будет удален с панели состояния, а в самом первом индикаторе появится надпись Process completed.
Затем в панели состояния следуют три стандартных индикатора CAP, NUM и SCRL, которые отображают текущее состояние клавиш <Caps Lock>, <Num Lock> и <Scroll Lock>.
Следующий индикатор, который мы рассмотрим, называется TEXT. Если вы выберите из меню Work строку Switch TEXT, то надпись TEXT в индикаторе заменится на PIC. Повторный выбор данной строки меню восстановит предыдущее состояние индикатора.
Последний индикатор в панели состояния START. Этот индикатор управляет тремя строками меню Work. При выборе строки Direct set ADD в индикаторе отображается строка ADD, а при выборе строки Direct set SUB - SUB. Во втором случае также меняется оформление индикатора. Если вы выберите из меню Work строку Disable ADD SUB, то индикатор будет заблокирован.
В приложении Status определены два класса - главный класс приложения CStateApp и класс главного окна приложения CStateWindow.
Главный класс приложения CStateApp наследуется от базового класса CWinApp. Объект StateApp класса CStateApp объявлен как глобальный и создается сразу после запуска приложения.
В класс CStateApp входит только метод InitInstance. Он создает главное окно приложения, представленное классом CStateWindow, наследованным от класса CFrameWnd.
Класс CStateWindow управляет главным окном приложения, создает панель состояния, а также обрабатывает сообщения.
Кроме ряда методов, в класс CStateWindow входит флаг bIndicatorTEXT, используемый для управления индикатором ID_INDICATOR_TEXT, и объект m_wndStatusBar класса CStatusBar, предназначенный для создания и отображения полосы progress bar.
Рассмотрим отдельные методы класса CStateWindow более подробно.
Конструктор класса CStateWindow используется для создания главного окна приложения. Для этого вызывается метод Create класса CFrameWnd. Обратите внимание, что метод Create создает окно с меню, которое имеет идентификатор IDR_MENU:
Create(NULL, "Status Bar Sample", WS_OVERLAPPEDWINDOW, rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));
В конструкторе класса CStateWindow также устанавливается начальное состояние флага bIndicatorTEXT:
bIndicatorTEXT = TRUE;
Таблица сообщений класса CStateWindow обрабатывает командные сообщения от меню Work, а также содержит макрокоманды ON_UPDATE_COMMAND_UI и ON_WM_CREATE.
Макрокоманда ON_UPDATE_COMMAND_UI вызывает метод OnUpdateTEXT для обновления состояния индикатора ID_INDICATOR_TEXT панели состояния:
ON_UPDATE_COMMAND_UI(ID_INDICATOR_TEXT, OnUpdateTEXT)
Макрокоманда ON_WM_CREATE вызывает метод OnCreate во время создания окна:
ON_WM_CREATE()
Для обработки командных сообщений от меню Work в таблицу сообщений класса CStateWindow включены несколько макрокоманд ON_COMMAND и ON_COMMAND_EX. Они вызывают обработчики OnMenuProcessBar, OnMenuDisableADD_SUB, OnMenuSwitchTEXT, OnMenuDirectADD_SUB и OnMenuExit:
ON_COMMAND(ID_WORK_PROCESS, OnMenuProcessBar) ON_COMMAND(ID_WORK_DISABLE_ADDSUB, OnMenuDisableADD_SUB) ON_COMMAND(ID_WORK_ON_SWITCH_TEXT, OnMenuSwitchTEXT) ON_COMMAND(ID_WORK_EXIT, OnMenuExit) ON_COMMAND_EX(ID_WORK_DIRECT_ADD, OnMenuDirectADD_SUB) ON_COMMAND_EX(ID_WORK_DIRECT_SUB, OnMenuDirectADD_SUB)
Метод OnCreate класса CStateWindow сначала вызывает метод OnCreate базового класса CFrameWnd:
if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;
Затем мы создаем панель состояния, указывая в качестве ее родительского окна главное окно приложения:
if(!m_wndStatusBar.Create(this)) { // Ошибка при создании панели состояния TRACE0("Failed to create status bar\n"); return -1; }
После того, как панель состояния создана, вызываем метод SetIndicators, чтобы установить индикаторы:
if(!m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { // Ошибка при установке индикаторов TRACE0("Failed to set indicators\n"); return -1; }
Массив, содержащий идентификаторы индикаторов indicators определен в приложении следующим образом:
static UINT indicators[] = { ID_SEPARATOR, // Самый первый индикатор ID_INDICATOR_OVR, // Индикатор OVR ID_INDICATOR_PROGRESS, // Резервирование места для // progress bar ID_INDICATOR_CAPS, // Индикатор клавиши <Caps Lock> ID_INDICATOR_NUM, // Индикатор клавиши <Num Lock> ID_INDICATOR_SCRL, // Индикатор клавиши <Scroll Lock> ID_INDICATOR_TEXT, // Индикатор TEXT/PIC ID_INDICATOR_ADD, // Индикатор ADD/SUB (начальное // состояние START) };
Порядок идентификаторов в массиве indicators соответствует порядку в котором индикаторы будут отображаться в панели состояния. Размер всех индикаторов, кроме первого, выбирается автоматически, так чтобы текст индикатора полностью в нем поместился. Текст индикатора, который отображается в нем по умолчанию, берется из соответствующих строковых ресурсов приложения. Так, например, в последнем индикаторе панели состояния, который имеет идентификатор ID_INDICATOR_ADD будет отображаться строка START, имеющая тот же идентификатор и определенная в ресурсах приложения следующим образом:
STRINGTABLE DISCARDABLE BEGIN ID_INDICATOR_ADD "START" END
Все индикаторы панели состояния, кроме индикатора ID_INDICATOR_PROGRESS, отображаются стандартным образом. Стиль индикатора ID_INDICATOR_PROGRESS устанавливается отдельно:
m_wndStatusBar.SetPaneInfo( m_wndStatusBar.CommandToIndex(ID_INDICATOR_PROGRESS), ID_INDICATOR_PROGRESS, SBPS_DISABLED | // текст не отображается SBPS_NOBORDERS, // рамка вокруг индикатора отсутствует 150 ); // ширина индикатора 150 пикселов
Метод SetPaneInfo запрещает отображение текста внутри индикатора и убирает выделяющую рамку. Кроме того, метод SetPaneInfo устанавливает размер индикатора 150 пикселов.
Когда пользователь выбирает из меню Work строку Process, на месте индикатора ID_INDICATOR_PROGRESS создается линейный индикатор progress bar, плавно меняющий свое состояние. Обработка командного сообщения от строки Process меню Work осуществляется методом OnMenuProcessBar класса CStateWindow.
Метод OnMenuProcessBar определяет координаты индикатора ID_INDICATOR_PROGRESS и записывает их во временную переменную rectProgress:
RECT rectProgress; m_wndStatusBar.GetItemRect( m_wndStatusBar.CommandToIndex(ID_INDICATOR_PROGRESS), &rectProgress);
Затем на месте этого индикатора создается линейный индикатор progress bar. Орган управления progress bar представлен объектом ctrlProgressBar класса CProgressCtrl:
CProgressCtrl ctrlProgressBar;
Непосредственно для создания progress bar используется метод Create класса CProgressCtrl. В качестве параметров этому методу указываются атрибуты WS_CHILD и WS_VISIBLE, координаты rectProgress, объект m_wndStatusBar и идентификатор 1:
if(!ctrlProgressBar.Create(WS_CHILD | WS_VISIBLE, rectProgress, &m_wndStatusBar, 1)) { // Ошибка при создании progress bar TRACE0("Failed to create progress bar\n"); return; }
После создания полосы progress bar устанавливаем границы (от 0 до 100), в которых можно менять его значение:
ctrlProgressBar.SetRange(0, 100);
Выбираем шаг приращения для progress bar, равный единице:
ctrlProgressBar.SetStep(1);
Затем начинаем в цикле изменять значение линейного индикатора progress bar. Чтобы замедлить ход заполнения линейного индикатора, делаем короткую задержку, вызывая функцию Sleep:
for(int i=0;i<100;i++) { Sleep(10); ctrlProgressBar.StepIt(); }
Когда линейный индикатор progress bar окажется заполнен, вызываем метод SetWindowText, который отображает сообщение Process completed в самом первом индикаторе панели состояния:
m_wndStatusBar.SetWindowText("Process completed");
После завершения метода OnMenuProcessBar объект ctrlProgressBar, представляющий линейный индикатор progress bar, уничтожается и одновременно его изображение исчезает с панели состояния.
Когда пользователь выбирает из меню Work строки Direct set ADD и Direct set SUB, в класс окна поступают командные сообщения с идентификаторами ID_WORK_DIRECT_ADD и ID_WORK_DIRECT_SUB. Для их обработки вызывается метод OnMenuDirectADD_SUB:
ON_COMMAND_EX(ID_WORK_DIRECT_ADD, OnMenuDirectADD_SUB) ON_COMMAND_EX(ID_WORK_DIRECT_SUB, OnMenuDirectADD_SUB)
В качестве параметра nID методу OnMenuDirectADD_SUB передается соответствующий идентификатор:
BOOL CStateWindow::OnMenuDirectADD_SUB(UINT nID) { }
Порядок индикаторов в панели состояния не меняется, например, индикатор с идентификатором ID_INDICATOR_ADD, всегда будет иметь в нашем приложении индекс 7. Однако чтобы продемонстрировать метод CommandToIndex и сделать метод OnMenuDirectADD_SUB более независимым от расположения индикаторов, мы определяем индекс индикатора ID_INDICATOR_ADD:
int nIndex = m_wndStatusBar.CommandToIndex(ID_INDICATOR_ADD);
Следующим шагом мы устанавливаем нормальный режим отображения индикатора ID_INDICATOR_ADD. Для этого вызываем метод SetPaneStyle, указав ему индекс индикатора и атрибут SBPS_NORMAL:
m_wndStatusBar.SetPaneStyle(nIndex, SBPS_NORMAL);
Затем определяем, какое командное сообщение послужило причиной вызова метода OnMenuDirectADD_SUB. Если метод вызван для обработки командного сообщения от строки Direct set ADD меню Work, отображаем в индикаторе текст ADD:
if(nID == ID_WORK_DIRECT_ADD) { // Выводим текст ADD m_wndStatusBar.SetPaneText(nIndex, "ADD"); }
Если метод OnMenuDirectADD_SUB вызван для обработки командного сообщения от строки Direct set SUB меню Work, изменяем внешний вид индикатора и отображаем в нем текст SUB:
else if(nID == ID_WORK_DIRECT_SUB) { // Изменяем внешний вид индикатора m_wndStatusBar.SetPaneStyle(nIndex, SBPS_POPOUT); // Выводим текст SUB m_wndStatusBar.SetPaneText(nIndex, "SUB");
Для вывода текста в индикаторе ID_INDICATOR_ADD в методе OnMenuDirectADD_SUB мы используем метод SetPaneText класса CStatusBar. Метод SetPaneText не меняет размера индикатора. Поэтому, если вы желаете отобразить текст большей длины, надо увеличить размер индикатора с помощью метода SetPaneInfo.
Метод OnMenuDirectADD_SUB класса CStateWindow вызывается для обработки командного сообщения с идентификатором ID_WORK_DISABLE_ADDSUB, передаваемым при выборе из меню Work строки Disable ADD SUB.
Метод OnMenuDisableADD_SUB определяет индекс индикатора ID_INDICATOR_ADD, а затем блокирует его.
Чтобы узнать индекс индикатора ID_INDICATOR_ADD, мы вызываем метод CommandToIndex:
int nIndex = m_wndStatusBar.CommandToIndex(ID_INDICATOR_ADD);
Для блокировки индикатора вызываем метод SetPaneStyle, которому указываем индекс индикатора и атрибут SBPS_DISABLED:
m_wndStatusBar.SetPaneStyle(nIndex, SBPS_DISABLED);
Методы OnMenuSwitchTEXT и OnUpdateTEXT используются в приложении совместно для управления состоянием индикатора ID_INDICATOR_TEXT.
Метод OnMenuSwitchTEXT вызывается для обработки командного сообщения с идентификатором ID_WORK_ON_SWITCH_TEXT. Это сообщение поступает в случае выбора из меню Work строки Switch TEXT:
ON_COMMAND(ID_WORK_ON_SWITCH_TEXT, OnMenuSwitchTEXT)
Единственная задача метода OnMenuSwitchTEXT заключается в изменении состояния флага bIndicatorTEXT. Если флаг bIndicatorTEXT имеет значение TRUE, тогда метод OnMenuSwitchTEXT меняет его на FALSE и наоборот:
void CStateWindow::OnMenuSwitchTEXT() { bIndicatorTEXT = !bIndicatorTEXT; }
Метод OnUpdateTEXT класса CStateWindow, вызывается макрокомандой ON_UPDATE_COMMAND_UI из таблицы сообщений класса CStateWindow:
ON_UPDATE_COMMAND_UI(ID_INDICATOR_TEXT, OnUpdateTEXT)
Мы используем этот метод, чтобы изменить текст, отображаемый в индикаторе ID_INDICATOR_TEXT. В зависимости от состояния флага bIndicatorTEXT, установленного методом OnMenuSwitchTEXT, метод OnUpdateTEXT отображает в индикаторе либо строку TEXT, либо строку PIC:
void CStateWindow::OnUpdateTEXT(CCmdUI* pCmdUI) { // В зависимости от состояния флага bIndicatorTEXT // отображаем в индикаторе ID_INDICATOR_TEXT // строку TEXT или PIC if(bIndicatorTEXT) pCmdUI->SetText("TEXT"); // отображаем строку TEXT else pCmdUI->SetText("PIC"); // отображаем строку PIC // Разрешаем отображение текста в индикаторе pCmdUI->Enable(); }
В качестве параметра pCmdUI методу OnUpdateTEXT передается указатель на объект класса CCmdUI. Этот объект представляет объект интерфейса приложения (строку меню, кнопку панели управления или индикатор панели состояния). В контексте данного конкретного метода этот объект представляет индикатор панели состояния, имеющий идентификатор ID_INDICATOR_TEXT.
Для управления индикатором мы используем методы SetText и Enable класса CCmdUI. Эти методы устанавливают текст индикатора и снимают с него блокировку (если блокировка ранее была установлена).
Пользователь может завершить приложение, выбрав из меню Work строку Exit. В этом случае приложению передается командное сообщение с идентификатором ID_WORK_EXIT. Соответствующая макрокоманда ON_COMMAND из таблицы сообщений класса CStateWindow вызывает для обработки этого сообщения метод OnMenuExit:
ON_COMMAND(ID_WORK_EXIT, OnMenuExit)
Метод OnMenuExit завершает работу приложения, для чего вызывает метод DestroyWindow, определенный в классе CWnd для главного окна приложения:
DestroyWindow();