3. Меню, панели управления и панели состояния

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

Многие приложения также имеют панель управления - небольшую полоску, содержащую ряд кнопок. Как правило, эта полоска располагается ниже меню в главном окне приложения. Кнопки панелей управления дублируют некоторые, как правило наиболее часто используемые, строки меню. Их использование позволяет значительно ускорить и облегчить работу с приложением.

Меню и панели управления служат для передачи команд приложению. Напротив, панель состояния отражает текущее состояние приложения и может давать пользователю краткие подсказки, состоящие буквально из нескольких строк.

Все однооконные и многооконные приложения, созданные с использованием 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, которая уже заполнена текущими параметрами строки меню.

Меню без класса CMenu

Как и для других элементов пользовательского интерфейса, для управления меню в состав библиотеки классов 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

Метод 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 позволяет динамически создавать окно, пользуясь информацией из файла ресурсов. В случае успешного завершения метод LoadFrame возвращает ненулевое значение, а в противном случае - ноль:


virtual BOOL LoadFrame(
   UINT nIDResource,
   DWORD dwDefaultStyle = WS_OVERLAPPEDWINDOW|FWS_ADDTOTITLE,
   CWnd* pParentWnd = NULL,
   CCreateContext* pContext = NULL
);

Параметры метода LoadFrame практически идентичны параметрам метода Create, описанного ранее. Исключение составляет первый параметр - nIDResource. Он представляет идентификатор, общий для нескольких ресурсов, используемых при создании окна. К таким ресурсам относятся меню (будет использоваться как меню окна), строковый ресурс (заголовок окна), пиктограмма (отображается в случае минимизации окна) и таблица клавиш акселерации (используется для ускоренного выбора строк меню).

Класс CMenu

Вы можете создать меню и без использования методов Create или LoadFrame. Для этого вы должны будете создать объект класса CMenu XE "CMenu" и вызвать для него несколько методов.

Конструктор класса CMenu

Объект класса CMenu не является меню, он только представляет существующее меню. Вы можете создать объект класса CMenu как локальный, а после использования удалить. На меню это не повлияет:


{
   CMenu myMenu;
}

Метод LoadMenu

После объявления объекта класса CMenu вы можете загрузить меню из ресурсов приложения, воспользовавшись для этой цели методом LoadMenu. В случае успешного завершения метод LoadMenu возвратит ненулевое значение, и нуль в противном случае:


BOOL LoadMenu(LPCTSTR lpszResourceName);
BOOL LoadMenu(UINT nIDResource);

Метод LoadMenu загрузит меню, заданное именем lpszResourceName или идентификатором nIDResource, и свяжет его с соответствующим объектом класса CMenu. Теперь вы можете использовать для управления загруженным меню другие методы класса CMenu.

После того как меню загружено, его можно “подключить” к окну. Для этого следует воспользоваться методом SetMenu входящим в класс CWnd.

Метод 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 позволяют выделять отдельные строки меню и даже создавать элементы меню, содержащие не только текст но и изображение.

Класс CCmdUI

В 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 предназначена для использования в таблицах сообщений приложения и имеет следующий формат:


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 ClassWizard и команды обновления

Если приложение подготовлено с использованием 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. Методы этого класса позволяют заблокировать отдельные элементы меню, панелей управления и панелей состояния, отметить их символами Ö или ·.

Класс CCmdUI один из немногих классов библиотеки MFC, который не имеет других базовых классов. Поэтому для объектов данного класса доступно относительно мало методов. Вы можете использовать только методы, определенные в самом классе CCmdUI.

Метод Описание
Enable Устанавливает или снимает блокировку
SetCheck Помечает строку меню символом Ö
SetRadio Помечает строку меню символом ·
SetText Устанавливает текст. Обычно используется для изменения текста в индикаторах панели состояния

Метод Enable

Виртуальный метод Enable позволяет установить или снять блокировку с объекта интерфейса пользователя, представленного объектом класса CCmdUI. Метод имеет единственный параметр bOn. Если параметр bOn содержит значение TRUE или не указан совсем, то блокировка снимается. Если параметр bOn содержит значение FALSE, то блокировка устанавливается:


virtual void Enable(BOOL bOn = TRUE);

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

Метод Enable также можно использовать для блокирования органов диалоговых панелей управления.

Метод SetCheck

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


virtual void SetCheck(int nCheck = 1);

Если вы используете метод SetCheck для управления меню и задали в качестве параметра nCheck нулевое значение, то соответствующая строка меню выделяется символом Ö, если параметр nCheck не указан или равен 1, то выделение снимается.

