Microsoft Visual C++ и MFC. Программирование для Win95 и WinNT

         

Класс CTime - дата и время


Для работы с календарными датами и временем в состав библиотеки классов MFC включен класс CTime. Класс основан на элементе типа time_t, в котором будет храниться дата и время. Элемент типа time_t объявлен как private, поэтому вы не можете обращаться непосредственно к этому элементу. Для этого в состав класса CTime входит набор методов.

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

Если вам требуется создать массив, состоящий из объектов класса CTime, используйте конструктор без указания параметров:

CTime();

Объекты, созданные этим конструктором не инициализированы. Перед тем как ими пользоваться, запишите в них дату и время:

CTime(const CTime& timeSrc);

Параметр timeSrc определяет уже существующий объект класса CTime. Новый объект класса инициализируется значением даты и времени, взятым из существующего объекта.

Если в программе уже существует переменная типа time_t, в которой хранится значение, то можно создать новый объект класса CTime, основываясь на этой переменной:

CTime(time_t time);

Параметр time определяет переменную типа time_t, значение которой будет записано в создаваемый объект.

Если существует несколько переменных, в которых отдельно хранятся год, месяц, день, часы, минуты и секунды, то можно создать объект класса CTime, сразу же записав в него значения из этих переменных:

CTime(int nYear, int nMonth, int nDay,

             int nHour, int nMin, int nSec, int nDST = -1);

Первые 6 параметров метода определяют время и дату. Параметр nYear должен содержать год, nMonth - месяц, nDay - день, nHour - час, nMin - минуты, nSec - секунды. NYear может принимать значения от 1900 до 2038, nMonth - от 1 до 12 и nDay от 1 до 31.

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


Следующий конструктор позволяет создать объект класса CTime и записать в него дату и время, определенные в формате, принятом в операционной системе MS-DOS:

CTime(WORD wDosDate, WORD wDosTime, int nDST = -1);

Параметры wDosDate и wDosTime должны содержать, соответственно, дату и время в формате MS-DOS. Параметр nDST управляет режимом перехода на летнее время. Мы уже рассматривали его выше.

Кроме класса CTime, существует, как минимум, еще две структуры, в которых может храниться значения даты и времени. Первая такая структура называется SYSTEMTIME. В ней хранятся текущие значения даты и времени. Структура SYSTEMTIME определена следующим образом:



typedef struct _SYSTEMTIME {

      WORD wYear;                 // год

      WORD wMonth;                                // месяц

      WORD wDayOfWeek;                      // день недели

      WORD wDay;                   // календарная дата

      WORD wHour;                  // часы

      WORD wMinute;                               // минуты

      WORD wSecond;                             // секунды

      WORD wMilliseconds;      // миллисекунды

} SYSTEMTIME;

Если необходимо создать объект класса CTime, сразу записав в него текущие значения времени и даты, используйте конструктор, представленный ниже:

CTime(const SYSTEMTIME& sysTime, int nDST = -1);

Параметр sysTime является указателем на структуру типа SYSTEMTIME. Необязательный параметр nDST управляет режимом отсчета даты и описан нами выше.

Вторая структура, в которой хранятся значения даты и времени, называется FILETIME. Она служит для хранения 64-битового числа, представляющего дату и время как количество 100 наносекундных интервалов времени, прошедших с первого января 1601 года.

typedef struct _FILETIME {

      DWORD dwLowDateTime;               // младшие 32 бита

      DWORD dwHighDateTime;                // старшие 32 бита

} FILETIME, *PFILETIME, *LPFILETIME;

Конструктор имеет следующий прототип:

CTime(const FILETIME& fileTime, int nDST = -1);


Класс CUserException


Если какая-либо операция при работе приложения закончилась с ошибкой, оно может вызвать функцию AfxMessageBox, чтобы сообщить об этом пользователю, а затем вызвать исключение с объектом класса CUserException. Чтобы создать объект класса CUserException и вызвать исключение, воспользуйтесь функцией AfxThrowUserException:

void AfxThrowUserException();



Класс диалоговой панели


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

//=====================================================

// Класс CMyDialog - класс диалоговой панели

//=====================================================

class CMyDialog : public CDialog

{

public:

      CMyDialog();

      CString m_Text;

protected:

      virtual void DoDataExchange(CDataExchange* pDX);

     

      // Обработчики сообщений от кнопок диалоговой панели

      afx_msg void OnDefault();

      virtual void OnCancel();

      virtual void OnOK();

      DECLARE_MESSAGE_MAP()

};

Обратите внимание на то, как мы определяем конструктор класса CMyDialog. После названия конструктора стоит символ двоеточие и название конструктора класса CDialog. При этом в качестве параметра, конструктору CDialog передается идентификатор диалоговой панели "DIALOGPANEL":

// Конструктор класса CMyDialog

CMyDialog::CMyDialog() : CDialog("DIALOGPANEL")

{

      // Инициализируем переменную m_Text

      m_Text = "";

}

Основное назначение конструктора CMyDialog - вызвать конструктор класса CDialog, указав ему в качестве параметра идентификатор диалоговой панели "DIALOGPANEL". Именно конструктор класса CDialog выполняет создание диалоговой панели.

В конструкторе также инициализируется переменная m_Text, входящая в класс CMyDialog. В нее записывается пустая строка.



Класс главной диалоговой панели приложения


Большой интерес представляет файл DialogDlg.h, показанный в листинге 4.3. Этот класс содержит объявление класса главной диалоговой панели приложения CDialogDlg.

Класс CDialogDlg наследуется от базового класса CDialog, определенного в библиотеке классов MFC. Конструктор класса имеет один необязательный параметр pParent, используемый для передачи индекса главного окна приложения. Приложение Dialog не имеет главного окна. Роль главного окна выполняет сама диалоговая панель, поэтому параметр pParent не используется.

Непосредственно после объявления конструктора класса следует объявление элементов данных класса, которые добавлены средствами MFC AppWizard или ClassWizard. Они расположены между двумя комментариями AFX_DATA. Не рекомендуется вручную изменять код приложения, расположенный между этими комментариями.

После блока AFX_DATA следует блок AFX_VIRTUAL. В этом блоке MFC AppWizard и ClassWizard добавляют объявления переопределенных виртуальных методов базового класса.

Сначала в этом блоке объявлен только один метод DoDataExchange, переопределенный в нашем проекте. Этот метод применяется для связывания с органами управления диалоговой панели элементов управляющего ей класса.

Практически со всеми приложениями связана пиктограмма, которая будет отображаться при минимизации приложения. Обычно эта пиктограмма определяется на этапе регистрации класса главного окна приложения. Приложение Dialog не имеет настоящего главного окна. Вместо него используется диалоговая панель. Поэтому отображение пиктограммы приложения не происходит автоматически и мы должны управлять этим сами. Идентификатор пиктограммы m_hIcon определен в классе CDialogDlg после блока AFX_VIRTUAL.

Диалоговая панель CDialogDlg будет обрабатывать ряд сообщений. Объявления обработчиков сообщений созданных средствами MFC AppWizard или ClassWizard располагаются между двумя комментариями AFX_MSG, образующими блок AFX_MSG.

После создания проекта в классе CDialogDlg объявлены 4 обработчика сообщений OnInitDialog, OnSysCommand, OnPaint и OnQueryDragIcon. Эти методы определены в файле DialogDlg.cpp, описанном ниже.


      // В следующем блоке ClassWizard размещает переопределение

      // виртуальных методов

      //{{AFX_VIRTUAL(CAboutDlg)

protected:

      // Поддержка DDX/DDV

      virtual void DoDataExchange(CDataExchange* pDX);

      //}}AFX_VIRTUAL

// Implementation

protected:

      //{{AFX_MSG(CAboutDlg)

      //}}AFX_MSG

      DECLARE_MESSAGE_MAP()

};

// Конструктор класса CAboutDlg

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)

{

      //{{AFX_DATA_INIT(CAboutDlg)

      //}}AFX_DATA_INIT

}

// Метод DoDataExchange класса CAboutDlg

void CAboutDlg::DoDataExchange(CDataExchange* pDX)

{

      CDialog::DoDataExchange(pDX);

      //{{AFX_DATA_MAP(CAboutDlg)

      //}}AFX_DATA_MAP

}

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

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)

      //{{AFX_MSG_MAP(CAboutDlg)

             // Класс CAboutDlg не обрабатывает никаких сообщений

      //}}AFX_MSG_MAP

