Виртуальный метод PreCreateWindow определен в классе CWnd. Он вызывается непосредственно перед созданием окна, связанного с объектом класса. В качестве параметра cs этому методу передается структура CREATESTRUCT, определяющая характеристики создаваемого окна. Приложение может изменить данные, записанные в этой структуре, чтобы повлиять на внешний вид создаваемого окна.
Классы, наследованные от CWnd, в том числе CView и CFrameWnd, переопределяют этот метод, изменяя структуру cs. В следующей таблице описано назначение полей структуры CREATESTRUCT.
| Поле структуры CREATESTRUCT | Описание | ||
| lpCreateParams | Указатель на данные, используемые при создании окна | ||
| hInstance | Идентификатор приложения | ||
| hMenu | Идентификатор меню | ||
| hwndParent | Идентификатор родительского окна. Содержит NULL, если окно не имеет родительского окна | ||
| cy | Высота окна | ||
| cx | Ширина окна | ||
| y | Определяет y-координату верхнего левого угла окна. Для дочерних окон координаты задаются относительно родительского окна. Для родительского окна координаты указываются в экранной системе координат | ||
| x | Определяет x-координату верхнего левого угла окна. Координаты задаются также как и для поля y | ||
| style | Стиль класса | ||
| lpszName | Указатель на строку, закрытую двоичным нулем, в которой находится имя окна | ||
| lpszClass | Имя класса окна (смотри том 11 из серии “Библиотека системного программиста”) | ||
| dwExStyle | Дополнительные стили окна |
MFC AppWizard переопределяет для вас метод PreCreateWindow, но не вносит в структуру cs никаких изменений и вызывает метод PreCreateWindow базового класса CView.
BOOL CSingleView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Здесь вы можете внести изменения в структуру cs
// Вызов метода PreCreateWindow базового класса CView
return CView::PreCreateWindow(cs);
}
MFC AppWizard переопределяет для класса CMainFrame виртуальный метод PreCreateWindow, но не вносит в структуру cs никаких изменений и вызывает метод PreCreateWindow базового класса CFrameWnd.
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Здесь вы можете внести изменения в структуру cs
return CFrameWnd::PreCreateWindow(cs);
}
Класс CSingleDoc содержит переопределения еще двух виртуальных методов - AssertValid и Dump, входящих в базовый класс CObject. Описание методов AssertValid и Dump вы можете найти в разделе “Класс CObject - основной класс MFC” главы “Некоторые классы MFC”.
Обратите внимание, что описание этих методов и их определение расположено в блоке #ifdef _DEBUG. Поэтому эти методы используются только для отладочной версии приложения. Когда выполняется окончательное построение приложения, эти методы не переопределяются.
//////////////////////////////////////////////////////////////
// Диагностические методы класса CSingleDoc
#ifdef _DEBUG
void CSingleDoc::AssertValid() const
{
CDocument::AssertValid();
}
void CSingleDoc::Dump(CDumpContext& dc) const
{
CDocument::Dump(dc);
}
#endif //_DEBUG
Во время отладки приложения в состав класса CSingleView включаются переопределения виртуальных методов AssertValid и Dump. Эти методы определены в базовом классе CObject и используются при отладке приложения. Когда выполняется окончательное построение приложения, эти методы не переопределяются.
//////////////////////////////////////////////////////////////
// Диагностические методы класса CSingleView
#ifdef _DEBUG
void CSingleView::AssertValid() const
{
CView::AssertValid();
}
void CSingleView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
#endif //_DEBUG
Когда вы выполняете построение отладочной версии приложения, в состав класса CMainFrame включаются переопределения виртуальных методов AssertValid и Dump. Эти методы определены в базовом классе CObject и используются при отладке приложения.
Когда отладочный режим отключен, символ _DEBUG не определен и поэтому методы AssertValid и Dump класса CObject не переопределяются.
//////////////////////////////////////////////////////////////
// Диагностические методы класса CMainFrame
#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
CFrameWnd::AssertValid();
}
void CMainFrame::Dump(CDumpContext& dc) const
{
CFrameWnd::Dump(dc);
}
#endif //_DEBUG
На этом мы заканчиваем рассмотрение исходных текстов приложения с однооконным интерфейсом, созданных системой автоматизированного проектирования MFC AppWizard. В следующей главе мы расскажем как вы можете доработать полученное приложение, чтобы создать простейший графический редактор, с возможностью записи подготовленных в нем документов в файл.
Если метод не изменяет объект, для которого он вызывается, такой метод можно объявить с ключевым словом const. Ключевое слово const указывается после закрывающей скобки списка аргументов метода. Вы должны указать, что метод не изменяет объект и в объявлении и в определении метода.
Методы, объявленные как const, не могут изменять элементы класса или вызывать другие методы, объявленные без ключевого слова const. Нарушение этих правил вызовет ошибку на этапе компиляции приложения.
В библиотеке классов MFC вы встретите много методов, объявленных как const. Их использование повышает надежность приложения, так как компилятор сможет обнаружить ошибки, связанные с непреднамеренным изменением элементов класса.
Ниже мы привели пример класса, для которого метод GetWeight определен как const. Если вы попытаетесь модифицировать элемент данных weight непосредственно из метода GetWeight, компилятор сообщит об ошибке.
#include <iostream.h>
void main(void);
// Класс ClassMen включает элемент данных и два метода для
// обращения к нему
class ClassMen
{
public:
void SetWeight(int newWeight);
int GetWeight() const;
private:
int weight;
};
// Метод GetWeight позволяет определить значение элемента
// weight. Этот метод объявлен как const и не может
// модифицировать объекты класса ClassMen
int ClassMen::GetWeight() const
{
return weight;
}
// Метод SetWeight позволяет изменить значение weight.
// Такой метод нельзя объявлять как const
void ClassMen::SetWeight(int newWeight)
{
weight = newWeight;
}
// Главная функция программы
void main(void)
{
// Создаем объект класса ClassMen
ClassMen alex;
// Устанавливаем значение элемента weight объекта alex
alex.SetWeight(75);
// Отображаем значение элемента weight объекта alex
cout << alex.GetWeight() << "\n";
}
В классе CSingleDoc переопределены два виртуальных метода OnNewDocument и Serialize. Виртуальный метод OnNewDocument определен в классе CDocument, от которого непосредственно наследуется класс CSingleDoc. А вот виртуальный метод Serialize определен в классе CObject. Цепочка наследования классов в этом случае длиннее:
CSingleDoc <- CDocument <- CCmdTarget <- CObject
Метод OnNewDocument вызывается, когда надо создать новый документ для приложения. Если вы переопределяете метод OnNewDocument (в данном случае за вас это делает MFC AppWizard), то сначала необходимо вызвать метод OnNewDocument базового класса, и только затем можно выполнять инициализацию документа. Более подробно об использовании метода OnNewDocument мы расскажем в следующих главах, когда к шаблону прложения, созданному MFC AppWizard, мы будем добавлять собственный код.
BOOL CSingleDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
// TODO: здесь можно выполнить инициализацию
// документа
return TRUE;
}
Если создание нового документа прошло успешно, метод OnNewDocument должен вернуть значение TRUE, а в противном случае FALSE. Когда вызывается метод OnNewDocument базового класса CDocument, следует выполнять проверку возвращаемого им значения. Если CDocument::OnNewDocument вернет FALSE, значит создание документа на уровне класса CDocument не прошло и следует прекратить дальнейшие действия.
Большой интерес представляет метод Serialize. Он вызывается в тех случаях, когда надо загрузить документ из файла на диске или наоборот, записать его в файл. Метод Serialize вызывается, когда пользователь выбирает из меню File строки Open или Save.
//////////////////////////////////////////////////////////////
// Сохранение и восстановление документа
void CSingleDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: здесь выполняется запись документа в файл
}
else
{
// TODO: здесь выполняется чтение документа из файла
}
}
В качестве параметра ar методу Serialize передается объект класса CArchive, связанный с файлом в который надо записать или из которого надо прочитать документ.
Метод Serialize вызывается и для загрузки и для сохранения документа. Чтобы узнать, что надо делать с документом, используется метод IsStoring класса CArchive. Если он возвращает ненулевое значение, значит вы должны сохранить состояние документа в файле. В противном случае вы должны считать документ из файла. Боле подробно использование метода Serialize для сохранения и восстановления документов, описано в разделе “Запись и восстановление объектов” главы “Некоторые классы MFC”.
Виртуальные методы OnPreparePrinting, OnBeginPrinting и OnEndPrinting, определенные в классе CView, вызываются, если пользователь желает распечатать документ, отображенный в данном окне просмотра.
| Метод | Назначение | ||
| OnPreparePrinting | Вызывается перед печатью документа | ||
| OnBeginPrinting | Используется, для получения шрифтов и других ресурсов GDI | ||
| OnEndPrinting | Используется для освобождения ресурсов, полученных методом OnBeginPrinting |
В нашей первой книге, посвященной библиотеке классов MFC, мы не будем останавливаться на проблемах, связанных с печатью документов. Поэтому оставьте шаблоны методов OnPreparePrinting, OnBeginPrinting и OnEndPrinting, подготовленные MFC AppWizard без изменения.
//////////////////////////////////////////////////////////////
// Методы используемые для печати документов
BOOL CSingleView::OnPreparePrinting(CPrintInfo* pInfo)
{
// Выполняется обработка по умолчанию
return DoPreparePrinting(pInfo);
}
void CSingleView::OnBeginPrinting(CDC* /*pDC*/,
CPrintInfo* /*pInfo*/)
{
// TODO: здесь можно выполнить дополнительную инициализацию
// перед печатью документа
}
void CSingleView::OnEndPrinting(CDC* /*pDC*/,
CPrintInfo* /*pInfo*/)
{
// TODO: здесь можно выполнить действия после печати
// документа
}
Если исходный текст метода очень короткий, то такой метод обычно определяется непосредственно внутри класса. Вы можете указать, что вместо вызова необходимо выполнять подстановку его тела. Для этого перед ее объявлением следует указать ключевое слово inline. Вот пример определения методов SetWeight и GetWeight непосредственно внутри класса:
class line
{
public:
void SetLength(int newLength) { length = newLength; }
int GetLength() { return length; }
private:
int length;
};
Если исходный код методов не такой короткий, то при определении класса указывается только объявление метода, а его определение размещается отдельно. Встраиваемые методы также можно определить вне класса. Когда вы определяете метод отдельно от класса, то имени метода должно предшествовать имя класса и оператор разрешения области видимости ::.
class convert
{
public:
void GetString() { scanf(sText,”%s”); }
void ShowString() { puts(sText); }
int ConvertString();
void DummyString();
private:
char sText[80];
};
void convert::ConvertString(void)
{
int i;
for(i = 0; sText[i] != ‘\0’; i++ ) {
sText[i] = tolower(sText[i]);
}
return i;
}
inline void convert::DummyString(void)
{
int i = 0;
while(sText[i++])
sText[i] = 0;
}
Чтобы вызвать метод, надо сначала указать имя объекта класса, для которого будет вызван метод, а затем через точку имя метода. Вместо имени объекта можно использовать указатель на объект. В этом случае вместо символа точки надо использовать оператор ->. Если метод вызывается из другого метода этого же класса, то имя объекта и оператор выбора элемента указывать не надо.
Следующий пример демонстрирует вызов методов класса convert, исходный текст которого приведен выше:
void main()
{
convert ObjectA;
ObjectA.GetString();
ObjectA.ConvertString();
ObjectA.ShowString();
convert *pObjectB = new convert;
pObjectB->GetString();
pObjectB->ConvertString();
pObjectB->ShowString();
}
Методы класса могут быть перегружены. В одном и том же классе можно определить несколько методов с одинаковыми именами, но различным набором параметров.
Существует несколько версий компилятора Microsoft Visual C++. Наибольшее распространение получили версии 1.51, 2.0 и 2.2. В начале 1996 года появилась новая версия Visual C++ 4.0.
Microsoft Visual C++ версии 1.51 содержит только 16-разрядный компилятор. Он позволяет разрабатывать программы для операционной системы MS-DOS и Windows 3.1. Эта версия компилятора не позволяет создавать приложения, предназначенные специально для 32-х разрядных операционных систем Windows NT и Windows 95.
Версия 2.0 содержит 32-разрядный компилятор. С его помощью можно создавать приложения для Windows NT и Windows 95. Сама среда Microsoft Visual C++ версии 2.0 может работать только в 32-разрядной операционной системе Windows NT или Windows 95. Как это ни печально, но Visual C++ версии 2.0 не позволит вам создать приложения для операционных систем MS-DOS и Windows 3.1. Для этого предлагается воспользоваться предыдущей версией компилятора.
На этом различия между Visual C++ версий 1.51 и 2.0 заканчиваются. В Visual C++ версии 1.51 используется 16-и разрядная библиотека классов MFC версии 2.5, а Visual C++ 2.0 поставляется с 32-разрядной библиотекой MFC версии 3.0.
При переходе к 32-разрядным приложениям фирма Microsoft отказалась поддерживать элементы управления VBX. Вместо них MFC 3.0 позволяет работать с элементами управления OLE. Новый стандарт на элементы управления получил название OCX (OLE Custom Controls).
Фирма Microsoft приложила большие усилия, чтобы организовать совместимость своего компилятора с различными архитектурами компьютеров. Специальные версии Visual C++ работают на компьютерах с MIPS архитектурой, на компьютерах с процессорами Alpha, Motorola PowerPC. Это позволяет легко переносить разработанные приложения с одной аппаратной платформы на другую.
Популярность библиотеки классов MFC настолько высока, что такие фирмы, как Watcom и Symantec, известные своими компиляторами Си и Си++, приобрели у Microsoft лицензии на эту библиотеку. Практически единственной широко известной в России фирмой, которая поставляет компиляторы Си++ с собственной библиотекой классов является Borland. Компиляторы Borland C++ включают библиотеку классов OWL (Object Windows Library). Эта библиотека совершенно не совместима с библиотекой MFC, поэтому программы, построенные с ее использованием, нельзя транслировать компилятором Visual C++. Компилятор Borland C++ версии 5.0 позволяет транслировать приложения, созданные с использованием библиотеки классов MFC.
![]() |
Интегрированная среда разработчика объединяет одной оболочкой Microsoft Visual C++ версии 4.0, Microsoft Visual Test, Microsoft Fortran PowerStation, Microsoft Development Library и Microsoft Visual SourceSafe |
Когда вы запускаете приложение в среде операционной системы Windows, начинает работать новый процесс. Если запустить это же приложение еще один раз, будет запущен новый процесс. Обычно такие процессы никак не связаны друг с другом. Когда приложение завершается, оканчивается соответствующий процесс.
Каждый процесс может состоять из нескольких нитей (thread) или задач. В рамках одного процесса его задачи выполняются параллельно. До сих пор мы рассматривали только приложения, имеющие единственную задачу. Однако, часто бывает полезно, чтобы внутри одного приложения одновременно выполнялись несколько задач. Например, одна задача может отвечать за взаимодействие с пользователем - принимать от него команды, данные, отображать информацию на экране. Вторая задача может выполнять дополнительную фоновую работу, например печать документа, обмен данными через модем, проводить вычисления.
Библиотека классов MFC позволяет создавать многозадачные приложения.
Приложения Windows 95 и Windows NT могут быть многозадачными. Такие приложения состоят из нескольких независимых задач. Дополнительные задачи могут использоваться, чтобы выполнять фоновую работу. Класс CWinApp представляет основную задачу приложения. Если вам надо создать дополнительные задачи, вы должны воспользоваться классом CWinThread.
![]() | Приложение Pview 95 позволяет просмотреть список процессов, загруженных в памяти и остановить любой процесс |
Множественное наследование выполняется подобно единичному наследованию. В отличие от единичного наследования у порожденного класса может быть несколько базовых классов. На рисунке 1.2 представлен пример множественного наследования классов. Класс DerivedClaass имеет два базовых класса BaseClassOne и BaseClassSecond. Класс DerivedClaass и еще один класс BaseClass используются при множественном наследовании класса DerivedClaassSecond.