В случае использования метода SetCheck для управления кнопкой панели управления, параметр nCheck задает новое состояние кнопки. Если параметр nCheck равен нулю, кнопка переходит в нажатое положение, если параметр nCheck не указан или равен единице - кнопка переходит в отжатое положение, а если параметр nCheck равен 2, кнопка принимает промежуточное состояние.

Вы можете использовать метод SetCheck для управления внешним видом индикаторов панелей состояния. Если параметр nCheck равен нулю, то рамка индикатора изменяется таким образом, что он будет располагается выше общего уровня панели состояния. Если параметр nCheck равен 1, тогда индикатор переходит в нормальное состояние.

Метод Enable также можно использовать для выбора положения переключателей в диалоговых панелей управления.

Метод SetRadio

Виртуальный метод 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

Виртуальный метод SetText может быть использован для изменения текста, отображаемого в индикаторе панели состояния, в строке меню, в названии кнопок и некоторых органах диалоговых панелей управления. В качестве параметра lpszText надо указать текстовую строку, которую надо вывести:


virtual void SetText(LPCTSTR lpszText);

Следует отметить, что при использовании метода SetText для изменения текста в индикаторах панели состояния, вы должны отдельно позаботиться об изменении размера индикатора. Метод SetText не меняет размер индикатора, вы должны сами рассчитать ширину текста и изменить размер индикатора с помощью соответствующего метода. Более подробно об изменении параметров индикаторов панели состояния мы расскажем в разделе “Панель состояния”.

Элементы данных класса CCmdUI

Помимо представленных методов, в состав класса входит и несколько элементов данных. Они позволяют определить идентификатор строки меню, кнопки панели управления или индикатора панели состояния, для которого вызван метод обработчик.

Метод Описание
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

Создайте новый проект под названием 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

В приложении MultiMenu определены два класса - главный класс приложения CStateApp и класс главного окна приложения CStateWindow.

Главный класс приложения CMultiMenuApp

Главный класс приложения CMultiMenuApp наследуется от базового класса CWinApp. Объект MultiMenuApp класса CMultiMenuApp объявлен как глобальный и создается сразу после запуска приложения.

В класс CMultiMenuApp входит только метод InitInstance. Он создает главное окно приложения, представленное классом CMultiMenuWindow, наследованным от класса CFrameWnd XE "CFrameWnd" .

Класс главного окна приложения CMultiMenuWindow

Класс CMultiMenuWindow управляет главным окном приложения, создает меню, загружает панель управления а также обрабатывает сообщения, в том числе командные сообщения и команды обновления от меню.

Фактически все методы, определенные в классе CMultiMenuWindow, можно условно разделить на три группы. В первую группу попал только один метод OnCreate. Он обрабатывает сообщение WM_CREATE, поступающее в момент создания окна приложения. Вторая группа состоит из шести методов - OnDisable, OnCommand, OnExit, OnConstruct, OnRestrictMenu и OnFullMenu. Эти методы используются для обработки командных сообщений от меню приложения. И, наконец, третья группа методов включает три метода - OnUpdateProcess, OnUpdateConstruct и OnUpdateDisable, которые обрабатывают команды обновления от трех различных строк меню приложения.

В состав класса также входит несколько элементов данных. Это флаги bEnable, bRadio и nCheck, управляющие характеристиками трех строк меню, а также объект m_wndStatusBar класса CStatusBar, представляющий панель состояния нашего приложения.

Рассмотрим отдельные методы класса CMultiMenuWindow более подробно.

Конструктор класса 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

Таблица сообщений класса 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 класса 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;   
}

Более подробно о принципах устройства панелей состояния мы расскажем в отдельном разделе, который носит название “Панель состояния”.

Метод OnDisable класса CMultiMenuWindow

Когда пользователь выбирает из меню 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, который является обработчиком команд обновления от этой строки меню.

Метод OnCommand класса CMultiMenuWindow

Когда пользователь выбирает строку Process из меню File (для укороченного варианта меню) или из меню Mission (для полного варианта меню), или просто нажимает комбинацию клавиш <Ctrl+P>, приложению поступает командное сообщение, которое имеет идентификатор ID_MISSION_PROCESS. Для обработки этого сообщения вызывается метод CMultiMenuWindow класса CMultiMenuWindow. Данный метод отображает на экране сообщение Command not implemented.

Метод OnConstruct класса CMultiMenuWindow