END_MESSAGE_MAP()

//////////////////////////////////////////////////////////////

// Ниже определены различные методы класса CDialogDlg

// Конструктор класса CDialogDlg

CDialogDlg::CDialogDlg(CWnd* pParent /*=NULL*/)

      : CDialog(CDialogDlg::IDD, pParent)

{

      //{{AFX_DATA_INIT(CDialogDlg)

             // В этом блоке ClassWizard размещает инициализацию

             // элементов данных класса

      //}}AFX_DATA_INIT

      // Вызов LoadIcon не требует последующего вызова

      // DestroyIcon, если вы используете программный интерфейс

      // Win32

      m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

}

// Метод DoDataExchange класса CDialogDlg

void CDialogDlg::DoDataExchange(CDataExchange* pDX)

{

      CDialog::DoDataExchange(pDX);

      //{{AFX_DATA_MAP(CDialogDlg)

             // Здесь ClassWizard размещает вызовы методов DDX и DDV

      //}}AFX_DATA_MAP

}

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

BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)

      //{{AFX_MSG_MAP(CDialogDlg)



      ON_WM_SYSCOMMAND()

      ON_WM_PAINT()

      ON_WM_QUERYDRAGICON()

      //}}AFX_MSG_MAP

END_MESSAGE_MAP()

// Метод OnInitDialog класса CDialogDlg

BOOL CDialogDlg::OnInitDialog()

{

      CDialog::OnInitDialog();

      // Добавление строки "About..." к системному меню приложения

      // Проверяем, что идентификатор IDM_ABOUTBOX относится к

      // системным командам

      ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

      ASSERT(IDM_ABOUTBOX < 0xF000);

      CMenu* pSysMenu = GetSystemMenu(FALSE);

      CString strAboutMenu;

      strAboutMenu.LoadString(IDS_ABOUTBOX);

      if (!strAboutMenu.IsEmpty())

      {

             pSysMenu->AppendMenu(MF_SEPARATOR);

             pSysMenu->AppendMenu(MF_STRING,

                                                                                IDM_ABOUTBOX, strAboutMenu);

      }

      // Выбираем пиктограмму для диалоговой панели. Если главное

      // окно приложения не является диалоговой панелью, этот код

      // не нужен

      SetIcon(m_hIcon,TRUE);  // выбираем пиктограмму большого

                                                                      // размера

      SetIcon(m_hIcon,FALSE); // выбираем пиктограмму маленького

                                                                      // размера

     

      // TODO: Здесь вы можете выполнить дополнительную

      // инициализацию

     

      return TRUE; 

}

// Метод OnSysCommand класса CDialogDlg

void CDialogDlg::OnSysCommand(UINT nID, LPARAM lParam)

{

      if ((nID & 0xFFF0) == IDM_ABOUTBOX)

      {

             CAboutDlg dlgAbout;

             dlgAbout.DoModal();

      }

      else

      {

             CDialog::OnSysCommand(nID, lParam);

      }

}

// Если вы добавили кнопку минимизации к диалоговой панели,

// следующий код нужен, чтобы отобразить пиктограмму

// Метод OnPaint класса CDialogDlg

void CDialogDlg::OnPaint()



{

      if (IsIconic())

      {

             CPaintDC dc(this); // контекст устройства

             SendMessage(WM_ICONERASEBKGND,

                                                                (WPARAM) dc.GetSafeHdc(), 0);

             // Выравниваем по центру пиктограмму

             int cxIcon = GetSystemMetrics(SM_CXICON);

             int cyIcon = GetSystemMetrics(SM_CYICON);

             CRect rect;

             GetClientRect(&rect);

             int x = (rect.Width() - cxIcon + 1) / 2;

             int y = (rect.Height() - cyIcon + 1) / 2;

             // Отображаем пиктограмму

             dc.DrawIcon(x, y, m_hIcon);

      }

      else

      {

             CDialog::OnPaint();

      }

}

// Данный метод вызывается для определения формы курсора,

// отображаемого, когда пользователь переносит

// минимизированное окно

// Метод OnQueryDragIcon класса CDialogDlg

HCURSOR CDialogDlg::OnQueryDragIcon()

{

      return (HCURSOR) m_hIcon;

}


Классы


В программах, написанных на языке С, данные и функции, предназначенные для их обработки определяются отдельно. Такое разделение затрудняет структурированное программирование и создает дополнительные возможности для ошибок, которые трудно обнаружить.

В С++ введено новое понятие - класс. Класс позволяет объединить данные и оперирующие ими функции в одной структуре. Такое объединение обычно называют инкапсуляцией данных и связанных с ними функций. Инкапсуляция позволяет скрыть конкретную реализацию класса, облегчая отладку и модификацию программ.

Объявление класса имеет следующий вид:

class [<tag>]

{

     

<member-list>

} [<declarators>];    

Когда вы определяете класс, то сначала указывается ключевое слово class, а затем в качестве аргумента <tag> имя самого класса. Это имя должно быть уникальным среди имен других классов, определенных в вашей программе.

Затем в фигурных скобках следует список элементов класса <member-list>. В качестве элементов класса могут фигурировать данные (переменные), битовые поля, функции, вложенные классы, а также некоторые другие объекты. Вы можете включить качестве элемента класса указатель на другие объекты этого класса.

Классы образуют собственное пространство имен. Имена элементов одного класса могут совпадать с именами элементов другого класса и даже с именами других переменных и функций определенных в программе.

Функции входящие в класс, называются функциями-элементами, или следуя терминологии объектно-ориентированного подхода, методами. Далее мы будем называть такие функции методами. Внутри класса вы можете свободно обращаться ко всем его элементам функциям и данным, без указания имени класса или имени объекта этого класса.

После закрывающей фигурной скобки в аргументе <declarators> можно объявить один или несколько объектов данного класса. Объекты класса можно объявить и позже, точно так же как объявляются переменные простых типов:

[class] tag declarators;

Ключевое слово class перед именем класса можно опустить.

Для динамического создания и удаления объектов классов можно пользоваться операторами new и delete. Эти операторы позволяют легко строить списки классов, деревья и другие сложные структуры.



Классы библиотеки MFC


Библиотека классов MFC содержит большое количество разнообразных классов. Так MFC версии 4.0 включает немного меньше 200 классов. Каждый класс, как правило, содержит от нескольких единиц до нескольких десятков различных методов и элементов данных. Конечно это значительно усложняет освоение программирования с помощью MFC. Но не стоит расстраиваться и опускать руки. Вам вовсе не обязательно знать как устроены все 200 классов. Большинство приложений можно построить на основе нескольких основных классов. Более того, самое простое приложение, которое мы предложим вашему вниманию, будет использовать только один класс из библиотеки MFC.

Мы не станем приводить здесь всю иерархию классов MFC. Вы можете изучить ее, воспользовавшись документацией или справочной системой среды Visual C++. Чтобы просмотреть иерархию классов в справочной системе Visual C++, выберите из окна Project Workspace страницу InfoView, откройте описание MFC 4.0, а затем из раздела Class Library Reference выберите статью Hierarcy Chart (рис. 2.1).

Рис. 2.1. Просмотр иерархии классов MFC

Сейчас мы кратко рассмотрим назначение основных классов библиотеки MFC и их связь друг с другом.



Классы, не имеющие базового класса


Кроме классов, наследованных от базового класса CObject, библиотека MFC включает ряд самостоятельных классов. У них нет общего базового класса и они имеют различное назначение.

Несколько классов, которые не наследуются от базового класса CObject, мы уже описали. К ним относятся класс CCmdUI, CFileStatus, CDataExchange, CFieldExchange и CDaoFieldExchange.



Классы приложения


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

MFC AppWizard создает для приложения Single, обладающего однооконным интерфейсом, 4 основных класса. Эти классы представляют основу любого однооконного приложения, созданного MFC AppWizard.

Класс приложения

Базовый класс

Описание

CSingleApp

CWinApp

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

CMainFrame

CFrameWnd

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

CSingleDoc

CDocument

Класс документа приложения

CSingleView

CView

Класс окна просмотра документа

