Microsoft Visual C++ и MFC. Программирование для Windows 95 и Windows NT (часть 2)© Александр Фролов, Григорий ФроловТом 28, М.: Диалог-МИФИ, 1996, 288 стр. Панель состоянияВ нижней части большинства современных приложений 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 можно указать один или несколько атрибутов, объединенных логической операцией ИЛИ.
Параметр 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, // Индикатор клавиши Порядок идентификаторов в массиве 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(); |