Когда пользователь выбирает из меню Mission строку Construction, приложению поступает командное сообщение с идентификатором ID_MISSION_CONSTRUCT. Для обработки этого сообщения вызывается метод OnConstruct класса CMultiMenuWindow. Метод OnConstruct изменяет состояние флага bRadio, меняя значение bRadio с TRUE на FALSE и наоборот:


bRadio = !bRadio;

Флаг bRadio управляет отображением символа · около строки Construction меню Mission. Флаг bRadio проверяется методом OnUpdateConstruct, который является обработчиком команд обновления от этой строки меню.

Методы OnRestrictMenu и OnFullMenu класса CMultiMenuWindow

Приложение 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();
Метод OnUpdateProcess класса CMultiMenuWindow

Команды обновления от строк Process меню File и Mission передаются для обработки методу OnUpdateProcess класса CMultiMenuWindow. Этот метод блокирует или снимает блокировку со строки Process в зависимости от значения флага bEnable:


pCmdUI->Enable(bEnable); 
Метод OnUpdateConstruct класса CMultiMenuWindow

Команда обновления от строки Construction меню Mission передается для обработки методу OnUpdateConstruct класса CMultiMenuWindow. Этот метод устанавливает или снимаем отметку · со строки Construction в зависимости от значения флага bRadio:


pCmdUI->SetRadio(bRadio);
Метод OnUpdateDisable класса CMultiMenuWindow

Команда обновления от строки Disable меню Menu передается для обработки методу OnUpdateDisable класса CMultiMenuWindow. Этот метод устанавливает или снимаем отметку Ö со строки Disable, в зависимости от значения флага nCheck:


pCmdUI->SetCheck(nCheck);
Метод OnMenuExit класса CMultiMenuWindow

Пользователь может завершить приложение, выбрав из меню File строку Exit. В этом случае приложению передается командное сообщение с идентификатором ID_FILE_EXIT. Соответствующая макрокоманда ON_COMMAND из таблицы сообщений класса CStateWindow вызывает для обработки этого сообщения метод OnMenuExit:


ON_COMMAND(ID_WORK_EXIT, OnMenuExit)

Метод OnMenuExit завершает работу приложения, для чего вызывает метод DestroyWindow, определенный в классе CWnd, для главного окна приложения:


void CMultiMenuWindow::OnExit()
{
   // Завершаем приложение
   DestroyWindow();
   return;
}

Component Gallery и контекстное меню

Новые операционные системы 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 для главного окна приложения и для окна просмотра. Тогда если вы нажмете правую кнопку мыши в то время, когда указатель мыши находится в окне просмотра, то отображается одно контекстное меню, а если вы нажмете правую кнопку мыши когда ее указатель расположен вне окна просмотра - отображается другое контекстное меню.

Класс CMultiView

Все изменения в программном коде приложения 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, в таблицу сообщений класса 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()
Метод OnContextMenu класса CMultiView

Когда пользователь нажимает правую кнопку мыши в окне, макрокоманда 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 ненулевое значение, а в противном случае нуль.

Метод PreTranslateMessage класса CMultiView

Кроме добавления новой макрокоманды к таблице сообщений класса 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, которые помогут сделать панель управления еще более удобной для пользователя.

Метод SetBarStyle класса 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 класса 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

Все кнопки, кроме трех, недоступны и отображаются серым цветом.

При нажатии на первые две доступные кнопки из панели управления на экране появляется сообщение о том, что данная команда недоступна. Если вы нажмете на последнюю кнопку в панели управления, то приложение завершит свою работу.

Как устроено приложение CBarApp

Обратите внимание на первые строки файла 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

Метод EnableDocking класса CFrameWnd разрешает пристыковку панели управления к определенным границам окна:

void EnableDocking(DWORD dwDockStyle);

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

Флаг Описание
CBRS_ALIGN_TOP Панель управления можно пристыковать к верхней границе окна
CBRS_ALIGN_BOTTOM Панель управления можно пристыковать к нижней границе окна
CBRS_ALIGN_LEFT Панель управления можно пристыковать к левой границе окна
CBRS_ALIGN_RIGHT Панель управления можно пристыковать к правой границе окна
CBRS_ALIGN_ANY Панель управления можно пристыковать к любой границе окна

Метод EnableDocking класса CControlBar

Чтобы панель управления можно было перемещать с одной стороны окна к другой, надо вызвать метод 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 и FloatControlBar класса CFrameWnd

Чтобы пристыковать панель управления к границе окна, надо вызвать метод 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.