Кроме основных классов, создается также класс CAboutDlg, наследованный от базового класса CDialog, который отвечает за диалоговую панель About. Если во время определения характеристик приложения вы включите возможность работы с базами данных или использование технологии OLE, общий список классов приложения может стать шире.



Ключевое слово this


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



Коллекции


Перед программистом часто возникает задача упорядоченного хранения объектов и переменных. Для этого он вынужден самостоятельно реализовывать такие структуры как списки, сложные массивы. Библиотека классов MFC позволяет значительно облегчить решение этих задач.

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



Командные сообщения


Сообщения WM_COMMAND от меню, кнопок панели управления и клавиш акселераторов. В отличие от оконных сообщений и сообщений от органов управления, командные сообщения могут быть обработаны более широким спектром объектов. Эти сообщения обрабатывают не только объекты, представляющие окна, но также объекты классов, представляющих приложение, документы и шаблон документов.

Характерной особенностью командных сообщений является идентификатор. Идентификатор командного сообщения определяет объект, который вырабатывает (посылает) данное сообщение.

В приложении MFMenu строка Beep меню Test имеет идентификатор ID_TEST_BEEP. Когда пользователь выберет данную строку, в очередь приложения поступит сообщение WM_COMMAND с идентификатором ID_TEST_BEEP или другими словами, командное сообщение ID_TEST_BEEP.



Командные сообщения и приложение Single


Большинство командных сообщений от элементов меню и панели управления поступают для обработки главному окну приложения, представленному объектом класса CMainFrame.

Главное окно приложения сразу передает командное сообщение для обработки окну просмотра. Окно просмотра представлено объектом класса CSingleView.

Если класс окна просмотра не имеет метода для обработки сообщения, оно передается документу. Документ приложения Single является объектом класса CSingleDoc.

Если документ приложения также не может обработать командное сообщение, оно передается объекту, представляющему шаблон документа.

В случае, если шаблон документа также не содержит обработчика сообщения, проверяется таблица сообщений класса главного окна приложения.

Если и главное окно приложения не может обработать поступившее командное сообщение, оно направляется объекту главного класса приложения CSingleApp.



Командные сообщения с идентификаторами ID_WINDOW_


Командные сообщения с идентификаторами ID_WINDOW_ соответствуют элементам меню Window многооконных приложений, созданных при помощи средств MFC AppWizard. Обработка этих командных сообщений возложена на метод OnMDIWindowCmd класса CMDIFrameWnd.

Идентификатор командного сообщения

Описание

ID_WINDOW_NEW

Открыть новое окно с текущим документом

ID_WINDOW_ARRANGE

Выровнять пиктограммы в нижней части окна MDI

ID_WINDOW_CASCADE

Выполнить каскадное размещение окон

ID_WINDOW_TILE_HORZ

Расположить окна рядом по горизонтали

ID_WINDOW_TILE_VERT

Расположить окна рядом по вертикали

ID_WINDOW_SPLIT

Разделить окно на две части



Командные сообщения с идентификаторами ID_APP_


В MFC определены только два командных сообщения с идентификаторами ID_APP_. Они предназначены для завершения приложения и вывода информации о приложении и его авторе.

                                                    

Идентификатор командного сообщения

Описание

ID_APP_EXIT

Завершить приложение.

Данное командное сообщение обрабатывается методом OnAppExit класса CWinApp. Метод OnAppExit передает сообщение WM_CLOSE главному окну приложения

ID_APP_ABOUT

Отобразить на экране краткую справку о программе - диалоговую панель About.

Ни один из классов MFC не выполняет обработки этого сообщения по умолчанию, но MFC AppWizard автоматически создает необходимый для этого программный код



Командные сообщения с идентификаторами ID_EDIT_


Командные сообщения с идентификаторами ID_EDIT_ соответствуют элементам меню Edit приложений, созданных при помощи средств MFC AppWizard. Это меню обычно используется для выполнения различных операций над документом, отображаемым в окне просмотра.

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

Класс CView не содержит стандартных обработчиков для командных сообщений, имеющих идентификаторы ID_EDIT_. Вы должны их реализовать самостоятельно в своем классе окна просмотра.

Идентификатор командного сообщения

Описание

ID_EDIT_CLEAR

Удалить выделенный объект

ID_EDIT_CLEAR_ALL

Удалить содержимое документа

ID_EDIT_COPY

Скопировать выделенный объект в универсальный буфер обмена clipboard

ID_EDIT_CUT

Удалить выделенный объект и записать его в clipboard

ID_EDIT_FIND

Отобразить на экране диалоговую панель для поиска заданного объекта в документе

ID_EDIT_PASTE

Вставить в документ содержимое Clipboard

ID_EDIT_REPEAT

Повторить последнюю операцию

ID_EDIT_REPLACE

Отобразить диалоговую панель для поиска и замены текста

ID_EDIT_SELECT_ALL

Выбрать (выделить) весь документ

ID_EDIT_UNDO

Отменить последнюю операцию

ID_EDIT_REDO

Выполнить последнюю отмененную операцию



Командные сообщения с идентификаторами ID_FILE_


Командные сообщения с идентификаторами ID_FILE_ соответствуют элементам меню File приложений, созданных при помощи средств MFC AppWizard. Обработчики этих сообщений входят в состав различных классов MFC, в том числе CWinApp и CDocument.

Идентификатор командного сообщения

Описание

ID_FILE_NEW

Создать новый документ. Класс CWinApp содержит стандартный обработчик этого сообщения - метод OnFileNew. Если вы желаете его использовать, необходимо поместить в таблицу сообщений главного класса приложения соответствующую макрокоманду (см. приложение Single)

ID_FILE_OPEN

Открыть документ, записанный на диске. Класс CWinApp содержит стандартный обработчик этого сообщения - метод OnFileOpen. Если вы желаете его использовать, необходимо поместить в таблицу сообщений главного класса приложения соответствующую макрокоманду (см. приложение Single)

ID_FILE_CLOSE

Закрыть текущий документ. Класс CDocument содержит метод OnFileClose, предназначенный для обработки этого командного сообщения. Метод OnFileClose вызывает метод SaveModified, если документ приложения был изменен, а затем вызывает метод OnCloseDocument

ID_FILE_SAVE

Сохранить текущий документ. За обработку этого командного сообщения отвечает метод OnSaveDocument класса CDocument

ID_FILE_SAVE_AS

Сохранить текущий документ под новым именем. За обработку этого командного сообщения отвечает метод OnSaveDocument класса CDocument

ID_FILE_SAVE_COPY_AS

Сохранить копию текущего документа под новым именем

ID_FILE_PAGE_SETUP

Вызывает диалоговую панель выбора формата документа

ID_FILE_PRINT_SETUP

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

ID_FILE_PRINT

Выполнить печать текущего документа

ID_FILE_PRINT_PREVIEW

Перейти в режим предварительного просмотра документа перед печатью

ID_FILE_MRU_FILE1...

FILE16

Открыть один из наиболее часто используемых файлов приложения



Командные сообщения с идентификаторами ID_HELP_


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

Класс CWinApp содержит методы для обработки командных сообщений, связанных со справочной системой. Если вы используете справочную систему, вы должны сами вызывать соответствующие методы класса CWinApp для обработки командных сообщений ID_HELP_.

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

                                                    

Идентификатор командного сообщения

Описание

ID_HELP_INDEX

Отобразить список статей из справочной базы данных, записанной в HLP-файле

ID_HELP_USING

Отобразить подсказку об использовании справочной системы

ID_CONTEXT_HELP

Перейди в режим контекстной подсказки. Передается также при нажатии комбинации клавиш <Shift+F1>

ID_HELP

Получить справочную информацию по данному контексту

ID_DEFAULT_HELP

Получить справочную информацию определенную по умолчанию для данного контекста



Командные сообщения с идентификаторами ID_VIEW_


Командные сообщения с идентификаторами ID_VIEW_ соответствуют элементам меню View приложений, созданных при помощи средств MFC AppWizard. За обработку командных сообщений ID_VIEW_ отвечает класс CFrameWnd.

Идентификатор командного сообщения

Описание

ID_VIEW_TOOLBAR

Отобразить или скрыть панель управления toolbar

ID_VIEW_STATUS_BAR

Отобразить или скрыть панель состояния status bar