Рис. 1.2. Множественное наследование
Вместо имени единственного базового класса указывается список <base-list> имен базовых классов, разделенный запятыми. Непосредственно перед названиями базовых классов могут быть указаны спецификаторы доступа public, private и protect. Их назначение мы рассмотрим в разделе “Разграничение доступа к элементам базового класса”.
class [<tag>[:[<base-list>]]
{
<member-list>
} [<declarators>];
Порядок, в котором вы перечислите базовые классы влияет только на последовательность в которой вызываются конструкторы и деструкторы базовых классов. Конструкторы базовых классов вызываются в том порядке, в котором они перечислены (слева на право). Деструкторы базовых классов вызываются в обратном порядке.
Один и тот же класс нельзя указывать два или более раза в качестве базового класса (за исключением тех случаев, когда он является непрямым базовым классом).
В следующем примере определены два базовых класса BaseFirst и BaseSecond. От них наследован один новый класс Derived. Результирующий класс Derived объединяет элементы обоих базовых классов и добавляет к ним собственные элементы.
// Класс BaseFirst
class BaseFirst
{
// Элементы класса BaseFirst
};
// Класс BaseSecond
class BaseSecond
{
// Элементы класса BaseSecond
};
// Класс Derived, наследованный от базового класса Base
class Derived : BaseFirst, BaseSecond
{
// Элементы класса Derived
};
Так как библиотека классов MFC не использует множественное наследование, мы не станем останавливаться на нем более подробно. При необходимости вы можете получить дополнительную информацию из справочников или учебников по языку Си++ (см. список литературы).
Библиотека классов MFC предлагает вам модель приложения, основанную на том, что приложение предоставляет пользователю средства для просмотра и изменения документов. С помощью меню или кнопок панели управления toolbar пользователь может создать новый документ или открыть документ, записанный в файле.
Пользователь может работать с документом, просматривая или редактируя его. Пользователь взаимодействует с документом через окно просмотра - View. Один документ одновременно может иметь несколько окон для его просмотра.
Измененный документ можно сохранить в файле на диске и продолжить с ним работу в следующий раз. Процесс сохранения документа и его загрузки в приложение называется записью и восстановлением объектов - serialize.
Процедура создания приложения, имеющего однооконный или многооконный интерфейс, несколько отличается от рассмотренной нами в разделе “Создание приложения с диалоговой панелью”. Однако первые этапы создания приложений, имеющих различный пользовательский интерфейс, полностью совпадают.
Вы можете наследовать от класса CMemFile собственные классы. При этом вы можете реализовать свой механизм выделения памяти для файла, чтения и записи данных. Для этого в состав CMemFile входят виртуальные методы Alloc, Free, Realloc, Memcpy и GrowFile.
Методы Alloc, Realloc и Free вызываются другими методами класса CMemFile чтобы выделить блок оперативной памяти для файла, изменить его размер и вернуть после использования операционной системе. Если вы решили сами управлять распределением памяти для файла, вы должны переназначить все эти методы.
Метод Alloc вызывается другими методами класса, когда необходимо получить блок оперативной памяти размера nBytes. Метод возвращает указатель на этот блок:
BYTE * Alloc(DWORD nBytes);
Когда размер файла изменяется, может возникнуть необходимость изменения размера блока памяти, используемого файлом. Для этого методы класса CMemFile могут вызывать метод Realloc:
BYTE * Realloc(BYTE* lpMem, DWORD nBytes);
В качестве параметра методу Realloc передается указатель lpMem на блок памяти и число nBytes, определяющее новый размер блока памяти файла. Метод Realloc возвращает указатель на новый блок памяти. Его адрес может измениться. Если операционная система не может изменить размер блока памяти, метод Realloc возвращает значение NULL.
После использования блока памяти, его необходимо освободить и вернуть операционной системе. Для этого предназначен метод Free:
void Free(BYTE * lpMem);
В качестве параметра lpMem задается адрес блока памяти файла, который надо освободить.
Виртуальные методы класса CFile Read и Write, переназначенные в классе CMemFile, вызывают метод Memcpy. Метод Memcpy предназначен для обмена данными. Вы можете переопределить этот метод в своем классе:
BYTE *
Memcpy(BYTE* lpMemTarget, BYTE* lpMemSource, UINT nBytes);
Переменная lpMemSource указывает на область памяти размера nBytes байт, которая должна быть записанная по адресу lpMemTarget. Метод Memcpy возвращает значение соответствующее параметру lpMemTarget.
Если происходит изменение длины файла, вызывается метод GrowFile. В качестве параметра dwNewLen указывается новый размер файла. Вы можете переназначить этот метод в своем классе:
void GrowFile(DWORD dwNewLen);
Пожалуй, самая важная возможность, предоставляемая программисту средствами языка Си++, заключается в механизме наследования. Вы можете наследовать от определенных ранее классов новые производные классы. Класс, от которого происходит наследование, называется базовым. Новый класс называется производным.
Производный класс включает в себя элементы базового класса и может дополнять их собственными элементами данных и методами. За счет наследования появляется возможность повторного использования кода программы.
Производный класс сам может служить базовым классом. Вы можете наследовать от него другие классы. Полученный в результате такого наследования класс будет включать в себя элементы всех его базовых классов.
От одного общего базового класса можно наследовать несколько новых производных классов. Производный класс сам может служить базовым классом для новых классов. Таким образом возможна древовидная структура наследования классов.
На рисунке 1.1 мы привели пример структуры наследования классов. От единственного базового класса BaseClass наследуются три класса DerivedClassOne, DerivedClassSecond и DerivedClassThird. Первые два из них сами выступают в качестве базовых классов.

Рис. 1.1. Наследование
Практически вся структура библиотеки классов MFC основывается на механизме наследования. Так, например, большинство классов MFC имеет базовый класс CObject, который обеспечивает наиболее общие свойства унаследованных от него классов.
Можно выделить две основных формы наследования. Когда производный класс наследуется от единственного базового класса - единичное наследование. Наследование, при котором производный класс наследуется сразу от нескольких базовых классов - это множественное наследование.
В этом разделе мы опишем несколько классов, которые вы будете использовать при создании собственных приложений.
В первую очередь, мы рассмотрим класс CObject, который является базовым для большей части классов библиотеки MFC. Затем мы изучим классы, предназначенные для хранения информации, классы, связанные с файловой системой компьютера, процедурой сохранения и восстановления объектов в файле на диске, классы реализующие механизм исключений.
Для отображения информации используются различные объекты графического интерфейса - GDI объекты. Для каждого из этих объектов библиотека MFC содержит описывающий его класс, наследованный от базового класса CGdiObject (рис. 2.7).

Рис. 2.7. Класс CGdiObject
| Класс | Описание | ||
| CBitmap | Растровое изображение bitmap | ||
| CBrush | Кисть | ||
| CFont | Шрифт | ||
| CPalette | Палитра цветов | ||
| CPen | Перо | ||
| CRgn | Область внутри окна |
Виртуальный метод DoDataExchange, который мы переопределяем в классе диалоговой панели, первоначально определен в классе CWnd. Он служит для реализации механизмов автоматического обмена данными - Dialog Data Exchange (DDX) и автоматической проверки данных - Dialog Data Validation (DDV).
Механизм автоматического обмена данными позволяет привязать к органам управления диалоговой панели переменные или элементы данных класса диалоговой панели. Ряд специальных функций, определенных в библиотеке MFC, вызываются методом DoDataExchange и выполняют обмен данными между органами управления диалоговой панели и соответствующими элементами данных класса диалоговой панели. Такой обмен работает в обоих направлениях. Информация из органов управления диалоговой панели может записываться в элементы данных класса, или в обратном направлении - информация из элементов данных класса может отображаться в диалоговой панели.
Названия всех функций, обеспечивающих обмен данными, начинаются символами DDX_. Например определены функции DDX_Text, DDX_Radio, DDX_Check, DDX_Scroll и т. д. Практически каждый тип органов управления диалоговой панели имеет собственную функцию для выполнения процедуры обмена данными.
Все функции DDX_ имеют три параметра. Первый параметр содержит указатель на объект класса CDataExchange. Этот объект определяет параметры обмена, в том числе направление, в котором надо выполнить обмен данными. Второй параметр определяет идентификатор органа управления, с которым выполняется обмен данными. Третий параметр содержит ссылку на элемент данные класса диалоговой панели, связанный с данным органом управления.
Ниже представлен прототип функции DDX_, предназначенной для обмена информацией между полем редактирования диалоговой панели и элементом данных, входящим в класс диалоговой панели. Этот элемент имеет класс CString:
void AFXAPI
DDX_Text(CDataExchange* pDX, int nIDC, CString& value);
Метод DoDataExchange позволяет выполнять проверку данных, которые пользователь вводит в диалоговой панели. Для этого предназначен ряд функций DDV_. Эти функции позволяют гарантировать, что данные, введенные пользователем в диалоговой панели, соответствуют определенным условиям.

При написании настоящих приложений большую роль играет обработка всевозможных ошибочных ситуаций, возникающих во время работы приложения. В качестве примера таких ошибочных ситуаций может служить неудачная попытка получить блок оперативной памяти, попытка открыть несуществующий файл и т. д.
От того насколько внимательно ваше приложение обрабатывает все ошибочные ситуации, зависит насколько надежно и устойчиво оно будет работать. Например, если приложение по мере своей работы должно получать у операционной системы большие блоки памяти и вы не учтете возможности, что оперативной памяти окажется недостаточно, приложение может вызвать срабатывание защиты памяти.
В языке Си++ реализованы специальные операторы try, throw и catch, предназначенные для обработки ошибочных ситуаций, которые называются исключениями.
В разделе “Долгий путь сообщения” главы “Введение в MFC” мы начали рассказ о том, как обрабатываются командные сообщения. Теперь изучим обработку командных сообщений более подробно.
Процесс обработки командных сообщений значительно отличается от обработки других сообщений. Обычные сообщения обрабатываются только тем объектом, которому они поступили. Если таблица сообщений класса объекта не содержит обработчика сообщения, будут просмотрены таблицы сообщений его базовых классов. В том случае, если ни один из базовых классов также не содержит обработчика сообщения, выполняется обработка сообщения по умолчанию.
Судьба командных сообщений гораздо сложнее. Командное сообщение, переданное для обработки объекту приложения, может последовательно передаваться другим объектам приложения. Один из объектов, класс (или базовый класс) которого содержит обработчик этого сообщения, выполняет его обработку. Так, например, командное сообщение, переданное главному окну приложения, в конечном счете может быть обработано активным окном просмотра.
Существует стандартная последовательность объектов приложения, которым передаются командные сообщения. Каждый объект в этой последовательности может обработать командное сообщение, если в его таблице сообщений или таблице сообщений базовых классов есть соответствующая макрокоманда. Необработанные сообщения передаются дальше, другим объектам приложения.
Объекты различных классов обрабатывают командные сообщения по-разному. Например, объекты, представляющие главное окно приложения, сначала предоставляют возможность обработать полученное сообщение другим объектам, в том числе активному окну просмотра и соответствующему ему документу. Только если сообщение остается необработанным, просматривается таблица сообщений класса главного окна приложения. Если и здесь командное сообщение не обрабатывается, оно направляется другим объектам приложения.
Как вы знаете из предыдущих томов серии “Библиотека системного программиста”, работа приложений операционной системы Windows основана на обработке сообщений. Когда пользователь работает с устройствами ввода/вывода компьютера, например клавиатурой или мышью, драйверы этих устройств создают сообщения, описывающие его действия. Каждое нажатие на клавиши клавиатуры вызывает генерацию ряда сообщений, определяющих, какая клавиша нажата. Перемещение мыши вызывает сообщения, описывающие траекторию перемещения указателя мыши и т. д. Другие сообщения могут вырабатываться операционной системой или самими приложениями.
Сообщения сначала попадают в системную очереди сообщений операционной системы. Из нее сообщения передаются приложениям, которым они предназначены, и записываются в очередь приложений. Каждое приложение имеет собственную очередь сообщений.
Приложение в цикле, который называется циклом обработки сообщений, получает сообщения из очереди приложения и направляет их соответствующей функции окна, которая и выполняет обработку сообщения. Цикл обработки сообщений обычно состоял из оператора while в котором циклически вызывались функции GetMessage и DispatchMessage:
MSG message;
while(GetMessage(&message, 0, 0, 0))
{
DispatchMessage(&message);
}
Для более сложных приложений цикл обработки сообщений содержал вызовы других функций, например TranslateMessage, TranslateAccelerator. Они обеспечивали предварительную обработку сообщений.
Каждое окно приложения имеет собственную функцию окна. В процессе обработки сообщения операционная система вызывает функцию окна и передает ей структуру, описывающую очередное сообщение.
Функция обработки, сообщения опознает, какое именно сообщение поступило для обработки и выполняет соответствующие действия. Сообщения распознаются по его коду. Обычно функция окна содержит оператор switch, который служит для определения кода сообщений. Вот пример типичной функции окна:
long FAR PASCAL _export
WndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam)
Иногда удобно, чтобы все объекты данного класса имели общие элементы данных, которые используются совместно. За счет этого можно существенно сократить количество глобальных переменных, улучшая структуру программы.
Общие элементы данных класса следует объявить с ключевым словом static. Все общие элементы класса надо определить в тексте программы, зарезервировав за ними место в оперативной памяти:
class CWindow
{
public:
int xLeftTop, xRightBottom;
int yLeftTop, yRightBottom;
static char title[80];
void SetTitle(char*);
};
char Cwindow::title[80] = “заголовок окна”;
Каждый объект класса Cwindow будет иметь уникальные координаты, определяемые элементами данных xLeftTop, xRightBottom, yLeftTop, yRightBottom и одинаковый заголовок, хранимый элементом данных title.
Общие элементы данных находятся в области действия своего класса. Методы класса могут обращаться к общим элементам точно так же, как к остальным данным из класса:
void SetTitle(char* sSource)
{
strcpy(title, sSource);
}
Чтобы получить доступ к общим элементам из программы, надо объявить их как public. Для обращения к такой переменной перед ее именем надо указать имя класса и оператор ::.
printf(Cwindow::title);
Внимательно изучив различные ресурсы приложения Single вы должны заметить, что четыре типа ресурсов имеют элементы с одинаковым идентификатором. Существует меню, строковый ресурс, таблица акселераторов и пиктограмма, которые имеют один и тот же идентификатор IDR_MAINFRAME.
MFC AppWizard способен создавать шаблоны приложений, использующих однооконный или многооконный интерфейс. Приложения с однооконным интерфейсом имеют одно окно для отображения документа. Многооконные приложения способны одновременно открыть несколько окон просмотра документов.
В этой главе мы остановимся на приложениях с однооконным интерфейсом. Приложения с многооконным интерфейсом будут рассмотрены в следующей главе, которая называется “Многооконный интерфейс”.
Сейчас мы рассмотрим модель “документ - окно просмотра”, лежащую в основе как однооконных, так и многооконных приложений, созданных с использованием библиотеки классов MFC.
Практически все приложения имеют пользовательский интерфейс, построенный на основе окон. Это может быть диалоговая панель, одно окно или несколько окон, связанных вместе. Основные свойства окон представлены классом CWnd, наследованным от класса CCmdTarget.
Вы очень редко будете создавать объекты класса CWnd. Класс CWnd сам является базовым классом для большого количества классов, представляющих разнообразные окна. На рисунке 2.3 представлена только небольшая часть дерева наследования класса CWnd.