Недокументированные возможности класса CMainFrame

Изучая пример приложения 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

Создайте новый проект под названием 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

Создайте новый файл ресурсов и включите его в проект под именем 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 и запустите его. На экране появится главное окно приложения MultiBar, в котором отображаются сразу три панели управления - Extended, Player и Slyle. Панели управления Extended и Player присоединены к верхней и нижней границам окна, а панель управления Slyle отображается в отдельном мини-окне (рис. 3.16).

Рис. 3.16. Приложение MultiBar

Приложение MultiBar имеет меню View, состоящее из названий панелей управления Extended, Player и Slyle. Выбирая из меню View названия панелей управления, вы можете убрать их с экрана и опять отобразить (закрыть и открыть).

Панели управления, которые отображаются в мини-окнах, также можно закрыть, если нажать на переключатель в верхнем правом углу окна. Чтобы снова открыть закрытую панель управления, используйте меню View.

Панель управления Player

Панель управления Player состоит из девяти кнопок, сгруппированных в четыре отдельные группы. Панель Player предназначена для управления музыкальным центром. Все кнопки имеют краткие подсказки.

При нажатии на кнопки панели управления Player на экране появляется сообщение, о том, что команда не поддерживается. Обратите внимание на кнопки Pause, Type, CD и WAVE.

Кнопка Pause работает как переключатель. Если вы на нее нажмете - она остается в нажатом положении до тех пор, пока вы не нажмете на нее еще один раз. Кнопки Type, CD и WAVE работают вместе как переключатель с зависимой фиксацией. Одновременно может быть нажата только одна из этих кнопок.

Панель управления Player можно присоединить к трем из четырех сторон главного окна приложения - к верхней, левой и нижней. К правой стороне окна панель Player не присоединяется (рис. 3.17). Вы также можете использовать для панели Player отдельное мини окно, форму которого можно изменять.

Рис. 3.17. Изменение формы панели управления Player

Панель управления Extended

Панель 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

Панель управления Slyle отображается исключительно в отдельном мини-окне. Вы не сможете пристыковать ее ни к одной из сторон главного окна приложения MultiBar. Кнопки панели управления Slyle отображаются в три ряда по четыре кнопки в каждом ряду. В отличие от панелей управления Extended и Player, форма панели управления Slyle не изменяется.

Два верхних ряда кнопок в панели управления MultiBar имеют краткие подсказки. Если указатель мыши задержится над ними на некоторое время, то около него появится маленькое окно tooltip с кратким описанием соответствующей кнопки. Кнопки из нижнего ряда подсказок не имеют.

Как устроено приложение MultiBar

В приложении MultiBar определены три класса CMultiBarApp, CMultiBarWindow и CExtendedBar. Классы CMultiBarApp и CMultiBarWindow представляют основные классы приложения, класс CExtendedBar представляет одну из панелей управления и будет рассмотрен ниже.

Главный класс приложения CMultiBarApp

Главный класс приложения 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 класса 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 определены в классе главного окна приложения CMultiBarWindow и используются для обработки сообщений от кнопок панелей управления.

Фактически методы BarCommandOne и BarCommandRange не выполняют никакой полезной работы. Единственное, что делает метод BarCommandOne, - это отображает на экране сообщение о том, что выбранная вами команда не реализована. Для этого мы используем метод MessageBox:


void CMultiBarWindow::BarCommandOne()
{
   // Отображаем сообщение о том, что команда не реализована
   MessageBox("Command not implemented");	
}

Метод BarCommandRange вообще не выполняет никакой полезной работы. Однако не спешите исключать его из программы. Если вы удалите методы BarCommandOne и BarCommandRange из программы и удалите соответствующие макрокоманды из таблицы сообщений класса окна, тогда все кнопки, для обработки сообщений от которых использовались эти методы, окажутся заблокированы. MFC определит, что сообщения от кнопок не обрабатываются и запретит их использование:

void CMultiBarWindow::BarCommandRange( UINT nID )
{
   // Обработчик не выполняет никакой работы
}
Метод AddStringToComboBox класса CMultiBarWindow

Метод 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 входят всего два метода - это конструктор класса 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

Создайте новый проект под названием 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

В приложении DialogBar определены всего только два класса - это главный класс приложения CDlgBarApp и класс главного окна приложения CDlgBarWindow. Как и во многих других приложениях, посвященных использованию панелей управления, главное окно приложения по сути является его единственным окном. Однако мы сохранили за ним это почетное название, чтобы внести однообразие в описание приложений, созданных с использованием MFC AppWizard и без него.