Описание стандартных команд и методов, предназначенных для их обработки, вы можете получить из справочной системы Visual C++. В следующих книгах серии “Библиотека системного программиста” мы изучим наиболее важные стандартные командные сообщения более подробно.



Константы


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

Ключевое слово const указывают перед объявлением переменной. Такая переменная не может быть модифицирована. Попытки изменить ее вызывают ошибку на этапе компиляции.

В программе, приведенной ниже, объявляются две константы. Одна типа int, другая типа char:

// Включаемый файл для потокового ввода/вывода

#include <stdio.h>

int main(void)

{

      // Объявляем две константы

      const int     max_nuber = 256;

      // Выводим текстовую строку на экран

      printf("Const Number is %d \n", max_nuber);

      return 0;

}

Ключевое слово const можно указывать при объявлении постоянных указателей, которые не могут менять своего значения. Заметим, что объект (переменная), определяемый постоянным указателем, может быть изменен:

int  iNumber;

int  *const ptrNumber = &iNumber;



Конструктор и деструктор класса CMainFrame


MFC AppWizard определяет для вас конструктор и деструктор класса CMainFrame. Вы можете расположить в конструкторе и деструкторе CMainFrame код для инициализации объектов класса.

// Конструктор класса CMainFrame

CMainFrame::CMainFrame()

{

      // TODO:

}

// Деструктор класса CMainFrame

CMainFrame::~CMainFrame()

{

}



Конструктор и деструктор класса CSingleDoc


Конструктор CSingleDoc, подготовленный MFC AppWizard, содержит пустой блок. Вы можете поместить в него код инициализации для объектов класса. Следует иметь в виду, что для приложений, построенных на основе однооконного интерфейса, объект класса документ создается всего один раз. Когда пользователь создает новый документ или открывает документ уже записанный в файле, используется старый объект класса, представляющего документ. Более подробно вы узнаете об этом в следующей главе.

// Конструктор класса CSingleDoc

CSingleDoc::CSingleDoc()

{

      // TODO:

}

Вместе с конструктором класса CSingleDoc, создается деструктор ~CSingleDoc. Деструктор не содержит кода и представляет собой такую же заготовку как и конструктор.

// Деструктор класса CSingleDoc

CSingleDoc::~CSingleDoc()

{

}



Конструктор и деструктор класса CSingleView


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

// Конструктор класса CSingleView

CSingleView::CSingleView()

{

      // TODO:

}

Вместе с конструктором класса CSingleView, MFC AppWizard определяет деструктор ~ CSingleView. Сразу после создания проекта деструктор не выполняет никаких действий. В дальнейшем вы можете использовать его совместно с конструктором CSingleView.

// Деструктор класса CSingleView

CSingleView::~CSingleView()

{

}



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


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

Конструктор класса CString, используемый по умолчанию, не имеет параметров. Он создает пустую строку. В последствии вы можете записать в нее любой текст.

CString();

Для класса CString определен конструктор копирования. Он позволяет создать строку на основе другой, ранее определенной строки. Этот и все остальные конструкторы могут вызвать исключение CMemoryException, если для создания строки недостаточно оперативной памяти.

CString(const CString& stringSrc);

     

throw(CMemoryException);

Конструктор, представленный ниже, позволяет создать строку из nRepeat символов ch. Параметр nRepeat можно не указывать. В этом случае строка будет содержать единственный символ ch.

CString(TCHAR ch, int nRepeat = 1);

     

throw(CMemoryException);

Вы можете создать новую строку и скопировать в нее nLength символов из массива lpch. Строка, расположенная в массиве lpch, может не иметь завершающего двоичного нуля. Для этого используйте следующий указатель:

CString(LPCTSTR lpch, int nLength);

     

throw(CMemoryException);

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

Для создания строки вы можете воспользоваться одним из трех конструкторов, представленных ниже. Как видите, конструкторы отличаются только типом параметра:

CString(const unsigned char* psz);

     

throw(CMemoryException);

CString(LPCWSTR lpsz);

     

throw(CMemoryException);

CString(LPCSTR lpsz);

     

throw(CMemoryException);



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


Конструктор класса CDialogDlg вызывает конструктор базового класса CDialog. При этом ему передается идентификатор диалоговой панели IDD и идентификатор главного окна приложения pParent. При создании объекта класса CDialogDlg не указываются никакие параметры. Поэтому pParent по умолчанию принимается равным NULL.

//////////////////////////////////////////////////////////////

// Конструктор класса CDialogDlg

CDialogDlg::CDialogDlg(CWnd* pParent /*=NULL*/)

      : CDialog(CDialogDlg::IDD, pParent)

{

      //{{AFX_DATA_INIT(CDialogDlg)

             // В этом блоке ClassWizard размещает инициализацию

             // элементов данных класса

      //}}AFX_DATA_INIT

      // Вызов LoadIcon не требует последующего вызова

      // DestroyIcon, если вы используете программный интерфейс

      // Win32

      m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

}

В теле конструктора расположен блок AFX_DATA_INIT. В него ClassWizard будет добавлять код инициализации элементов данных класса CDialogDlg. Конструктор также инициализирует m_hIcon, записывая в него идентификатор пиктограммы IDR_MAINFRAME.

Функция AfxGetApp возвращает указатель на объект главного класса приложения. Такой объект для данного приложения всегда один. В нашем случае AfxGetApp определяет указатель на объект theApp. Вот прототип этой функции:

CWinApp* AfxGetApp();



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


Конструктор класса CSingleApp не выполняет никаких действий и состоит из пустого блока. Вы можете разместить в конструкторе класса CSingleApp код для инициализации приложения, однако лучше всего для этого воспользоваться методом InitInstance.

//////////////////////////////////////////////////////////////

// Конструктор класса CSingleApp

CSingleApp::CSingleApp()

{

      // TODO: Здесь вы можете разместить свой код

}



Конструкторы и деструкторы класса


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

Язык С++ предоставляет удобное средство для инициализации и удаления объектов класса. Для этого предусмотрены специальные методы. Они называются конструкторами и деструкторами.

Функция конструктор имеет такое же имя как имя класса и позволяет выполнить инициализацию объекта класса в момент его создания. Конструктор может иметь параметры. Их надо будет указать при определении объекта данного класса. Класс может иметь несколько конструкторов с разными параметрами, то есть конструкторы могут быть перегружены.

Класс BookList, представленный ниже, имеет два конструктора BookList. Первый конструктор не имеет параметров, второй конструктор имеет один параметр типа int:

class BookList

{

      // Конструкторы класса

      void      BookList();

      void      BookList(int);

      // Остальные члены класса

};

// Первый конструктор класса

BookList::BookList(void)

{

}

// Второй конструктор класса

BookList::BookList(int iList)

{

}

Когда вы создаете объекты класса, вы можете указать параметры для конструктора. Ниже создаются два объекта класса BookList - FirstBook и SecondBook:

BookList FirstBook;

BookList SecondBook(100);

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

Имя деструктора также соответствует имени класса, но перед ним должен стоять символ тильда. Деструктор вызывается автоматически, когда объект уничтожается. Например, если определить объект данного класса внутри блока, то при выходе из блока для объект будет вызвана функция деструктор. Функция деструктор не имеет параметров, поэтому она не может быть перегружена, а значит у данного класса может быть только один деструктор.

Ниже представлен класс Object, для которого определен деструктор ~Object:

class Object

{

      void      ~Object();

      // Остальные члены класса

};

Object::~Object(void)

{

}



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


Для класса CObject определены два конструктора. Первый конструктор используется по умолчанию и не имеет параметров. Именно этот конструктор вызывается конструкторами классов, наследованных от CObject:

CObject();

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

Для класса CObject конструктор копирования описан как private и не имеет определения. Если вы попытаетесь передать по значению объект класса, наследованного от CObject, и не определите конструктор копирования в своем классе, вы получите ошибку на этапе компиляции.

В случае, если бы конструктор копирования класса CObject не был описан, компилятор создал бы его автоматически. Таким образом описание конструктора копирования как private, принуждает вас явно определить конструктор копирования для порожденных классов (конечно если конструктор копирования необходим):

private:

      CObject(const CObject& objectSrc);

Для динамического создания и удаления объектов класса CObject вы можете воспользоваться операторами new и delete. Эти операторы переопределены в классе CObject, однако назначение и основные принципы их работы остались прежними.