Рис. 2.3. Класс CWnd
Перечислим классы, наследованные от базового класса CWnd.
Обрамляющие окна (класс CFrameWnd)
Класс CFrameWnd представляет окна, выступающие в роли обрамляющих окон (frame window), в том числе главные окна приложения. От этого класса также наследуются классы CMDIChildWnd и CMDIFrameWnd, используемые для отображения окон многооконного интерфейса MDI. Класс CMDIFrameWnd представляет главное окно приложения MDI, а класс CMDIChildWnd - его дочерние окна MDI. Класс CMiniFrameWnd применяется для отображения окон уменьшенного размера. Такие окна обычно используются для отображения в них панели управления.
Окна органов управления
В предыдущих томах серии “Библиотека системного программиста” мы рассказывали о том, что существует ряд органов управления, встроенных в операционную систему. К ним относятся кнопки, полосы прокрутки, редакторы текста, переключатели и т. д.
Для работы с этими органами управления в библиотеке MFC предусмотрены специальные классы, наследованные непосредственно от класса CWnd.
| Класс | Орган управления | ||
| CAnimateCtrl | Используется для отображения видеоинформации | ||
| CBitmapButton | Кнопка с рисунком | ||
| CButton | Кнопка | ||
| CComboBox | Список с окном редактирования | ||
| CEdit | Поле редактирования | ||
| CHeaderCtrl | Заголовок для таблицы | ||
| CHotKeyCtrl | Предназначен для ввода комбинации клавиш акселераторов | ||
| CListBox | Список | ||
| CListCrtl | Может использоваться для отображения списка пиктограмм | ||
| CProgressCtrl | Линейный индикатор | ||
| CPropertySheet | Блокнот. Может состоять из нескольких страниц | ||
| CRichEditCtrl | Окно редактирования, в котором можно редактировать форматированный текст | ||
| CScrollBar | Полоса просмотра | ||
| CSliderCtrl | Движок | ||
| CSpinButtonCtrl | Обычно используется для увеличения или уменьшения значения какого-нибудь параметра | ||
| CStatic | Статический орган управления | ||
| CTabCtrl | Набор “закладок” | ||
| CToolBarCtrl | Панель управления | ||
| CToolTipCtrl | Маленькое окно содержащее строку текста | ||
| CTreeCtrl | Орган управления, который позволяет просматривать иерархические структуры данных |