Главный класс приложения CDlgBarApp

Главный класс приложения CDlgBarApp наследуется от базового класса CWinApp. Объект DlgBarApp класса CDlgBarApp объявлен как глобальный и создается сразу после запуска приложения.

В класс CDlgBarApp входит только метод InitInstance. Он создает главное окно приложения, представленное классом CDlgBarWindow, наследованным от класса CFrameWnd. Мы не станем подробно рассматривать этот метод, так как он фактически идентичен одноименному методу приложений Bar и MultiBar, представленных выше.

Класс главного окна приложения CDlgBarWindow

Класс 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

Конструктор класса CDlgBarWindow используется для создания главного окна приложения. Мы рассказывали о процедуре создания главного окна приложения в томе 24 серии “Библиотека системного программиста”, посвященном библиотеке MFC, поэтому сейчас не будем на нем останавливаться более подробно.

Таблица сообщений класса CDlgBarWindow

Таблица сообщений класса 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 класса 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 устанавливает расположение диалоговой панели управления в верхней части окна приложения.

Метод DlgBarCombo класса CDlgBarWindow

Когда пользователь выбирает строку из списка combo-box в диалоговой панели управления, в ее родительское окно, которое в нашем случае является главным окном приложения, поступает сообщение. Для его обработки вызывается метод DlgBarCombo класса CDlgBarWindow.

Метод DlgBarCombo выводит на экран сообщение о том, что пользователь сделал выбор из списка combo-box:


void CDlgBarWindow::DlgBarCombo()
{
   // Отображаем сообщение о том, что сделан выбор из списка 
   MessageBox("Combo-box selection changed");
}
Метод BarCommand класса CDlgBarWindow

Командные сообщения от кнопок 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

Если вам надо добавить панель управления к приложению, созданному с использованием средств 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

Создайте новый проект под названием 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

В приложении Status определены два класса - главный класс приложения CStateApp и класс главного окна приложения CStateWindow.

Главный класс приложения CDlgBarApp

Главный класс приложения CStateApp наследуется от базового класса CWinApp. Объект StateApp класса CStateApp объявлен как глобальный и создается сразу после запуска приложения.

В класс CStateApp входит только метод InitInstance. Он создает главное окно приложения, представленное классом CStateWindow, наследованным от класса CFrameWnd.

Класс главного окна приложения CStateWindow

Класс CStateWindow управляет главным окном приложения, создает панель состояния, а также обрабатывает сообщения.

Кроме ряда методов, в класс CStateWindow входит флаг bIndicatorTEXT, используемый для управления индикатором ID_INDICATOR_TEXT, и объект m_wndStatusBar класса CStatusBar, предназначенный для создания и отображения полосы progress bar.

Рассмотрим отдельные методы класса CStateWindow более подробно.

Конструктор класса CStateWindow

Конструктор класса CStateWindow используется для создания главного окна приложения. Для этого вызывается метод Create класса CFrameWnd. Обратите внимание, что метод Create создает окно с меню, которое имеет идентификатор IDR_MENU:


Create(NULL, "Status Bar Sample", WS_OVERLAPPEDWINDOW,
      rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU)); 

В конструкторе класса CStateWindow также устанавливается начальное состояние флага bIndicatorTEXT:


bIndicatorTEXT = TRUE;
Таблица сообщений класса CStateWindow

Таблица сообщений класса 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 класса 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 пикселов.

Метод OnMenuProcessBar класса CStateWindow

Когда пользователь выбирает из меню 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, уничтожается и одновременно его изображение исчезает с панели состояния.

Метод OnMenuDirectADD_SUB класса CStateWindow

Когда пользователь выбирает из меню 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

Метод 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 класса CStateWindow

Методы 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. Эти методы устанавливают текст индикатора и снимают с него блокировку (если блокировка ранее была установлена).

Метод OnMenuExit класса CStateWindow

Пользователь может завершить приложение, выбрав из меню Work строку Exit. В этом случае приложению передается командное сообщение с идентификатором ID_WORK_EXIT. Соответствующая макрокоманда ON_COMMAND из таблицы сообщений класса CStateWindow вызывает для обработки этого сообщения метод OnMenuExit:


ON_COMMAND(ID_WORK_EXIT, OnMenuExit)

Метод OnMenuExit завершает работу приложения, для чего вызывает метод DestroyWindow, определенный в классе CWnd для главного окна приложения:


DestroyWindow();