Контекст отображения (класс CDC)


Для отображения информации в окне или на любом другом устройстве приложение должно получить так называемый контекст отображения. Основные свойства контекста отображения определены в классе CDC. От него наследуется четыре различных класса, представляющие контекст отображения различных устройств (рис. 2.6).

Рис. 2.6. Класс CDC

В следующей таблице приведено краткое описание классов, наследованных от CDC.

Класс

Описание

CClientDC

Контекст отображения, связанный с внутренней областью окна (client area). Для получения контекста конструктор класса вызывает функцию программного интерфейса GetDC, а деструктор - функцию ReleaseDC

CMetaFileDC

Класс предназначен для работы с метафайлами

CPaintDC

Конструктор класса CPaintDC для получения контекста отображения вызывает метод CWnd::BeginPaint, деструктор метод CWnd::EndPaint. Объекты данного класса могут использовать только при обработке сообщения WM_PAINT. Это сообщение обычно обрабатывает метод OnPaint

CWindowDC

Контекст отображения, связанный со всем окном. Для получения контекста конструктор класса вызывает функцию программного интерфейса GetWindowDC, а деструктор - функцию ReleaseDC



Литература


1. А. В. Фролов, Г. В. Фролов. Библиотека системного программиста. Тома 11-13. Операционная система Microsoft Windows 3.1 для программиста. Часть 1-3. Москва, "Диалог-МИФИ", 1994

2.     А. В. Фролов, Г. В. Фролов. Библиотека системного программиста. Том 14. Графический интерфейс GDI в Microsoft Windows. Москва, "Диалог-МИФИ", 1994

3.     А. В. Фролов, Г. В. Фролов. Библиотека системного программиста. Том 17. Операционная система Microsoft Windows 3.1 для программиста. Дополнительные главы, "Диалог-МИФИ", 1995

4.     А. В. Фролов, Г. В. Фролов. Библиотека системного программиста. Том 22. Операционная система Microsoft Windows 95 для программиста. "Диалог-МИФИ", 1996

5.     Эллис М., Строуструп В. Справочное руководство по языку программирования С++ с комментариями: Пер. с англ. М.: Мир, 1992

6.     Пол Ирэ. Объектно-ориентированное программирование с использованием С++: Пер. с англ./Ире Пол. - К.: НИПФ “ДиаСофт Лтд.”, 1995

7.     Том Сван. Программирование для Windows в Borland C++: Пер. с англ. - М.: БИНОМ, 1995



Макрокоманда ON_COMMAND


Макрокоманды ON_COMMAND предназначены для обработки командных сообщений. Командные сообщения поступают от меню, кнопок панели управления и клавиш акселераторов. Характерной особенностью командных сообщений является то, что с ними связан идентификатор сообщения.

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

ON_COMMAND(id, memberFxn)

В общем случае командные сообщения не имеют обработчиков, используемых по умолчанию. Существует только небольшая группа стандартных командных сообщений, имеющих методы обработчики, вызываемые по умолчанию. Эти сообщения соответствуют стандартным строкам меню приложения. Так, например, если вы (или MFC AppWizard) присвоите строке Open меню File идентификатор ID_FILE_OPEN, то для его обработки будет вызван метод OnFileOpen, определенный в классе CWinApp. Список стандартных командных сообщений и их описание представлены в главах “Однооконный интерфейс” и “Многооконный интерфейс”.

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



Макрокоманда ON_COMMAND_RANGE


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

Она назначает один метод memberFxn для обработки ряда командных сообщений, идентификаторы которых лежат в интервале от id1 до id2.

ON_COMMAND_RANGE(id1, id2, memberFxn)



Макрокоманда ON_CONTROL_RANGE


Макрокоманда ON_CONTROL_RANGE обрабатывает сообщения, идентификаторы которых находятся в интервале от id1 до id2. Параметр wNotifyCode содержит код извещения. Метод-обработчик указывается параметром memberFxn.

ON_CONTROL_RANGE(wNotifyCode, id1, id2, memberFxn)



Макрокоманда ON_MESSAGE


Макрокоманда ON_MESSAGE обрабатывает сообщения, определенные пользователем. Идентификатор сообщения (его имя) указывается параметром message. Метод, который вызывается для обработки сообщения, указывается параметром memberFxn.

ON_MESSAGE(message, memberFxn)



Макрокоманда ON_<name>


Макрокоманды ON_<name> предназначены для обработки сообщений от органов управления. Такие сообщения могут передаваться органами управления диалоговой панели. Сообщения от органов управления не имеют обработчиков, используемых по умолчанию. При необходимости вы должны определить их самостоятельно.

Все макрокоманды ON_<name> имеют два параметра. В первом параметре id указывается идентификатор органа управления. Сообщения от этого органа управления будут обрабатываться методом memberFxn.

 ON_BN_CLICKED(id, memberFxn)



Макрокоманда ON_REGISTERED_MESSAGE


Макрокоманда ON_REGISTERED_MESSAGE обслуживает сообщения операционной системы Windows, зарегистрированные с помощью функции RegisterWindowMessage. Параметр nMessageVariable указывает идентификатор сообщения, для которого будет вызываться метод memberFxn.

ON_REGISTERED_MESSAGE(nMessageVariable, memberFxn)



Макрокоманда ON_UPDATE_COMMAND_UI


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

Параметр id указывает идентификатор сообщения, а параметр memberFxn имя метода для его обработки.

ON_UPDATE_COMMAND_UI(id, memberFxn)

Методы, предназначенные для обработки данного класса сообщений, должны быть определены с ключевым словом afx_msg и иметь один параметр - указатель на объект класса CCmdUI. Для удобства, имена методов, предназначенных для обновления пользовательского интерфейса, начинаются с OnUpdate:

afx_msg void OnUpdate<имя_обработчика>(CCmdUI* pCmdUI);

В качестве параметра pCmdUI методу передается указатель на объект класса CCmdUI. В нем содержится информация о объекте пользовательского интерфейса, который надо обновить - строке меню или кнопке панели управления. Класс CCmdUI также включает методы, позволяющие изменить внешний вид представленного им объекта пользовательского интерфейса.

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

При этом посылается несколько сообщений, по одному для каждой строки меню. С помощью макрокоманд ON_UPDATE_COMMAND_UI можно определить методы-обработчики, ответственные за обновление внешнего вида каждой строки меню и соответствующей ей кнопке на панели управления. Эти методы могут изменять состояние строки меню - отображать ее серым цветом, запрещать ее выбор, отображать около нее символ v и т. д.

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

Дополнительную информацию об обновлении пользовательского интерфейса приложения вы можете получить в разделе “Однооконный интерфейс”.



Макрокоманда ON_UPDATE_COMMAND_UI_RANGE


Макрокоманда ON_UPDATE_COMMAND_UI_RANGE обеспечивает обработку сообщений, предназначенных для обновления пользовательского интерфейса, идентификаторы которых лежат в интервале от id1 до id2. Параметр memberFxn указывает метод используемый для обработки.

ON_UPDATE_COMMAND_UI_RANGE(id1, id2, memberFxn)



Макрокоманда ON_WM_<name>


Обрабатывает стандартные сообщения операционной системы Windows. Вместо <name> указывается имя сообщения без префикса WM_. Так, например для обработки сообщения WM_SIZE предназначена макрокоманда ON_WM_SIZE.

Для обработки сообщений, определенных в таблице сообщений макрокомандами ON_WM_<name>, вызываются одноименные методы. Имя метода обработчика соответствует названию сообщения, без учета префикса WM_.

В классе CWnd определены обработчики для стандартных сообщений. Эти обработчики будут использоваться по умолчанию. Вот некоторые из таких методов.

Сообщение

Макрокоманда

Метод обработчик

WM_CHAR

ON_WM_CHAR()

afx_msg void

OnChar(UINT, UINT, UINT);

WM_CREATE

ON_WM_CREATE()

afx_msg int

OnCreate(LPCREATESTRUCT );

WM_HSCROLL

ON_WM_HSCROLL()

afx_msg void

OnHScroll(UINT, UINT, CWnd*);

WM_KEYDOWN

ON_WM_KEYDOWN()

afx_msg void

OnKeyDown(UINT, UINT, UINT);

WM_KEYUP

ON_WM_KEYUP()

afx_msg void

