Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

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 можно указать один или несколько атрибутов, объединенных логической операцией ИЛИ.

Атрибут

Описание

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,      // Индикатор клавиши 
   ID_INDICATOR_NUM,       // Индикатор клавиши 
   ID_INDICATOR_SCRL,      // Индикатор клавиши 
   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();
[Назад] [Содеожание] [Дальше]