Microsoft Visual C++ версии 4.0 имеет удобные средства для просмотра исходных текстов файлов проекта, кодов классов приложения, ресурсов, а также для получения справочной информации.
Откройте окно Project Workspace. Обычно оно расположено в левой части экрана, но вы можете переместить его в другое место и даже закрыть. Если окно закрыто, откройте его. Для этого выберите из меню View строку Project Workspace. Окно Project Workspace состоит из нескольких страниц. Вы можете открыть их, нажимая на соответствующие закладки. Количество страниц зависит от того, установлена ли справочная система и открыт ли проект.
| Закладка | Описание | ||
![]() | ClassView. Средство для просмотра и редактирования классов приложения | ||
![]() | ResourceView. Позволяет просматривать и редактировать ресурсы приложения | ||
![]() | FileView. Выполняет просмотр файлов приложения | ||
![]() | InfoView. Справочная система Microsoft Visual C++. В нее включена информация о языке Си, Си++, библиотеки классов MFC, функций програмного интерфейса Windows |
Откройте окно Project Workspace и выберите страницу FileView. Так как сначала в проекте нет ни одного файла, вы увидите пустую папку проекта. Теперь надо создать новый текстовый файл и набрать в нем исходный текст нашего первого приложения.
Чтобы создать новый файл, вы можете нажать кнопку New Source File (

Листинг 2.1. Файл MFHello.cpp
// Включаемый файл для MFC
#include <afxwin.h>
//=====================================================
// Класс CMFHelloApp
// Наследуем от базового класса CWinApp главный
// класс приложения CMFHelloApp
//=====================================================











В отличие от объектов, представляющих окна типа frame (объекты классов CFrameWnd, CMDIFrameWnd и CMDIChildWnd) окно просмотра в первую очередь проверяет собственную таблицу сообщений. И только в том случае, если командное сообщение не может быть обработано, оно передается документу, связанному с данным окном просмотра. Последовательность обработки командных сообщений документом представлена ниже.
Эта группа включает сообщения, предназначенные для обработкой функцией окна. Практически все сообщения, идентификаторы которых начинаются префиксом WM_, за исключением сообщения WM_COMMAND, относятся к этой группе.
Оконные сообщения предназначаются для обработки объектами, представляющими окна. Это могут быть практически любые объекты класса CWnd или классов, наследованных от него, например CFrameWnd, CMDIFrameWnd, CMDIChildWnd, CView, CDialog. Характерной чертой этих классов является то, что они включают идентификатор окна.
Большинство этих сообщений имеют параметры, детально характеризующие сообщение. Например сообщение WM_SIZE передается окну, когда пользователь меняет его размер и имеет параметры, определяющие новый размер окна.
В состав класса CFile входят методы, позволяющие выполнять над файлами различные операции, например копирование, переименование, удаление, изменение атрибутов.
Операционная система MS-DOS содержит команду REN, позволяющую переименовывать файлы. Класс CFile включает статический метод Rename, выполняющий функции этой команды:
static void PASCAL
Rename(LPCTSTR lpszOldName, LPCTSTR lpszNewName);
throw(CFileException);
Метод Rename изменяет имя файла, определенного параметром lpszOldName на lpszNewName. Метод нельзя использовать для переименования каталогов. В случае возникновения ошибки метод вызывает исключение.
Для удаления файлов предназначена команда DEL операционной системы MS-DOS. Класс CFile включает статический метод Remove, позволяющий удалить указанный файл:
static void PASCAL Remove(LPCTSTR lpszFileName);
throw(CFileException);
Параметр lpszFileName должен содержать путь удаляемого файла. Метод Remove не позволяет удалять каталоги. Если удалить файл невозможно, например, из-за неправильно указанного имени файла, то метод вызывает исключение.
Чтобы определить дату и время создания файла, его длину и атрибуты, предназначен статический метод GetStatus. Существует две разновидности метода - первый определен как виртуальный, а второй - как статический методы.
Виртуальная версия метода GetStatus определяет состояние открытого файла, связанного с данным объектом класса CFile. Вызывайте этот метод только тогда, когда объект класса CFile создан и файл открыт:
BOOL GetStatus(CFileStatus& rStatus) const;
Статическая версия метода GetStatus позволяет определить характеристики файла, не связанного с объектом класса CFile. Чтобы воспользоваться этим методом, не обязательно предварительно открывать файл.
static BOOL PASCAL
GetStatus(LPCTSTR lpszFileName, CFileStatus& rStatus);
Параметр lpszFileName должен содержать путь к файлу. Путь может быть полным или не полным - относительно текущего каталога.
Параметр rStatus должен содержать указатель на структуру типа CFileStatus, в которую заносится информация о файле.
|
Поле структуры CFileStatus |
Описание |
|
CTime m_ctime |
Дата и время создания файла. Описание класса CTime представлено нами в главе “Дата и время” |
|
CTime m_mtime |
Дата и время последней модификации файла |
|
CTime m_atime |
Дата и время, когда последний раз выполнялось чтение из файла |
|
LONG m_size |
Размер файла в байтах |
|
BYTE m_attribute |
Атрибуты файла |
|
char m_szFullName[_MAX_PATH] |
Полное имя файла в стандарте операционной системы Windows. Виртуальная версия метода не заполняет это поле |
|
Атрибут |
Описание |
|
normal |
Нормальный файл |
|
readOnly |
Файл, который можно открыть только для чтения |
|
hidden |
Скрытый файл |
|
system |
Системный файл |
|
volume |
Метка тома |
|
directory |
Каталог |
|
archive |
Архивный |
Для класса CObject описан оператор присваивания. Он описан с ключевым словом private и не имеет реализации:
private:
void operator =( const CObject& src );
Таким образом для классов, наследованных от CObject запрещается выполнение операции копирования по умолчанию. Если такая операция необходима, вы должны явно определить ее в своем классе. Если вы не определите в своем классе оператор присваивания, но попытаетесь им воспользоваться, компилятор выдаст сообщение об ошибке.
Оператор new создает объект заданного типа. При этом он выделяет память, необходимую для хранения объекта и возвращает указатель, указывающий на него. Если по каким-либо причинам получить память не удается, оператор возвращает нулевое значение. Оператор new позволяет сразу инициализировать созданную переменную. Приведем формат оператора new:
new type-name [initializer];
new (type-name) [initializer];
В качестве аргумента type-name надо указать имя типа создаваемого объекта. Дополнительный аргумент initializer позволяет присвоить созданному объекту начальное значение. Вот простой пример вызова оператора new:
char *litera;
int *pi;
litera = new char;
pi = new int(3,1415);
В этом примере оператор new используется для создания двух объектов - одного типа char, а другого типа int. Указатели на эти объекты записываются в переменные litera и pi. Заметим, что объект типа int сразу инициализируется значением 3,1415.
Чтобы освободить память, полученную оператором new, надо вызвать оператор delete. Вы должны передать оператору delete указатель pointer, ранее полученный оператором new:
delete pointer;
Оператор new позволяет создавать объекты не только простых типов, он может использоваться для динамического создания массивов. Следующий фрагмент кода создает массив из ста элементов типа long. Указатель на первый элемент массива записывается в переменную pData:
long *pData = new long[100];
Чтобы удалить массив, созданный оператором new, надо воспользоваться другой формой вызова оператора delete:
delete [] pointer;
Прямоугольные скобки указывают на то, что надо удалить массив элементов.
Оператор try открывает блок кода, в котором может произойти ошибка. Если ошибка произошла, то оператор throw вызывает исключение. Исключение обрабатывается специальным обработчиком исключений. Обработчик исключения представляет собой блок кода, который начинается оператором catch.
Допустим ваше приложение должно вычислять значение выражения res = 100 / (num * (num - 7)). Если вы зададите значение переменной num, равное 0 или 7, то произойдет ошибка деления на нуль. Участок программы, в котором может случиться ошибка, объединим в блок оператора try. Вставим перед вычислением выражения проверку переменной nem на равенство нулю и семи. Если переменная num примет запрещенные значения, вызовем исключение, воспользовавшись оператором throw.
Сразу после блока try поместите обработчик исключения catch. Он будет вызываться в случае ошибки.
Пример такой программы, получившей название Exception, мы привели в листинге 1.1. Программа Exception принимает от пользователя значение переменной num, а затем вычисляет выражение res = 100 / (num * (num - 7)) и отображает полученный результат на экране.
В случае, если пользователь введет число 0 или 7, тогда вызывается исключение throw. В качестве параметра оператору throw указывается переменная num. Заметим, что так как переменная num имеет тип long, считается что данное исключение также будет иметь тип long.
После вызова оператора throw управление сразу передается обработчику исключения соответствующего типа. Определенный нами обработчик отображает на экране строку "Exception, num = ", а затем выводит значение переменной num.
После обработки исключения, управление не возвращается в блок try, а передается оператору, следующему после блока catch данного обработчика исключения. Программа выведет на экран строку “Stop program” и завершит свою работу.
Если пользователь введет разрешенные значения для переменной num, тогда исключение не вызывается. Программа вычислит значение res и отобразит его на экране. В этом случае обработчик исключения не выполнится и управление перейдет на оператор, следующий за блоком обработки исключения. Программа выведет на экран строку “Stop program” и завершит работу.
Непосредственно от класса CObject наследуются ряд классов, которые сами являются базовыми для остальных классов MFC. В первую очередь это класс CCmdTarget, представляющий основу структуры любого приложения. Основной особенностью класса CCmdTarget и классов, наследованных от него является то, что объекты этих классов могут получать от операционной системы сообщения и обрабатывать их.
Структура классов, связанных с классом CCmdTarget представлена на рисунке 2.2.

Рис. 2.2. Класс CCmdTarget
После создания объекта класса CFile можно открыть файл, вызвав метод Open. Методу Open надо указать путь к открываемому файлу и режим его использования. Прототип метода Open имеет следующий вид:
virtual BOOL
Open(LPCTSTR lpszFileName, UINT nOpenFlags,
CFileException* pError = NULL);
В качестве параметра lpszFileName надо указать имя открываемого файла. Можно указывать только имя файла или полное имя файла, включающее полный путь к нему.
Второй параметр nOpenFlags определяет действие, выполняемое методом Open с файлом, а также атрибуты файла. Ниже представлен список возможных значений параметра nOpenFlags:
| Возможные значения nOpenFlags | Описание | ||
| CFile::modeCreate | Создается новый файл. Если указанный файл существует, то его содержимое стирается и длина устанавливается равной нулю | ||
| CFile::modeNoTruncate | Этот флаг предназначен для использования совместно с флагом CFile::modeCreate. Если создается уже существующий файл, то его содержимое не будет удалено | ||
| CFile::modeRead | Файл открывается только для чтения | ||
| CFile::modeReadWrite | Файл открывается для чтения и записи | ||
| CFile::modeWrite | Файл открывается только для записи | ||
| CFile::modeNoInherit | Указывает, что файл не должен наследоваться порожденным процессом | ||
| CFile::shareCompat | Открывает файл в режиме совместимости. Любой другой процесс может открыть этот файл несколько раз. Операция вызывает ошибку, если файл уже открыт другим процессом в любом другом режиме кроме режима совместимости | ||
| CFile::shareDenyNone | Не запрещается доступ к открываемому файлу ни на чтение, ни на запись. Вызывает ошибку, если файл уже открыт в режиме совместимости любым другим процессом | ||
| CFile::shareDenyRead | После того как файл открыт, другим процессам запрещается его чтение. Вызывает ошибку, если уже открыт в режиме совместимости или для чтения другим процессом | ||
| CFile::shareDenyWrite | После того как файл открыт, другим процессам запрещается запись в него. Вызывает ошибку, если уже открыт в режиме совместимости или для записи другим процессом | ||
| CFile::shareExclusive | После того как файл открыт, другим процессам запрещается запись и чтение из этого файла. Вызывает ошибку, если файл уже открыт для чтения или для записи любым процессом | ||
| CFile::typeText | Используется классами, порожденными от класса CFile, например CStdioFile, для работы с файлами в текстовом режиме. Текстовый режим обеспечивает преобразование комбинации символа возврата каретки и символа перевода строки. | ||
| CFile::typeBinary | Используется классами, порожденными от класса CFile, например CStdioFile, для работы с файлами в двоичном режиме |
Необязательный параметр pError, который является указателем на объект класса CFileException, используется только в том случае, если выполнение операции с файлом вызывает ошибку. Если вы указали параметр pError и случилась ошибка, то в объект будет записана дополнительная информация.
Метод Open возвращает ненулевое значение, если файл открыт и нуль в случае ошибки. Ошибка при открытии файла может случится, например, если методу Open указан несуществующий файл.
В отладочной версии приложения вы можете использовать класс CDumpContext. Он позволяет выдавать состояние различных объектов в текстовом виде.
Класс CMemoryState позволяет локализовать проблемы, связанные с динамическим выделением оперативной памяти. Такие проблемы обычно возникают, когда пользователь выделяет память, используя оператор new, а затем забывает ввернуть эту память операционной системе.
Для управления панелью состояния используется класс CStatusBar. Чтобы создать для окна панель состояния следует выполнить следующие действия.
Создать объект класса CStatusBar. Он будет представлять панель состояния и управлять ей
Вызвать метод Create класса CStatusBar, чтобы создать панель и связать ее с объектом представляющим панель состояния
Вызвать метод SetIndicators класса CStatusBar, чтобы связать каждый индикатор панели состояния с идентификатором текстовой строки
MFC AppWizard автоматически добавляет к созданному им приложению программный код, служащий для отображения панели состояния.
Объект m_wndStatusBar класса CStatusBar определяется как элемент класса CMainFrame. Это вполне естественно, так как панель состояния принадлежит именно окну класса CMainFrame.
protected:
CStatusBar m_wndStatusBar;
Создание панели состояния и отображение ее на экране выполняется во время обработки метода OnCreate класса CMainFrame сразу после создания панели управления.
Методы Create и SetIndicators, создающие панель, вызываются в одной строке.
// Создаем панель 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;
}
В качестве родительского окна панели состояния методу Create указано ключевое слово this. Таким образом, родительским окном панели состояния, также как и панели управления, является главное окно приложения, представленное объектом класса CMainFrame.
Информация, которая должна отображаться в панели состояния, определяется идентификаторами, записанными в массиве. Каждый такой идентификатор представляет текстовую строку из таблицы ресурсов приложения.
В нашем приложении массив indicators, описывающий панель состояния, определен в файле MainFrm.cpp непосредственно после таблицы сообщений.
static UINT indicators[] =
{
ID_SEPARATOR,
|
Константа |
Описание |
|
CBRS_ALIGN_TOP |
Панель управления может закрепляться в верхней части окна |
|
CBRS_ALIGN_BOTTOM |
Панель управления может закрепляться в нижней части окна |
|
CBRS_ALIGN_LEFT |
Панель управления может закрепляться на левой стороне окна |
|
CBRS_ALIGN_RIGHT |
Панель управления может закрепляться на правой стороне окна |
|
CBRS_ALIGN_ANY |
Панель управления может закрепляться с любой стороны окна |
|
CBRS_FLOAT_MULTI |
Несколько панелей управления могут объединяться вместе в одном мини-окне |
|
CBRS_TOOLTIPS |
Когда пользователь устанавливает курсор на органы управления панели toolbar, их названия отображаются в маленькой прямоугольной рамке tool tips. Более подробно о tool tips вы можете прочитать в томе 22 серии “Библиотека системного программиста” |
|
CBRS_FLYBY |
Текст в панели состояния изменяется, одновременно с отображением tool tips. В противном случае текст в панели состояния меняется только когда пользователь нажмет кнопку в панели управления |
|
CBRS_SIZE_DYNAMIC |
Когда панель управления отображается в окне, пользователь может изменять ее размеры. При этом органы управления панели будут отображаться в нескольких строках. Этот стиль доступен только для Visual C++ версии 4.0 и выше |
Класс CToolBar представляет панель управления toolbar. Обычно панель управления содержит несколько кнопок и разделителей между ними. Для панели управления определен специальный ресурс типа Toolbar.
Чтобы создать панель управления toolbar, следует выполнить следующие действия.
Создать ресурс, описывающий панель toolbar
Создать объект класса CToolBar. Он будет представлять панель управления
Вызвать метод Create класса CToolBar, чтобы создать панель и связать ее с объектом представляющим панель управления
Вызвать метод LoadToolBar класса CToolBar. Он загрузит ресурс диалоговой панели и завершит построение панели управления
Чтобы облегчить вам работу, MFC AppWizard создает для приложения и панель управления и панель состояния. Для этого он включает в состав ресурсов приложения ресурс, описывающий панель управления. В нашем приложении определен только один такой ресурс, имеющий идентификатор IDR_MAINFRAME.
Объект m_wndToolBar класса CToolBar для управления этой панелью определяется в классе CMainFrame. Этот объект объявлен как protected, поэтому обращаться к нему можно только из класса главного окна приложения CMainFrame.
protected:
CToolBar m_wndToolBar;
Создание самой панели управления и отображение ее на экране выполняется во время обработки метода OnCreate класса CMainFrame. При этом методы Create и LoadToolBar вызываются в одной строке.
if (!m_wndToolBar.Create(this)
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
// Ошибка при создании панели управления toolbar
TRACE0("Failed to create toolbar\n");
return -1;
}
В качестве родительского окна панели управления методу Create указано ключевое слово this. Таким образом, родительским окном является главное окно приложения, представленное объектом класса CMainFrame.
После создания панели управления сразу вызывается метод LoadToolBar, загружающий ресурс панели управления IDR_MAINFRAME. Если хотя бы один метод - Create или LoadToolBar, завершится с ошибкой, метод OnCreate класса CMainFrame возвращает -1.
Наиболее интересная задача, которую выполняет метод OnCreate, заключается в отображении панелей управления и состояния. Для панели управления и панели состояния в библиотеке классов MFC предусмотрены два класса CStatusBar и CToolBar.
Классы CStatusBar и CToolBar имеют длинную цепочку наследования.
CStatusBar <- | <- CControlBar <- CWnd <- CCmdTarget <- CObject
CToolBar <- |
Полное описание этих классов заняло бы слишком много места, поэтому мы ограничимся рассказом о самых главных методах, необходимых для работы с панелями управления и состояния.
Многие современные приложения, в том числе все приложения имеющие оконный интерфейс и созданные с использованием средств MFC AppWizard, имеют панель управления. Эта панель располагается как правило ниже меню главного окна приложения и содержит ряд кнопок.
//////////////////////////////////////////////////////////////
// Панель управления Toolbar
IDR_MAINFRAME TOOLBAR DISCARDABLE 16, 15
BEGIN
BUTTON ID_FILE_NEW
BUTTON ID_FILE_OPEN
BUTTON ID_FILE_SAVE
SEPARATOR
BUTTON ID_EDIT_CUT
BUTTON ID_EDIT_COPY
BUTTON ID_EDIT_PASTE
SEPARATOR
BUTTON ID_FILE_PRINT
BUTTON ID_APP_ABOUT
END
Обратите внимание, что идентификаторы кнопок панели управления соответствуют идентификаторам некоторых строк меню приложения. Поэтому эти кнопки дублируют соответствующие строки меню.
Образ кнопок панели управления расположен в файле Toolbar.bmp, записанном в подкаталоге res каталога проекта.
//////////////////////////////////////////////////////////////
// Изображение Bitmap, определяющее кнопки приложения
IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp"
Класс CPrintInfo предназначен для управления печатью документов на принтере. Когда пользователь отправляет документ на печать или выполняет предварительный просмотр документа перед печатью, создается объект класса CPrintInfo. Он содержит различную информацию о том, какие страницы документа печатается и т. д.
Кроме описанных нами классов библиотека MFC включает большое количество классов, предназначенных для организации технологии OLE. Из-за ограниченного объема книги мы не будем рассматривать приложения, поддерживающие OLE технологию.
В соответствии с правилами языка С различные функции, определенные в программе, должны иметь разные имена. Это не всегда удобно, так как могут быть несколько функций, выполняющих сходные действия, но немного отличающиеся по набору параметров.
Язык С++ позволяет иметь несколько функций с одинаковыми именами, но различным набором параметров. Такие функции называются перегруженными, так как одно и то же имя используется для обозначения различных функций.
В качестве примера рассмотрим функции Sqware, предназначенные для вычисления площади прямоугольников и квадратов:
int Sqware(int a, int b);
int Sqware(int a);
Как видите, эти функции имеют одинаковые имена, но разные параметры. Первая функция, предназначенная для вычисления площади прямоугольника имеет два параметра, задающие длины его сторон. Вторая функция позволяет вычислить площадь квадрата и содержит только один параметр, определяющий длину стороны квадрата. Вот определения этих функций:
int Sqware(int a, int b) {
return (a * b);
}
int Sqware(int a) {
return (a * a);
}
Вы можете вызывать обе функции Sqware из своей программы. Компилятор определит по количеству и типу параметров, какую конкретно функцию надо выполнить:
void main() {
int value;
value = Sqware(10, 20);
print(“Площадь прямоугольника равна %d”, value);
value = Sqware(10);
print(“Площадь квадрата равна %d”, value);
}
Си++ позволяет вам легко вводить новые типы данных. Так, например, вы можете определить класс для работы с комплексными числами или числами в полярной системе координат. Естественно, что удобнее всего проводить вычисления с объектами таких классов при помощи операторов, а не специальных методов или функций.
В Си++ вы можете переопределить большинство операторов языка для работы с вашими типами данных. Вот список операторов, которые вы можете переопределить:
| ! | = | < | > | += | –= | ||||||
| != | , | –> | –>* | & | | | ||||||
| ( ) | [ ] | new | delete | >> | <<= | ||||||
| ^= | &= | |= | << | >>= | == | ||||||
| ~ | *= | /= | %= | % | ^ | ||||||
| + | - | * | / | ++ | –– | ||||||
| <= | >= | && |
Переопределение операторов вызывает перегрузку операторов. Как в случае перегруженных функций и методов, перегруженные операторы вызываются в зависимости от количества и типа их параметров. О перегруженных функциях вы можете прочитать в разделе “Перегрузка имен функций”.
Для переопределения операторов предназначено ключевое слово operator. Вы должны определить функцию или метод, имя которого состоит из ключевого слова operator и самого оператора. Параметры этой функции должны соответствовать параметрам переопределяемого оператора.
Вы можете переопределить оператор для объектов класса, используя соответствующий метод класса или дружественную функцию класса. Когда вы переопределяете оператор с помощью метода класса, то в качестве неявного параметра метод принимает ключевое слово this, являющееся указателем на данный объект класса. Поэтому если переопределяется бинарный оператор, то переопределяющий его метод класса должен принимать только один параметр, а если переопределяется унарный оператор - метод класса вообще не должен иметь параметров.
Если оператор переопределяется при помощи дружественной функции, то он должен принимать два параметра для бинарных операторов и один параметр для унарных операторов.
В порожденном классе можно определить методы и элементы данных с именами, которые уже используются в базовом классе. Соответствующие методы и элементы данных базового класса оказываются скрыты. Чтобы обратиться к ним, необходимо указывать полное имя, включающее имя базового класса, оператор :: и имя элемента класса.
Первое приложение MFHello, которое мы создадим с использованием библиотеки классов MFC будет очень простое. Единственное, что оно будет делать - это отображать на экране маленькую диалоговую панель, содержащую строку “Hello, MFC!”.
Исходный текст приложения, представленный в листинге 2.1, состоит всего из двенадцати строк, не считая строк комментариев. В нашем первом приложении мы использовали единственный класс библиотеки MFC, наследованный от базового класса CWinApp.
Чтобы создать новый проект, выберите из меню File строку New. На экране появится диалоговая панель New, содержащая одноименный список New и две кнопки. Выберите из списка New строку Project Workspace. Откроется диалоговая панель New project workspace (рис. 4.1 из главы “Приложение с главной диалоговой панелью”). В ней вы должны указать тип приложения, который будет разрабатываться, имя проекта и расположение каталога для записи в него файлов проекта.
Самые простые приложения с использованием библиотеки классов MFC мы будем создавать без использования автоматизированных средств разработки приложений MFC AppWizard. Поэтому в качестве типа приложения выберите из списка Type строку Application.
В поле Name введите имя нового проекта. Наш первый проект мы назвали именем MFHello. Расположение каталога для размещения файлов проекта отображается в поле Location. По умолчанию каталог проекта называется точно также как сам проект и будет размещен в каталоге Projects среды разработки Visual C++. Вы можете выбрать для проекта любой другой каталог, если нажмете на кнопку Browse.
Теперь нажмите кнопку Create. Будут созданы служебные файлы проекта. Они получат названия MFHello.mak, MFHello.ncb и MFHello.mdp. Файл MFHello.mdp является основным файлом проекта. В этих файлах определяется, какие исходные файлы содержит проект, указываются характеристики создаваемого приложения, конфигурация самой среды разработки Visual C++.
В файле ресурсов приложения указана единственная пиктограмма, имеющая идентификатор IDR_MAINFRAME. Эта пиктограмма содержится в файле Dialog.ico, в каталоге res.
//////////////////////////////////////////////////////////////
// Пиктограмма
IDR_MAINFRAME ICON DISCARDABLE "res\\Dialog.ico"
Пиктограмма IDR_MAINFRAME содержит два цветных изображения с разрешением 32х32 и 16х16 пикселов (рис. 4.7). Вы можете изменить эти пиктограммы по своему усмотрению.

Рис. 4.7. Пиктограммы приложения Dialog
В файле ресурсов приложения Single определены две пиктограммы IDR_SINGLETYPE и IDR_MAINFRAME. Каждая из этих пиктограмм содержит по два изображения различного размера 32х32 и 16х16 пикселов.
//////////////////////////////////////////////////////////////
// Пиктограммы
IDR_MAINFRAME ICON DISCARDABLE "res\\Single.ico"
IDR_SINGLETYPE ICON DISCARDABLE "res\\SingleDoc.ico"
Пиктограмма IDR_MAINFRAME представляет минимизированное приложение (рис. 5.11). Эта же пиктограмма отображается в левом верхнем углу главного окна приложения.

Рис. 5.11. Пиктограмма IDR_MAINFRAME
Точно такая же пиктограмма используется всеми приложениями, построенными на основе MFC AppWizard, вне зависимости от типа их интерфейса с пользователем.
Пиктограмма IDR_SINGLETYPE может быть использована для представления документа, с которым работает приложение (рис. 5.12). Приложение с однооконным интерфейсом практически не использует эту пиктограмму.

Рис. 5.12. Пиктограмма IDR_SINGLETYPE
От класса CCmdTarget наследуется класс CWinThread, представляющий подзадачи приложения. Простые приложения, которые мы будем рассматривать в первой книге, посвященной MFC, имеют только одну подзадачу. Эта подзадача, называемая главной, представляется классом CWinApp, наследованным от класса CWinThread.
Виртуальный метод Dump позволяет получить дамп объекта данного класса:
virtual void Dump(CDumpContext& dc) const;
Метод Dump имеет единственный параметр dc, определяющий контекст отображения для вывода дампа объекта. Часто в качестве параметра dc используется предопределенный объект afxDump. Он позволяет передавать информацию в окно отладчика Visual C++. Объект afxDump определен только для отладочной версии приложения.
Вы можете переопределить метод Dump для своего класса. Переопределенный метод должен сначала вызывать метод Dump базового класса, а затем выводить значения элементов самого класса. Для вывода значений элементов объекта класса в контекст dc можно использовать операторы <<, переопределенные для класса CDumpContext.
Если класс определен с макрокомандами IMPLEMENT_DYNAMIC или IMPLEMENT_SERIAL, то метод Dump класса CObject будет отображать также имя самого класса.
Для класса CFigure, описанного выше, метод Dump можно определить следующим образом:
void CFigure::Dump(CDumpContext &dc) const
{
// Вызываем метод Dump базового класса
CObject::Dump(dc);
// Выводим в контекст dc значение элемента m_area
// класса CFigure
dc << "Площадь = " << m_area;
}
Ниже описаны последовательности обработки командных сообщений объектами различных классов.
Чтобы переместить указатель текущей позиции файла в новое положение, можно воспользоваться одним из следующих методов класса CFile - Seek, SeekToBegin, SeekToEnd. В состав класса CFile также входят методы, позволяющие установить и изменить длину файла - GetLength, SetLength.
Когда вы открываете файл, указатель текущей позиции файла находится в самом начале файла. Когда вы читаете данные из файла, этот указатель перемещается в конец файла и указывает на данные, которые будут получены очередной операцией чтения из файла.
Чтобы переместить указатель текущей позиции файла в любое место, можно воспользоваться универсальным методом Seek. Он позволяет переместить указатель на определенное число байт относительно начала, конца файла или текущей позиции указателя:
virtual LONG Seek(LONG lOff, UINT nFrom);
throw(CFileException);
Параметр lOff определяет число байт, на которое надо переместить указатель текущей позиции файла. Параметр lOff может быть положительной или отрицательной величиной. Если lOff больше нуля, то указатель смещается в сторону конца файла, если lOff меньше нуля, то указатель смещается в начало файла.
Параметр nFrom определяет, относительно чего задано это смещение. В качестве nFrom можно указать три различные константы, также определенные в классе CFile:
| Константа | Параметр lOff задает смещение относительно | ||
| CFile::begin | Начала файла | ||
| CFile::current | Текущей позиции файла | ||
| CFile::end | Конца файла |
В случае успешного перемещения указателя файла в новую позицию метод Seek возвращает новое смещение указателя относительно начала файла. Если новая позиция указателя задана неправильно, например, вы пытаетесь переместить указатель в позицию перед началом файла, вызывается исключение.
Чтобы переместить указателя файла в начало или конец файла, наиболее удобно использовать специальные методы. Метод SeekToBegin перемещает указатель в начало файла, а метод SeekToEnd - в его конец. Приведем прототип метода SeekToBegin:
void SeekToBegin();
Названия файлов, входящих в проект Dialog, вы можете просмотреть на странице FileView в окне Project Workspace. В состав нашего проекта входят следующие основные файлы:
| Имя файла | Описание | ||
| Dialog.h | Основной включаемый файл приложения. В нем описан главный класс приложения CDialogApp, а также подключены другие включаемые файлы, необходимые для библиотеки MFC | ||
| Dialog.cpp | Основной файл приложения. В нем определены методы главного класса приложения и глобальные объекты | ||
| Dialog.rc | Файл ресурсов. В этом файле описаны все ресурсы приложения. Сами ресурсы могут быть записаны в каталоге RES, расположенном в главном каталоге проекта | ||
| Resource.h | Файл содержит определения идентификаторов ресурсов приложения, например идентификаторы строк меню | ||
| res\Dialog.ico | Пиктограмма приложения | ||
| res\Dialog.rc2 | В этом файле определены ресурсы, которые нельзя редактировать с помощью редактора ресурсов среды Visual C++ | ||
| DialogDlg.h | Файл, в котором определен класс главной диалоговой панели приложения | ||
| DialogDlg.cpp | В этом файле определены методы класса главной диалоговой панели | ||
| ReadMe.txt | Текстовый файл, содержащий описание проекта. В нем кратко рассмотрен каждый файл, входящий в проект, перечислены классы приложения, а также представлена некоторая другая дополнительная информация | ||
| Dialog.clw | Файл содержит информацию, необходимую для правильной работы ClassWizard | ||
| StdAfx.h, StdAfx.cpp | Использование этих файлов позволяет ускорить процесс повторного построения проекта. Более подробное описание файлов представлено ниже |
Приложение Except, исходный текст которого представлен в листинге 3.3, показывает как можно выполнить обработку исключительных ситуаций. Оно содержит блок try и несколько обработчиков исключений для объектов типа CMemoryException, CFileException, CException, а также универсальный обработчик. Если в блоке try вызывается исключение, связанное с ошибкой в файловой системе или системе распределения памяти, оно обрабатывается соответствующими блоками catch. Если исключение вызвано с объектом другого типа, но наследованным от класса CException, например CArchiveException, CNotSupportedException или CResourceException, тогда оно обрабатывается блоком catch для объектов CException. И наконец, если объект исключения не имеет базовым классом CException, оно обрабатывается в последнем блоке catch.
Листинг 3.3. Файл Except.cpp
#include "stdafx.h"
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
)
{
try
{
CFile file("This file is absent", CFile::modeRead);
// Здесь могут быть операторы, вызывающие другие
// исключения
}
// Обработчик для исключения типа CMemoryException
catch(CMemoryException* ptrException)
{
MessageBox(NULL,"Memory Exception", "Exception",
MB_OK | MB_ICONSTOP);
ptrException -> Delete();
}
// Обработчик для исключения типа CFileException
catch(CFileException* ptrException)
{
if(ptrException -> m_cause ==
CFileException::fileNotFound)
MessageBox(NULL,"File Not Found", "Exception",
Приложения, которые мы рассматривали до сих пор, фактически никак не могли взаимодействовать с пользователем. Они не имели ни меню, ни панели управления. И самое главное - они не содержали обработчиков сообщений.
Сейчас мы рассмотрим приложение MFMenu, которое имеет меню и содержит обработчики сообщений, передаваемых приложению, когда пользователь открывает меню и выбирает из него строки. Для создания приложения MFMenu мы также не станем пользоваться средствами автоматизированной разработки MFC AppWizard, и наберем текст приложения вручную. Этот позволит вам лучше разобраться с механизмом сообщений.
Создайте новый проект под названием MFMenu. В качестве типа приложения выберите из списка Type строку Application (рис. 4.1 из главы “Приложение с главной диалоговой панелью”). Наберите в редакторе исходный текст приложения и сохраните его в файле MFMenu.cpp (листинг 2.7). Чтобы быстрее набрать текст приложения, вы можете получить его, изменив исходный текст приложения MFStart. Затем включите этот файл в проект.
Листинг 2.7. Файл MFMenu.cpp
// Включаемый файл для MFC
#include <afxwin.h>
#include "MFMenuRes.h"
//=====================================================
// Класс CMFMenuApp - главный класс приложения
//=====================================================
class CMFMenuApp : public CWinApp
{
public:
// Мы будем переопределять метод InitInstance,
// предназначенный для инициализации приложения
virtual BOOL InitInstance();
};
// Создаем объект приложение класса CMFMenuApp
CMFMenuApp MFMenuApp;
//=====================================================
// Класс CMFMenuWindow - представляет главное окно
//=====================================================
class CMFMenuWindow : public CFrameWnd
{
public:
// Объявляем конструктор класса CMFMenuWindow
CMFMenuWindow();
// Объявляем методы для обработки команд меню
afx_msg void MenuCommand();
afx_msg void ExitApp();
Первое созданное нами приложение не имело главного окна. Для вывода сообщения на экран мы использовали функцию AfxMessageBox, которая очень похожа на функцию MessageBox программного интерфейса операционной системы Windows.
Следующее приложение, которое мы будем рассматривать, немного сложнее. При запуске оно будет отображать на экране компьютера обычное окно, имеющее заголовок, системное меню и кнопки управления.
Точно так же как в приложении MFHello, в нашем втором приложении мы будем использовать класс CWinApp в качестве главного класса приложения. Для управления окном приложения мы создадим еще один класс, наследуемый от базового класса CFrameWnd, входящего в библиотеку MFС.
Создайте новый проект, как мы рассказывали выше, и назовите его именем MFStart. В качестве типа приложения выберите из списка Type строку Application (рис. 4.1 из главы “Приложение с главной диалоговой панелью”). Автоматизированные средства разработки приложений MFC AppWizard мы рассмотрим позже.
Наберите в редакторе исходный текст приложения и сохраните его в файле MFStart.cpp (листинг 2.2). Наш пример без учета строк комментариев состоит всего из двадцати строк исходного текста, поэтому набор текста не займет у вас много времени. Включите набранный файл в проект.
Листинг 2.2. Файл MFStart.cpp
// Включаемый файл для MFC
#include <afxwin.h>
//=====================================================
// Класс CMFStartApp
// Наследуем от базового класса CWinApp главный
// класс приложения CMFStartApp
//=====================================================
class CMFStartApp : public CWinApp
{
public:
// Мы будем переопределять метод InitInstance,
// предназначенный для инициализации приложения
virtual BOOL InitInstance();
};
// Создаем объект приложение класса CMFStartApp
CMFStartApp MFStartApp;
//=====================================================
// Класс CMFStartWindow
// Наследуем от базового класса CFrameWnd класс
// CMFStartWindow. Он будет представлять главное



В состав компиляторов Microsoft Visual C++ встроены средства, позволяющие программисту облегчить разработку приложений. В первую очередь к ним относятся MFC AppWizard и ClassWizard.
Как известно, в любом деле самое трудное - начало. Это высказывание в полной мере справедливо по отношению к разработке приложений Windows. Мы рассказывали в предыдущих книгах серии “Библиотека системного программиста” и вы могли убедиться в этом сами, что исходные тексты даже простых приложений Windows имеют большие размеры.
На создание работающего каркаса или шаблона приложения у программиста уходит слишком много времени. Обычно, чтобы не набирать исходный текст такого шаблона каждый раз, новые приложения создают на основе уже отлаженных примеров, изменяя их по своему усмотрению.
Благодаря MFC AppWizard среда разработчика Microsoft Visual C++ позволяет быстро создавать шаблоны новых приложений. При этом программисту не приходится писать ни одной строки кода. Достаточно ответить на ряд вопросов, которые задает MFC AppWizard, выбрав какое приложение вы желаете создать, и исходные тексты шаблона приложения вместе с файлами ресурсов готовы. Эти тексты можно сразу оттранслировать и получить готовый загрузочный модуль приложения.
Конечно, сегодня никакие средства автоматизированной разработки не смогут создать программу полностью без вашего участия - иначе зачем были бы нужны программисты? Прикладную часть приложения вы должны будете написать сами.
Однако MFC AppWizard окажет вам очень сильную помощь. Так, чтобы создать многооконный текстовый редактор обладающий справочной системой, в который к тому же можно включать OLE объекты и, вам не придется написать ни единой строчки кода программы. Исходные тексты такого приложения можно автоматически разработать с помощью AppWizard буквально за две минуты.
Но даже когда шаблон приложения полностью готов, Microsoft Visual C++ не оставляет вас один на один с его текстом. Встроенный в среду Visual C++ редактор ресурсов позволяет быстро создавать новые меню, диалоговые панели, добавлять кнопки к панели управления (панели toolbar).
Средства ClassView и ClassWizard позволят подключить к созданным и отредактированным ресурсам управляющий ими код. Большую часть работы по описанию и определению функций, обрабатывающих сообщения от меню, органов управления диалоговых панелей и т. д. также берут на себя ClassView и ClassWizard.
В этом разделе мы расскажем о том, как создать простейшее приложение с единственной диалоговой панелью. Диалоговая панель будет содержать несколько кнопок, статическое текстовое поле и поле редактирования. В следующей главе мы расскажем, как создать более сложное приложение с главной диалоговой панелью при помощи средств автоматизированного проектирования MFC AppWizard и ClassWizard.
Создайте новый проект под названием MFDialog. В качестве типа приложения выберите из списка Type строку Application (рис. 4.1). Наберите в редакторе исходный текст приложения и сохраните его в файле MFDialog.cpp (листинг 2.13).
Листинг 2.13. Файл MFDialog.cpp
// Включаемый файл для MFC
#include <afxwin.h>
#include "resource.h"
//=====================================================
// Класс CMFDialogApp - главный класс приложения
//=====================================================
class CMFDialogApp : public CWinApp
{
public:
// Мы будем переопределять метод InitInstance,
// предназначенный для инициализации приложения
virtual BOOL InitInstance();
};
// Создаем объект приложение класса CMFDialogApp
CMFDialogApp MFDialogApp;
//=====================================================
// Класс 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();
// Макрокоманда необходима, так как класс
// CMyDialog обрабатывает сообщения от органов
// управления диалоговой панели
DECLARE_MESSAGE_MAP()
};
// Конструктор клаасса CMyDialog
CMyDialog::CMyDialog() : CDialog(CMyDialog::IDD)
{
// Инициализируем переменную m_Text
m_Text = "";

|
Кнопка |
Название |
Описание |
![]() |
Select |
Если вы нажмете эту кнопку, то сможете выбрать органы управления, которые уже расположены в диалоговой панели |
![]() |
Picture |
Рисунок |
![]() |
Static Text |
Статическое текстовое поле |
![]() |
Edit Box |
Поле редактирования |
![]() |
Group Box |
Прямоугольник, объединяющий группу органов управления |
![]() |
Button |
Кнопка |
![]() |
Check Box |
Переключатель в виде прямоугольника |
![]() |
Radio Button |
Переключатель круглой формы (радиопереключатель) |
![]() |
Combo Box |
Список с окном редактирования |
![]() |
List Box |
Список |
![]() |
Horizontal Scroll Bar |
Горизонтальная полоса просмотра |
![]() |
Vertical Scroll Bar |
Вертикальная полоса просмотра |
![]() |
Animate |
Окно просмотра видео |
![]() |
Tab Control |
Позволяет размещать в диалоговой панели несколько страниц органов управления, каждая из которых имеет “закладку”. Пользователь одновременно видит закладки всех страниц и может выбрать любую из них. |
![]() |
Tree Control |
Позволяет просматривать иерархические (древовидные) структуры данных. Такой орган управления можно использовать для отображения структуры каталогов на диске, заголовков статей справочной системы и т. д. |
![]() |
List Control |
Может использоваться для отображения списка пиктограмм |
![]() |
Hot Key |
Орган управления Hot Key предназначен для ввода комбинации клавиш акселераторов |
![]() |
Slider |
Движок. Обычно используется при отображении видеоинформации. Позволяет перейти к просмотру произвольного кадра |
![]() |
Progress |
Линейный индикатор. Позволяет отображать на экране ход какого-нибудь процесса, например процесса копирования файлов |
![]() |
Spin |
Обычно используется для увеличения или уменьшения значения какого-нибудь параметра |
![]() |
Custom Control |
Орган управления, внешний вид и назначение которого определяется пользователем |





Процедура создания немодальной диалоговой панели несколько отличается от процедуры создания модальной диалоговой панели. Как и в случае с модальной диалоговой панелью, в первую очередь вы должны создать шаблон диалоговой панели и добавить его в файл ресурсов приложения.
Затем надо создать класс управляющий диалоговой панелью - класс диалоговой панели. Этот класс наследуется непосредственно от базового класса CDialog. В классе диалоговой панели следует определить методы для обработки сообщений от органов управления диалоговой панели.
Следующие шаги выполняются только для немодальных диалоговых панелей. Для класса диалоговой панели надо определить конструктор, объявив его как public. Чтобы открыть немодальную диалоговую панель, создайте объект класса диалоговой панели, обратившись к определенному вами конструктору. В этот момент диалоговая панель еще не появляется на экране. Для этого надо вызвать метод Create класса CDialog.
Вы можете вызывать метод Create либо непосредственно из конструктора класса диалоговой панели, либо уже после создания объекта. Если диалоговая панель имеет стиль WS_VISIBLE, она сразу появится на экране. В противном случае для этого надо вызвать метод ShowWindow. Как видите, для отображения немодальной диалоговой панели мы не использовали метод DoModal.
В классе CDialog определены два прототипа метода Create. Один позволяет указать диалоговую панель через ее текстовое имя, а другой через числовой идентификатор:
BOOL
Create(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL);
BOOL
Create(UINT nIDTemplate, CWnd* pParentWnd = NULL);
Параметр lpszTemplateName должен содержать указатель на строку, закрытую нулем, содержащую имя ресурса диалоговой панели. Если вы используете метод Create, имеющий второй прототип, вместо имени шаблона, надо указать в параметре nIDTemplate его идентификатор.
Параметр pParentWnd задает родительское окно диалоговой панели. Если в качестве pParentWnd указать NULL, то в качестве родительского окна выступает главное окно приложения.
Метод Create возвращает управление сразу после того, как диалоговая панель отобразилась на экране. Он возвращает ненулевое значение, если создание диалоговой панели завершилось успешно и нуль в противном случае.
Чтобы закрыть немодальную диалоговую панель, можно воспользоваться методом DestroyWindow (метод DestroyWindow определен в классе CWnd и вы можете вызвать его для объектов класса диалоговой панели).
Теперь вам остается удалить объект, управляющий диалоговой панелью. Для этого в классе диалоговой панели можно переопределить виртуальный метод PostNcDestroy (этот метод первоначально определен в базовом классе CWnd). В нем вы можете вызвать оператор delete, передав ему в качестве параметра указатель на данный объект this.
В этом разделе мы рассмотрим однооконное приложение, созданное с использованием средств MFC AppWizard и расскажем, как его можно совершенствовать.
Создайте новое приложение с однооконным интерфейсом и назовите его Single. При определении свойств приложения оставьте все предложения по умолчанию. Наше приложение не будет поддерживать ни технологию OLE, ни базу данных, ни сетевые технологии. За счет этого оно будет меньше размером, что позволит лучше понять структуру приложений MFC. Процедура создания приложений с использованием MFC AppWizard описана в разделе “Приложение с оконным интерфейсом” и сейчас мы на ней останавливаться не будем.
В состав проекта Single входят следующие основные файлы:
| Имя файла | Описание | ||
| Single.h | В этом файле перечислены другие включаемые файлы и описан главный класс приложения CSingleApp | ||
| Single.cpp | Основной файл приложения. В нем определены методы основного класса приложения CSingleApp. | ||
| MainFrm.h, | Содержит описание класса frame, который называется CMainFrame. Класс CMainFrame наследуется от базового класса CFrameWnd определенного в библиотеке классов MFC | ||
| MainFrm.cpp | Файл содержит определения методов класса CMainFrame | ||
| SingleDoc.h | Содержит описание класса документов приложения - CSingleDoc | ||
| SingleDoc.cpp | Включает определение методов класса CSingleDoc | ||
| SingleView.h | Содержит описание класса окна просмотра приложения - CSingleView | ||
| SingleView.cpp | Включает определение методов класса CSingleView | ||
| Single.rc | Файл ресурсов. В этом файле описаны все ресурсы приложения. Сами ресурсы могут быть записаны в каталоге RES, расположенном в главном каталоге проекта | ||
| Resource.h | Файл содержит определения идентификаторов ресурсов приложения, например, идентификаторы строк меню | ||
| res\Single.ico | Пиктограмма приложения | ||
| res\Single.rc2 | В этом файле определены ресурсы, которые нельзя редактировать с помощью редактора ресурсов среды Visual C++ | ||
| res\Toolbar.bmp | Файл содержит изображение кнопок панели управления toolbar | ||
| StdAfx.h, StdAfx.cpp | Использование этих файлов позволяет ускорить процесс повторного построения проекта. Более подробное описание файлов представлено ниже | ||
| Single.clw | Файл содержит информацию, необходимую для правильной работы ClassWizard | ||
| ReadMe.txt | Текстовый файл, содержащий описание проекта. В нем кратко рассмотрен каждый файл, входящий в проект, перечислены классы приложения, а также представлена другая дополнительная информация |
Постройте проект Single и запустите полученное приложение. На экране появиться главное окно приложения (рис. 5.10). Как видите, оно имеет меню, панели управления и состояния. Попробуйте выбрать различные строки из меню приложения.
Некоторые из строк меню приложения уже работают. Например, когда вы выбираете из меню File строку Open, на экране открывается стандартная диалоговая панель для выбора файла. Вы можете выбрать из этой панели любой файл и открыть его. Однако от этого изменится только заголовок окна приложения - в нем появится название открытого файла. Содержимое файла будет недоступно. Чтобы вы смогли просматривать и изменять содержимое открытого файла, необходимо добавить специальный код. Мы займемся этим в разделе “Простейший графический редактор” данной главы.

Рис. 5.10. Приложение Single
Теперь мы представим вам приложение, которое использует класс CStdioFile для записи в файл информации о файловой системе компьютера. Для определения характеристик файловой системы мы воспользовались функцией GetVolumeInformation из программного интерфейса Win32.
Эта функция позволяет определить различные характеристики файловой системы, установленной на данном томе. Ниже представлен прототип функции GetVolumeInformation:
BOOL GetVolumeInformation(
LPCTSTR lpRootPathName,
LPTSTR lpVolumeNameBuffer,
DWORD nVolumeNameSize,
LPDWORD lpVolumeSerialNumber,
LPDWORD lpMaximumComponentLength,
LPDWORD lpFileSystemFlags,
LPTSTR lpFileSystemNameBuffer,
DWORD nFileSystemNameSize
);
Первый параметр lpRootPathName перед вызовом функции должен указывать на строку, содержащую путь корневого каталога тома, информацию о котором надо получить. Если вы присвоите lpRootPathName значение NULL, по умолчанию будет выбран корневой каталог текущего тома.
Параметр lpVolumeNameBuffer должен содержать указатель на буфер, в который будет записано имя тома. Длина буфера определяется параметром nVolumeNameSize. Если вас не интересует имя тома, вы можете указать для параметра lpVolumeNameBuffer значение NULL. В этом случае значение nVolumeNameSize не используется.
Параметр lpVolumeSerialNumber должен содержать адрес переменной типа DWORD, в которую будет записан серийный номер тома. Если вас не интересует серийный номер, вы можете указать для параметра значение NULL.
Параметр lpMaximumComponentLength должен содержать адрес переменной, в которую будет записана максимальная длина файла, для данной файловой системы. Для операционной системы Windows 95 эта переменная будет содержать значение 255, несмотря на то, что тип файловой системы остался FAT.
Параметр lpFileSystemFlags указывает на переменную, которая может быть одной или комбинацией нескольких констант, перечисленных в следующей таблице.
| Константа | Описание | ||
| FS_CASE_IS_PRESERVED | При записи файлов на диск сохраняется написание (заглавные и прописные символы) имени файла | ||
| FS_CASE_SENSITIVE | Файловая система различает заглавные и прописные символы в именах файлов | ||
| FS_UNICODE_STORED_ON_DISK | Файловая система обеспечивает кодировку имен файлов Unicode | ||
| FS_FILE_COMPRESSION | Обеспечивается возможность сжатия отдельных файлов, расположенных на томе. Эта константа не может быть использована вместе с другими | ||
| FS_VOL_IS_COMPRESSED | Данный том является компрессованным. Такой том может быть создан системами типа DoubleSpace. Эта константа не может быть использована вместе с другими |
Сначала вы должны создать новый проект и выбрать тип проекта AppWizard. Определите расположение каталога для размещения в нем файлов проекта и имя проекта. Когда вы создадите проект, на экране появится первая диалоговая панель MFC AppWizard. Внешний вид этой панели мы уже приводили на рисунке 4.2.
Теперь надо определить, какой тип пользовательского интерфейса будет иметь приложение. Выберите приложение с однооконным интерфейсом (Single document). Начиная с этого момента процедура создания приложений с разными пользовательскими интерфейсами будет отличаться от описанной нами в предыдущем разделе.
Нажмите кнопку Next. На экране появится следующая диалоговая панель MFC AppWizard. Если вы создаете приложение с однооконным интерфейсом, диалоговая панель будет иметь внешний вид, показанный на рисунке 5.1.

Рис. 5.1. Второй шаг MFC AppWizard
Если приложение будет работать с базами данных, то вам надо указать AppWizard, на каком уровне создаваемый шаблон приложения будет поддерживать базы данных. Сейчас мы не будем подробно останавливаться на работе с базами данных. Наше первое приложение с однооконным интерфейсом не будет работать с базами данных, поэтому переведите переключатель What database support would you like to include? в положение None. Затем нажмите кнопку Next>. На экране появится следующая диалоговая панель MFC AppWizard (рис. 5.2).

Рис. 5.2. Третий шаг MFC AppWizard
В этой диалоговой панели вам предстоит выбрать, будет ли приложение поддерживать технологию OLE, и если будет, то в каком режиме.
Первый переключатель What OLE compound document support would you like to include? определяет как приложение будет поддерживать технологию OLE. Мы описали использование этого переключателя в следующей таблице.
| Переключатель | Описание | ||
| None | Приложение не использует технологию OLE | ||
| Container | Приложение сможет включать в свои документы, другие объекты и ссылки на них, то есть будет выступать в качестве клиента OLE | ||
| Mini-server | Обеспечивается работа основных возможностей сервера OLE. Документы или объекты, подготовленные в приложении, можно будет включать в другие приложения. Однако приложение нельзя будет использовать автономно. Объекты, подготовленные в приложении, можно будет встраивать, однако нельзя будет встроить ссылку на объект, записанный в отдельном файле | ||
| Full-server | Обеспечивается работа всех возможностей сервера OLE. Объекты, подготовленные в приложении, можно будет включать в другие приложения или встраивать ссылку на объект, записанный в отдельном файле. Приложение можно будет использовать автономно | ||
| Both container and server | Приложение сможет работать и как сервер и как клиент OLE |





|
Переключатель |
Описание |
|
Thick frame |
Главное окно имеет рамку, позволяющую изменить его размер |
|
Minimize box |
Главное окно содержит кнопку для сворачивания его в пиктограмму |
|
Maximize box |
Главное окно содержит кнопку для увеличения его размера до максимально возможного |
|
System menu |
Главное окно приложения будет иметь системное меню |
|
Minimized |
При запуске приложения его главное окно будет уменьшено до пиктограммы. Потом вы сможете открыть окно |
|
Maximized |
При запуске приложения его главное окно принимает максимально возможный размер |

|
Переключатель |
Описание |
|
Thick frame |
Все MDI окна приложения имеют рамку, позволяющую изменить их размер |
|
Minimize box |
MDI окна содержат кнопку для сворачивания их в пиктограмму |
|
Maximize box |
MDI окна содержат кнопку для увеличения их размера до максимально возможного |
|
Minimized |
Открываемые окна MDI будут уменьшены до пиктограммы |
|
Maximized |
Открываемые окна MDI будут иметь максимально возможный размер |

ClassView предоставляет очень интересную и полезную возможность просмотра дерева наследования классов приложения. Для этого выберите название интересующего вас класса из списка классов и откройте временное меню, нажав правую кнопку мыши. Строки Derived Classes и Base Classes позволяют просмотреть последовательность классов, порожденных от данного класса или последовательность базовых классов.
На рисунке 2.17 мы воспользовались возможностями ClassView для изучения последовательности базовых классов для основного класса приложения MFHello.

Рис. 2.17. Порядок наследования, диалоговая панель Base Classes and Members
Диалоговая панель Base Classes and Members не имеет кнопок OK и Cancel. Она закрывается в том случае, если вы выберите из нее элемент класса или переключитесь на другое окно. Иногда удобно, чтобы диалоговая панель постоянно присутствовала на экране. Вы можете “прикрепить” ее к главному окну Microsoft Visual C++. Нажмите кнопку


В левой части этой панели отображается список классов в порядке наследования. Базовые классы расположены ниже и правее наследуемых классов. Вы наглядно можете определить, что основным базовым классом для CMFHelloApp является класс CObject. Из этого класса наследуются классы CCmdTarget, CWinThread, CWinApp и только затем класс CMFHelloApp, определенный в приложении.
В правой части экрана отображаются элементы класса, выбранного из списка классов. Вы можете просмотреть не только элементы классов, определенных в приложении, но также и элементы классов из библиотеки MFC. Все элементы класса разделены на три группы в зависимости от их области видимости - Public, Protected и Private.
Панель Base Classes and Members позволяет легко перейти к редактированию любого элемента класса. Для этого выберите название этого элемента из списка и сделайте по нему двойной щелчок левой кнопкой мыши.
Чтобы вам было легче отличить методы от данных класса, перед методами располагается символ f, а перед данными d. Непосредственно перед именем метода или данных расположен символ, позволяющий отличить статические и виртуальные методы. Перед названиями статических методов расположен символ


|
Тип метода |
Отображать |
|
All |
Все методы |
|
Virtual |
Виртуальные методы |
|
Static |
Статические методы |
|
Non- Static |
Все методы, кроме статических |
|
Non- Virtual |
Все методы, кроме виртуальных |
|
Non- Static Non-Virtual |
Все методы, кроме статических и виртуальных |
|
None |
Не отображать методы класса |
|
Тип элементов данных |
Отображать |
|
All |
Все элементы данных |
|
Static |
Статические элементы данных |
|
Non- Static |
Все данные, кроме статических |
|
None |
Не отображать элементы данных |
Последняя страница Class Info диалоговой панели ClassWizard позволяет просмотреть основные характеристики класса, изменить фильтр сообщений Windows, изменить внешний класс и внешние переменные.
Выберите из списка Class name имя интересующего вас класса. В группе File details отображается справочная информация о выбранном классе. Поле Header содержит название включаемого файла, в котором определен класс, поле Source - имя файла реализации класса (в нем размещаются определения методов класса и некоторых данных класса). В поле Base class отображается название базового класса, а в поле Resource - идентификатор ресурса (если он есть), обслуживаемого классом. Например, если вы исследуете класс, наследованный от класса CDialog, который обслуживает диалоговую панель, то в поле Base class выводится CDialog, а в поле Resource идентификатор диалоговой панели.
Основные характеристики классов, отображаемые в полях группы File details, нельзя изменить в автоматическом режиме. Вы можете редактировать только характеристики класса, расположенные в группе Advanced options.
В зависимости от того, какой тип объекта Windows - диалоговую панель, окно, дочернее окно и т. д., обслуживает класс, он может получать различные множества сообщений.
Список сообщений, для которых класс может содержать методы-обработчики, отображается в списке Messages на странице Message Map главной диалоговой панели ClassWizard. Так как операционная система Windows имеет очень много различных сообщений, то ClassWizard помещает в этот список только часть этих сообщений.
Список сообщений формируется в соответствии с типом объекта Windows, обслуживаемом данным классом. Отбор сообщений происходит по фильтру, который отображается в поле Message filter страницы Class Info диалоговой панели ClassWizard. Вы можете поменять этот фильтр. Вам доступны следующие фильтры сообщений:
| Список Message filter | Объект, сообщения от которого должны обрабатываться | ||
| Child Window | Дочернее окно | ||
| Dialog | Диалоговое окно | ||
| MDI Child Frame | Дочернее окно MDI | ||
| Not a Window | Не окно | ||
| Topmost Frame | Окно frame window - главное окно приложения | ||
| Window | Окно |
ClassWizard позволяет отобразить (привязать) органы управления на диалоговой панели или форме к внешним (foreign) объектам классов CRecordset or CDaoRecordset. Для этого можно использовать список Foreign class и поле Foreign variable. Более подробно об этой возможности мы расскажем в одной из следующих книг серии.