OnKeyUp(UINT, UINT, UINT);

WM_LBUTTONDOWN

ON_WM_LBUTTONDOWN()

afx_msg void

OnLButtonDown(UINT, CPoint);

WM_LBUTTONUP

ON_WM_LBUTTONUP()

afx_msg void

OnLButtonUp(UINT, CPoint);

WM_PAINT

ON_WM_PAINT()

afx_msg void

OnPaint();

WM_SIZE

ON_WM_SIZE()

afx_msg void

OnSize(UINT, int, int);

WM_TIMER

ON_WM_TIMER()

afx_msg void

OnTimer(UINT);

WM_VSCROLL

ON_WM_VSCROLL()

afx_msg void

OnVScroll(UINT, UINT, CWnd*);

Все методы-обработчики определены с ключевым словом afx_msg. Оно позволяет отличить эти методы от остальных методов класса. На этапе препроцессорной обработки ключевое слово afx_msg удаляется. Определение afx_msg вы можете найти в файле afxwin.h:

#define afx_msg

Макрокоманды ON_WM_<name> не имеют параметров. Однако методы, которые вызываются для обработки соответствующих сообщений, имеют параметры, количество и назначение которых зависит от обрабатываемого сообщения.

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



Массивы - шаблон CArray


Библиотека MFC включает классы для организации массивов байт, слов, двойных слов, указателей, объектов класса CString и указателей на объекты класса CObject. В MFC версии 4.0 добавлен шаблон класса массива, позволяющий создавать на его основе собственные массивы переменных любых типов и массивы объектов любых классов. Для доступа к элементам массива можно использовать оператор [].

Ниже представлен прототип шаблона CArray.

template <class TYPE, class ARG_TYPE>

      class CArray : public CObject

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

Мы будем использовать шаблон CArray для организации массива в приложении Single (см. раздел “Простейший графический редактор” главы “Однооконный интерфейс”).



Массивы, списки и словари


В состав MFC включен целый набор классов, предназначенных для хранения информации в массивах, списках и словарях. Все эти классы наследованы от базового класса CObject.

Не смотря на то, что в языке Си определено понятие массива, классы MFC обеспечивают вам более широкие возможности. Вы, например, можете динамически изменять размер массива, определенного с помощью соответствующего класса.

Для представления массивов предназначены следующие классы.

Класс

Массив содержит

CByteArray

Байты

CDWordArray

Двойные слова

CObArray

Указателей на объекты класса CObject

CPtrArray

Указателей типа void

CStringArray

Объекты класса CString

CUIntArray

Элементы класса unsigned integer или UINT

CWordArray

Слова

Фактически все перечисленные в таблице классы различаются только типом элементов массива. Поэтому вместо использования этих классов гораздо проще воспользоваться шаблоном CArray. Используя шаблон CArray, вы можете определять массивы из элементов любых типов и классов. Шаблон CArray наследует свойства класса CObject.

Для построения массивов вы можете также воспользоваться шаблоном CTypedPtrArray. Этот шаблон не наследуется от базового класса CObject, поэтому использовать методы класса CObject для него нельзя.

Для решения многих задач используются такие структуры хранения данных, как списки. MFC включает ряд классов, наследованных от базового класса CObject, которые предоставляют программисту готовое средство для создания собственных списков. В этих классах определены все методы необходимые при работе со списками - добавление нового элемента, вставка нового элемента, определение следующего или предыдущего элемента в списке, удаление элемента и т. д.

Класс

Список содержит элементы

CObList

Указатели на объекты класса CObject

CPtrList

Указатели типа void

CStringList

Объекты класса CString

Перечисленные в таблице классы позволяют построить списки из элементов любых типов и объектов любых классов. Однако удобнее пользоваться шаблоном CList, также наследованным от базового класса CObject. Для построения списков вы можете также использовать шаблон CTypedPtrList. Этот шаблон не наследуется от базового класса CObject.


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

Класс

Ключевое поле

Поле, связанное с ключевым

CMapPtrToPtr

Указатель типа void

Указатель типа void

CMapPtrToWord

Указатель типа void

Слово

CMapStringToOb

Объекты класса CString

Указатели на объекты класса CObject

CMapStringToPtr

Объекты класса CString

Указатель типа void

CMapStringToString

Объекты класса CString

Объекты класса CString

CMapWordToOb

Слово

Указатели на объекты класса CObject

CMapWordToPtr

Слово

Указатель типа void

Вы можете создавать словари, имеющие поля любых типов и классов, если воспользуетесь шаблоном CMap. Шаблон CMap наследуется от базового класса CObject. Для построения словарей можно также использовать шаблон CTypedPtrMap. Шаблон CTypedPtrMap не наследуется от базового класса CObject.


Меню (класс CMenu)


Практически каждое приложение имеет собственное меню. Оно как правило отображается в верхней части главного окна приложения. Для управления меню в состав MFC включен специальный класс CMenu, наследованный непосредственно от базового класса CObject.

Для управления меню и панелями управления используется также класс CCmdUI. Этот класс не наследуется от базового класса CObject.

Объекты класса CCmdUI создаются, когда пользователь выбирает строку меню или нажимает кнопки панели управления. Методы класса CCmdUI позволяют управлять строками меню и кнопками панели управления. Так например, существует метод, который делает строку меню неактивной.



Метод DoDataExchange


Диалоговая панель приложения содержит только две кнопки и не имеет связанных с ними переменных. Однако метод DoDataExchange переопределен. Фактически он не выполняет ни какой работы. Единственное что он делает, это вызывает метод DoDataExchange базового класса CDialog.

Если вы добавите к диалоговой панели новые органы управления и свяжете их средствами ClassWizard с элементами данных класса CDialogDlg, то в блоке AFX_DATA_MAP будут размещены вызовы методов DDE и DDV, необходимые для выполнения обмена данными.

void CDialogDlg::DoDataExchange(CDataExchange* pDX)

{

      CDialog::DoDataExchange(pDX);

      //{{AFX_DATA_MAP(CDialogDlg)

             // Здесь ClassWizard размещает вызовы методов DDX и DDV

      //}}AFX_DATA_MAP

}



Метод Flush


Когда вы используете метод Write или WriteHuge для записи данных на диск, они некоторое время могут находится во временном буфере. Чтобы удостоверится, что необходимые изменения внесены в файл на диске, используйте метод Flush:

virtual void Flush();

     

throw(CFileException);



Метод GetDocument


В секции атрибутов класса CSingleView после комментария Attributes объявлен метод GetDocument. Этот метод возвращает указатель на документ, связанный с данным окном просмотра. Если окно просмотра не связано ни с каким документом, метод возвращает значение NULL.

Интересно, что метод GetDocument имеет две реализации. Одна используется для отладочной версии приложения, а другая для окончательной.

Окончательная версия GetDocument определена непосредственно после самого класса окна просмотра CSingleView как встраиваемый (inline) метод. Когда вы используете страницу ClassView окна Project Workspace, чтобы просмотреть определение метода GetDocument, вы увидите именно этот код.

// Окончательная версия приложения

#ifndef _DEBUG    

inline CSingleDoc* CSingleView::GetDocument()

      { return (CSingleDoc*)m_pDocument; }

#endif

Отладочная версия GetDocument расположена в файле реализации класса окна просмотра SingleView.cpp. Откройте этот файл вручную, выбрав его название из страницы FileView окна Project Workspace.

// Отладочная версия приложения

#ifdef _DEBUG      

CSingleDoc* CSingleView::GetDocument()

{

      ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSingleDoc)));

      return (CSingleDoc*)m_pDocument;

}

#endif //_DEBUG

Макрокоманда RUNTIME_CLASS возвращает указатель на структуру CRuntimeClass, содержащую информацию о классе CSingleDoc. Метод IsKindOf, определенный в классе CObject, проверяет, принадлежит ли объект, на который указывает m_pDocument, к классу CSingleDoc или классу наследованному от CSingleDoc. Если в приложении есть ошибка и m_pDocument не указывает на документ приложения, макрокоманда ASSERT отображает соответствующее сообщение и прерывает работу приложения.



Метод InitInstance


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

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

Самая простая реализация InitInstance у приложений, пользовательский интерфейс которых основан на диалоговой панели.

BOOL CDialogApp::InitInstance()

{

      // ...

}

Если вы указали, что ваше приложение должно иметь трехмерный графический интерфейс, то метод InitInstance вызывает метод Enable3dControls или Enable3dControlsStatic, определенные в классе CWinApp. Эти методы разрешают использование трехмерных органов управления. Какой из этих методов будет использоваться определяется на этапе работы препроцессора в зависимости от того, определен или нет символ _AFXDLL.

// Использовать для приложения трехмерный интерфейс

#ifdef _AFXDLL

      Enable3dControls();

#else

      Enable3dControlsStatic();

#endif

Символ _AFXDLL определяется средой Visual C++, если вы используете библиотеку классов MFC как библиотеку DLL. Если же код MFC подключается к приложению как статическая библиотека, этот символ не определен.

Вы можете удалить вызов методов Enable3dControls, если уже после создания проекта решите, что приложение должно иметь простой “плоский” интерфейс. И наоборот, вы можете добавить вызов этого метода, если на этапе разработки приложения забыли указать на необходимость использования трехмерного интерфейса.

Затем метод InitInstance создает диалоговую панель, которая и будет выполнять роль пользовательского интерфейса приложения. Для этого сначала создается объект dlg класса CDialogDlg, который управляет диалоговой панелью. Затем адрес этого объекта присваивается элементу данных m_pMainWnd главного класса приложения.


CDialogDlg dlg;

m_pMainWnd = &dlg;

Только после этого вызывается метод DoModal для объекта dlg класса CDialogDlg. Он создает модальное диалоговое окно и отображает его на экране. Диалоговая панель, которую создает MFC AppWizard, показана нами на рисунке 2.2. Она имеет всего две кнопки OK и Cancel. Когда пользователь нажимает на одну из этих кнопок, метод DoModal возвращает идентификатор этой кнопки. По умолчанию кнопка OK имеет идентификатор IDOK, а кнопка Cancel - IDCANCEL.

int nResponse = dlg.DoModal();

В исходный текст метода InitInstance включается два оператора if и else if, которые определяют, какая кнопка была нажата. Вы можете поместить после этих операторов ваш собственный код. Он будет вызываться при нажатии на соответствующую кнопку в диалоговой панели.

if (nResponse == IDOK)

{

      // Поместите здесь код, который будет выполняться

      // когда пользователь нажмет кнопку OK

}

else if(nResponse == IDCANCEL)

{

      // Поместите здесь код, который будет выполняться

      // когда пользователь нажмет кнопку Cancel

}

Все! Теперь диалоговое окно закрыто и вам надо завершить приложение. Для этого достаточно, чтобы метод InitInstance вернул значение FALSE.

return FALSE;



Если вы разрабатываете приложение, основанное на однооконном или многооконном интерфейсе, тогда объект главного класса приложения управляет одним или несколькими объектами класса шаблона документа. Они, в свою очередь, управляют созданием документов. Один шаблон используется для всех документов данного типа. Так как однооконные приложения, как правило, работают только с документами одного типа, они используют только один шаблон документов.

После создания шаблона документа, обрабатывается командная строка приложения. Для этого создается объект cmdInfo класса CCommandLineInfo.

Объект cmdInfo передается методу ParseCommandLine, определенному в классе CWinApp. Он заполняет объект cmdInfo, данными, взятыми из командной строки приложения. Подготовленный объект cmdInfo передается методу ProcessShellCommand класса CWinApp для обработки.

Если обработка завершилась успешно, метод ProcessShellCommand возвращает ненулевое значение. Если при обработке командной строки приложения возникли ошибки, метод ProcessShellCommand возвращает нуль.

После успешной инициализации приложения и обработки командной строки метод InitInstance возвращает значение TRUE. Начинается обработка цикла сообщений.

Кроме конструктора и метода InitInstance в главном классе приложения CSingleApp определен метод OnAppAbout. Он расположен в блоке AFX_MSG. Поэтому для работы с этим методом вы можете использовать ClassWizard.


Метод IsKindOf


Метод IsKindOf определяет отношение объекта и класса, представленного указателем pClass на структуру CRuntimeClass. Метод правильно работает только для классов, в объявлении которых указаны макрокоманды DECLARE_DYNAMIC или DECLARE_SERIAL.

BOOL IsKindOf(const CRuntimeClass* pClass) const;

Метод возвращает ненулевое значение, если объект, для которого он вызван, принадлежит классу заданному параметром pClass или классу наследованному от него. В противном случае метод возвращает нулевое значение.



Метод IsSerializable


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

BOOL IsSerializable() const;



Метод OnAppAbout


Метод OnAppAbout вызывается для обработки командного сообщения ID_APP_ABOUT. Это сообщение поступает в очередь приложения, когда пользователь выбирает из меню Help строку About. Он создает объект класса CAboutDlg, представляющий модальную диалоговую панель и отображает ее на экране.

// Метод OnAppAbout

void CSingleApp::OnAppAbout()

{

      CAboutDlg aboutDlg;

      aboutDlg.DoModal();

}

Описание класса CAboutDlg, а также определение его методов содержится в файле Single.cpp. Класс CAboutDlg приложения Single полностью соответствует классу CAboutDlg приложения Dialog, описанного в предыдущей главе. Мы не будем повторять описание класса CAboutDlg, вы можете самостоятельно найти его в листинге 4.4.



Метод OnCreate


Метод OnCreate определен в классе CWnd следующим образом.

afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

Параметр lpCreateStruct содержит указатель на объект CREATESTRUCT, содержащий характеристики создаваемого окна. Эта структура уже была нами описана ранее.

При нормальной работе OnCreate должен вернуть значение 0, чтобы продолжить создание окна. Если OnCreate возвращает –1, окно будет удалено (уничтожено).

MFC AppWizard переопределяет метод OnCreate в классе CMainFrame. Поэтому для обработки сообщения WM_CREATE будет вызван именно переопределенный метод OnCreate класса CMainFrame.

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

      // Вызываем метод OnCreate базового класса

      if (CFrameWnd::OnCreate(lpCreateStruct) == -1)

             return -1;

      // Создаем панель управления toolbar

      if (!m_wndToolBar.Create(this)

             !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))

      {

             // Ошибка при создании панели управления toolbar

             TRACE0("Failed to create toolbar\n");

             return -1;     

      }

      // Создаем панель состояния status bar

      if (!m_wndStatusBar.Create(this)

             !m_wndStatusBar.SetIndicators(indicators,

               sizeof(indicators)/sizeof(UINT)))

      {

             // Ошибка при создании панели состояния status bar

             TRACE0("Failed to create status bar\n");

             return -1;

      }

      // TODO: вы можете изменить характеристики панели

      // управления, убрав некоторые флаги CBRS_

      m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |

             CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);

      // TODO: вы можете запретить перемещение панели управления,

      // если удалите следующие три строки программы

      m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);

      EnableDocking(CBRS_ALIGN_ANY);

      DockControlBar(&m_wndToolBar);

      return 0;

}

Основное назначение метода OnCreate заключается в том, что он сначала вызывает метод OnCreate базового класса CFrameWnd, а затем создает и отображает внутри главного окна панель управления toolbar и панель состояния status bar.

Метод OnCreate базового класса CFrameWnd выполняет обработку сообщения WM_CREATE по умолчанию и возвращает нулевое значение если обработка прошла без ошибок.

В случае возникновения ошибок при обработке сообщения в базовом классе возвращается -1. Метод CMainFrame::OnCreate при этом также прерывает дальнейшую обработку и возвращает -1, вызывая удаление окна.



Метод OnDraw


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

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

В том случае, если внешний вид документа при выводе его на печать должен отличаться от вывода в окно, вы можете вызвать метод IsPrinting контекста устройства, чтобы определить, для чего вызывается OnDraw.

BOOL IsPrinting() const;

Метод IsPrinting возвращает ненулевое значение, если объект контекста устройства, для которого он вызывается, является контекстом принтера. Если контекст представляет другое устройства отображения, например окно, тогда метод IsPrinting возвращает нуль.

MFC AppWizard переопределяет для вас метод OnDraw класса CView следующим образом:

void CSingleView::OnDraw(CDC* pDC)

{

      CSingleDoc* pDoc = GetDocument();

      ASSERT_VALID(pDoc);

      // TODO: Здесь вы можете расположить код, для отображения

      // данных в контексте устройства pDC

}

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



Метод OnInitDialog


Когда вы отображаете диалоговую панель на экране, вызывая методы DoModal, Create или CreateIndirect, функции диалоговой панели передается сообщение WM_INITDIALOG. Вы не имеете доступа непосредственно в функции диалога. Ее реализация содержится в базовом классе CDialog.

В ответ на сообщение WM_INITDIALOG вызывается метод OnInitDialog, объявленный как виртуальный метод класса CDialog. Метод OnInitDialog вызывается непосредственно перед выводом панели на экран.

Таблица сообщений класса CDialogDlg не содержит макрокоманд для обработки сообщения WM_INITDIALOG. Метод OnInitDialog вызывается непосредственно MFC.

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

Для приложения Dialog MFC AppWizard уже переопределил метод OnInitDialog. В реализации метода добавляется новая строка к системному меню диалоговой панели для вызова краткой справки о приложении. Затем вызывая метод SetIcon, определенный в базовом классе CWnd, мы выбираем пиктограммы для приложения.

Метод OnInitDialog возвращает значение TRUE. Это означает, что фокус ввода будет установлен на первый орган управления диалоговой панели. Первый орган диалоговой панели можно выбрать в редакторе диалоговой панели, выбрав из меню Layout строку Tab Order.

Если во время инициализации диалоговой панели метод OnInitDialog устанавливает фокус ввода другому органу управления, метод должен вернуть значение FALSE.

BOOL CDialogDlg::OnInitDialog()

{

      CDialog::OnInitDialog();

      // Добавление строки "About..." к системному меню приложения

      // Проверяем, что идентификатор IDM_ABOUTBOX относится к

      // системным командам

      ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

      ASSERT(IDM_ABOUTBOX < 0xF000);

      CMenu* pSysMenu = GetSystemMenu(FALSE);

      CString strAboutMenu;

      strAboutMenu.LoadString(IDS_ABOUTBOX);

      if (!strAboutMenu.IsEmpty())

      {

             pSysMenu->AppendMenu(MF_SEPARATOR);

             pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX,

                   strAboutMenu);

      }

      // Выбираем пиктограмму для диалоговой панели. Если главное

      // окно приложения не является диалоговой панелью этот код

      // не нужен

      SetIcon(m_hIcon,TRUE);  // Выбираем пиктограмму большого

                                                                      // размера

      SetIcon(m_hIcon,FALSE); // Выбираем пиктограмму маленького

                                                                      // размера

     

      // TODO: Здесь вы можете выполнить дополнительную

      // инициализацию

      return TRUE;

}



Метод OnPaint (отображение пиктограммы приложения)


Диалоговая панель может иметь кнопку минимизации. Нажав эту кнопку или выбрав строку Minimaze из системного меню, пользователь может свернуть диалоговую панель (приложение) до размера пиктограммы.

К сожалению диалоговая панель, в отличие от обычного окна, не содержит встроенных средств отображения пиктограммы приложения. Поэтому приложение должно само заботиться об отображении пиктограммы. MFC AppWizard облегчает эту задачу. Если вы выберите приложение с интерфейсом на основе диалоговой панели, то в исходный текст добавляется определение метода OnPaint. Рассмотрим метод OnPaint подробнее.

void CDialogDlg::OnPaint()

{

      // Определяем размер диалоговой панели

      if (IsIconic())

      {

      // Если диалоговая панель минимизирована, отображаем

      // пиктограмму

             CPaintDC dc(this); // получаем контекст устройства

             SendMessage(WM_ICONERASEBKGND,

                   (WPARAM) dc.GetSafeHdc(), 0);

             int cxIcon = GetSystemMetrics(SM_CXICON);

             int cyIcon = GetSystemMetrics(SM_CYICON);

             // Определяем размеры внутренней области окна

             CRect rect;

            GetClientRect(&rect);

             // Выравниваем пиктограмму по центру

             int x = (rect.Width() - cxIcon + 1) / 2;

             int y = (rect.Height() - cyIcon + 1) / 2;

             // Отображаем пиктограмму

             dc.DrawIcon(x, y, m_hIcon);

      }

      else

      {

             // Выполняем обработку по умолчанию

             CDialog::OnPaint();

      }

}

После вызова метода OnPaint он проверяет состояние диалоговой панели. Для этого вызывается метод IsIconic, определенный в классе CWnd. Если окно, или в нашем случае диалоговая панель, связанная с объектом, для которого вызывается метод IsIconic, минимизировано, возвращается ненулевое значение, в противном случае - нуль.

BOOL IsIconic() const;

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

Если вызов OnPaint произошел в следствии минимизации диалоговой панели, тогда определяются размеры внутренней области минимизированного окна и размеры пиктограммы. Затем пиктограмма отображается в центре минимизированного окна.



Метод OnQueryDragIcon


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

MFC AppWizard определяет метод OnQueryDragIcon для класса CDialogDlg, просто возвращая идентификатор пиктограммы приложения.

HCURSOR CDialogDlg::OnQueryDragIcon()

{

      // Возвращаем идентификатор пиктограммы

      return (HCURSOR) m_hIcon;

}



Метод OnSysCommand (системное меню)


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

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

afx_msg void OnSysCommand(UINT nID, LPARAM lParam);

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

Параметр nID

Описание

SC_CLOSE

Закрывает объект CWnd

SC_HOTKEY

Активизирует объект CWnd, связанный с комбинацией клавиш, определенной приложением. Младшее слово параметра lParam содержит идентификатор активизируемого окна

SC_HSCROLL

Свертка по горизонтали

SC_KEYMENU

Выбор из меню при помощи комбинации клавиш

SC_MAXIMIZE,

SC_ZOOM

Максимизировать объект CWnd

SC_MINIMIZE,

SC_ICON

Минимизировать объект CWnd

SC_MOUSEMENU

Выбор из меню при помощи мыши

SC_MOVE

Перемещение окна CWnd

SC_NEXTWINDOW

Переключение на следующее окно

SC_PREVWINDOW

Переключение на предыдущее окно

SC_RESTORE

Восстановление нормального расположения и размера окна

SC_SCREENSAVE

Запустить приложение, предохраняющее экран монитора, указанное в секции [boot] файла SYSTEM.INI

SC_SIZE

Изменить размер окна CWnd

SC_TASKLIST

Запустить или активизировать приложение Task Manager

SC_VSCROLL

Свертка по вертикали

Если строка системного меню выбрана с использованием мыши, параметр lParam содержит координаты курсора. Младшее слово определяет х-координату, а старшее y-координату.


Виртуальный метод OnSysCommand определен в классе CDialog и выполняет обработку сообщений WM_SYSCOMMAND в соответствии с их идентификаторами. Естественно, он не может правильно обработать сообщения от строк меню добавленных вами.

Чтобы обработать сообщения от новых строк системного меню (для нашего приложения это строка About), необходимо переопределить виртуальный метод OnSysCommand.

Сообщения, имеющие стандартные идентификаторы nID, необходимо передавать для обработки по умолчанию методу OnSysCommand базового класса CDialog.

void CDialogDlg::OnSysCommand(UINT nID, LPARAM lParam)

{

      // Пользователь выбрал строку About системного меню

      if((nID & 0xFFF0) == IDM_ABOUTBOX)

      {

             CAboutDlg dlgAbout;

             dlgAbout.DoModal();

      }

      // Все другие сообщение передаем для обработки методу

      // OnSysCommand базового класса CDialog

      else

      {

             CDialog::OnSysCommand(nID, lParam);

      }

}

Реализация метода OnSysCommand, созданная MFC AppWizard для класса CDialogDlg, определяет причину вызова. Если метод OnSysCommand вызван потому что пользователь выбрал из системного меню строку About, создается объект класса CAboutDlg. Класс CAboutDlg представляет собой класс для управления диалоговой панелью About. Затем вызывается метод DoModal, который и отображает диалоговую панель About на экране.

Если метод OnSysCommand вызван по любой другой причине, тогда вызывается метод OnSysCommand базового класса CDialog, который выполняет обработку этого сообщения по умолчанию.

Описание класса CAboutDlg, а также определение его методов, содержится в файле DialogDlg.cpp (листинг 4.4). Мы не будем подробно описывать класс CAboutDlg, так как он фактически представляет собой упрощенный вариант класса CDialogDlg.