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

Базы данных и библиотека MFC


Сегодня без преувеличения можно сказать, что основной областью применения компьютеров стало хранение и обработка различной информации. Для этого предназначено большое количество различных систем управления базами данных (СУБД).
Такие системы позволяют хранить большие объемы данных (десятки и сотни тысяч записей) и обеспечивать быстрый поиск необходимой информации.
Диапазон применения СУБД огромен. СУБД используются везде, начиная от небольших домашних баз данных, содержащих картотеку домашней библиотеки книг, и кончая распределенными базами данных, объединяющих десятки банков, офисов, магазинов и хранящих сведения о клиентах, товарах, производителях.
До недавнего времени на рынке персональных компьютеров превалировали базы данных для операционной системы MS-DOS. Среди них наиболее распространены Clipper, Clarion, Dbase, FoxPro 2.0 и некоторые другие.
С развитием операционной системы Windows практически все крупные производители программного обеспечения выпустили собственные системы управления базами данных для этой операционной системы. Так, Microsoft производит и распространяет две различные СУБД примерно одного класса - FoxPro for Windows и Access, Borland выпускает Object Vision и Paradox for Windows. Даже фирмы, производящие СУБД для больших и малых компьютеров, выпустили версии своих систем для операционной системы Windows.
В этой книге рассматривается интерфейс ODBC (Open Database Connectivity), разработанный Microsoft. Этот интерфейс позволяет приложениям Windows получить доступ к данным различных систем управления базами данных, используя запросы на языке SQL. При этом можно получить доступ к данным любой СУБД, для которой существует ODBC драйвер. Так, например, в состав дистрибутива Visual C++ входят ODBC драйверы для баз данных в формате Access, Btrieve, dBase, FoxPro, Excel, Paradox, а также для обычных текстовых файлов. Кроме того, поставляются ODBC драйверы и для удаленных СУБД - SQL Server и Oracle.
Библиотека классов MFC, поставляемая в составе Visual C++ содержит классы, предназначенные для упрощения взаимодействия с ODBC драйверами. Мы кратко расскажем про эти классы и расскажем как использовать систему автоматизированной разработки приложений AppWizard для создания приложений, поддерживающих работу с базами данных.


Базы данных


Расширены возможности работы с базами данных. Внесены изменения и дополнения в классы, взаимодействующие с базами данных через ODBC, в том числе модифицированы классы CRecordset и CDatabase. Добавлены новые компоненты к Component Gallery, которые можно использовать для связи с базами данных.


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


Новые операционные системы Windows 95 и Windows NT версии 4.0 и приложения, разработанные для них, значительно шире используют правую кнопку мыши, чем ранние версии Windows. Обычно при нажатии правой кнопки мыши на экране появляется временное меню, внешний вид которого зависит от выбранного объекта.
Современные приложения используют правую клавишу мыши для вывода контекстного меню. В диалоговой панели Component Gallery расположен компонент Pop-up Menu. Он позволяет подключить контекстное меню к любому окну приложения.
Если вы желаете подключить контекстное меню к вашему приложению, выберите в диалоговой Component Gallery панели компонент Pop-up Menu и нажмите кнопку Insert. На экране появится диалоговая панель Pop-up Menu. В списке Add pop-up menu to перечислены классы проекта, представляющие окна и диалоговые панели. К одному из них вы можете добавить контекстное меню. По умолчанию к проекту добавляется новое меню, состоящее из трех строк, которому присваивается идентификатор, состоящий из префикса CG_IDR_POPUP_, названия приложения и части названия класса окна, к которому добавлено меню.
Далее мы опишем добавление компонента Pop-up Menu к приложению Multi, рассмотренному в разделе “Приложение Multi”.
Загрузите в Microsoft Visual C++ проект Multi, откройте диалоговую панель Component Gallery, выберите компонент Pop-up Menu и нажмите кнопку Insert. На экране появится диалоговая панель Pop-up Menu (рис. 3.5). Выберите из списка Add pop-up menu to класс CMultiView.
По умолчанию к проекту добавляется новое меню, состоящее из трех строк, которому присваивается идентификатор CG_IDR_POPUP_MULTI_VIEW.
Component Gallery и контекстное меню
Рис. 3.5. Диалоговая панель Pop-up Menu
Название идентификатора контекстного меню отображается в поле Menu resource ID диалоговой панели Pop-up Menu. Вы можете заменить его по своему усмотрению.
Нажмите кнопку OK. Диалоговая панель Pop-up Menu закроется. В исходных текстах приложения будут выполнены все необходимые изменения, а к ресурсам добавиться новое меню с идентификатором CG_IDR_POPUP_MULTI_VIEW.

Редактор ресурсов Microsoft Visual C++ позволяет изменять шаблон контекстного меню по вашему усмотрению. Из него можно удалить строки, добавленные Component Gallery по умолчанию, и вставить строки нужные вам.

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

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

Рис. 3.6. Контекстное меню, которое использует компонент Pop-up Menu

В файл ресурсов будет добавлено определение контекстного меню CG_IDR_POPUP_MULTI_VIEW. Как видите, оно не отличается от меню, которые вы создавали или использовали ранее, за исключением того, что соответствующее меню верхнего уровня обозначено строкой _POPUP_ (рис. 3.6). Эта строка не будет отображаться в контекстном меню.

Если в приложении имеется несколько окон, то вы можете добавить к каждому окну свое контекстное меню. Для этого вставьте в проект компонент Pop-up Menu несколько раз, указывая в поле Add pop-up menu to различные классы окон. Конечно, каждое вставленное в проект меню может состоять из различного набора строк.

Например, если у вас многооконное приложение, то вы можете вставить компонент Pop-up Menu для главного окна приложения и для окна просмотра. Тогда если вы нажмете правую кнопку мыши в то время, когда указатель мыши находится в окне просмотра, то отображается одно контекстное меню, а если вы нажмете правую кнопку мыши когда ее указатель расположен вне окна просмотра - отображается другое контекстное меню.


Диалоговая панель


В ресурсах приложения определена диалоговая панель с идентификатором IDD_ABOUTBOX. Она содержит краткую информацию о приложении и отображается на экране, когда пользователь выбирает из меню Help строку About Multi:
//////////////////////////////////////////////////////////////
// Диалоговая панель
IDD_ABOUTBOX DIALOG DISCARDABLE  0, 0, 217, 55
CAPTION "About Multi"
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
FONT 8, "MS Sans Serif"
BEGIN
   ICON          IDR_MAINFRAME,IDC_STATIC,11,17,20,20
   LTEXT         "Multi Version 1.0",IDC_STATIC,40,10,119,8,
                  SS_NOPREFIX
   LTEXT         "Copyright \251 1996",IDC_STATIC,40,25,119,8
   DEFPUSHBUTTON "OK",IDOK,178,7,32,14,WS_GROUP
END

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


Диалоговая панель управления и MFC AppWizard


Если вам надо добавить панель управления к приложению, созданному с использованием средств MFC AppWizard, вы должны выполнить точно такие же действия, какие были нами описаны в предыдущих разделах. Сначала надо создать шаблон диалоговой панели, затем добавить к классу окна, в котором будет отображаться диалоговая панель управления, элемент класса CDialogBar, и наконец, создать диалоговую панель управления, вызвав соответствующий метод Create.
Наибольший интерес представляет использование ClassWizard для добавления обработчиков сообщений от диалоговой панели управления. Откройте в редакторе ресурсов шаблон диалоговой панели управления и запустите ClassWizard.
ClassWizard обнаружит новый ресурс и предложит создать или выбрать для него управляющий класс (рис. 3.20). Сообщения от диалоговой панели управления передаются для обработки в ее родительское окно. Поэтому в качестве управляющего класса надо выбрать класс этого окна. Так как в большинстве случаев в качестве родительского окна для панелей управления, в том числе и для диалоговой панели управления, выступает главное окно приложения, надо выбрать класс CMainFrame.
Диалоговая панель управления и MFC AppWizard
Рис. 3.20. Диалоговая панель Adding a Class
Переведите переключатель диалоговой панели Adding a Class в положение Select an existing class и нажмите на кнопку OK. На экране появится диалоговая панель Select Class, содержащая список классов, определенных в приложении (рис. 3.21).
Диалоговая панель управления и MFC AppWizard
Рис. 3.21. Диалоговая панель Select Class
Выберите из списка Class list класс главного окна приложения CMainFrame и нажмите на кнопку Select. ClassWizard сообщит о том, что выбранный класс CMainFrame не относится к классам, которые предназначены для управления диалоговыми панелями (рис. 3.22). Для продолжения нажмите на кнопку Yes.
Диалоговая панель управления и MFC AppWizard
Рис. 3.22. Предупреждение
Теперь на экране появится окно ClassWizard, в котором вы можете назначить обработчики сообщений от диалоговой панели управления, также просто как для меню или панели управления toolbar.


Диалоговая панель управления


В том случае, если в панели управления приложения надо разместить много разнообразных органов управления, то значительно удобнее создать его на основе другого класса, имеющего название CDialogBar:
CDialogBar <- CControlBar <- CWnd <- CCmdTarget <- CObject
Этот класс, также как класс CToolBar, наследован от базового класса CControlBar. Класс CDialogBar позволяет создать диалоговую панель управления на основе шаблона диалоговой панели. В отличии от обычной диалоговой панели, диалоговая панель управления, созданная на основе класса CDialogBar, имеет все свойства панели управления. Она может быть присоединена к одной из границ окна или может отображаться в отдельном мини-окне.
Приложение может иметь несколько панелей управления, созданных на основе классов CToolBar и CDialogBar.


Для самостоятельного изучения


Классы библиотеки MFC, описанные нами, содержат большое количество различных методов, оставшихся неохваченными данной книгой. Мы не имеем возможности уделить им больше времени и места, поэтому предлагаем вам самостоятельно изучить остальные методы этих классов.
Для этого вы можете использовать документацию, поставляемую в электронном виде вместе с Microsoft Visual C++ или обратиться к библиотеке Microsoft Development Library. Библиотека Microsoft Development Library является незаменимым средством для разработчика приложений Microsoft Visual C++ и других пакетов Microsoft. В нее включена документация, книги, отдельные статьи, а также большое количество исходных текстов всевозможных приложений.
Наиболее свежую информацию вы можете получить через глобальную сеть Internet. В первую очередь обратите свое внимание на WWW и FTP сервера Microsoft, содержащие сведения обо всех продуктах этой компании, включая Microsoft Visual C++. На сервере Microsoft вы сможете найти ссылки на другие сервера сети, также посвященные проблемам программирования в среде Microsoft Visual C++ с использованием библиотеки классов MFC.


Добавление компонент Microsoft


Для добавления к вашему проекту новой компоненты, выберите соответствующую пиктограмму и нажмите кнопку Insert. Дальнейший процесс вставки компонента зависит от него самого. Наиболее общий сценарий выглядит следующим образом. Когда вы нажмете кнопку Insert, у вас запрашивается различная дополнительная информация о том, что вы желаете получить. Это могут быть какие-либо характеристики компонента, названия классов, файлов и т. д. Когда вы введете всю необходимую информацию, начнется модификация проекта. На этом этапе в проект могут быть добавлены новые ресурсы и классы, а в исходные тексты приложения могут быть автоматически внесены исправления.
Сейчас мы расскажем об использовании компонент Microsoft на примере компоненты Splash Screen. Далее, когда мы будем рассказывать о меню, панелях управления и панелях состояния мы также будем делать ссылки на Component Gallry.


Добавление компонентов в проект


Процедура использования Component Gallery для добавления разрабатываемому приложению новых возможностей предельно проста. Выберите из меню Insert строку Component. На экране появится диалоговая панель Component Gallery (рис. 2.1).
Она состоит из нескольких страниц, в которых отображаются различные пиктограммы. Именно эти пиктограммы и представляют компоненты, которые вы можете добавить к вашему приложению.
Количество страниц Component Gallery и набор компонент зависит от версии Visual C++ и постоянно расширяется. В Component Gallery можно включить компоненты, разработанные другими фирмами, например Blue Sky Software, VideoSoft. Более того, вы можете включить в Component Gallery собственные компоненты, разработанные вами. В простейшем случае, в качестве таких компонент могут выступать классы приложений, которые вы создали.
Добавление компонентов в проект
Рис. 2.1. Диалоговая панель Component Gallery


Документ


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


Дополнительные панели управления


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


Дополнительные возможности панели состояния


Если во время работы приложения выполняется какой-либо длительный процесс, например загрузка или сохранение документа, тогда в панели состояния можно вывести линейный индикатор progress bar. С ее помощью вы легко сможете показать ход данного процесса.
Многие офисные приложения, и сама среда Microsoft Visual C++, использует панель состояния подобным образом. Так, например, когда вы сохраняете документ в текстовом процессоре Microsoft Word, то в панели состояния отображается линейный индикатор progress bar, отражающий процесс сохранения документа.
Методика размещения полосы progress bar на панели состояния достаточно проста. В тот момент, когда потребуется вывести полосу progress bar, просто создайте ее, указав в качестве родительского окна панель состояния. Координаты линейного индикатора progress bar желательно выбрать таким образом, чтобы он отображался на месте одного из индикаторов. Предварительно вы можете убрать рамку с этого индикатора и заблокировать его так, чтобы в нем не отображался текст.


Дополнительные возможности панели управления


Панель управления, созданная на основе класса CToolBar, состоит из одних только кнопок и разделителей. Стандартные средства для отображения в ней других органов управления, таких как поле редактирование или список, отсутствуют.
Однако, вы все же можете вывести в панели toolbar другие органы управления. Так как панель управления является ни чем иным как дочерним окном, то вы можете самостоятельно разместить в нем другие органы управления.
Для этого предлагается использовать следующий метод.
¨ В том месте панели управления toolbar, где вы желаете разместить дополнительный орган управления, вставьте разделитель
¨ Сразу после создания панели управления, измените размер разделителя вместо которого надо вставить другой орган управления. Присвойте ему другой идентификатор
¨ Создаем на месте разделителя нужный вам орган управления. Указываем, для него в качестве родительского окна - идентификатор панели управления
В состав класса CToolBar входит метод SetButtonInfo. Этот метод позволяет изменить внешний вид панели управления. Используя метод SetButtonInfo, можно изменить идентификатор, изображение, режим работы и размер разделителей кнопок панели управления:
void
SetButtonInfo(
   int nIndex,
   UINT nID,
   UINT nStyle,
   int iImage
);
Параметр nIndex метода SetButtonInfo определяет индекс кнопки или разделителя. Остальные параметры задают новые характеристики для этой кнопки.
Параметр nID позволяет задать новый идентификатор кнопки или разделителя.
Параметр nStyle определяет режимы работы данной кнопки и может содержать комбинацию флагов, которые уже были нами представлены при описании метода SetButtonStyle класса CToolBar.
Последний параметр метода позволяет изменить изображение кнопки. Новое изображение берется из ресурса панели управления. При этом используется изображение с порядковым номером, заданным параметром iImage.
Если вы вызвали метод SetButtonInfo и указали ему через параметр nIndex индекс разделителя, то назначение параметра iImage изменяется. В этом случае параметр iImage будет определять новую ширину разделителя.

Перед тем как изменять характеристики кнопок панели управления, вы можете воспользоваться методом GetButtonStyle класса CToolBar, чтобы узнать текущие характеристики кнопки:

void

GetButtonInfo(

   int nIndex,

   UINT& nID,

   UINT& nStyle,

   int& iImage

) const;

В параметре nIndex надо указать индекс кнопки, о которой надо получить информацию. Через остальные три параметра методу GetButtonInfo передаются ссылки на переменные, в которые будут записаны характеристики кнопок.

Через параметр nID вы получите идентификатор кнопки, через параметр nStyle - режим ее работы, а через параметр iImage - индекс изображения кнопки в ресурсе панели управления.

Если метод GetButtonInfo вызывается для разделителя, то через параметр iImage вы получите не индекс изображения, а ширину разделителя в пикселах.

Когда вы создаете на панели управления дополнительный орган управления, например поле редактирования, вы должны указать координаты прямоугольной области которую он будет занимать. Для определения этих координат следует воспользоваться методом GetItemRect класса CToolBar:

virtual void GetItemRect(int nIndex, LPRECT lpRect);

Вызов метода GetItemRect заполняет структуру lpRect координатами прямоугольной области, занимаемой кнопкой или разделителем, с индексом nIndex.


Доводка приложения


Взгляните на ресурсы приложения Dater. Для этого откройте страницу ResourceView в окне Project Workspace.
Обратите внимание на шаблон диалоговой панели IDD_DATER_FORM (рис. 5.10). Этот шаблон используется окном просмотра, созданным на основе класса CRecordView. Окно просмотра содержит в себе органы управления, определенные в шаблоне диалоговой панели.
Доводка приложения
Рис. 5.10. Шаблон диалоговой панели IDD_DATER_FORM
Сразу после того, как MFC AppWizard создаст проект, в этом шаблоне будет размещен одна только текстовая строка TODO: Place form controls on this dialog, предлагающая вам разместить на ней органы управления.
Удалите с шаблона эту строку, а затем создайте на ней четыре текстовых редактора, по одному для каждого поля таблицы базы данных Address Pad. Присвойте им идентификаторы IDC_NAME, IDC_ADDRESS, IDC_PHONE и IDC_PRIORITY. Около текстовых редакторов поместите краткие строки описания - Name, e-Mail, Phone и Priority. Сохраните изменения в файле ресурсов. Доработанный шаблон диалоговой панели представлен на рисунке 5.11.
Теперь вы можете выполнить наиболее интересную операцию в создании приложения Dater - привязать при помощи MFC ClassWizard к полям шаблона диалоговой панели IDD_DATER_FORM переменные, представляющие различные поля таблицы базы данных.
Доводка приложения
Рис. 5.11. Доработанный шаблон диалоговой панели IDD_DATER_FORM
Запустите MFC ClassWizard. В окне MFC ClassWizard выберите из списка ClassName имя класса окна просмотра - CDaterView и откройте страницу Member Variables. На этой странице вы увидите список идентификаторов полей редактирования шаблона диалоговой панели IDD_DATER_FORM.
Выбериете один из идентификаторов и нажмите на кнопку Add Variable. На экране появится диалоговая панель Add Member Variable (рис. 5.12). В этой панели вы должны определить переменную, которая будет отображаться в поле с данным идентификатором. В списке Category отображается категория органа управления к которому вы добавляете переменную. Для полей редактирования из этого списка будет выбрана строка Value. В списске Variable type отображается тип переменной, выбранной в поле Member variable name. Нажмите кнопку OK.

Доводка приложения

Рис. 5.12. Диалоговая панель Add Member Variable

В нашем случае список Member variable name содержит строки, представляющие различные поля записи таблицы базы данных Address Pad. Выбирая остальные идентификаторы шаблона диалоговой панели IDD_DATER_FORM поставьте им в соответствие поля базы данных, как это показано на рисунке 5.13.

Доводка приложения

Рис. 5.13. Диалоговая панель MFC ClassWizard

Если у вас возникли проблемы во время добавления переменных к полям диалоговых панелей (список идентификаторов в панели MFC ClassWizard пуст), возможно вам надо будет изменить язык для диалоговой панели IDD_DATER_FORM.

Так например, если ваш компьютер настроен на работу с русским языком, диалоговая панель IDD_DATER_FORM также должна быть русской. Чтобы поменять язык, вызовите панель свойств для диалоговой панели IDD_DATER_FORM и выберите из списка Language строку Russian (рис. 5.14). Дополнительные сведения о выборе языка смотрите в разделе “Национальные ресурсы”.

Доводка приложения

Рис. 5.14. Свойства диалоговой панели IDD_DATER_FORM

Откройте для редактирования метод GetDefaultSQL класса CDaterSet:

CString CDaterSet::GetDefaultSQL()

{

   return _T("[TextBase].[txt]");

}

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

CString CDaterSet::GetDefaultSQL()

{

   return _T("[TextBase.txt]");

}

Все! Теперь можно построить проект и запустить полученное приложение. На экране откроется главное окно приложения Dater (рис. 5.15). В окне просмотра отображаются поля базы данных Address Pad. Вы можете просмотреть все записи базы, используя меню Record и панель управления приложения.

Доводка приложения

Рис. 5.15. Приложение Dater


Форма панели управления


Панель управления может иметь постоянную форму, которую пользователь не может изменить, или может быть динамически изменяемой. В последнем случае пользователь может менять форму панели управления с помощью мыши.
Можно изменить форму панели управления или нет, определяется методом Create класса CToolBar. Если при создании панели управления был установлен флаг CBRS_SIZE_DYNAMIC ее форму можно менять, а если был установлен флаг CBRS_SIZE_FIXED - нельзя.
В состав класса CToolBar входит метод SetButtonStyle. Этот метод позволяет определить режим работы кнопок панели управления, сгруппировать несколько кнопок вместе:
void SetButtonStyle(
   int nIndex,
   UINT nStyle
);
Параметр nIndex выбирает индекс кнопки или разделителя в панели управления, а параметр nStyle позволяет установить новый режим работы для выбранной кнопки или разделителя. Индекс кнопки или разделителя соответствует ее порядковому номеру в панели управления.
В качестве параметра nStyle можно указать комбинацию из следующих флагов:

Флаг
Режим кнопки или разделителя
TBBS_BUTTON
Стандартная кнопка
TBBS_SEPARATOR
Разделитель
TBBS_CHECKBOX
Переключатель
TBBS_GROUP
С данной кнопки начинается группа кнопок
TBBS_CHECKGROUP
С данной кнопки начинается группа переключателей
TBBS_WRAPPED
Этот флаг позволяет создать панель управления, в которой кнопки расположены в несколько рядов. Установите этот флаг для самых последних кнопок в каждом ряду. Кнопка, следующая за кнопкой с установленным флагом TBBS_WRAPPED, отображается в новом ряду

¨ Заметим, что стиль TBBS_WRAPPED не описан в документации Microsoft Visual C++, но активно используется в примерах приложений и работает как положено
Перед тем, как изменить режим работы кнопки или указать группу кнопок, рекомендуется определить текущий режим кнопки. Для этого следует воспользоваться методом GetButtonStyle класса CToolBar:
UINT GetButtonStyle(int nIndex) const;
Метод возвращает комбинацию флагов, определяющих режим работы кнопки с индексом nIndex. Мы уже рассматривали эти флаги при описании метода SetButtonStyle класса CToolBar.
Вы можете определить индекс (порядковый номер) любой кнопки панели управления, если знаете ее идентификатор. Для этого предназначен метод CommandToIndex класса CToolBar. Он возвращает индекс кнопки, имеющей идентификатор nIDFind. Если вы укажите идентификатор несуществующей кнопки, тогда метод CommandToIndex возвращает значение -1:
int CommandToIndex(UINT nIDFind);
Обратную задачу выполняет метод GetItemID класса CToolBar. Этот метод возвращает идентификатор кнопки с индексом nIndex. Если в качестве параметра nIndex указать индекс разделителя, тогда метод GetItemID возвращает идентификатор ID_SEPARATOR:
UINT GetItemID(int nIndex) const;


Главное окно многооконного приложения


Большинство командных сообщений передаются главному окну приложения. Если приложение имеет многооконный интерфейс, то главное окно приложения представляет объект класса CMDIFrameWnd или класса, наследованного от базового класса CMDIFrameWnd.
Получив сообщение, главное окно приложения сначала предоставляет возможность обработать сообщение активному дочернему окну MDI. Окна MDI представляют собой объекты класса CMDIChildWnd или класса наследованного от него.
Только если окно MDI не может обработать сообщение, будет просмотрена таблица сообщений класса главного окна приложения. Следует сразу заметить, что в свою очередь, окно MDI передает сообщения другим объектам (см. ниже).
Если главное окно приложения не может обработать сообщение, оно передается объекту главного класса приложения. Напомним, что главный класс приложения наследуется от базового класса CWinApp и приложение имеет только один объект этого класса.


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


Главный класс приложения CMultiApp управляет работой всего приложения. Методы этого класса выполняют инициализацию приложения, обработку цикла сообщений и вызываются при завершении приложения. Через окно Project Workspace можно просмотреть названия методов класса и загрузить их в текстовый редактор (рис. 1.8).
Главный класс приложения
Рис. 1.8. Окно Project Workspace, класс CMultiApp
Класс CMultiApp определен в файле Multi.h следующим образом:
//////////////////////////////////////////////////////////////// Класс CMultiApp
class CMultiApp : public CWinApp
{
public:
   CMultiApp();
// Overrides
   //{{AFX_VIRTUAL(CMultiApp)
public:
   virtual BOOL InitInstance();
   //}}AFX_VIRTUAL
// Implementation
   //{{AFX_MSG(CMultiApp)
   afx_msg void OnAppAbout();
   //}}AFX_MSG
   // Класс CMultiApp может получать сообщения
   DECLARE_MESSAGE_MAP()
};
В приложении определен только один объект базового класса приложения theApp. Этот объект должен быть один вне зависимости от того, какой интерфейс имеет приложение - однооконный, многооконный или основанный на диалоговой панели:
CMultiApp theApp;


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


Класс CDaterApp приложения Dater не содержит в себе ничего особенного и практически не отличается от соответствующего класса однооконного приложения Single, созданного MFC AppWizard и не работающего с базами данных:
//////////////////////////////////////////////////////////////
// Класс CDaterApp
/
class CDaterApp : public CWinApp
{
public:
   CDaterApp();
// Overrides
   //{{AFX_VIRTUAL(CDaterApp)
   public:
   virtual BOOL InitInstance();
   //}}AFX_VIRTUAL
// Implementation
   //{{AFX_MSG(CDaterApp)
   afx_msg void OnAppAbout();
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};
Класс CDaterApp содержит конструктор, а также методы InitInstance и OnAppAbout.


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


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


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


Главный класс приложения CMultiBarApp наследуется от базового класса CWinApp. Объект MyMultiBarApp класса CMultiBarApp объявлен как глобальный и создается сразу после запуска приложения.
В класс CMultiBarApp входит только метод InitInstance. Он создает главное окно приложения, представленное классом CMultiBarWindow, наследованным от класса CFrameWnd.
В класс CMultiBarWindow входят три объекта - m_wndPlayerBar, m_wndStyleBar и m_wndExtendedBar, представляющие панели управления Player, Style и Extended:
class CMultiBarWindow : public CFrameWnd
{
// Определяем панели управления
protected:
   // Панель управления Player
   CToolBar       m_wndPlayerBar;
   // Панель управления Style
   CToolBar       m_wndStyleBar;
   // Панель управления Extended
   CExtendedBar   m_wndExtendedBar;
   // ...
}
Панели управления Player и Style представлены объектами класса CToolBar. Панель управления Extended представлена объектом m_wndExtendedBar класса CExtendedBar. Класс CExtendedBar определен в нашем приложении. Он наследуется от базового класса CToolBar и дополняет его двумя элементами m_edit и m_combo_box. Эти элементы представляют текстовый редактор и список combo-box, которые будут размещены на панели управления:
class CExtendedBar : public CToolBar
{
public:
   // Дополнительные органы управления панели Extended
   CEdit m_edit;          // текстовый редактор
   CComboBox m_combo_box; // список с текстовым редактором
};
В таблице сообщений класса CMultiBarWindow, находится макрокоманда ON_WM_CREATE. Поэтому в процессе создания главного окна приложения вызывается метод OnCreate. Мы используем метод OnCreate для создания сразу трех панелей управления. Рассмотрим метод OnCreate более подробно.


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


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


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


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

Метод
Описание
m_nID
Идентификатор объекта, для которого вызвано сообщение
m_nIndex
Индекс объекта, для которого вызвано сообщение
m_pMenu
Указатель на меню. Если команда обновления передана не от меню, m_pOther содержит значение NULL
m_pOther
Указатель на панель состояния или панель управления для объекта которой выполняется обновление. Если команда обновления передана от меню, m_pOther содержит значение NULL



Как хранится ресурс, описывающий панели управления


Каждый ресурс, представляющий панель управления в редакторе ресурсов Microsoft Visual C++, выступает как единое целое. Загрузив в редактор панель управления вы можете менять внешний вид кнопок, задавать их идентификаторы и строки описания, не открывая других ресурсов или дополнительных файлов.
Если вы просмотрите исходный текст файла ресурсов приложения, то обнаружите, что на самом деле ресурс панели управления состоит из двух, а если быть точнее, то из трех частей.
Первая часть описывает панель управления:
//////////////////////////////////////////////////////////////
// Панель управления toolbar
IDR_MAINFRAME TOOLBAR DISCARDABLE  16, 15
BEGIN
    BUTTON      ID_FILE_NEW
    BUTTON      ID_FILE_OPEN
    BUTTON      ID_FILE_SAVE
    SEPARATOR
    BUTTON      ID_EDIT_CUT
    BUTTON      ID_EDIT_COPY
    BUTTON      ID_EDIT_PASTE
    SEPARATOR
    BUTTON      ID_FILE_PRINT
    BUTTON      ID_APP_ABOUT
    BUTTON      ID_TOOL_EXIT
END
В нашем примере эта панель имеет идентификатор IDR_MAINFRAME. После идентификатора следует ключевое слово TOOLBAR, а затем дополнительные параметры, описывающие режим использования ресурса и два числа, определяющие размер кнопок панели.
Затем в блоке BEGIN - END идет описание каждой кнопки панели. После ключевого слова BUTTON, представляющего кнопку следует ее идентификатор. Между описаниями кнопок могут располагаться ключевые слова SEPARATOR.
В нашем маленьком примере присутствуют два таких слова. Они означают, что между кнопками, разделенными строкой SEPARATOR, увеличено расстояние. За счет увеличения расстояния между отдельными кнопками достигается эффект разделения кнопок панели управления на три отдельные группы. Это, в свою очередь, улучшает восприятие приложения и делает его более удобным для пользователя.
Как видите, в первой части ресурса панели управления отсутствуют сами изображения кнопок. Они располагаются отдельно и представляют вторую часть ресурса. Все кнопки представлены одним изображением bitmap, имеющим тот же идентификатор, что и соответствующий ресурс TOOLBAR:

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

// Bitmap

IDR_MAINFRAME   BITMAP   MOVEABLE PURE   "res\\Toolbar.bmp"

Изображение кнопок нашей панели управления IDR_MAINFRAME хранится в файле Toolbar.bmp (рис. 3.9). Файл записан в каталоге RES основного каталога проекта. Все кнопки панели управления расположены последовательно, одна за другой. Порядок, в котором они расположены, соответствует порядку, в котором кнопки описаны в ресурсе TOOLBAR, и порядку в котором они будут отображаться на экране во время работы приложения.

Как хранится ресурс, описывающий панели управления

Рис. 3.9. Файл Toolbar.bmp с изображением кнопок панели управления

Между отдельными изображениями кнопок отсутствуют промежутки, даже если в описании ресурса TOOLBAR присутствуют разделители SEPARATOR.

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

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

STRINGTABLE DISCARDABLE

BEGIN

    ID_FILE_NEW    "Create a new document\nNew"

    ID_FILE_OPEN   "Open an existing document\nOpen"

    ID_FILE_SAVE   "Save the active document\nSave"

    ID_FILE_PRINT  "Print the active document\nPrint"

    ID_EDIT_COPY   "Copy the selection and put it on the

                    Clipboard\nCopy"

    ID_EDIT_CUT    "Cut the selection and put it on the

                    Clipboard\nCut"

    ID_EDIT_PASTE  "Insert Clipboard contents\nPaste"

    ID_APP_ABOUT   "Display program information, version

                   number and copyright\nAbout"

END

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


Как работает приложение DialogBar


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


Как работает приложение MultiMenu


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


Как работает приложение Status


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


Как создать панель состояния


Процесс создания панели состояния во многом схож с процессом создания панелей управления.
Сначала надо создать объект для управления панелью состояния. Обычно для этого включают объект класса CStatusBar непосредственно в класс окна приложения, в котором будет размещена панель состояния.
Конструктор класса CStatusBar не имеет параметров и выглядит так:
CStatusBar();
В некоторых случаях, вместо использования класса CStatusBar, от него предварительно наследуют дополнительный класс. В этом случае, для создания панели состояния используют именно этот класс.
Следующим шагом является создание самой панели состояния. Панель состояния создается вызовом метода Create класса CStatusBar:
BOOL Create(
   CWnd* pParentWnd,
   DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_BOTTOM,
   UINT nID = AFX_IDW_STATUS_BAR
);
Через первый параметр метода pParentWnd вы должны указать окно, для которого создается панель состояния.
Второй параметр dwStyle позволяет задать характеристики панели состояния, в том числе ее расположение внутри окна. Панель состояния является дочерним окном, поэтому в параметре dwStyle надо указать атрибут WS_CHILD. Если вы не укажите атрибут WS_CHILD, ничего страшного тоже не случится - этот атрибут установится автоматически во время вызова метода Create. А вот атрибут WS_VISIBLE более важен. Если его опустить, то панель состояния хотя и будет создана, но на экране не появится.
Вы можете разместить панель состояния либо в верху, либо внизу окна. По умолчанию панель состояния размещается в нижней части окна (используется атрибут CBRS_BOTTOM). Чтобы панель состояния была размещена вверху окна, укажите в качестве параметра dwStyle атрибут CBRS_TOP. Если вы используете параметр dwStyle, надо обязательно указать либо атрибут CBRS_BOTTOM, либо атрибут CBRS_TOP.
Последний параметр метода nID определяет идентификатор дочернего окна панели состояния. По умолчанию используется идентификатор AFX_IDW_STATUS_BAR.
¨ Приложения, созданные MFC AppWizard, имеют меню View, содержащее строки Toolbar и Status bar. Строка Status bar с идентификатором ID_VIEW_STATUS_BAR позволяет закрывать и снова открывать панель состояния. Обработка стандартного командного сообщения ID_VIEW_STATUS_BAR выполняется методом OnUpdateControlBarMenu класса CFrameWnd. Метод OnUpdateControlBarMenu может управлять отображением панели управления только в том случае, если она имеет идентификатор AFX_IDW_STATUS_BAR. Более подробно о методе OnUpdateControlBarMenu можно прочитать в разделе “Недокументированные возможности класса CMainFrame”.

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

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

Метод SetIndicators загружает строковые ресурсы, соответствующие идентификаторам индикаторов, и размещает их на панели состояния:

BOOL SetIndicators(

   const UINT* lpIDArray,

   int nIDCount

);

Через параметр lpIDArray, методу SetIndicators, надо передать указатель на массив идентификаторов панели состояния. Общее количество индикаторов панели состояния, определенных в массиве lpIDArray, задается параметром nIDCount.

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

nIDCount = sizeof(indicators)/sizeof(UINT);

Переменная indicators представляет массив идентификаторов панели состояния.

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

Когда индикаторы созданы, вы можете изменить некоторые их характеристики, воспользовавшись методом SetPaneInfo:

void

SetPaneInfo(

   int nIndex,

   UINT nID,

   UINT nStyle,

   int cxWidth

);

Параметр nIndex определяет порядковый номер индикатора в панели состояния или, другими словами, его индекс. Характеристики этого индикатора будут меняться.

Метод SetPaneInfo позволяет изменить расположение индикаторов на панели, или даже заменить существующий индикатор новым индикатором. Для этого можно указать новый идентификатор через параметр nID. Если вы не знаете идентификатор индикатора, тогда можете определить его с помощью метода GetItemID. Метод GetItemID возвращает идентификатор индикатора с индексом nIndex:



UINT GetItemID(int nIndex) const;

Обратная операция выполняется при помощи метода CommandToIndex. Метод CommandToIndex возвращает индекс индикатора, имеющего идентификатор nIDFind. Если идентификатор указан неверно, возвращается значение -1:

int CommandToIndex(UINT nIDFind) const;

После короткого отступления вернемся к рассказу о параметрах метода SetPaneInfo.

Внешний вид идентификатора, заданного параметрами nIndex и nID, определяется параметрами nStyle и cxWidth. В качестве nStyle можно указать один или несколько атрибутов, объединенных логической операцией ИЛИ.

Атрибут

Описание

SBPS_NOBORDERS

Убрать трехмерную рамку вокруг индикатора

SBPS_POPOUT

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

SBPS_DISABLED

Если указать этот атрибут, то в индикаторе не будет отображаться текст из соответствующего строкового ресурса

SBPS_STRETCH

Один из индикаторов панели состояния может менять свой размер в зависимости от размера окна. Атрибут SBPS_STRETCH предназначен для выбора этого индикатора

SBPS_NORMAL

Стандартный индикатор

Параметр cxWidth определяет ширину индикатора. Когда вы создаете панель состояния и устанавливаете индикаторы, вызывая метод SetIndicators, размер индикаторов определяется автоматически исходя из ширины текста индикатора.

¨ Если первый элемент массива идентификаторов, переданного методу SetIndicators, содержит константу ID_SEPARATOR, то для первого индикатора панели состояния по умолчанию устанавливаются атрибуты SBPS_NOBORDERS и SBPS_STRETCH

Узнать текущие характеристики индикатора можно при помощи метода GetPaneInfo. Он позволяет определить идентификатор, стиль и ширину индикатора с индексом nIndex:

void

GetPaneInfo(

   int nIndex,

   UINT& nID,

   UINT& nStyle,

   int& cxWidth

) const;

Идентификатор записывается в переменную, ссылка на которую передается через параметр nID, набор атрибутов, определяющих внешний вид индикатора - в переменную nStyle, а ширина - в переменную cxWidth.

Если вам требуется определить или установить только стиль индикатора в панели управления, то вместо методов GetPaneInfo и SetPaneInfo лучше использовать два других метода класса CStatusBar - метод GetPaneStyle и метод SetPaneStyle.


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


Во всех примерах, которые мы привели выше, для создания панели управления мы вызывали метод Create класса CToolBar во время создания окна приложения. В принципе, панель управления можно создать и позже, когда окно уже отображается на экране.
Возьмите приложение Bar. Добавьте к нему меню Tools, состоящее из одной строки Show Bar. Определите метод OnShowBar, который будет вызываться для обработки команд от этой строки меню. Не забудьте вставить в таблицу сообщений класса окна соответствующую макрокоманду.
Перенесите код для создания панели управления из метода OnCreate в метод OnShowBar. Теперь панель управления будет создаваться только после того, как вы выберите из меню Tools строку Show Bar.
Постройте проект и запустите приложение. Вы заметите, что панель управления почему-то возникает не сразу после выбора строки Show Bar. Чтобы панель управления появилась, необходимо еще изменить размер окна приложения.
Оказывается, метод Create класса CToolBar устанавливает нулевой размер окна панели управления. Настоящий размер панели управления выбирается позже, в зависимости от ее характеристик, а также размеров и характеристик родительского окна.
Чтобы установить правильные размеры и расположение панели управления следует вызвать метод RecalcLayout. Метод RecalcLayout входит в класс CFrameWnd и вызывается автоматически, если вы используете методы CFrameWnd::ShowControlBar, CFrameWnd::OnIdleUpdateCmdUI, CFrameWnd::OnSize, CFrameWnd::FloatControlBar, CMDIChildWnd::Create, а также некоторые другие.


Как связаться с авторами


Авторы имеют почтовый адрес в сети GlasNet. Все свои замечания и предложения по содержанию книг серий "Библиотека системного программиста", а также "Персональный компьютер - шаг за шагом" вы можете присылать нам по следующему адресу:
:   frolov@glas.apc.org
Наш почтовый адрес доступен не только пользователям сети GlasNet. Абоненты других компьютерных сетей также могут передавать нам сообщения. Ниже мы приводим наш адрес в различных сетях:

Глобальная сеть
Наш адрес
CompuServe
>internet:frolov@glas.apc.org
GlasNet
frolov@glas.apc.org
Internet
frolov@glas.apc.org
Relcom
frolov@glas.apc.org
UUCP
uunet!cdp!glas!frolov

Вы также можете присылать свои пожелания почтой по адресу:
*        Издательский отдел АО "ДИАЛОГ-МИФИ".

Индекс 115409, город Москва, улица Москворечье,

дом 31, корпус 2.
Приносим свои извинения за то, что не можем ответить на каждое письмо. Мы также не занимаемся рассылкой книг, дискет и исходных текстов к нашим книгам. По этому вопросу обращайтесь непосредственно в издательство “Диалог-МИФИ”.


Как устроен компонент Splash Screen


Откройте окно просмотра проекта Project Workspace и обратите внимание на произошедшие в нем изменения. На странице ClassView появился новый класс CSplashWnd, включающий несколько методов и элементов данных. В главном классе приложения появился новый метод PreTranslateMessage.
На странице ResourceView к ресурсам приложения добавился ресурс Bitmap с идентификатором IDB_SPLASH. На странице FileView также произошли изменения. В ней добавился файл Splash.cpp, а в папке зависимых файлов Dependencies появились имена файлов Splash.h и Splash16.bmp.
Число 16 на конце имени файла Splash16.bmp означает, что в изображении используется шестнадцать цветов. К сожалению, компонент Splash Screen не работает с изображениями, имеющими большее количество цветов.
Кроме перечисленных изменений, хорошо заметных из окна просмотра проекта Project Workspace, имеют место и изменения в существующих методах главного класса приложения. Такие изменения обнаружить значительно труднее, особенно если программный код приложения слишком велик и данный компонент не имеет полной документации.
Для облегчения поиска фрагментов измененного кода они обозначаются специальными комментариями следующего вида:
// CG:
Символы CG являются сокращением от Component Gallry. После символов CG: как правило следует словесное описание добавленного программного кода. Так как Microsoft Visual C++ не умеет вставлять в текст программы русскоязычные комментарии, мы полностью заменили текст комментариев и добавили собственное описание программного кода компоненты Splash Screen.


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


Обратите внимание на первые строки файла Bar.cpp. Они содержат директивы #include, которые включают в исходный текст два файла - afxwin.h и afxext.h:
// Включаемый файл для MFC
#include
#include
// Включаемый файл для ресурсов приложения
#include "resource.h"
С файлом afxwin.h вы уже знакомы. В этом файле определены классы, методы, константы и другие структуры для библиотеки классов MFC. Кроме того, файл afxwin.h автоматически подключает другой включаемый файл - windows.h.
Файл afxext.h необходим, так как в нем описываются классы, используемые для создания панели управления, в том числе сам класс панели управления - CToolBar.
Кроме системных файлов afxwin.h и afxext.h, в исходный текст файла Bar.cpp включен файл resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++ и содержит определение различных идентификаторов приложения.
В приложении Bar определены два класса CBarApp и CBarWindow. Главный класс приложения CBarApp наследуется от базового класса CWinApp. Объект MyBarApp класса CBarApp объявлен как глобальный и создается сразу после запуска приложения.
В класс CBarApp входит единственный метод InitInstance. Метод InitInstance создает главное окно приложения, представленное классом CBarWindow, наследованным от класса CFrameWnd.
Взаимодействие главного класса приложения и главного класса окна приложения мы описывали в первой книге серии “Библиотека системного программиста”, посвященной программированию в Microsoft Visual C++ с использованием библиотеки классов MFC.
Обратите внимание, что в состав класса CBarWindow входит объект m_wndToolBar класса CToolBar. Именно этот объект и будет представлять панель управления. Включение объекта класса CToolBar в состав класса главного окна приложения вполне закономерно, так как панель управления, точно также как и меню, является атрибутом именно главного окна приложения:
class CBarWindow : public CFrameWnd
{
protected:
   CToolBar    m_wndToolBar;

// ...

}

Объект m_wndToolBar, представляющий панель управления, объявлен как protected. Доступ к нему открыт только для методов класса CBarWindow. Если надо открыть доступ к панели управления из вне класса CBarWindow,  тогда ключевое слово protected надо заменить на public.

В таблице сообщений класса CBarWindow находится макрокоманда ON_WM_CREATE. Поэтому в процессе создания главного окна приложения вызывается метод OnCreate. Мы используем метод OnCreate для создания панели управления.

Метод OnCreate класса CBarWindow сначала вызывает метод OnCreate базового класса CFrameWnd. Затем создается панель управления toolbar. Для этого вызывается метод Create объекта m_wndToolBar. В качестве указателя на родительское окно панели управления методу Create передается ключевое слово this, указывающее на текущий объект, то есть на главное окно приложения.

После создания панели управления вызывается метод LoadToolBar, загружающий панель управления с идентификатором IDR_MAINFRAME. Если вы запустите приложение под отладкой, то в случае возникновения ошибок при создании панели управления вызывается макрокоманда TRACE0. Она отображает сообщение об ошибке на странице Debug панели Output. Панель Output обычно располагается в нижней части окна Microsoft Visual C++.

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

Чтобы обработать командные сообщения от панели управления, в таблицу сообщений класса CBarWindow включены макрокоманды ON_COMMAND. В приложении Bar мы обрабатываем командные сообщения только от трех кнопок панели управления - ID_FILE_OPEN, ID_FILE_SAVE и ID_TOOL_EXIT:

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

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

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

BEGIN_MESSAGE_MAP(CBarWindow, CFrameWnd)

   // Макрокоманда необходима для перехвата сообщения

   // WM_CREATE. Для обработки сообщения вызывается

   // метод OnCreate

   ON_WM_CREATE()

   // Обработка сообщений от кнопок панели управления

   ON_COMMAND(ID_FILE_OPEN, CBarWindow::BarCommand)

   ON_COMMAND(ID_FILE_SAVE, CBarWindow::BarCommand)

   ON_COMMAND(ID_TOOL_EXIT, CBarWindow::BarCommand)

END_MESSAGE_MAP()

Чтобы не усложнять исходный текст приложения мы вызываем для обработки командных сообщений от кнопок панели управления один и тот же метод BarCommand. Метод BarCommand входит в состав класса CBarWindow. Единственное, что делает метод BarCommand - это выводит на экран сообщение, что данная команда не реализована.

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


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


Список всех классов, входящих в проект Dater, а также их методов можно просмотреть в окне Project Workspace на странице ClassView (рис. 5.16).
Как устроено приложение Dater
Рис. 5.16. Окно Project Workspace, страница ClassView
В приложение Dater входят следующие классы.

Класс
Базовый класс
Назначение
CAboutDlg
CDialog
Управляет информационной диалоговой панелью About
CDaterApp
CWinApp
Главный класс приложения
CDaterDoc
CDocument
Представлляет документ приложения
CDaterSet
CRecordset
Представлляет запись таблицы базы данных
CDaterView
CRecordView
Управляет окном просмотра приложения. В этом окне отображаются записи таблицы базы данных
CMainFrame
CFrameWnd
Главное окно приложения



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


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


Класс CCmdUI


В MFC реализован специальный механизм для обновления таких объектов интерфейса пользователя как меню, панели управления и панели состояния. Этот механизм предусматривает передачу приложению команд обновления пользовательского интерфейса (update command user interface). Для обработки этих команд предназначена макрокоманда ON_UPDATE_COMMAND_UI, размещаемая в таблице сообщений класса.
Для каждой строки меню, для каждой кнопки панели управления и для каждого индикатора панели состояния передается отдельное сообщение.
Когда передаются команды обновления интерфейса пользователя? Многое зависит от самого обновляемого объекта.
В случае меню, команды обновления передаются в момент, когда пользователь открывает меню. Для каждой строки меню посылается отдельное сообщение.
Для кнопок панели управления и индикаторов панели состояния команды обновления передаются в период “бездействия” приложения, когда очередь сообщений приложения пуста.


Класс CMenu


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


Класс CMultiView


Все изменения в программном коде приложения Multi, выполненные при вставке в него компонента Pop-up Menu, происходят только в классе окна, к которому добавляется контекстное меню. Компонент Pop-up Menu добавляет макрокоманду ON_WM_CONTEXTMENU к таблице сообщений класса CMultiView, а также встсавляет в класс CMultiView методы OnContextMenu и PreTranslateMessage.
В определении класса CMultiView добавляется только метод-обработчик OnContextMenu. Все остальные элементы класса не изменяются. После добавления к проекту Pop-up Menu класс CMultiView, определенный в файле MultiView.h будет выглядеть следующим образом:
class CMultiView : public CView
{
protected:
   // CG: Метод OnContextMenu добавлен компонентом Pop-up Menu
   afx_msg void OnContextMenu(CWnd*, CPoint point);
   CMultiView();
   DECLARE_DYNCREATE(CMultiView)
// Attributes
public:
   virtual BOOL PreTranslateMessage(MSG* pMsg);
   CMultiDoc* GetDocument();
// Operations
public:
// Overrides
   //{{AFX_VIRTUAL(CMultiView)
public:
   virtual void OnDraw(CDC* pDC);
   virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
   virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
   virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
   virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
   //}}AFX_VIRTUAL
// Implementation
public:
   virtual ~CMultiView();
#ifdef _DEBUG
   virtual void AssertValid() const;
   virtual void Dump(CDumpContext& dc) const;
#endif
protected:
   //{{AFX_MSG(CMultiView)
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};
Остальные классы приложения остаются без изменения.


Класс CSplashWnd


Практически весь программный код, отвечающий за отображение заставки Splash Screen, содержится в классе CSplashWnd. Этот класс включается в состав проекта и вы можете просмотреть его содержимое в окне проекта Project Workspace на странице ClassView (рис. 2.4).
Класс CSplashWnd
Рис. 2.4. Окно Project Workspace, класс CSplashWnd
Определение класса CSplashWnd находится в файле Splash.h. Мы привели его полностью в листинге 2.1.
Класс CSplashWnd создан на основе базового класса CWnd. Он включает в себя ряд методов и несколько элементов данных. Класс CSplashWnd может обрабатывать сообщения, поэтому для него в  файле Splash.h определена макрокоманда DECLARE_MESSAGE_MAP, а в файле реализации - Splash.cpp - таблица сообщений класса.
Листинг 2.1. Файл Splash.h
// CG: This file was added by the Splash Screen component.
#ifndef _SPLASH_SCRN_
#define _SPLASH_SCRN_
// Splash.h : header file
//
//////////////////////////////////////////////////////////////
//   Splash Screen class
class CSplashWnd : public CWnd
{
// Construction
protected:
   CSplashWnd();
// Attributes:
public:
   CBitmap m_bitmap;
// Operations
public:
   static void EnableSplashScreen(BOOL bEnable = TRUE);
   static void ShowSplashScreen(CWnd* pParentWnd = NULL);
   static void PreTranslateAppMessage(MSG* pMsg);
// Overrides
   // ClassWizard generated virtual function overrides
   //{{AFX_VIRTUAL(CSplashWnd)
   //}}AFX_VIRTUAL
// Implementation
public:
   ~CSplashWnd();
   virtual void PostNcDestroy();
protected:
   BOOL Create(CWnd* pParentWnd = NULL);
   void HideSplashScreen();
   static BOOL c_bShowSplashWnd;
   static CSplashWnd* c_pSplashWnd;
// Generated message map functions
protected:
   //{{AFX_MSG(CSplashWnd)
   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
   afx_msg void OnPaint();
   afx_msg void OnTimer(UINT nIDEvent);
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};
#endif
Методы класса CSplashWnd определены в файле реализации - Splash.cpp. Этот файл также добавляется к проекту Multi. Мы привели полный исходный текст этого файла в листинге 2.2.

Листинг 2.2. Файл Splash.cpp

// CG: Файл Splash.cpp добавляется в проект во время вставки

// компонента Splash Screen и содержит реализацию класса

// CSplashWnd

#include "stdafx.h"

#include "resource.h"

#include "Splash.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char BASED_CODE THIS_FILE[] = __FILE__;

#endif

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

//   Splash Screen class

BOOL CSplashWnd::c_bShowSplashWnd;

CSplashWnd* CSplashWnd::c_pSplashWnd;

CSplashWnd::CSplashWnd()

{

}

CSplashWnd::~CSplashWnd()

{

   // Clear the static window pointer.

   ASSERT(c_pSplashWnd == this);

   c_pSplashWnd = NULL;

}

BEGIN_MESSAGE_MAP(CSplashWnd, CWnd)

   //{{AFX_MSG_MAP(CSplashWnd)

   ON_WM_CREATE()

   ON_WM_PAINT()

   ON_WM_TIMER()

   //}}AFX_MSG_MAP

END_MESSAGE_MAP()

void CSplashWnd::EnableSplashScreen(BOOL bEnable /*= TRUE*/)

{

   c_bShowSplashWnd = bEnable;

}

void CSplashWnd::ShowSplashScreen(CWnd* pParentWnd /*= NULL*/)

{

   if (!c_bShowSplashWnd c_pSplashWnd != NULL)

      return;

   // Allocate a new splash screen, and create the window.

   c_pSplashWnd = new CSplashWnd;

   if (!c_pSplashWnd->Create(pParentWnd))

      delete c_pSplashWnd;

   else

      c_pSplashWnd->UpdateWindow();

}

BOOL CSplashWnd::PreTranslateAppMessage(MSG* pMsg)

{

   if (c_pSplashWnd == NULL)

      return FALSE;

   // If we get a keyboard or mouse message, hide the splash

   // screen.

   if (pMsg->message == WM_KEYDOWN

       pMsg->message == WM_SYSKEYDOWN

       pMsg->message == WM_LBUTTONDOWN

       pMsg->message == WM_RBUTTONDOWN

       pMsg->message == WM_MBUTTONDOWN

       pMsg->message == WM_NCLBUTTONDOWN

       pMsg->message == WM_NCRBUTTONDOWN

       pMsg->message == WM_NCMBUTTONDOWN)

   {

      c_pSplashWnd->HideSplashScreen();

      return TRUE;   // message handled here



   }

   return FALSE;   // message not handled

}

BOOL CSplashWnd::Create(CWnd* pParentWnd /*= NULL*/)

{

   if (!m_bitmap.LoadBitmap(IDB_SPLASH))

      return FALSE;

   BITMAP bm;

   m_bitmap.GetBitmap(&bm);

   return CreateEx(0, AfxRegisterWndClass(0,

               AfxGetApp()->LoadStandardCursor(IDC_ARROW)),

               NULL, WS_POPUP | WS_VISIBLE, 0, 0, bm.bmWidth,

               bm.bmHeight, pParentWnd->GetSafeHwnd(), NULL);

}

void CSplashWnd::HideSplashScreen()

{

   // Destroy the window, and update the mainframe.

   DestroyWindow();

   AfxGetMainWnd()->UpdateWindow();

}

void CSplashWnd::PostNcDestroy()

{

   // Free the C++ class.

   delete this;

}

int CSplashWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

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

      return -1;

   // Center the window.

   CenterWindow();

   // Set a timer to destroy the splash screen.

   SetTimer(1, 750, NULL);

   return 0;

}

void CSplashWnd::OnPaint()

{

   CPaintDC dc(this);

   CDC dcImage;

   if (!dcImage.CreateCompatibleDC(&dc))

      return;

   BITMAP bm;

   m_bitmap.GetBitmap(&bm);

   // Paint the image.

   CBitmap* pOldBitmap = dcImage.SelectObject(&m_bitmap);

   dc.BitBlt(0, 0, bm.bmWidth, bm.bmHeight,

               &dcImage, 0, 0, SRCCOPY);

   dcImage.SelectObject(pOldBitmap);

}

void CSplashWnd::OnTimer(UINT nIDEvent)

{

   // Destroy the splash screen window.

   HideSplashScreen();

}

Вы всегда можете получить файл Splash.cpp следуя инструкциям в начале раздела “Заставка для приложения”. Теперь опишем отдельные методы класса.


Класс дочернего окна MDI


Многооконное приложение строится с использованием большего числа классов, чем однооконное приложение. Помимо классов главного окна приложения и классов окна просмотра документа, в нем определен еще один класс, непосредственно связанный с отображением дочерних окон MDI. Этот класс называется CChildFrame и он наследуется от базового класса CMDIChildWnd, определенного в библиотеке MFC:
class CChildFrame : public CMDIChildWnd
{
   DECLARE_DYNCREATE(CChildFrame)
public:
   CChildFrame();
// Attributes
public:
// Operations
public:
// Overrides
   //{{AFX_VIRTUAL(CChildFrame)
   virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
   //}}AFX_VIRTUAL
// Implementation
public:
   virtual ~CChildFrame();
#ifdef _DEBUG
   virtual void AssertValid() const;
   virtual void Dump(CDumpContext& dc) const;
#endif
protected:
   //{{AFX_MSG(CChildFrame)
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};
Элементы класса CChildFrame вы можете просмотреть в окне Project Workspace на странице ClassView (рис. 1.11).
Класс дочернего окна MDI
Рис. 1.11. Окно Project Workspace, класс CChildFrame
Объекты класса CChildFrame представляют дочерние окна MDI главного окна приложения. Внутри этих окон отображаются окна просмотра документа.


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


Класс документа приложения CDaterDoc представляет документ, с которым работает приложение. В него входит элемент m_daterSet класса CDaterSet, также определенного в нашем приложении, который представляет запись базы данных.
Кроме этого элемента в классе CDaterDoc определены конструктор, деструктор, метод OnNewDocument, а также методы AssertValid и Dump:
class CDaterDoc : public CDocument
{
protected:
   CDaterDoc();
   DECLARE_DYNCREATE(CDaterDoc)
// Attributes
public:
   CDaterSet m_daterSet;
// Operations
public:
// Overrides
   //{{AFX_VIRTUAL(CDaterDoc)
   public:
   virtual BOOL OnNewDocument();
   //}}AFX_VIRTUAL
// Implementation
public:
   virtual ~CDaterDoc();
#ifdef _DEBUG
   virtual void AssertValid() const;
   virtual void Dump(CDumpContext& dc) const;
#endif
protected:
   //{{AFX_MSG(CDaterDoc)
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};


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


Класс документа приложения CMultiDoc наследуется от базового класса CDocument библиотеки MFC. Определение этого класса вы можете найти в файле MultiDoc.h. Мы привели структуру класса CMultiDoc на рисунке 1.12.
Класс документа приложения
Рис. 1.12. Окно Project Workspace, класс CMultiDoc
MFC AppWizard определяет класс CMultiDoc одинаково для однооконных и для многооконных приложений. Единственное исключение составляет название класса документа, которое создается на основе имени проекта:
class CMultiDoc : public CDocument
{
protected:
   CMultiDoc();
   DECLARE_DYNCREATE(CMultiDoc)
// Attributes
public:
// Operations
public:
// Overrides
   //{{AFX_VIRTUAL(CMultiDoc)
public:
   virtual BOOL OnNewDocument();
   virtual void Serialize(CArchive& ar);
   //}}AFX_VIRTUAL
// Implementation
public:
   virtual ~CMultiDoc();
#ifdef _DEBUG
   virtual void AssertValid() const;
   virtual void Dump(CDumpContext& dc) const;
#endif
protected:
protected:
   //{{AFX_MSG(CMultiDoc)
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};


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


Класс CDlgBarWindow управляет главным окном приложения, создает диалоговую панель управления, а также обрабатывает сообщения, поступающие от диалоговой панели управления:
class CDlgBarWindow : public CFrameWnd
{
// Определяем панель управления
protected:
   // Панель управления на основе класса CDialogBar      
   CDialogBar       m_wndDialogBar;
  
protected:
   // Метод OnCreate используется для создания диалоговой
   // панели управления
   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
public:
   // Объявляем конструктор класса CDlgBarWindow
   CDlgBarWindow();
   // Объявляем методы для обработки команд от диалоговой
   // панелей управления
   afx_msg BOOL DlgBarCommand(UINT nID);
   afx_msg void DlgBarCombo();
  
   // Макрокоманда необходима, так как класс
   // CDlgBarWindow обрабатывает сообщения
   DECLARE_MESSAGE_MAP()   
};
Кроме ряда методов, в класс CDlgBarWindow входит элемент m_wndDialogBar класса CDialogBar. Этот элемент представляет диалоговую панель управления, которая будет отображаться в главном окне приложения.
Рассмотрим отдельные методы класса CDlgBarWindow более подробно.
Сейчас для нас наиболее важен метод OnCreate, который собственно, и создает диалоговую панель управления приложения DialogBar. В таблице сообщений класса CDlgBarWindow, находится макрокоманда ON_WM_CREATE. Поэтому в процессе создания главного окна приложения вызывается метод OnCreate.


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


Класс CMainFrame предназначен для управления главным окном приложения. Для этого класса определены конструктор, деструктор, методы PreCreateWindow, OnCreate, AssertValid и Dump. В него также входят два элемента данных m_wndToolBar и m_wndStatusBar, представляющие панель управления и панель состояния:
class CMainFrame : public CFrameWnd
{
protected:
   CMainFrame();
   DECLARE_DYNCREATE(CMainFrame)
// Attributes
public:
// Operations
public:
// Overrides
   //{{AFX_VIRTUAL(CMainFrame)
   virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
   //}}AFX_VIRTUAL
// Implementation
public:
   virtual ~CMainFrame();
#ifdef _DEBUG
   virtual void AssertValid() const;
   virtual void Dump(CDumpContext& dc) const;
#endif
protected: 
   CStatusBar  m_wndStatusBar;
   CToolBar    m_wndToolBar;
protected:
   //{{AFX_MSG(CMainFrame)
   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};
Мы не стали приводить исходные тексты методов класса CMainFrame, так как они практически не отличаются от методов класса CMainFrame любого другого однооконного приложения созданного MFC AppWizard.


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


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


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


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


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


Внутри главного окна приложения отображаются панели управления и состояния, дочерние MDI окна, используемые для просмотра документов. Для управления главным окном приложения используется класс CMainFrame, определенный в файле MainFrm.h.
Вы можете изучить класс CMDIFrameWnd, просмотрев его структуру в окне Project Workspace, на странице ClassView (рис. 1.10). Выполните двойной щелчок левой кнопкой мыши по названию класса или по названию интересующего вас метода, и соответствующий программный код загрузится в окно редактора Microsoft Visual C++.
Класс главного окна приложения
Рис. 1.10. Окно Project Workspace, класс CMainFrame
Ниже мы привели определение класса CMainFrame:
class CMainFrame : public CMDIFrameWnd
{
   DECLARE_DYNAMIC(CMainFrame)
public:
   CMainFrame();
// Attributes
public:
// Operations
public:
// Overrides
   //{{AFX_VIRTUAL(CMainFrame)
   virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
   //}}AFX_VIRTUAL
// Implementation
public:
   virtual ~CMainFrame();
#ifdef _DEBUG
   virtual void AssertValid() const;
   virtual void Dump(CDumpContext& dc) const;
#endif
protected:
   CStatusBar  m_wndStatusBar;
   CToolBar    m_wndToolBar;
protected:
   //{{AFX_MSG(CMainFrame)
   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};
Класс главного окна многооконного приложения CMainFrame практически полностью соответствует классу главного окна однооконного приложения. Даже названия этих классов одинаковы. Однако обратите внимание, что класс CMainFrame наследуется от базового класса CMDIFrameWnd, а не от CFrameWnd, как это было для однооконного приложения.


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


Класс окна просмотра документа, также как класс документа и главный класс приложения, имеют своего двойника в однооконном приложении. Так, в приложении Single определен класс окна просмотра CSingleView, совпадающий с классом CMultiView.
Класс окна просмотра документа
Рис. 1.13. Окно Project Workspace, класс CMultiView
Вы можете просмотреть список методов, входящих в класс CMultiView, если откроете в окне Project Workspace страницу ClassView (рис. 1.13). А сейчас приведем определение класса CMultiView:
class CMultiView : public CView
{
protected:
   CMultiView();
   DECLARE_DYNCREATE(CMultiView)
// Attributes
public:
   CMultiDoc* GetDocument();
// Operations
public:
// Overrides
   //{{AFX_VIRTUAL(CMultiView)
public:
   virtual void OnDraw(CDC* pDC); 
   virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
   virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
   virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
   virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
   //}}AFX_VIRTUAL
// Implementation
public:
   virtual ~CMultiView();
#ifdef _DEBUG
   virtual void AssertValid() const;
   virtual void Dump(CDumpContext& dc) const;
#endif
protected:
   //{{AFX_MSG(CMultiView)
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};
Как видите, класс CMultiView наследуется от базового класса CView. Вы, однако, можете наследовать этот класс и от некоторых других классов библиотеки MFC.


Класс окна просмотра приложения - CDaterView


Большой интерес представляет класс окна просмотра приложения CDaterView. В нем содержится указатель m_pSet на объект класса CDaterSet, который представляет запись базы данных. Обратите внимание, что определение указателя находится внутри комментариев вида //{{AFX_DATA. Эти комментарии используются MFC ClassWizard:
class CDaterView : public CRecordView
{
protected:
   CDaterView();
   DECLARE_DYNCREATE(CDaterView)
public:
   //{{AFX_DATA(CDaterView)
   enum { IDD = IDD_DATER_FORM };
   CDaterSet* m_pSet;
   //}}AFX_DATA
// Attributes
public:
   CDaterDoc* GetDocument();
// Operations
public:
// Overrides
   //{{AFX_VIRTUAL(CDaterView)
public:
   virtual CRecordset* OnGetRecordset();
   virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
   virtual void DoDataExchange(CDataExchange* pDX);
   virtual void OnInitialUpdate();
   //}}AFX_VIRTUAL
// Implementation
public:
   virtual ~CDaterView();
#ifdef _DEBUG
   virtual void AssertValid() const;
   virtual void Dump(CDumpContext& dc) const;
#endif
protected:
   //{{AFX_MSG(CDaterView)
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};
Помимо конструктора и деструктора в классе CDaterView определен целый ряд методов - PreCreateWindow, GetDocument, OnGetRecordset, DoDataExchange, OnInitialUpdate, а также AssertValid и Dump. Опишем наиболее важные из этих методов более подробно.


Класс панели состояния


Для управления панелями состояния в состав библиотеки MFC включен класс CStatusBar. Как и классы CToolBar и CDialogBar, предназначенные для работы с панелями управления, класс CStatusBar также наследуется от базового класса CControlBar:
CStatusBar <- CControlBar <- CWnd <- CCmdTarget <- CObject


Класс записи базы данных - CDaterDoc


Центральным классом приложений, которые взаимодействуют с базами данных через драйвера ODBC, является класс, наследованный от базового класса CRecordset. В нашем приложении в качестве этого класса выступает класс CDaterSet:
class CDaterSet : public CRecordset
{
public:
   CDaterSet(CDatabase* pDatabase = NULL);
   DECLARE_DYNAMIC(CDaterSet)
// Field/Param Data
   //{{AFX_FIELD(CDaterSet, CRecordset)
   CString   m_NAME;
   CString   m_ADDRESS;
   long   m_PRIORITY;
   CString   m_PHONE;
   //}}AFX_FIELD
// Overrides
   //{{AFX_VIRTUAL(CDaterSet)
   public:
   virtual CString GetDefaultConnect();
   virtual CString GetDefaultSQL();
   virtual void DoFieldExchange(CFieldExchange* pFX);
   //}}AFX_VIRTUAL
// Implementation
#ifdef _DEBUG
   virtual void AssertValid() const;
   virtual void Dump(CDumpContext& dc) const;
#endif
};
Класс CDaterSet содержит в себе переменные, представляющие поля записи базы данных. Эти переменные размещаются внутри комментариев вида //{{AFX_FIELD.
В нашем случае эти переменные называются m_NAME, m_ADDRESS, m_PRIORITY и m_PHONE. Они представляют поля NAME, ADDRESS, PRIORITY и PHONE соответственно.
В классе CDaterSet также определены конструктор класса и несколько методов - GetDefaultConnect, GetDefaultSQL, DoFieldExchange, а также AssertValid и Dump.


Классы панелей управления


В состав библиотеки MFC включены два класса для работы с панелями управления - CToolBar и CDialogBar. Оба они наследуются от базового класса CControlBar, реализующего основные функции панелей управления. Кроме того, от базового класса CControlBar наследуется еще один класс - CStatusBar. Он предназначен для работы с панелями состояния и будет рассмотрен позже:
CToolBar   <- | <- CControlBar <- CWnd <- CCmdTarget <- CObject
CDialogBar <- |
CStatusBar <- |
Класс CToolBar представляет панель управления, состоящую из ряда кнопок. При желании, вы, конечно, сможете разместить на панели управления и другие органы управления, например списки или поля редактирования, однако такая возможность требует дополнительного программирования.
Если требуется создать панель, содержащую различные органы управления, а не только кнопки, то гораздо удобнее воспользоваться классом CDialogBar. Класс CDialogBar, также наследованный от базового класса CControlBar, позволяет создать панель управления на основе хорошо известного вам шаблона диалоговой панели. Более подробно о возможностях использования класса CDialogBar мы расскажем несколько позже, а сейчас остановим свое внимание на классе CToolBar.
Кнопки панели управления могут работать как обычные кнопки, как переключатели и как переключатели с зависимой фиксацией. Тип кнопок панели управления выбирается методами класса CToolBar.
Чтобы создать панель управления, сначала необходимо определить объект класса CToolBar, который будет представлять данную панель. Если панель имеет много органов управления, можно наследовать от класса CToolBar новый класс, расширить его дополнительными методами, и использовать полученный класс вместо класса CToolBar.
Объект CToolBar обычно включают как элемент главного окна приложения. Так, например, в приложениях, созданных с помощью средств MFC AppWizard, панель управления объявляется как элемент класса CMainFrame, наследованного от класса CFrameWnd или CMDIFrameWnd (в зависимости от интерфейса приложения). Класс CMainFrame как раз представляет главное окно приложения, внутри которого расположены панели управления, окна просмотра и т. д.

Конструктор класса CToolBar не имеет параметров:

CToolBar();

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

BOOL Create(

   CWnd* pParentWnd,

   DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_TOP,

   UINT nID = AFX_IDW_TOOLBAR

);

Только первый параметр метода pParentWnd является обязательным. В нем надо указать идентификатор родительского окна для панели управления. В качестве такого окна обычно выступает главное окно приложения, элементом класса которого является объект класса CToolBar.

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

Флаг

Описание

CBRS_BOTTOM

Панель управления отображается в нижней части окна

CBRS_FLOATING

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

CBRS_FLYBY

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

CBRS_SIZE_DYNAMIC

Размер панели управления можно изменять. При этом кнопки в панели управления перестраиваются в несколько рядов

CBRS_SIZE_FIXED

Панель состояния имеет фиксированную форму (размер)

CBRS_TOOLTIPS

Для кнопок панели управления отображаются их краткие описания в окнах tool tips

CBRS_TOP

Панель управления отображается в верхней части окна

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

¨ Приложения, созданные MFC AppWizard, имеют меню View, содержащее строки Toolbar и Status bar. Строка Toolbar с идентификатором ID_VIEW_TOOLBAR позволяет закрывать и снова открывать панель управления. Обработка стандартного командного сообщения ID_VIEW_TOOLBAR выполняется методом OnUpdateControlBarMenu класса CFrameWnd. Сразу отметим, что метод OnUpdateControlBarMenu может управлять отображением панели управления только в том случае, если она имеет идентификатор AFX_IDW_TOOLBAR. Более подробно о методе OnUpdateControlBarMenu можно прочитать в разделе “Недокументированные возможности класса CMainFrame”.



Метод Create возвращает ненулевое значение в случае успешного создания панели или нуль в случае ошибки.

После того как вы создали панель управления, надо загрузить ресурс панели управления. Для этого предназначен метод LoadToolBar класса CToolBar. Метод LoadToolBar имеет две реализации:

BOOL LoadToolBar(LPCTSTR lpszResourceName);

BOOL LoadToolBar(UINT nIDResource);

В качестве параметра lpszResourceName следует указать имя ресурса панели управления. Если вы знаете идентификатор ресурса панели управления, используйте второй прототип метода и укажите идентификатор в качестве параметра nIDResource.

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

В Microsoft Visual C++ версии 2.х и более ранних версиях ресурс типа toolbar отсутствует. Вместо этого ресурса в файле ресурсов приложения записывалось только изображение кнопок панели управления. А вместо метода LoadToolBar класса CToolBar использовались вызовы двух других методов этого же класса - LoadBitmap и SetButtons.

Метод LoadBitmap загружал из ресурсов приложения изображение кнопок панели управления, а метод SetButtons устанавливал соответствие каждой кнопке панели управления ее изображения и идентификатора. Так как при создании новых приложений лучше использовать новый метод для отображения панелей управления, предполагающий работу с ресурсами toolbar и методом LoadToolBar, то мы не будем останавливаться на методах LoadBitmap и SetButtons.

На этом создание панели управления можно считать оконченным. Теперь ваше приложение имеет полноценную панель управления. Однако стоит изучить еще несколько методов класса CToolBar и его базового класса CControlBar, которые помогут сделать панель управления еще более удобной для пользователя.


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


MFC AppWizard создает для приложения Multi, обладающего многооконным интерфейсом, шесть основных классов, что на один класс больше, чем для однооконного приложения. Пять классов из шести представляют основу любого многооконного приложения, созданного MFC AppWizard. Шестой класс управляет информационной диалоговой панелью About.
Список названий классов, а также входящие в них методы и элементы данных можно просмотреть на странице ClassView окна Project Workspace (рис. 1.7). В отдельной папке Globals представлены глобальные объекты и переменные приложения. Приложение Multi имеет только один глобальный объект theApp. Это объект главного класса приложения.
Классы приложения Multi
Рис. 1.7. Окно Project Workspace, классы приложения
В следующей таблице кратко описано назначение отдельных классов приложения Multi. Более подробный рассказ об этих классах и их методах расположен ниже.

Класс приложения
Базовый класс
Описание
CMultiApp
CWinApp
Главный класс приложения
CMainFrame
CMDIFrameWnd
Класс главного окна приложения
CChildFrame
CMDIChildWnd
Класс дочернего окна MDI
CMultiDoc
CDocument
Класс документа приложения
CMultiView
CView
Класс окна просмотра документа

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


Команды панелей управления


Каждый раз когда пользователь нажимает на кнопки в панели управления или работает с дополнительными органами управления (текстовым редактором и списком combo-box), в окно приложения, являющееся родительским окном панели управления, поступают соответствующие сообщения.
Кнопки панели управления передают в родительское окно командные сообщения. Идентификатор этих командных сообщений соответствует идентификатору нажатой кнопки. В родительское окно панели управления также поступают сообщения от дополнительных органов управления - текстового редактора и списка combo-box. Коды извещения этих сообщений определяют их назначение.
Таблица сообщений класса CMultiBarWindow обрабатывает целый ряд сообщений от меню и панелей управления. Кроме них в таблице сообщений класса CMultiBarWindow располагается макрокоманда ON_WM_CREATE, которая вызывает метод OnCreate во время создания окна:
ON_WM_CREATE()
Для обработки командных сообщений от кнопок панелей Player и Style мы вызываем методы BarCommandOne и BarCommandRange, входящие в класс CMultiBarWindow.
Метод BarCommandOne отображает на экране сообщение о том, что данная команда не реализована - Command not implemented. Метод BarCommandRange не выполняет никаких действий:
// Обработчики команд от панели управления Player
ON_COMMAND(ID_STOP, BarCommandOne)
ON_COMMAND(ID_PLAY, BarCommandOne)
ON_COMMAND(ID_PAUSE, BarCommandOne)
ON_COMMAND_RANGE(ID_LEFT, ID_RIGHT, BarCommandRange)
ON_COMMAND_RANGE(ID_TYPE, ID_WAVE,  BarCommandRange)
// Обработчик команд от панели управления Style
ON_COMMAND_RANGE(ID_UNDERLINE, ID_MARK_4, BarCommandRange)
Для перехвата командных сообщений от панели управления Player и Style используются макрокоманды ON_COMMAND и ON_COMMAND_RANGE. Макрокоманда ON_COMMAND вызывает метод, указанный во втором параметре командного сообщения, соответствующего идентификатору, приведенному в первом параметре. Макрокоманда ON_COMMAND_RANGE. работает аналогично ON_COMMAND, но позволяет вызвать метод обработчик, указанный в третьем параметре, сразу для нескольких командных сообщений. Идентификаторы обрабатываемых сообщений находятся в промежутке значений, указанных первым и вторым параметрами макрокоманды.

Для обработки командных сообщений от кнопок панели управления Extended мы также используем метод BarCommandRange. Исключение составляет только кнопка ID_ADD. Сообщения от этой кнопки обрабатываются методом AddStringToComboBox класса CMultiBarWindow:

// Обработчики команд от панели управления Extended

ON_COMMAND(ID_ADD, AddStringToComboBox)

ON_COMMAND_RANGE(ID_FOTO, ID_DISK,  BarCommandRange)

В таблице сообщений класса CMultiBarWindow также расположены макрокоманды для обработки командных сообщений от меню View. Для их обработки используется метод ShowStyle класса CMultiBarWindow:

// Обработчики команд меню View

ON_COMMAND_EX(ID_Style, ShowStyle)

ON_COMMAND_EX(ID_Extended, ShowStyle)

ON_COMMAND_EX(ID_Player, ShowStyle)

Командные сообщения от всех строк меню View обрабатываются одним методом ShowStyle,  однако его вызов осуществляется с помощью макрокоманды ON_COMMAND_EX. Эта макрокоманда вызывает для обработки командного сообщения с идентификатором, заданным первым параметром, метод указанный во втором параметре. В отличие от макрокоманды ON_COMMAND, макрокоманда ON_COMMAND_EX передает методу-обработчику идентификатор полученного командного сообщения.

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


Комбинированный редактор


Некоторые многооконные приложения позволяют одновременно работать с документами различных типов. Примером такого приложения является сама среда разработки Visual C++. В ней вы одновременно можете открыть окно редактирования исходного текста приложения и редактор ресурсов. Большинство систем управления базами данных также позволяют работать с документами различных типов. Так FoxPro и Access позволяют в одном окне просматривать поля базы данных, а в другом разрабатывать диалоговую форму для отображения информации из этой базы данных.
Ранее мы уже научились создавать приложения, которые могут отображать графические объекты (окружности, прямоугольники и т. д.) и текстовую  информацию. Теперь мы приступим к разработке наиболее сложного приложения, которое может одновременно работать с документами двух различных типов - графическими и текстовыми.
Для упрощения возьмите уже готовое многооконное приложение, которое уже может работать с простейшими графическими документами - Multi.


Компоненты Microsoft


На странице Microsoft диалоговой панели Component Gallery расположены компоненты, разработанные фирмой Microsoft. Среди них содержатся много интересных компонент, добавляющих к вашему приложению различные возможности практически без дополнительных затрат с вашей стороны. Среди них компонент, позволяющий приложениям использовать обменный буфер Windows Clipboard, компонент, выполняющий фоновые работы во время бездействия приложения, компонент, содержащий готовые диалоговые панели для ввода пароля и отображения хода различных процессов и многое многое другое.
Чтобы получить полные описания каждого компонента, достаточно выбрать его в панели Component Gallery и нажать на кнопку с вопросительным знаком. Сейчас мы кратко опишем назначение компонент Microsoft, а затем рассмотрим использование одной из компонент на примере.

Пиктограмма
Назначение соответствующего компонента
Компоненты Microsoft
Предназначен для использования со средствами автоматизированной разработки проектов - Custom AppWizard
Компоненты Microsoft
Компонент добавляет к классам окон просмотра, наследованным от базового класса CView, методы для обработки команд меню Edit, предназначенные для взаимодействия с обменным буфером Clipboard
Компоненты Microsoft
Позволяет добавить к окну приложения панель управления, созданную на основе шаблона диалоговой панели
Компоненты Microsoft
Компонент выполняет регистрацию всех типов документов приложения. После этого документы приложения могут быть открыты и распечатаны при помощи приложений Windows Explorer или File Manager
Компоненты Microsoft
Этот компонент позволяет вам создать уникальный глобальный идентификатор - GUID. Такие идентификаторы используются при создании приложений, работающих с технологией OLE, регистрационной базой Windows и др. Созданный идентификатор записывается в буфер clipboard. Модификация проекта не выполняется, вы должны сами вставить идентификатор в исходный текст приложения
Компоненты Microsoft
Добавляет метод OnIdle к любому классу приложения, наследованному от базового класса CWinThread. Обычно в качестве такого класса выступает главный класс приложения. Метод OnIdle позволяет выполнять обработку во время бездействия приложения, когда очередь сообщений приложения пуста
Компоненты Microsoft
Компонент обеспечивает поддержку почтового API. Если вы не указали на необходимость работы с MAPI во время создания проекта и желаете, чтобы ваше приложение могло передавать и принимать почтовые сообщения, вставьте в проект этот компонент
Компоненты Microsoft
Включает поддержку OLE Automation
Компоненты Microsoft
Если во время создания приложения средствами MFC AppWizard вы не указали, что оно может использовать органы управления OCX, используйте этот компонент
Компоненты Microsoft
Компонент можно использовать для создания собственных органов управления, представляющих список или список с окном редактирования
Компоненты Microsoft
Используйте данный компонент, если ваше приложение должно работать с цветовыми палитрами
Компоненты Microsoft
Компонент добавляет к проекту диалоговую панель для ввода пароля и вызывает ее перед стартом приложения. Таким образом можно заблокировать  несанкционированный доступ к вашему приложению
Компоненты Microsoft
Позволяет добавить к окнам вашего приложения временное (контекстное) меню, которое открывается при нажатии на правую кнопку мыши
Компоненты Microsoft
Компонент представляет из себя диалоговую панель, содержащую линейный индикатор. Используйте данную панель для отображения хода различных процессов
Компоненты Microsoft
Компонент позволяет добавить к приложению блокнот Property Sheet (диалоговую панель с несколькими страницами) или ряд панелей выбора свойств Wizard
Компоненты Microsoft
Данный компонент позволяет в момент запуска приложения отображать на экране рисунок
Компоненты Microsoft
Используйте компонент для разделения окна на несколько отдельных частей
Компоненты Microsoft
Компонент позволяет добавить к панели состояния главного окна приложения индикаторы для вывода текущей даты и времени
Компоненты Microsoft
Компонент добавляет к диалоговой панели About приложения сведения о ресурсах системы
Компоненты Microsoft
Если вы добавите этот компонент, то сразу после запуска приложения отображается диалоговая панель с различными короткими замечаниями
Компоненты Microsoft
Компонент позволяет реализовать краткие подсказки (органы управления Tool Tips) для диалоговых панелей приложения
Компоненты Microsoft
Используйте этот компонент при создании мультимедийных приложений. После включения компонента в проект вы можете обращаться к программному интерфейсу мультимедиа
Компоненты Microsoft
Компонент обеспечивает работу с сокетами Windows, что позволяет ему взаимодействовать с другими приложениями по протоколу TCP/IP. Если вы не указали во время создания проекта, что приложение будет использовать сокеты, вы можете добавить в проект этот компонент

Как видите, некоторые компоненты, расположенные на странице Microsoft, дублируют возможности приложения, которыми можно управлять в ходе создания приложения средствами MFC AppWizard. Поэтому если во время начального создания проекта приложения вы не указали, что приложение будет работать с сокетами Windows то вместо кропотливого исправления проекта вручную можно просто добавить в него компонент Windows Sockets.
Конечно, все возможности, которые предоставляют компоненты Microsoft, легко реализовать самому. Однако зачем тратить драгоценное время, когда уже есть готовое решение.


Компоненты


Среда Microsoft Visual C++ предусматривает дополнительные возможности для повторного использования программного кода при создании новых программ. Для этого предназначена так называемая галерея компонентов - Component Gallery.
Component Gallery служит вместилищем для компонентов. В качестве компонентов выступают классы (возможно вместе с необходимыми ресурсами), органы управления ActiveX (OCX), а также сложные компоненты. Компоненты Component Gallery можно включать в ваше приложение и использовать по своему усмотрению.
При добавлении к проекту некоторых компонент в проект добавляются не только новые классы, но также новые ресурсы. В ряде случаев даже выполняется изменение исходных текстов самого приложения.


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


MFC AppWizard определяет для класса CChildFrame конструктор и деструктор. По умолчанию они не выполняют никаких действий. Вы можете изменить их для выполнения инициализации объектов класса дочернего окна MDI:
//////////////////////////////////////////////////////////////
// Конструктор и деструктор класса CChildFrame
CChildFrame::CChildFrame()
{
   // TODO:
}
CChildFrame::~CChildFrame()
{
}


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


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


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


Конструктор класса CMainFrame вызывает конструктор базового класса CRecordView и передает ему в качестве параметра символ IDD, определенный как идентификатор шаблона диалоговой панели IDD_DATER_FORM, используемого окном просмотра.
Конструктор CMainFrame также приваивает указателю m_pSet значение NULL:
CDaterView::CDaterView()
   : CRecordView(CDaterView::IDD)
{
   //{{AFX_DATA_INIT(CDaterView)
   m_pSet = NULL;
   //}}AFX_DATA_INIT
   // TODO:
}
Деструктор класса CMainFrame не содержжат программного кода:
CDaterView::~CDaterView()
{
}


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


Ниже представлены конструктор и деструктор класса CMainFrame. Изначально они не содержат программного кода и представляют собой простые заготовки. Вы можете использовать их для дополнительной инициализации объекта класса:
// Конструктор класса CMainFrame
CMainFrame::CMainFrame()
{
   // TODO:
}
// Деструктор класса CMainFrame
CMainFrame::~CMainFrame()
{
}

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


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


Конструктор и деструктор класса CMultiDoc не содержит программного кода. Вы можете добавить его по мере необходимости:
CMultiDoc::CMultiDoc()
{
   // TODO:
}
CMultiDoc::~CMultiDoc()
{
}


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


Конструктор и деструктор класса CMultiView не выполняют полезной работы. MFC AppWizard создает для них только пустые шаблоны, которые вы можете “наполнить” сами:
CMultiView::CMultiView()
{
   // TODO:
}
CMultiView::~CMultiView()
{
}


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


Конструктор класса CSplashWnd не содержит программного кода. Вы можете использовать его как заготовку, если решите расширить возможности компонента Splash Screen.
Деструктор класса CSplashWnd также не содержит ничего сложного, он используется исключительно для записи в указатель c_pSplashWnd значения NULL:
c_pSplashWnd = NULL;


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


Конструктор класса CSingleApp не выполняет никаких действий и состоит из пустого блока:
CDaterApp::CDaterApp()
{
   // TODO:
}


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


Конструктор класса CDaterSet вызывает конструктор базового класса CRecordset. В качестве параметра конструктору CDaterSet и конструктору базового класса передается указатель pdb на объект класса CDatabase, представляющий источник данных.
В приложении Dater конструктору CDaterSet параметр pdb не передается (см. класс CDaterDoc). Посмотрите описание конструктора класса CRecordset в документации Microsoft Visual C++. Если он вызывается без параметра или с параметром NULL, то конструктор автоматически создает объект класса CDatabase. С Этим объектом связывается источник данных, определенный в методе GetDefaultConnect:
CDaterSet::CDaterSet(CDatabase* pdb)
   : CRecordset(pdb)
{
   //{{AFX_FIELD_INIT(CDaterSet)
   m_NAME = _T("");
   m_ADDRESS = _T("");
   m_PRIORITY = 0;
   m_PHONE = _T("");
   m_nFields = 4;
   //}}AFX_FIELD_INIT
   m_nDefaultType = snapshot;
}
В конструкторе CDaterSet располагается блок операторов, ограниченный комментариями вида //{{AFX_FIELD_INIT, //}}AFX_FIELD_INIT. В нем выполняется инициализация элементов m_NAME, m_ADDRESS, m_PRIORITY и m_PHONE. Эти элементы входят в класс CDaterSet и представляют поля таблицы базы данных, связанной с соответствующим объектом CDaterSet (набором записей). Затем в этом же блоке инициализируется элемент m_nFields, принадлежащий базовому классу CRecordset.
Вы не должны модифицировать код, рассположенный внутри блока AFX_FIELD_INIT. Для этого используется MFC ClassWizard.
Последний оператор конструктора CDaterSet присваивает элементу m_nDefaultType, принадлежащему базовому классу CRecordset, значение snapshot (snapshot также входит в класс CRecordset). Этот элемент определяет тип набора записей, представленных классом CDaterSet. MFC AppWizard добавляет этот оператор, когда вы определяете режим работы с базой данных в диалоговой панели Database Options (рис. 5.7).


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


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


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


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


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


Конструктор класса, созданный MFC AppWizard, не выполняет никаких действий. В нем вы можете разместить код для инициализации объекта CMultiApp:
//////////////////////////////////////////////////////////////
// Конструктор класса CMultiApp
CMultiApp::CMultiApp()
{
   // TODO:
}


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


Конструктор класса CMultiMenuWindow используется для создания главного окна приложения, подключения меню, загрузки таблицы акселераторов и инициализации флагов.
Для создания окна приложения вызывается метод Create класса CFrameWnd. Обратите внимание, что метод Create создает окно с меню, которое имеет идентификатор IDR_MENU:
Create(NULL, "Status Bar Sample", WS_OVERLAPPEDWINDOW,
      rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));
Затем выполняется загрузка таблицы акселераторов IDR_ACCELERATOR с помощью метода LoadAccelTable класса CFrameWnd:
LoadAccelTable(MAKEINTRESOURCE(IDR_ACCELERATOR));
 И, в конце, конструктор устанавливает значение флагов bEnable, bRadio и nCheck. Флагам bEnable и bRadio присваивается значение TRUE, а флагу nCheck - нулевое значение.


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


Конструктор класса CStateWindow используется для создания главного окна приложения. Для этого вызывается метод Create класса CFrameWnd. Обратите внимание, что метод Create создает окно с меню, которое имеет идентификатор IDR_MENU:
Create(NULL, "Status Bar Sample", WS_OVERLAPPEDWINDOW,
      rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));
В конструкторе класса CStateWindow также устанавливается начальное состояние флага bIndicatorTEXT:
bIndicatorTEXT = TRUE;


Копирование панели управления


Чтобы ускорить процесс разработки панели управления приложения Bar, можно взять за основу панели управления ресурс toolbar приложения Multi, созданного с использованием средств MFC AppWizard. Мы описывали приложение Multi в разделе “Многооконное приложение”.
Не закрывая проект Bar и файл ресурсов Bar.rc, откройте файл ресурсов приложения Multi. Выберите панель управления toolbar и запишите ее в буфер clipboard. Затем вставьте эту панель в файл ресурсов приложения Bar - Bar.rc.
Оставьте название панели toolbar, изображения кнопок, их идентификаторы и текстовые описания без изменения. Добавьте в конце панели еще одну кнопку, и присвойте ей идентификатор ID_TOOL_EXIT. Введите текстовое описание кнопки - строку Exit\nExit.
Сохраните измененный файл ресурсов приложения Bar и закройте файл ресурсов приложения Multi без изменений. Исходный текст файла ресурсов Bar.rc представлен в листинге 3.5.
Листинг 3.5. Файл Bar.rc
//Microsoft Developer Studio generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
//
// Russian resources
#if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_RUS)
#ifdef _WIN32
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
#pragma code_page(1251)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
    "resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
    "\r\n"
    "\0"
END
#endif    // APSTUDIO_INVOKED

#endif    // Russian resources

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

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

// English (U.S.) resources

#if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_ENU)

#ifdef _WIN32

LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

#pragma code_page(1252)

#endif //_WIN32

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

//

// Toolbar

//

IDR_MAINFRAME TOOLBAR DISCARDABLE  16, 15

BEGIN

    BUTTON      ID_FILE_NEW

    BUTTON      ID_FILE_OPEN

    BUTTON      ID_FILE_SAVE

    SEPARATOR

    BUTTON      ID_EDIT_CUT

    BUTTON      ID_EDIT_COPY

    BUTTON      ID_EDIT_PASTE

    SEPARATOR

    BUTTON      ID_FILE_PRINT

    BUTTON      ID_APP_ABOUT

    BUTTON      ID_TOOL_EXIT

END

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

//

// Bitmap

//

IDR_MAINFRAME           BITMAP  MOVEABLE PURE   "res\\Toolbar.bmp"

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

//

// String Table

//

STRINGTABLE DISCARDABLE

BEGIN

    ID_TOOL_EXIT            "Exit\nExit"

END

#endif    // English (U.S.) resources

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

#ifndef APSTUDIO_INVOKED

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

// Generated from the TEXTINCLUDE 3 resource.

//

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

#endif    // not APSTUDIO_INVOKED

Идентификаторы всех ресурсов приложения Bar определены в файле resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++ (листинг 3.6).

Листинг 3.6. Файл resource.h

//{{NO_DEPENDENCIES}}

// Microsoft Developer Studio generated include file.

// Used by Bar.rc

//

#define IDR_HAND_BAR                    101

#define IDR_MAINFRAME                   128

#define ID_TOOL_EXIT                    32771

#define ID_BUTTON40001                  40001



#define ID_BUTTON40002                  40002

#define ID_BUTTON40003                  40003

#define ID_BUTTON40004                  40004

#define ID_BUTTON40005                  40005

#define ID_FILE_NEW                     0xE100

#define ID_FILE_OPEN                    0xE101

#define ID_FILE_SAVE                    0xE103

#define ID_FILE_PRINT                   0xE107

#define ID_EDIT_COPY                    0xE122

#define ID_EDIT_CUT                     0xE123

#define ID_EDIT_PASTE                   0xE125

#define ID_APP_ABOUT                    0xE140

// Next default values for new objects

//

#ifdef APSTUDIO_INVOKED

#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NEXT_RESOURCE_VALUE        103

#define _APS_NEXT_COMMAND_VALUE         40006

#define _APS_NEXT_CONTROL_VALUE         1000

#define _APS_NEXT_SYMED_VALUE           101

#endif

#endif

Изображения кнопок панели управления IDR_MAINFRAME располагаются в файле Toolbar.bmp (рис. 3.11). Файл Toolbar.bmp содержит только изображения кнопок, разделители SEPARATOR в нем не представлены.

Копирование панели управления

Рис. 3.11. Файл Toolbar.bmp с изображением кнопок панели управления

Постройте приложение Bar и запустите его на выполнение. На экране появится главное окно приложения, содержащее панель управления (рис. 3.12). Панель управления нельзя переместить в другое место, она жестко зафиксирована в верхней части окна.

Копирование панели управления

Рис. 3.12. Приложение Bar

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

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


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


Макрокоманда ON_UPDATE_COMMAND_UI предназначена для использования в таблицах сообщений приложения и имеет следующий формат:
ON_UPDATE_COMMAND_UI(id, memberFxn)
Параметр id определяет идентификатор строки меню, кнопки панели управления или индикатора панели состояния, для которых надо обработать команду обновления. Параметр memberFxn задает метод, выполняющий обновление.
Если один и тот же метод вызывается для обработки различных команд обновления, можно использовать другую макрокоманду - ON_UPDATE_COMMAND_UI_RANGE. Она вызывает метод memberFxn для обработки всех команд обновления, идентификаторы которых находятся в промежутке значений от id1 до id2:
ON_UPDATE_COMMAND_UI_RANGE(id1, id2, memberFxn)
Метод - обработчик команд обновления, который вызывается макрокомандами ON_UPDATE_COMMAND_UI и ON_UPDATE_COMMAND_UI_RANGE имеет следующий формат:
afx_msg void memberFxn(CCmdUI* pCmdUI);
Имя метода - обработчика обычно формируется из префикса OnUpdate и названия соответствующего объекта интерфейса пользователя - строки меню, кнопки панели управления или индикатора панели состояния.
В качестве параметра pCmdUI методу передается указатель на объект класса CCmdUI. Этот объект представляет элемент интерфейса пользователя (строку меню, кнопку панели управления…), для которого надо обработать команду обновления. Вызывая методы класса CCmdUI, вы можете легко изменять состояние соответствующего объекта интерфейса пользователя.
Более подробно самые важные методы класса CCmdUI мы рассмотрим в следующем разделе.


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


Как и для других элементов пользовательского интерфейса, для управления меню в состав библиотеки классов MFC включен специальный класс - класс CMenu. Класс CMenu - один из самых незаметных классов библиотеки MFC. Ваше приложение может активно работать с меню, но все же в исходных текстах вы не найдете ни одного объекта этого класса.
В приложениях, созданных с помощью MFC AppWizard меню создается автоматически вместе с панелью управления и панелью состояния. Для этого достаточно указать при создании шаблона документа общий идентификатор этих ресурсов:
// Объявляем указатель на шаблон документа
CSingleDocTemplate* pDocTemplate;
// Создаем шаблон документа
pDocTemplate = new CSingleDocTemplate(
   IDR_MAINFRAME,                // Идентификатор меню, панели
                                 // управления и пиктограммы
   RUNTIME_CLASS(CSingleDoc),    // Класс документа
   RUNTIME_CLASS(CMainFrame),    // Класс главного окна
   RUNTIME_CLASS(CSingleView));  // Класс окна просмотра
В случае многооконного приложения дополнительно указываются ресурсы, используемые когда все окна просмотра документов закрыты. Все эти ресурсы имеют один и тот же идентификатор. В примере, представленном ниже, это идентификатор IDR_MAINFRAME:
// Создаем главное окно многооконного приложения
CMainFrame* pMainFrame = new CMainFrame;
// Загружаем ресурсы с идентификатором IDR_MAINFRAME,
// в том числе и меню
if(!pMainFrame->LoadFrame(IDR_MAINFRAME))
   return FALSE;
В приложениях MFC AppWizard, имеющих однооконный или многооконный интерфейс, меню создается и изменяется самой библиотекой MFC. Несмотря на это, вы также можете управлять меню. Самый простой способ заключается в обработке команд обновления от меню. Проблеме использования этих команд мы посвятили раздел “Класс CCmdUI”.
Даже когда приложение создано без использования средств MFC AppWizard, процедура создания меню остается очень простой и также может не задействовать объекты класса CMenu напрямую.
В таких приложениях, как правило, создается главное окно на основе класса CFrameWnd. Для этого сначала создается соответствующий объект класса CFrameWnd, а затем вызывается либо метод Create, либо метод LoadFrame, который в свою очередь уже создает само окно вместе с меню.


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


Практически все приложения, имеющие оконный интерфейс, управляются посредством меню. Пользователь выбирает строки меню или, другими словами, команды, а приложение в ответ на это выполняет определенные действия, например, отображает диалоговые панели, выполняет вычисления и т. д.
Многие приложения также имеют панель управления - небольшую полоску, содержащую ряд кнопок. Как правило, эта полоска располагается ниже меню в главном окне приложения. Кнопки панелей управления дублируют некоторые, как правило наиболее часто используемые, строки меню. Их использование позволяет значительно ускорить и облегчить работу с приложением.
Меню и панели управления служат для передачи команд приложению. Напротив, панель состояния отражает текущее состояние приложения и может давать пользователю краткие подсказки, состоящие буквально из нескольких строк.
Все однооконные и многооконные приложения, созданные с использованием MFC AppWizard, по умолчанию имеют меню, панель управления и панель состояния.
В предыдущем томе из серии “Библиотека системного программиста”, посвященном использованию библиотеки классов MFC, и в первой главе этой книги, называющейся “Многооконное приложение“ мы уже рассказали об основных принципах устройства меню и панелей управления. Сейчас мы более подробно расскажем об этих наиболее важных элементах интерфейса приложения.


Меню


Самый простой и удобный способ создания меню приложения основан на использовании специального ресурса - шаблона меню. Если вы применяете для создания приложения средства MFC AppWizard, то однооконное приложение по умолчанию будет иметь один ресурс меню, а многооконное два.
Для создания и изменения меню приложения следует использовать редактор ресурсов Microsoft Visual C++. С помощью него вы разработаете меню буквально за несколько минут.
Редактор ресурсов позволяет для каждой строки меню определить ее название, идентификатор, текст подсказки, а также некоторые дополнительные характеристики. Но самым ценным является возможность, добавив к меню новые строки, сразу запустить MFC ClassWizard и определить методы приложения, которые будут использоваться для обработки командных сообщений от этих строк меню.
Чтобы добавить новую строку к меню, достаточно выполнить двойной щелчок левой кнопкой мыши по одному из пустых прямоугольников из точек, которые располагаются в конце главного меню (вы создадите еще одно меню верхнего уровня) и в низу каждого из меню (создается новая строка меню). При этом на экране появится диалоговая панель Menu Item Properties, которую вы должны заполнить (рис. 3.1).
Меню
Рис. 3.1. Редактор меню и диалоговая панель Menu Item Properties
Текст, который будет отображаться в строке меню, вы должны ввести в поле Caption. Затем в списке с полем редактирования ID надо выбрать идентификатор для этой строки меню. Если вы не заполните поле ID, редактор ресурсов самостоятельно создаст новый идентификатор на основе имени меню и строки.
В поле Prompt вы можете ввести текстовую строку, которая будет отображаться при выборе данного элемента меню в панели состояния приложения. Редактор ресурсов Microsoft Visual C++ создаст для введенного текста новый строковый ресурс и запишет его в файл ресурсов приложения, присвоив ему тот же идентификатор, что и меню. Так как строки меню и описывающий их текст, имеют одинаковые идентификаторы, то MFC сможет использовать их вместе. При выборе строки меню, MFC просто загружает строковый ресурс с идентичным идентификатором и отображает его в панели состояния.
Остальные переключатели диалоговой панели Menu Item Properties задают различные характеристики строк меню, отвечающие в первую очередь за их внешний вид. Изучите их самостоятельно, используя документацию Microsoft Visual C++.
Также легко можно просмотреть и изменить свойства уже существующих строк меню. Для этого надо выполнить по ним двойной щелчок левой кнопкой мыши. На экране появится уже описанная нами выше диалоговая панель Menu Item Properties, которая уже заполнена текущими параметрами строки меню.

В момент, когда пользователь открывает меню, приложению передаются команды обновления. В результате для всех строк меню, для которых в таблице сообщений приложения присутствуют макрокоманды ON_UPDATE_COMMAND_UI, вызываются соответствующие методы-обработчики. Они могут изменить состояние меню - заблокировать отдельные строки меню, выделить их символами · или Ö.


Метод AddStringToComboBox класса CMultiBarWindow


Метод AddStringToComboBox класса CMultiBarWindow выполняет обработку командных сообщений от кнопки ID_ADD панели управления Extended. Для этого он сначала считывает строку, введенную в текстовом редакторе m_edit, а затем добавляет ее в список m_combo_box:
void CMultiBarWindow::AddStringToComboBox()
{
   // Получаем строку, введенную в текстовом редакторе m_edit
   char  tmpStr[39];
   m_wndExtendedBar.m_edit.GetLine(0, tmpStr,40);             
   // Добавляем новую строку к списку m_combo_box
   m_wndExtendedBar.m_combo_box.AddString(tmpStr);
}


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


Командные сообщения от кнопок Set, Clear, и переключателя Alighn обрабатываются методом BarCommand класса CDlgBarWindow. В качестве параметра nID методу BarCommand передается идентификатор вызвавшего его органа управления:
BOOL CDlgBarWindow::DlgBarCommand(UINT nID)
{
   //...
}
В приложении мы определили для кнопок Set, Clear, и переключателей группы Alighn, описывающие их строковые ресурсы, которые имеют точно такие же идентификаторы. Поэтому когда вызывается метод DlgBarCommand и ему передается идентификатор кнопки или переключателя, мы загружаем строковый ресурс, который имеет точно такой же идентификатор и отображаем его на экране:
if(szCommandAbout.LoadString(nID))
   MessageBox(szCommandAbout);
else
{
   // Ошибка при загрузке строкового ресурса
   TRACE0("Failed to load string\n");
   return -1;     
}


Метод Create класса CSplashWnd


В классе CSplashWnd компонент Splash Screen переопределяет виртуальный метод Create базового класса CWnd:
BOOL CSplashWnd::Create(CWnd* pParentWnd /*= NULL*/)
{
/// ..
}
Класс CSplashWnd добаляет к этому методу программный код, который загружает ресурс изображения IDB_SPLASH. Для этого используется метод LoadBitmap:
if (!m_bitmap.LoadBitmap(IDB_SPLASH))
   return FALSE;
При помощи метода GetBitmap для объекта, представляющего изображения bitmap, определяются различные характеристики этого изображения, которые записываются в объект bm структуры BITMAP:
BITMAP bm;
m_bitmap.GetBitmap(&bm);
Последний оператор, завершающий метод Create, вызывает метод CreateEx, который и создает окно заставки:
return CreateEx(
   0,
   AfxRegisterWndClass(
      0,
      AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
   NULL,
   WS_POPUP | WS_VISIBLE,
   0, 0, bm.bmWidth, bm.bmHeight,
   pParentWnd->GetSafeHwnd(), NULL
);
Метод CreateEx определен в классе CWnd. Первый параметр метода задает дополнительные стили создаваемого окна заставки. Этот параметр содержит нулевое значение. Описание дополнительных стилей вы можете найти в документации Microsoft Visual C++.
Второй параметр метода CreateEx должен содержать имя класса Windows, на основе которого создается окно. Для этого параметра используется класс, зарегистрированный функцией AfxRegisterWndClass.
Третий параметр метода CreateEx определяет текст заголовка создаваемого окна. Так как для окна заставки заголовок не нужен, то в качестве этого параметра передается значение NULL.
В четвертом параметре должны быть указаны основные стили создаваемого окна. Мы используем в качестве этого параметра комбинацию флагов WS_POPUP и WS_VISIBLE. Эти флаги означают, что создается временное окно, которое сразу отображается на экране. Дополнительную информацию об основных и дополнительных стилях окон вы можете получить из 11 тома “Библиотеки системного программиста”.
Пятый, шестой, седьмой и восьмой параметры метода CreateEx определяют начальное положение, а также ширину и высоту окна. Начальное положение окна заставки выбирается по умолчанию - пятый и шестой параметры содержат нулевые значения, а ширина и высота берутся исходя из размеров изображения bitmap.

Предпоследний, девятый параметр метода определяет родительское окно для окна заставки. В качестве этого параметра используется идентификатор окна (полученный методом CWnd::GetSafeHwnd), указанного параметром pParentWnd метода CSplashWnd::Create. Метод GetSafeHwnd класса CWnd возвращает идентификатор окна, связанного с объектом класса CWnd. Если объект класса CWnd не связан с окном, то метод GetSafeHwnd возвращает значение NULL, и, в этом случае, окно заставки не имеет родительского окна.

Последний - десятый параметр метода CreateEx, может содержать указатель на дополнительные параметры. В данном вызове метода CreateEx этот параметр не используется.

Рассмотрим процедуру регистрации класса Windows для окна заставки более подробно. Для выполнения регистрации используется функция AfxRegisterWndClass, которая возвращает текстовую строку с именем зарегистрированного класса:

LPCTSTR AFXAPI

AfxRegisterWndClass(

   UINT nClassStyle,

   HCURSOR hCursor = 0,

   HBRUSH hbrBackground = 0,

   HICON hIcon = 0

);

Параметр nClassStyle указывает комбинацию стилей, используемых для создаваемого окна. Список стилей вы можете просмотреть в 11 томе “Библиотеки системного программиста” или в документации Microsoft Visual C++. Окна, созданные на основе зарегистрированного класса, будут использовать курсор с идентификатором hCursor, кисть с идентификатором hbrBackground и пиктограмму с идентификатором hIcon.

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


Метод Create


Метод Create создает и инициализирует окно, связанное с объектом CFrameWnd. В случае успешного завершения метод Create возвращает ненулевое значение, а в противном случае - ноль:
BOOL Create(
   LPCTSTR lpszClassName,               // Класс окна
   LPCTSTR lpszWindowName,              // Имя окна
   DWORD dwStyle = WS_OVERLAPPEDWINDOW, // Тип окна
   const RECT& rect = rectDefault,      // Расположение окна
   CWnd* pParentWnd = NULL,             // Родительское окно
   LPCTSTR lpszMenuName = NULL,         // Меню
   DWORD dwExStyle = 0,                 // Дополнительные
                                        // характеристики окна
   CCreateContext* pContext = NULL ); // Используется для
                                    // организации механизма
                                    // документ/окно просмотра
Обязательно надо указать только два первых параметра метода Create. Первый параметр lpszClassName служит для задания класса окна. В качестве него можно также указать значение NULL, тогда по умолчанию будет использован класс, определенный для окон CFrameWnd. Второй параметр lpszWindowName указывает имя окна - оно будет отображаться в заголовке окна.
Остальные параметры метода необязательные. Если их не указать, будут использованы значения, принятые по умолчанию. Нас, однако, сейчас интересует только параметр lpszMenuName. Через него вы можете указать имя ресурса меню, которое будет создано для данного окна. По умолчанию редактор ресурсов Microsoft Visual C++ присваивает созданным в нем ресурсам, в том числе меню, числовые идентификаторы. Чтобы получить из такого идентификатора значение, совместимое по типу с параметром lpszMenuName, следует использовать макрокоманду MAKEINTRESOURCE.
Следующий пример показывает, как можно создать окно с меню:
CMultiMenuWindow::CMultiMenuWindow()
{
   // Создаем окно приложения, соответствующее
   // данному объекту класса CMultiMenuWindow
   Create(NULL, "Multi Menu Sample", WS_OVERLAPPEDWINDOW,
      rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));
}
Класс CMultiMenuWindow, конструктор которого представлен выше, наследован от базового класса CFrameWnd. Конструктор CMultiMenuWindow создает окно с заголовком Multi Menu Sample. Это окно имеет меню IDR_MENU. Меню с идентификатором IDR_MENU должно быть определено в файле ресурсов приложения.


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


Когда пользователь выбирает строку из списка combo-box в диалоговой панели управления, в ее родительское окно, которое в нашем случае является главным окном приложения, поступает сообщение. Для его обработки вызывается метод DlgBarCombo класса CDlgBarWindow.
Метод DlgBarCombo выводит на экран сообщение о том, что пользователь сделал выбор из списка combo-box:
void CDlgBarWindow::DlgBarCombo()
{
   // Отображаем сообщение о том, что сделан выбор из списка
   MessageBox("Combo-box selection changed");
}


Метод DoDataExchange класса CDaterView


Виртуальный метод DoDataExchange класса CDaterView, первоначально определен в классе CWnd. Он служит для реализации механизмов автоматического обмена данными - Dialog Data Exchange (DDX) и автоматической проверки данных - Dialog Data Validation (DDV). Мы рассматривали этот механизм в 24 томе серии “Библиотека системного программиста”:
//////////////////////////////////////////////////////////////
// Метод DoDataExchange класса CDaterView
void CDaterView::DoDataExchange(CDataExchange* pDX)
{
   CRecordView::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CDaterView)
   DDX_FieldText(pDX,IDC_ADDRESS, m_pSet->m_ADDRESS, m_pSet);
   DDX_FieldText(pDX,IDC_NAME, m_pSet->m_NAME, m_pSet);
   DDX_FieldText(pDX,IDC_PHONE, m_pSet->m_PHONE, m_pSet);
   DDX_FieldText(pDX,IDC_PRIORITY, m_pSet->m_PRIORITY,m_pSet);
   //}}AFX_DATA_MAP
}
Механизм автоматического обмена данными привязывает к органам управления диалоговой панели переменные или элементы данных класса диалоговой панели. Так как окно просмотра построено на основе диалоговой панели, механизм автоматического обмена данными позволяет нам выполнять обмен данными между органами управления, размещенными в окне просмотра, и элементами класса окна просмотра. Обмен данными работает в обоих направлениях.
Обмен выполняется при помощи функций DDX_FieldText. Могут также использоваться и другие функции, например, DDX_FieldRadio, DDX_FieldCheck, DDX_FieldScroll. Практически каждый тип органов управления диалоговой панели имеет собственную функцию для выполнения процедуры обмена данными.
Всем функциям DDX_FieldText, вызываевым в методе DoDataExchange класса CDaterView передаются четыре параметра.
Первый параметр содержит указатель на объект класса CDataExchange. Этот объект определяет параметры обмена, в том числе направление, в котором надо выполнить обмен данными.
Второй параметр определяет идентификатор органа управления окна просмотра, с которым выполняется обмен данными (окно просмотра доолжно быть представлено классом CRecordView). В нашем случае это идентификаторы полей IDC_ADDRESS, IDC_NAME, IDC_PHONE и IDC_PRIORITY, котоорые принадлежат шаблону диалоговой панели используемому окном просмотра.
Третий параметр содержит ссылку на элемент данных класса CDaterSet, представляющий соответствующее поле базы данных. В нашем методе в качестве этого парамтера фигурируют m_pSet->m_ADDRESS, m_pSet->m_NAME, m_pSet->m_PHONE и m_pSet->m_PRIORITY.
Четвертый параметр содержит указатель на объект класса CDaterSet, с которым выполняется обмен данными. В нашем случае для всех методов в качестве этого параметра используется указатель m_pSet.


Метод DoFieldExchange класса CDaterSet


Метод DoFieldExchange выполняет обмен данными между элементами класса CDaterSet, представляющими поля набора записей, и источником данных:
void CDaterSet::DoFieldExchange(CFieldExchange* pFX)
{
   //{{AFX_FIELD_MAP(CDaterSet)
   pFX->SetFieldType(CFieldExchange::outputColumn);
   RFX_Text(pFX, _T("[NAME]"), m_NAME);
   RFX_Text(pFX, _T("[ADDRESS]"), m_ADDRESS);
   RFX_Long(pFX, _T("[PRIORITY]"), m_PRIORITY);
   RFX_Text(pFX, _T("[PHONE]"), m_PHONE);
   //}}AFX_FIELD_MAP
}
Метод DoFieldExchange содержит блок из комментариев //{{AFX_FIELD_MAP, в котором расположены несколько методов RFX_Text, которые выполняют обмен данными между полями источника данных (в нашем случае это поля NAME,  ADDRESS, PRIORITY, PHONE) и соответствующими элементами класса CDaterSet (m_NAME, m_ADDRESS, m_PRIORITY, m_PHONE).
Вы не должны вручную исправлять программный код в блоке AFX_FIELD_MAP. Для этого надо использовать MFC ClassWizard (рис. 5.17).
Метод DoFieldExchange класса CDaterSet
Рис. 5.17. Диалоговая панель MFC ClassWizard


Метод Enable


Виртуальный метод Enable позволяет установить или снять блокировку с объекта интерфейса пользователя, представленного объектом класса CCmdUI. Метод имеет единственный параметр bOn. Если параметр bOn содержит значение TRUE или не указан совсем, то блокировка снимается. Если параметр bOn содержит значение FALSE, то блокировка устанавливается:
virtual void Enable(BOOL bOn = TRUE);
Заблокированные строки меню и кнопки панелей управления отображаются серым цветом и не могут быть использованы до момента снятия блокировки. В случае блокировки индикатора панели состояния его  текст не будет отображаться.
Метод Enable также можно использовать для блокирования органов диалоговых панелей управления.


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


Чтобы панель управления можно было перемещать с одной стороны окна к другой, надо вызвать метод EnableDocking для окна и для каждой панели управления (если их несколько).
Формат вызова метода EnableDocking класса CFrameWnd соответствует формату метода EnableDocking класса CControlBar. Однако набор флагов, которые можно указать в качестве параметра dwStyle, расширен:
void EnableDocking(DWORD dwStyle);

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

Если ни один из флагов не установлен, и параметр dwStyle равен нулю, то данная панель управления не может быть пристыкована ни к одной границе окна. В этом случае надо вызвать метод CFrameWnd::FloatControlBar и панель управления появится в отдельном мини-окне.
Панель управления можно пристыковать только к тем границам окна, которые одновременно выбраны методами CFrameWnd::EnableDocking и CControlBar::EnableDocking.


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


Метод EnableDocking класса CFrameWnd разрешает пристыковку панели управления к определенным границам окна:
void EnableDocking(DWORD dwDockStyle);
Параметр dwDockStyle задает границы окна, к которым можно пристыковать панель управления. В качестве этого параметра надо использовать комбинации из флагов, перечисленных в следующей таблице.

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



Метод EnableSplashScreen класса CSplashWnd


Метод EnableSplashScreen класса CSplashWnd устанавливает флаг c_bShowSplashWnd, записывая в него значение, переданное через единственный параметр метода - bEnable:
c_bShowSplashWnd = bEnable;


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


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


Метод GetDefaultConnect класса CDaterSet


Метод GetDefaultConnect возвращает текстовую строку, которая определяет источник данных, который будет связан с объектом CDaterSet. Эта строка формируется MFC AppWizard, при выборе вами источника данных:
CString CDaterSet::GetDefaultConnect()
{
   return _T("ODBC;DSN=Address Pad");
}


Метод GetDefaultSQL класса CDaterSet


Метод GetDefaultSQL возвращает текстовую строку, которая должна содержать имя таблицы источника данных или выражение SELECT языка SQL. На основе этой таблицы или результата запроса SELECT будет сформирован набор записей для объекта CDaterSet:
CString CDaterSet::GetDefaultSQL()
{
   return _T("[TextBase.txt]");
}


Метод GetDocument класса CDaterView


Метод GetDocument возвращает указатель на документ, связанный с данным окном просмотра. Если окно просмотра не связано ни с каким документом, метод возвращает значение NULL.
Метод GetDocument имеет две реализации. Одна используется для отладочной версии приложения, а другая для окончательной.
Окончательная версия GetDocument определена непосредственно после самого класса окна просмотра CDaterView как встраиваемый (inline) метод. Когда вы используете страницу ClassView окна Project Workspace, чтобы просмотреть определение метода GetDocument, вы увидите именно этот код:
// Окончательная версия приложения
#ifndef _DEBUG 
inline CDaterDoc* CDaterView::GetDocument()
   { return (CDaterDoc*)m_pDocument; }
#endif
Отладочная версия GetDocument расположена в файле реализации класса окна просмотра DaterView.cpp. Откройте этот файл вручную, выбрав его название из страницы FileView окна Project Workspace:
// Отладочная версия приложения
#ifdef _DEBUG
CDaterDoc* CDaterView::GetDocument()
{
   ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDaterDoc)));
   return (CDaterDoc*)m_pDocument;
}
#endif //_DEBUG
Макрокоманда RUNTIME_CLASS возвращает указатель на структуру CRuntimeClass, содержащую информацию о классе CDaterDoc. Метод IsKindOf, определенный в классе CObject, проверяет, принадлежит ли объект, на который указывает m_pDocument, к классу CDaterDoc или классу наследованному от CDaterDoc. Если в приложении есть ошибка и m_pDocument не указывает на документ приложения, макрокоманда ASSERT отображает соответствующее сообщение и прерывает работу приложения.


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


В секции атрибутов класса CMultiView после комментария Attributes объявлен метод GetDocument. Этот метод возвращает указатель на документ, связанный с данным окном просмотра. Если окно просмотра не связано ни с каким документом, метод возвращает значение NULL.
Метод GetDocument имеет две реализации. Одна используется для отладочной версии приложения, а другая - для окончательной. Окончательная версия GetDocument определена непосредственно после самого класса окна просмотра CMultiView как встраиваемый (inline) метод:
#ifndef _DEBUG
inline CMultiDoc* CMultiView::GetDocument()
   { return (CMultiDoc*) m_pDocument; }
#endif
Переменная m_pDocument является элементом класса CView, определенным как protected. В документации на класс CView описание элемента m_pDocument отсутствует. Однако, вам достаточно знать, что после инициализации документа и окна просмотра в нем записан указатель на соответствующий документ. Если вы желаете получить дополнительную информацию, обратитесь к исходным текстам библиотеки MFC. Вы найдете определение класса CView и элемента этого класса m_pDocument в файле Afxwin.h.
Метод GetDocument совпадает с одноименным методом класса окна просмотра однооконного приложения за исключением того, что тип возвращаемого им указателя CMultiDoc.
Отладочная версия GetDocument расположена в файле реализации класса окна просмотра MultiView.cpp:
#ifdef _DEBUG
CMultiDoc* CMultiView::GetDocument()
{
   ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMultiDoc)));
   return (CMultiDoc*) m_pDocument;
}
#endif //_DEBUG


Метод HideSplashScreen класса CSplashWnd


Метод HideSplashScreen класса CSplashWnd закрывает окно заставки, вызывая метод DestroyWindow, а затем устанавливает флаг обновления для всех окон приложения с помощью метода UpdateWindow:
DestroyWindow();
AfxGetMainWnd()->UpdateWindow();


Метод InitInstance класса CDaterApp


Наибольший интерес представляет метод InitInstance класса CDaterApp, который создает шаблон документа приложения и добавляет его к списку шаблонов приложения. Кроме того, метод InitInstance разбирает командную строку приложения, загружает поддержку трехмерных органов упрпавления и выполняет еще некоторые действия:
BOOL CDaterApp::InitInstance()
{
#ifdef _AFXDLL
   Enable3dControls();
#else
   Enable3dControlsStatic();
#endif
   LoadStdProfileSettings();
   CSingleDocTemplate* pDocTemplate;
   pDocTemplate = new CSingleDocTemplate(
      IDR_MAINFRAME,
      RUNTIME_CLASS(CDaterDoc),
      RUNTIME_CLASS(CMainFrame),  
      RUNTIME_CLASS(CDaterView));
   AddDocTemplate(pDocTemplate);
   CCommandLineInfo cmdInfo;
   ParseCommandLine(cmdInfo);
   if (!ProcessShellCommand(cmdInfo))
      return FALSE;
   return TRUE;
}
При создании шаблона документа указывается идентификатор типа документа IDR_MAINFRAME, класс документа приложения CDaterDoc, класс главного окна приложения CMainFrame и класс окна просмотра CDaterView
В приложении определен целый ряд ресурсов с этим идентификатором - меню, панель управления, таблица акселераторов, пиктограмма и строковый ресурс. Наиболее интересны для нас сейчас меню и панель управления, так как они содержат строки и кнопки, управляющие просмотром записей базы данных.
Подробное описание метода InitInstance главного класса однооконного приложения можно получить в 24 томе серии “Библиотека системного программиста”.


Метод InitInstance класса CMultiApp


Основную работу по инициализации приложения выполняет метод InitInstance главного класса приложения, определенный в файле Multi.cpp. Как видите, он отличается от метода InitInstance, который используется для однооконных приложений:
//////////////////////////////////////////////////////////////
// Метод InitInstance
BOOL CMultiApp::InitInstance()
{
#ifdef _AFXDLL
   Enable3dControls();  
#else
   Enable3dControlsStatic();
#endif
   // Загружаем файл конфигурации
   LoadStdProfileSettings(); 
   // Создаем шаблон документа
   CMultiDocTemplate* pDocTemplate;
   pDocTemplate = new CMultiDocTemplate(
      IDR_MULTITYPE,
      RUNTIME_CLASS(CMultiDoc),
      RUNTIME_CLASS(CChildFrame),
      RUNTIME_CLASS(CMultiView));
   // Регистрируем шаблон документа
   AddDocTemplate(pDocTemplate);
   // Создаем главное окно приложения (MDI Frame window)
   CMainFrame* pMainFrame = new CMainFrame;
   if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
      return FALSE;
   m_pMainWnd = pMainFrame;
   // Выполняем стандартную обработку командной строки
   // приложения
   CCommandLineInfo cmdInfo;
   ParseCommandLine(cmdInfo);
   // Обрабатываем командную строку приложения
   if (!ProcessShellCommand(cmdInfo))
      return FALSE;
   // Отображаем окно
   pMainFrame->ShowWindow(m_nCmdShow);
   pMainFrame->UpdateWindow();
   return TRUE;
}
В начале InitInstance вызываются методы Enable3dControls и LoadStdProfileSettings. Они уже были описаны в предыдущем томе серии “Библиотека системного программиста”, посвященном MFC, поэтому мы не станем на них останавливаться и перейдем к рассмотрению шаблонов документа приложения.
Затем создается указатель pDocTemplate на объекты класса шаблона документов. Для однооконных приложений это класс CSingleDocTemplate, а для многооконных - CMultiDocTemplate. Создается новый объект класса и указатель на него записывается в переменную pDocTemplate. Для создания шаблона документа используется оператор new.

Конструктору класса CMultiDocTemplate передаются четыре параметра:

CMultiDocTemplate(

   UINT nIDResource,

   CRuntimeClass* pDocClass,

   CRuntimeClass* pFrameClass,

   CRuntimeClass* pViewClass

);

Первый параметр nIDResource определяет идентификатор ресурсов, используемых совместно с типом документов, управляемых шаблоном. К таким ресурсам относятся меню, пиктограмма, строковый ресурс, таблица акселераторов. Для приложения Multi в этом параметре указан идентификатор IDR_MULTITYPE.

Остальные три параметра pDocClass, pFrameClass и pViewClass содержат указатели на объекты класса CRuntimeClass, полученные с помощью макрокоманд RUNTIME_CLASS из классов документа CMultiDoc, дочернего окна MDI CChildFrame и окна просмотра CMultiView. Таким образом, шаблон документа объединяет всю информацию, относящуюся к данному типу документов.

Созданный шаблон документов заносится в список шаблонов, с которыми работает приложение. Для этого указатель на созданный шаблон документа передается методу AddDocTemplate из класса CWinApp. Указатель на шаблон документов передается через параметр pTemplate:

void AddDocTemplate(CDocTemplate* pTemplate);

Указатель pTemplate должен указывать на объект класса CDocTemplate, однако мы передаем через него указатель на объект класса CMultiDocTemplate. Это допустимо, так как класс CDocTemplate является базовым классом для CMultiDocTemplate.

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

После создания шаблона документа создается главное окно MDI (главное окно приложения).

Для создания главного окна приложения мы формируем объект класса CMainFrame и записываем указатель на него в pMainFrame. Класс CMainFrame определен в нашем приложении. Мы расскажем о нем немного позже:

// Создаем главное окно MDI (главное окно приложения)



CMainFrame* pMainFrame = new CMainFrame;

Затем для только что созданного объекта вызывается метод LoadFrame класса CFrameWnd. Он создает окно, загружает ресурсы, указанные первым параметром, и связывает их с объектом класса CMainFrame. Параметр метода LoadFrame определяет меню, пиктограмму, таблицу акселераторов и таблицу строк главного окна приложения:

if (!pMainFrame->LoadFrame(IDR_MAINFRAME))

   return FALSE;

Указатель на главное окно приложения, которым является главное окно MDI, записывается в элемент данных m_pMainWnd главного класса приложения. Элемент данных m_pMainWnd, определенн в классе CWinThread. Когда окно, представленное указателем m_pMainWnd закрывается, приложение автоматически будет завершено (в случае если приложение включает в себя несколько задач, завершается только соответствующая задача):

m_pMainWnd = pMainFrame;

Метод LoadFrame не отображает главное окно приложения на экране. Для этого надо вызвать методы ShowWindow и UpdateWindow:

// Отображаем главное окно приложения

pMainFrame->ShowWindow(m_nCmdShow);

// Обновляем содержимое окна

pMainFrame->UpdateWindow();

В заключение метода InitInstance обрабатывается командная строка приложения. Для этого создается объект cmdInfo класса CCommandLineInfo и для него вызываются методы ParseCommandLine и ProcessShellCommand:

// Просматриваем командную строку приложения в поиске

// стандартных команд и обрабатываем их

CCommandLineInfo cmdInfo;

ParseCommandLine(cmdInfo);

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

// приложения

if (!ProcessShellCommand(cmdInfo))

      return FALSE;


Метод LoadFrame


Виртуальный метод LoadFrame позволяет динамически создавать окно, пользуясь информацией из файла ресурсов. В случае успешного завершения метод LoadFrame возвращает ненулевое значение, а в противном случае - ноль:
virtual BOOL LoadFrame(
   UINT nIDResource,
   DWORD dwDefaultStyle = WS_OVERLAPPEDWINDOW|FWS_ADDTOTITLE,
   CWnd* pParentWnd = NULL,
   CCreateContext* pContext = NULL
);
Параметры метода LoadFrame практически идентичны параметрам метода Create, описанного ранее. Исключение составляет первый параметр - nIDResource. Он представляет идентификатор, общий для нескольких ресурсов, используемых при создании окна. К таким ресурсам относятся меню (будет использоваться как меню окна), строковый ресурс (заголовок окна), пиктограмма (отображается в случае минимизации окна) и таблица клавиш акселерации (используется для ускоренного выбора строк меню).


Метод LoadMenu


После объявления объекта класса CMenu вы можете загрузить меню из ресурсов приложения, воспользовавшись для этой цели методом LoadMenu. В случае успешного завершения метод LoadMenu возвратит ненулевое значение, и нуль в противном случае:
BOOL LoadMenu(LPCTSTR lpszResourceName);
BOOL LoadMenu(UINT nIDResource);
Метод LoadMenu загрузит меню, заданное именем lpszResourceName или идентификатором nIDResource, и свяжет его с соответствующим объектом класса CMenu. Теперь вы можете использовать для управления загруженным меню другие методы класса CMenu.
После того как меню загружено, его можно “подключить” к окну. Для этого следует воспользоваться методом SetMenu входящим в класс CWnd.


Метод OnAppAbout класса CDaterApp


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


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


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


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


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


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


Когда пользователь нажимает правую кнопку мыши в окне, макрокоманда ON_WM_CONTEXTMENU вызывает метод-обработчик OnContextMenu из класса этого окна. Методу OnContextMenu передаются два параметра:
afx_msg void OnContextMenu(CWnd* pWnd, CPoint pos);
Параметр pWnd содержит указатель на объект класса CWnd. Он представляет окно, в котором находился указатель мыши, когда была нажата правая кнопка мыши. Это может быть окно класса к которому принадлежит таблица сообщений или его дочернее окно.
Параметр pos, представляющий объект класса CPoint, содержит координаты указателя мыши, зафиксированные в момент нажатия правой кнопки мыши.
Реализация метода OnContextMenu добавляется в файле MultiView.cpp:
//////////////////////////////////////////////////////////////
// Метод OnContextMenu класса CMultiView
// CG: Метод OnContextMenu добавлен компонентом Pop-up Menu
void CMultiView::OnContextMenu(CWnd*, CPoint point)
{
   // Объект menu будет представлять контекстное меню
   CMenu menu;
   // Загружаем меню CG_IDR_POPUP_MULTI_VIEW
   VERIFY(menu.LoadMenu(CG_IDR_POPUP_MULTI_VIEW));
   // Получаем указатель на всплывающее меню
   CMenu* pPopup = menu.GetSubMenu(0);
   ASSERT(pPopup != NULL);
   // Получаем указатель на объект CWnd, представляющий окно
   //   для которого надо отобразить контекстное меню
   CWnd* pWndPopupOwner = this;
   while (pWndPopupOwner->GetStyle() & WS_CHILD)
      pWndPopupOwner = pWndPopupOwner->GetParent();
   // Отображаем контекстное меню
   pPopup->TrackPopupMenu(
      TPM_LEFTALIGN | TPM_RIGHTBUTTON,
      point.x, point.y,
      pWndPopupOwner);
}
Для вывода контекстного меню на экран используется метод TrackPopupMenu, входящий в класс CMenu. Контекстное меню можно открыть в любом месте экрана. Вне зависимости от расположения меню, все командные сообщения от него передаются одному определенному окну.
Параметры метода TrackPopupMenu задают расположение контекстного меню и выбирают для него окно, в которое будут передаваться командные сообщения:

BOOL TrackPopupMenu(

   UINT nFlags,

   int x,

   int y,

   CWnd* pWnd,

   LPCRECT lpRect = 0

);

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

Если в качестве nFlags указан атрибут TPM_CENTERALIGN, то контекстное меню отображается по центру относительно координаты, указанной параметром x. Если в параметре nFlags установлен атрибут TPM_LEFTALIGN, то параметр x определяет координату левой стороны меню, а если установлен атрибут TPM_RIGHTALIGN - правой.

Кроме атрибутов TPM_CENTERALIGN, TPM_LEFTALIGN или TPM_RIGHTALIGN в параметре nFlags можно установить атрибут TPM_LEFTBUTTON или TPM_RIGHTBUTTON. Атрибут TPM_LEFTBUTTON говорит, что выбор из меню осуществляется нажатием левой, а TPM_RIGHTBUTTON - правой кнопкой мыши.

Назначение параметра x зависит от атрибутов, установленных в параметре nFlags. Параметр y во всех случаях указывает расположение верхней стороны меню. Координаты x и y указываются в экранных координатах.

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

Контекстное меню закрывается, если вы нажмете на кнопку мыши вне меню. Параметр lpRect позволяет указать прямоугольник, внутри которого нажатие на кнопку мыши не будет вызывать закрытия меню. Если этот параметр равен NULL, меню закрывается, если пользователь нажал кнопку мыши в любом месте экрана вне меню.

В случае успешного завершения, метод TrackPopupMenu ненулевое значение, а в противном случае нуль.


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


Метод OnCreate класса CDlgBarWindow сначала вызывает метод OnCreate базового класса CFrameWnd:
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
   return -1;
Затем мы создаем диалоговую панель управления. Для этого вызываем метод Create для объекта m_wndDialogBar, входящего в класс CDlgBarWindow, и представляющего панель управления:
if (!m_wndDialogBar.Create(this, IDD_DIALOG_BAR,
   CBRS_TOP|CBRS_TOOLTIPS,
   IDD_DIALOG_BAR))
   {
      TRACE0("Failed to create dialog bar\n");
      return -1;     
   }
   return 0;
}
В качестве родительского окна для диалоговой панели управления мы указываем главное окно приложения. Ключевое слово this представляет указатель на текущий объект, то есть, в данном случае, окно CDlgBarWindow.
Второй параметр метода указывает идентификатор шаблона диалоговой панели, которая будет отображаться как диалоговая панель управления приложения. Мы указали идентификатор шаблона диалоговой панели IDD_DIALOG_BAR.
В качестве третьего параметра метода Create мы привели стиль CBRS_TOP. Стиль CBRS_TOP устанавливает расположение диалоговой панели управления в верхней части окна приложения.


Метод OnCreate класса CMainFrame


Метод OnCreate класса CMainFrame создает и отображает на экране панели управления и состояния:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
   if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
      return -1;
  
   if (!m_wndToolBar.Create(this)
      !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
   {
      // Ошибка создания панели управления
      TRACE0("Failed to create toolbar\n");
      return -1;   
   }
   if (!m_wndStatusBar.Create(this)
      !m_wndStatusBar.SetIndicators(indicators,
        sizeof(indicators)/sizeof(UINT)))
   {
      // Ошибка создания панели состояния
      TRACE0("Failed to create status bar\n");
      return -1;     
   }
   m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
      CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
   m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
   EnableDocking(CBRS_ALIGN_ANY);
   DockControlBar(&m_wndToolBar);
   return 0;
}
Структура indicators, описывающая индикаторы панели состояния, определена в файле MainFrm.h следующим образом:
static UINT indicators[] =
{
   ID_SEPARATOR,
   ID_INDICATOR_CAPS,
   ID_INDICATOR_NUM,
   ID_INDICATOR_SCRL,
};
Сейчас мы не станем подробно останавливаться на процедуре создания панелей состояния и управления. Во первых, в 24 томе мы уже рассматривали метод OnCreate однооконного приложения Single. Он фактически полностью повторяет метод OnCreate приложения Multi. Во вторых мы посвятили проблеме использования меню, панелей состояния и панелей управления отдельный раздел “Меню, панели управления и панели состояния”. Прочитав его, вы полностью поймете как устроен метод OnCreate класса CMainFrame.

Метод OnCreate создает главное окно приложения и отображает в нем панели управления и состояния. Описание метода OnCreate класса CMainFrame вы можете посмотреть в разделе “Приложение Multi”.
Во время добавления компонента Splash Screen метод OnCreate класса CMainFrame модифицируется. К нему добавляется вызов метода ShowSplashScreen класса CSplashWnd.
Метод ShowSplashScreen класса CSplashWnd, который создает и отображает на экране окно заставки, будет описан нами позже:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
   // Вызываем метод OnCreate базового класса
   if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
      return -1;
  
   // Создаем панель управления toolbar
   if (!m_wndToolBar.Create(this)
      !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
   {
      // Ошибка при создании панели управления toolbar
      TRACE0("Failed to create toolbar\n");
      return -1;
   }
   // Создаем панель состояния status bar
   if (!m_wndStatusBar.Create(this)
      !m_wndStatusBar.SetIndicators(indicators,
        sizeof(indicators)/sizeof(UINT)))
   {
      // Ошибка при создании панели состояния status bar
      TRACE0("Failed to create status bar\n");
      return -1;
   }
   // TODO: вы можете изменить характеристики панели
   // управления, убрав некоторые флаги CBRS_
   m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
      CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
   // TODO: вы можете запретить перемещение панели управления,
   // если удалите следующие три строки программы
   m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
   EnableDocking(CBRS_ALIGN_ANY);
   DockControlBar(&m_wndToolBar);
   // CG: The following line was added by the Splash Screen
   // component.
   CSplashWnd::ShowSplashScreen(this);
   return 0;
}



Метод OnCreate класса CMainFrame создает главное окно приложения, и размещает в нем панель управления IDR_MAINFRAME и стандаартную панель состояния.


Метод OnCreate класса CMultiBarWindow


Метод OnCreate класса CMultiBarWindow сначала вызывает метод OnCreate базового класса CFrameWnd.
Чтобы разрешить перемещение панелей управления, вызываем метод EnableDocking для главного окна приложения.  Чтобы разрешить присоединение панелей управления ко всем сторонам окна, передаем методу EnableDocking значение CBRS_ALIGN_ANY:
// Разрешаем присоединение панелей управления ко
// всем сторонам окна CMultiBarWindow
EnableDocking(CBRS_ALIGN_ANY);
¨ Создание панели управления Player
Затем мы приступаем к созданию трех панелей управления. Сначала создается панель управления Player. В ней расположен ряд кнопок, три из которых объединены в переключатель с зависимой фиксацией и еще одна кнопка является переключателем.
Чтобы создать панель управления Player, вызывается метод Create объекта m_wndPlayerBar. Ему передаются набор флагов, определяющий характеристики панели управления. Флаг CBRS_SIZE_DYNAMIC определяет, что пользователь сможет менять форму панели управления Player. Флаг CBRS_BOTTOM задает начальное положение панели - вверху окна приложения. Флаг CBRS_TOOLTIPS разрешает отображение кратких подсказок для тех кнопок панели управления, которые имеют соответствующие строковые ресурсы:
if (!m_wndPlayerBar.Create(this, WS_CHILD | WS_VISIBLE |
   CBRS_SIZE_DYNAMIC | CBRS_BOTTOM  |
   CBRS_TOOLTIPS, ID_Player))
{
   // Ошибка при создании панели управления
   TRACE0("Failed to create toolbar\n");
   return -1;
}
После создания панели управления загружаем ресурс IDR_PLAYER, описывающий кнопки панели управления:
if (!m_wndPlayerBar.LoadToolBar(IDR_PLAYER))
{
   // Ошибка при загрузке ресурса панели управления
   TRACE0("Failed to load toolbar\n");
   return -1;
}
Когда панель управления отображается в мини-окне, она имеет заголовок. Чтобы установить текст в этих заголовках, вызываем метод SetWindowText:
m_wndPlayerBar.SetWindowText("Player");     
Теперь мы указываем, что кнопки панели управления с идентификаторами ID_TYPE, ID_CD_DRV и ID_WAVE составляют трехпозиционный переключатель с зависимой фиксацией. Для этого мы последовательно определяем стиль каждой из этих кнопок и добавляем к ним стиль TBBS_CHECKGROUP:

nIndex = m_wndPlayerBar.CommandToIndex(ID_TYPE);

nBarStyle = m_wndPlayerBar.GetButtonStyle(nIndex) |

            TBBS_CHECKGROUP;

m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle);

nIndex = m_wndPlayerBar.CommandToIndex(ID_CD_DRV);

nBarStyle =  m_wndPlayerBar.GetButtonStyle(nIndex) |

             TBBS_CHECKGROUP;

m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle);

nIndex = m_wndPlayerBar.CommandToIndex(ID_WAVE);

nBarStyle =  m_wndPlayerBar.GetButtonStyle(nIndex) |

             TBBS_CHECKGROUP;

m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle);

Далее кнопку с идентификатором ID_PAUSE мы превращаем в переключатель. Для этого определяем стиль этой кнопки и добавляем к нему стиль TBBS_CHECKBOX:

nIndex = m_wndPlayerBar.CommandToIndex(ID_PAUSE);

nBarStyle =  m_wndPlayerBar.GetButtonStyle(nIndex) |

             TBBS_CHECKBOX;

m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle);

Когда стили всех кнопок панели управления установлены, разрешаем присоединять ее к любой стороне родительского окна. Для этого вызываем метод EnableDocking, передав ему в качестве параметра значение CBRS_ALIGN_ANY:

m_wndPlayerBar.EnableDocking(CBRS_ALIGN_ANY);

Последним шагом в процессе создания панели управления Player является вызов метода DockControlBar для окна приложения. Этот метод пристывковывает панель управления Player к родительскому окну:

DockControlBar(&m_wndPlayerBar);

¨ Создание панели управления Style

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

Чтобы создать панель управления Style вызывается метод Create объекта m_wndStyleBar. Ему передается набор флагов, определяющий характеристики панели управления. Флаг CBRS_SIZE_FIXED указывает, что панель управления имеет фиксированную форму, которую пользователь не сможет изменить. Флаг CBRS_TOP задает начальное положение панели - вверху окна приложения. Флаг CBRS_TOOLTIPS разрешает отображение кратких подсказок для тех кнопок панели управления, которые имеют соответствующие строковые ресурсы.



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

if (!m_wndStyleBar.Create(this, WS_CHILD | WS_VISIBLE |

   CBRS_SIZE_FIXED | CBRS_TOP | CBRS_TOOLTIPS, ID_Style))

{

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

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

   return -1;

}

После создания панели управления загружаем ресурс IDR_STYLE, описывающий кнопки панели управления:

if (!m_wndStyleBar.LoadToolBar(IDR_STYLE))

{

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

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

   return -1;

}

Когда панель управления создана, вызываем метод SetWindowText, чтобы установить текст в ее заголовке:

m_wndStyleBar.SetWindowText("Style");  

Панель управления Style задумана нами как панель управления, которая все время отображается в отдельном мини-окне. Поэтому мы запрещаем пристывковывать панель управления Player к родительскому окну. Для этого вызываем метод EnableDocking, указав ему в качестве параметра нулевое значение:

m_wndStyleBar.EnableDocking(0);

Чтобы установить форму панели управления (разделить кнопки на несколько рядов) добавляем к стилям кнопок, завершающих каждый ряд, стиль TBBS_WRAPPED:

nIndex = m_wndStyleBar.CommandToIndex(ID_SUBSCRIPT);

nBarStyle =  m_wndStyleBar.GetButtonStyle(nIndex) |

             TBBS_WRAPPED;

m_wndStyleBar.SetButtonStyle(nIndex, nBarStyle);

 

nIndex = m_wndStyleBar.CommandToIndex(ID_TEXT_JUSTIFY);

nBarStyle =  m_wndStyleBar.GetButtonStyle(nIndex) |

             TBBS_WRAPPED;

m_wndStyleBar.SetButtonStyle(nIndex, nBarStyle);

Когда стили всех кнопок установлены, вызываем метод FloatControlBar главного окна приложения, чтобы вывести панель управления Style в отдельном мини-окне. В качестве координат, в которых отображается панель Style, произвольно выбираем точку (100,100):

CPoint pointStyleBar(100, 100);

FloatControlBar(&m_wndStyleBar, pointStyleBar);



¨ Создание панели управления Extended

Панель управления Extended содержит дополнительные органы управления - текстовый редактор и список combo-box.

Чтобы создать панель управления Extended вызывается метод Create объекта m_wndExtendedBar. Ему передаются набор флагов, определяющий характеристики панели управления. Флаг CBRS_SIZE_DYNAMIC указывает, что пользователь может изменить форму панели управления. Флаг CBRS_TOP задает начальное положение панели - вверху окна приложения. Флаг CBRS_TOOLTIPS разрешает отображение кратких подсказок для тех кнопок панели управления, которые имеют соответствующие строковые ресурсы:

if(!m_wndExtendedBar.Create(this,

   WS_CHILD | WS_VISIBLE | CBRS_SIZE_DYNAMIC |

   CBRS_TOP | CBRS_TOOLTIPS, ID_Extended))

{

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

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

   return -1;

}

После создания панели управления загружаем ресурс IDR_ EXTENDED, описывающий кнопки панели управления:

if(!m_wndExtendedBar.LoadToolBar(IDR_EXTENDED))

{

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

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

   return -1;

}

Когда панель управления создана, вызываем метод SetWindowText, чтобы установить текст в ее заголовке:

m_wndExtendedBar.SetWindowText("Extended");     

Теперь мы приступаем к созданию дополнительных органов управления - текстового редактора и списка combo-box. Эти органы управления размещаются в панелях управления на месте разделителей.

¨ Отображаем текстовый редактор

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

m_wndExtendedBar.SetButtonInfo(2, IDW_EDIT,

                               TBBS_SEPARATOR, 130);

Этот метод увеличивает размер первого разделителя, имеющего индекс 2, до 130 пикселов в ширину. Теперь надо определить координаты прямоугольной области разделителя в которых будет размещен текстовый редактор:



CRect rectEdit;

m_wndExtendedBar.GetItemRect(2, &rectEdit);

Метод GetItemRect записывает в rectEdit координаты разделителя. Чтобы отделить текстовый редактор от соседних кнопок, уменьшаем ширину прямоугольника rectEdit, делая отступ по 6 пикселов с правой и с левой стороны:

rectEdit.left += 6;

rectEdit.right -= 6;

Координаты прямоугольной области для текстового редактора вычислены и мы вызываем метод Create для текстового редактора m_edit, который, собственно, и размещает текстовый редактор на панели управления:

if(!m_wndExtendedBar.m_edit.Create(WS_CHILD |

   ES_AUTOHSCROLL|WS_VISIBLE|WS_TABSTOP|WS_BORDER,

   rectEdit, &m_wndExtendedBar, IDW_EDIT))

{

   // Ошибка при создании текстового редактора

   TRACE0("Failed to create edit-box\n");

   return FALSE;

}

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

Стиль ES_AUTOHSCROLL позволяет вводить в текстовом редакторе длинные строки. Если строка не помещается в окне редактора, она сдвигается.

Стиль WS_VISIBLE устанавливается, чтобы текстовый редактор появился на экране сразу после создания. Если его не указать, то текстовый редактор останется невидимым.

Мы установили для текстового редактора стиль WS_BORDER, чтобы лучше выделить его на фоне панели управления. Этот стиль отображает вокруг текстового редактора тонкую рамку.

Панель управления не позволяет использовать клавишу для перемещения фокуса ввода между кнопками. Однако если вы размещаете на панели управления дополнительные органы управления, для них можно установить стиль WS_TABSTOP. Тогда вы получите возможность перемещать фокус ввода между ними, нажимая клавишу . Мы установили стиль WS_TABSTOP для двух дополнительных органов управления - текстового редактора и списка combo-box.



¨ Отображаем список combo-box

Теперь, когда текстовый редактор появился в панели управления, мы повторяем проделанные шаги и отображаем список combo-box.

Увеличиваем размер второго разделителя панели управления, который имеет индекс 4 до 150 пикселов:

m_wndExtendedBar.SetButtonInfo(4, IDW_COMBO,

   TBBS_SEPARATOR, 150);

Определяем координаты прямоугольной области панели управления, занимаемой этим разделителем, и уменьшаем ее ширину на 6 пикселов с каждой стороны:

CRect rectComboBox;

m_wndExtendedBar.GetItemRect(4, &rectComboBox);

rectComboBox.left += 6;

rectComboBox.right -= 6;

Список combo-box раскрывается вниз. Отводим для него дополнительно 80 пикселов. Если не увеличить вертикальные размеры прямоугольной области, предназначенной для размещения списка combo-box, то вы не сможете его открыть. Для этого просто не хватит высоты панели управления:

rectComboBox.bottom = rectComboBox.top + 80;

Для создания списка combo-box вызываем метод Create для объекта m_combo_box. Он размещает список в прямоугольной области rectComboBox:

if(!m_wndExtendedBar.m_combo_box.Create(

   CBS_DROPDOWN | WS_CHILD | WS_VISIBLE | WS_VSCROLL |

   ES_AUTOHSCROLL | CBS_DISABLENOSCROLL,

   rectComboBox, &m_wndExtendedBar, IDW_COMBO))

{

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

   TRACE0("Failed to create combo-box\n");

   return FALSE;

}

Как и при создании текстового редактора, мы передали методу Create несколько стилей, определяющих режим работы и характеристики списка combo-box.

Так как список размещается в панели управления, то он является его дочерним окном. Поэтому мы указали для него стиль WS_CHILD. Вы можете опустить стиль WS_CHILD. В этом случае он будет установлен автоматически в процессе создания списка combo-box.

Стиль WS_VISIBLE устанавливается, чтобы список появился на экране сразу после создания. Если его не указать, список останется невидимым.

Стиль WS_VSCROLL добавляет к списку combo-box вертикальную полосу просмотра, если в списке слишком много строк. Мы также добавили стиль CBS_DISABLENOSCROLL, означающий что вертикальная полоса просмотра отображается, даже если все строки помещаются в списке. В этом случае, однако, полоса просмотра отображается серым цветом и не доступна для использования.



Внешний вид списка combo- box определяется стилем CBS_DROPDOWN, который означает, что список будет открываться в случае нажатия на кнопку Метод OnCreate класса CMultiBarWindow.

Мы также установили для списка стиль WS_TABSTOP, который позволяет использовать клавишу для передачи ему фокуса ввода. Напомним, что этот стиль также установлен для текстового редактора.

Сразу после создания списка combo-box мы записываем в него три строки: One, Two и Third. Вы увидите эти строки если откроете список combo-box в панели управления приложения.

Для добавления новых строк к списку нами используется метод AddString класса CComboBox:

m_wndExtendedBar.m_combo_box.AddString("One");

m_wndExtendedBar.m_combo_box.AddString("Two");

m_wndExtendedBar.m_combo_box.AddString("Third");

Когда стили всех кнопок панели управления выбраны, разрешаем присоединять ее к любой стороне родительского окна. Для этого вызываем метод EnableDocking, передав ему в качестве параметра значение CBRS_ALIGN_ANY:

m_wndExtendedBar.EnableDocking(CBRS_ALIGN_ANY );

Последним шагом в процессе создания панели управления Extended является вызов метода DockControlBar для окна приложения. Этот метод пристывковывает панель управления Extended к родительскому окну:

DockControlBar(&m_wndExtendedBar);


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


Метод OnCreate класса CMultiMenuWindow сначала вызывает метод OnCreate базового класса CFrameWnd, чтобы создать главное окно приложения:
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
   return -1;
Затем мы создаем панель состояния, указывая в качестве ее родительского окна главное окно приложения. Для этого мы вызываем метод Create объекта m_wndStatusBar, представляющего панель состояния, передавая ему в качестве параметра значение this. В данном случае это означает, что окно приложения является родительским окном для панели состояния:
if(!m_wndStatusBar.Create(this))
{
   // Ошибка при создании панели состояния
   TRACE0("Failed to create status bar\n");
   return -1;  
}
После того, как панель состояния создана, отображаем на ней единственный индикатор, вызывая метод SetIndicators. В качестве первого параметра передаем методу SetIndicators идентификатор этого единственного индикатора панели состояния, записанный в переменной indicator. Второй параметр метода SetIndicators, равен единице. Он определяет, что индикатор в панели состояния будет только один:
if(!m_wndStatusBar.SetIndicators(&indicator,1))
{
   // Ошибка при установке индикатора
   TRACE0("Failed to set indicators\n");
   return -1;  
}
Более подробно о принципах устройства панелей состояния мы расскажем в отдельном разделе, который носит название “Панель состояния”.


Метод OnCreate класса CSplashWnd


Метод OnCreate класса вызывается при создании окна CSplashWnd, когда через таблицу сообщений проходит сообщение WM_CREATE. Реализация метода OnCreate класса CSplashWnd сначала вызывает метод OnCreate базового класса CWnd, который собственно и создает окно:
if (CWnd::OnCreate(lpCreateStruct) == -1)
   return -1;
Далее вызывается метод CenterWindow, который выполняет центровку окна на экране:
CenterWindow();
Метод CenterWindow определен в классе CWnd следующим образом:
void CenterWindow(CWnd* pAlternateOwner = NULL);
Если вызвать метод CenterWindow без указания параметров, или указать в качестве параметра значение NULL, то дочернее окно будет отцентровано относительно своего родительского окна, а всплывающее окно - относительно его владельца. Воспользовавшись параметром pAlternateOwner, вы можете указать другие окна относительно которых надо выполнить центровку.
Затем метод OnCreate создает таймер, посылающий окну CSplashWnd сообщения с идентификатором 1 каждые 750 миллисекунд:
SetTimer(1, 750, NULL);
Метод SetTimer определен в классе CWnd и имеет три параметра. Параметр nIDEvent задает идентификатор, который будет выступать в качестве идентификатора сообщений WM_TIMER от таймера. Таймер будет вырабатывать сообщения с периодом, приведенным во втором параметре метода - nElapse:
UINT SetTimer(
   UINT nIDEvent,
   UINT nElapse,
   void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD)
);
Если третий параметр lpfnTimer равен NULL, то сообщения от таймера передаются окну для которого вызван метод SetTimer. Именно так используется метод SetTimer компонентом Splash Screen. Все сообщения таймера поступают окну CSplashWnd и обрабатываются в его таблице сообщений.
В качестве параметра lpfnTimer можно указать имя функции обратного вызова, которая будет обрабатывать сообщения WM_TIMER вместо таблицы сообщений окна. Более подробно об использовании таймера и, в частности, о функции обратного вызова таймера читайте в 11 томе серии “Библиотека системного программиста”.
Метод SetTimer возвращает в случае успешного создания таймера его идентификатор - ненулевое значение. Если таймер не создан, метод возвращает нулевое значение.


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


Метод OnCreate класса CStateWindow сначала вызывает метод OnCreate базового класса CFrameWnd:
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
   return -1;
Затем мы создаем панель состояния, указывая в качестве ее родительского окна главное окно приложения:
if(!m_wndStatusBar.Create(this))
{
   // Ошибка при создании панели состояния
   TRACE0("Failed to create status bar\n");
   return -1;  
}
После того, как панель состояния создана, вызываем метод SetIndicators, чтобы установить индикаторы:
if(!m_wndStatusBar.SetIndicators(indicators,
                      sizeof(indicators)/sizeof(UINT)))
{
   // Ошибка при установке индикаторов
   TRACE0("Failed to set indicators\n");
   return -1;  
}
Массив, содержащий идентификаторы индикаторов indicators определен в приложении следующим образом:
static UINT indicators[] =
{
   ID_SEPARATOR,           // Самый первый индикатор
   ID_INDICATOR_OVR,       // Индикатор OVR
   ID_INDICATOR_PROGRESS,  // Резервирование места для
                           // progress bar
   ID_INDICATOR_CAPS,      // Индикатор клавиши
   ID_INDICATOR_NUM,       // Индикатор клавиши
   ID_INDICATOR_SCRL,      // Индикатор клавиши
   ID_INDICATOR_TEXT,      // Индикатор TEXT/PIC
   ID_INDICATOR_ADD,       // Индикатор ADD/SUB (начальное
                           // состояние START)
};
Порядок идентификаторов в массиве indicators соответствует порядку в котором индикаторы будут отображаться в панели состояния. Размер всех индикаторов, кроме первого, выбирается автоматически, так чтобы текст индикатора полностью в нем поместился. Текст индикатора, который отображается в нем по умолчанию, берется из соответствующих строковых ресурсов приложения. Так, например, в последнем индикаторе панели состояния, который имеет идентификатор ID_INDICATOR_ADD будет отображаться строка START, имеющая тот же идентификатор и определенная в ресурсах приложения следующим образом:
STRINGTABLE DISCARDABLE
BEGIN
    ID_INDICATOR_ADD        "START"
END
Все индикаторы панели состояния, кроме индикатора ID_INDICATOR_PROGRESS, отображаются стандартным образом. Стиль индикатора ID_INDICATOR_PROGRESS устанавливается отдельно:
m_wndStatusBar.SetPaneInfo(
   m_wndStatusBar.CommandToIndex(ID_INDICATOR_PROGRESS),
   ID_INDICATOR_PROGRESS,
   SBPS_DISABLED |   // текст не отображается
   SBPS_NOBORDERS,   // рамка вокруг индикатора отсутствует
   150 );            // ширина индикатора 150 пикселов
Метод SetPaneInfo запрещает отображение текста внутри индикатора и убирает выделяющую рамку. Кроме того, метод SetPaneInfo устанавливает размер индикатора 150 пикселов.


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


Когда пользователь выбирает из меню Menu строку Disable или нажимает комбинацию клавиш , приложению поступает командное сообщение, которое имеет идентификатор ID_MENU_DISABLE. Для обработки этого сообщения вызывается метод OnDisable класса CMultiMenuWindow. Этот метод изменяет состояние флагов bEnable и nCheck.
Значение флага bEnable изменяется с TRUE на FALSE и наоборот, а значение флага bEnable с 1 на 0 и наоборот:
bEnable = !bEnable;
nCheck = (nCheck == 1) ? 0 : 1;
Сам метод OnDisable не меняет состояния строк меню приложения, но изменение флагов bEnable и nCheck фиксируется обработчиком команд обновления меню.
Так, флаг bEnable управляет блокировкой строки Process меню Mission (для полного варианта меню) и строки Process меню File (для укороченного варианта меню). Флаг bEnable проверяется методом OnUpdateProcess, который является обработчиком команд обновления от этих строк меню.
Флаг nCheck управляет отображением символа Ö около строки Disable меню Menu. Флаг nCheck проверяется методом OnUpdateDisable, который является обработчиком команд обновления от этой строки меню.


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


Метод OnDraw первоначально определен в классе CView как виртуальный и вызывается, когда приложение должно перерисовать документ в окне просмотра. MFC AppWizard переопределяет для вас метод OnDraw класса CView следующим образом:
void CMultiView::OnDraw(CDC* pDC)
{
   CMultiDoc* pDoc = GetDocument();
   ASSERT_VALID(pDoc);
   // TODO:
}
Первые две строки метода OnDraw получают указатель pDoc на документ, связанный с данным окном просмотра.


Метод OnGetRecordset класса CDaterView


Метод OnGetRecordset класса CDaterView возвращает указатель m_pSet на запись базы данных:
//////////////////////////////////////////////////////////////
// Метод OnGetRecordset класса CDaterView
CRecordset* CDaterView::OnGetRecordset()
{
   return m_pSet;
}


Метод OnInitialUpdate класса CDaterView


Метод OnInitialUpdate класса окна просмотра CDaterView первоначально определен в базовом классе CView. Этот метод вызывается MFC перед отображением окна просмотра на экране:
//////////////////////////////////////////////////////////////
// Метод OnInitialUpdate класса CDaterView
void CDaterView::OnInitialUpdate()
{
   m_pSet = &GetDocument()->m_daterSet;
   CRecordView::OnInitialUpdate();
}
В момент вызова метода OnInitialUpdate окно просмотра уже связано с объектом документа приложения, поэтому можно использовать метод GetDocument.
В нашем случае метод GetDocument используется, чтобы записать в переменную m_pSet (входящую в класс CDaterView) укзатель на объект m_daterSet класса CDaterSet, представляющий записи базы данных и входящий в класс документа приложения - класс CDaterDoc.
Затем вызывается метод OnInitialUpdate базового класса CRecordView.


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


Когда пользователь выбирает из меню Work строки Direct set ADD и Direct set SUB, в класс окна поступают командные сообщения с идентификаторами ID_WORK_DIRECT_ADD и ID_WORK_DIRECT_SUB. Для их обработки вызывается метод OnMenuDirectADD_SUB:
ON_COMMAND_EX(ID_WORK_DIRECT_ADD, OnMenuDirectADD_SUB)
ON_COMMAND_EX(ID_WORK_DIRECT_SUB, OnMenuDirectADD_SUB)
В качестве параметра nID методу OnMenuDirectADD_SUB передается соответствующий идентификатор:
BOOL  CStateWindow::OnMenuDirectADD_SUB(UINT nID)
{
}
Порядок индикаторов в панели состояния не меняется, например, индикатор с идентификатором ID_INDICATOR_ADD, всегда будет иметь в нашем приложении индекс 7. Однако чтобы продемонстрировать метод CommandToIndex и сделать метод OnMenuDirectADD_SUB более независимым от расположения индикаторов, мы определяем индекс индикатора ID_INDICATOR_ADD:
int nIndex = m_wndStatusBar.CommandToIndex(ID_INDICATOR_ADD);
Следующим шагом мы устанавливаем нормальный режим отображения индикатора ID_INDICATOR_ADD. Для этого вызываем метод SetPaneStyle, указав ему индекс индикатора и атрибут SBPS_NORMAL:
m_wndStatusBar.SetPaneStyle(nIndex, SBPS_NORMAL);
Затем определяем, какое командное сообщение послужило причиной вызова метода OnMenuDirectADD_SUB. Если метод вызван для обработки командного сообщения от строки Direct set ADD меню Work, отображаем в индикаторе текст ADD:
if(nID == ID_WORK_DIRECT_ADD)
{
   // Выводим текст ADD
   m_wndStatusBar.SetPaneText(nIndex, "ADD");
}
Если метод OnMenuDirectADD_SUB вызван для обработки командного сообщения от строки Direct set SUB меню Work, изменяем внешний вид индикатора и отображаем в нем текст SUB:
else if(nID == ID_WORK_DIRECT_SUB)
{
   // Изменяем внешний вид индикатора
   m_wndStatusBar.SetPaneStyle(nIndex, SBPS_POPOUT);
   // Выводим текст SUB
   m_wndStatusBar.SetPaneText(nIndex, "SUB");
}
Для вывода текста в индикаторе ID_INDICATOR_ADD в методе OnMenuDirectADD_SUB мы используем метод SetPaneText класса CStatusBar. Метод SetPaneText не меняет размера индикатора. Поэтому, если вы желаете отобразить текст большей длины, надо увеличить размер индикатора с помощью метода SetPaneInfo.

Метод OnMenuDirectADD_SUB класса CStateWindow вызывается для обработки командного сообщения с идентификатором ID_WORK_DISABLE_ADDSUB, передаваемым при выборе из меню Work строки Disable ADD SUB.
Метод OnMenuDisableADD_SUB определяет индекс индикатора ID_INDICATOR_ADD, а затем блокирует его.
Чтобы узнать индекс индикатора ID_INDICATOR_ADD, мы вызываем метод CommandToIndex:
int nIndex = m_wndStatusBar.CommandToIndex(ID_INDICATOR_ADD);
   Для блокировки индикатора вызываем метод SetPaneStyle, которому указываем индекс индикатора и атрибут SBPS_DISABLED:
m_wndStatusBar.SetPaneStyle(nIndex, SBPS_DISABLED);


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


Пользователь может завершить приложение, выбрав из меню File строку Exit. В этом случае приложению передается командное сообщение с идентификатором ID_FILE_EXIT. Соответствующая макрокоманда ON_COMMAND из таблицы сообщений класса CStateWindow вызывает для обработки этого сообщения метод OnMenuExit:
ON_COMMAND(ID_WORK_EXIT, OnMenuExit)
Метод OnMenuExit завершает работу приложения, для чего вызывает метод DestroyWindow, определенный в классе CWnd, для главного окна приложения:
void CMultiMenuWindow::OnExit()
{
   // Завершаем приложение
   DestroyWindow();
   return;
}


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


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


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


Когда пользователь выбирает из меню Work строку Process, на месте индикатора ID_INDICATOR_PROGRESS создается линейный индикатор progress bar, плавно меняющий свое состояние. Обработка командного сообщения от строки Process меню Work осуществляется методом OnMenuProcessBar класса CStateWindow.
Метод OnMenuProcessBar определяет координаты индикатора ID_INDICATOR_PROGRESS и записывает их во временную переменную rectProgress:
RECT rectProgress;
m_wndStatusBar.GetItemRect(
   m_wndStatusBar.CommandToIndex(ID_INDICATOR_PROGRESS),
   &rectProgress);
Затем на месте этого индикатора создается линейный индикатор progress bar. Орган управления progress bar представлен объектом ctrlProgressBar класса CProgressCtrl:
CProgressCtrl ctrlProgressBar;
Непосредственно для создания progress bar используется метод Create класса CProgressCtrl. В качестве параметров этому методу указываются атрибуты WS_CHILD и WS_VISIBLE, координаты rectProgress, объект m_wndStatusBar и идентификатор 1:
if(!ctrlProgressBar.Create(WS_CHILD | WS_VISIBLE,
                           rectProgress, &m_wndStatusBar, 1))
{
   // Ошибка при создании progress bar
   TRACE0("Failed to create progress bar\n");
   return;
}
После создания полосы progress bar устанавливаем границы (от 0 до 100), в которых можно менять его значение:
ctrlProgressBar.SetRange(0, 100);
Выбираем шаг приращения для progress bar, равный единице:
ctrlProgressBar.SetStep(1);
Затем начинаем в цикле изменять значение линейного индикатора progress bar. Чтобы замедлить ход заполнения линейного индикатора, делаем короткую задержку, вызывая функцию Sleep:
for(int i=0;i<100;i++)
{
   Sleep(10); ctrlProgressBar.StepIt();
}
Когда линейный индикатор progress bar окажется заполнен, вызываем метод SetWindowText, который отображает сообщение Process completed в самом первом индикаторе панели состояния:
m_wndStatusBar.SetWindowText("Process completed");
После завершения метода OnMenuProcessBar объект ctrlProgressBar, представляющий линейный индикатор progress bar, уничтожается и одновременно его изображение исчезает с панели состояния.


Метод OnPaint класса CSplashWnd


Метод OnPaint класса CSplashWnd вызывается для обработки сообщений WM_PAINT, когда надо перерисовать изображение в окне. Компонент Splash Screen использует этот метод, чтобы вывести изображение bitmap в окне заставки:
void CSplashWnd::OnPaint()
{
   CPaintDC dc(this);
   CDC dcImage;
   if (!dcImage.CreateCompatibleDC(&dc))
      return;
   BITMAP bm;
   m_bitmap.GetBitmap(&bm);
   // Paint the image.
   CBitmap* pOldBitmap = dcImage.SelectObject(&m_bitmap);
   dc.BitBlt(0, 0, bm.bmWidth, bm.bmHeight,
               &dcImage, 0, 0, SRCCOPY);
   dcImage.SelectObject(pOldBitmap);
}


Метод OnTimer класса CSplashWnd


Метод OnTimer класса CSplashWnd вызывается для обработки сообщений от таймера, созданного в методе OnCreate. Этот метод вызывается всего один раз. При обработке первого сообщения от таймера он закрывает окно CSplashWnd и вместе с ним прекращает работу таймер.
Для того чтобы закрыть окно заставки, используется метод HideSplashScreen, определенный в классе CSplashWnd:
HideSplashScreen();


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


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


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


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


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


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


Метод PostNcDestroy класса CSplashWnd


Метод PostNcDestroy класса CSplashWnd вызывается уже после того как окно заставки закрыто и используется дл того, чтобы удалить объект класса CSplashWnd, представляющий это окно в нашем приложении:
delete this;


Метод PreCreateWindow класса CChildFrame


Метод PreCreateWindow вызывается перед созданием дочернего окна MDI. Вы можете использовать его, чтобы переопределить стили этого окна:
BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
{
   // TODO:
   return CMDIChildWnd::PreCreateWindow(cs);
}


Метод PreCreateWindow класса CDaterDoc


Метод OnNewDocument вызывается, когда надо создать новый документ для приложения. Метод OnNewDocument приложения Dater вызывает метод OnNewDocument базового класса CDocument:
BOOL CDaterDoc::OnNewDocument()
{
   if (!CDocument::OnNewDocument())
      return FALSE;
   // TODO:
   return TRUE;
}


Метод PreCreateWindow класса CDaterView


Метод PreCreateWindow вызывает метод PreCreateWindow базового класса CRecordView и выполняет обработку по умолчанию:
BOOL CDaterView::PreCreateWindow(CREATESTRUCT& cs)
{
   // TODO:
   return CRecordView::PreCreateWindow(cs);
}


Метод PreCreateWindow класса CMainFrame


Метод PreCreateWindow вызывается перед созданием окна и позволяет изменить его характеристики. В нашем приложении метод PreCreateWindow не используется и просто выполняет обрработку по умолчанию:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
   // TODO:
   return CMDIFrameWnd::PreCreateWindow(cs);
}

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


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


Виртуальный метод PreCreateWindow определен в классе CWnd. Он вызывается непосредственно перед созданием окна, связанного с объектом класса. MFC AppWizard переопределяет этот метод следующим образом:
BOOL CMultiView::PreCreateWindow(CREATESTRUCT& cs)
{
   // TODO:
   return CView::PreCreateWindow(cs);
}


Метод PreTranslateAppMessage класса CSplashWnd


Класс CSplashWnd имеет свою таблицу сообщений. Так как в классе CSplashWnd переопределен метод PreTranslateAppMessage, то он вызывается для каждого сообщения, поступающего в это окно перед его обработкой.
В самом начале метод PreTranslateAppMessage проверяет, существует ли окно заставки. Если нет, то дополнительная обработка сообщений не выполняется:
if (c_pSplashWnd == NULL)
   return FALSE;
Когда окно заставки уже создано, метод PreTranslateAppMessage определяет тип сообщения. Если сообщение поступило от мыши или клавиатуры, то для окна заставки, представленного объектом c_pSplashWnd, вызывается метод HideSplashScreen, который его закрывает не дожидаясь первого сообщения от таймера. Метод PreTranslateAppMessage в этом случае возвращает значение TRUE и обработка сообщения завершается:
if (pMsg->message == WM_KEYDOWN
   pMsg->message == WM_SYSKEYDOWN
   pMsg->message == WM_LBUTTONDOWN
   pMsg->message == WM_RBUTTONDOWN
   pMsg->message == WM_MBUTTONDOWN
   pMsg->message == WM_NCLBUTTONDOWN
   pMsg->message == WM_NCRBUTTONDOWN
   pMsg->message == WM_NCMBUTTONDOWN)
{
   c_pSplashWnd->HideSplashScreen();
   return TRUE;   // message handled here
}
Если поступило какое-либо другое сообщение, метод PreTranslateAppMessage возвращает значение FALSE и оно передается далее для дальнейшей обработки:
return FALSE;


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


Виртуальный метод PreTranslateMessage, переопределенный в главном классе приложения CMultiApp, первоначально определен в базовом классе CWinApp. Метод PreTranslateMessage вызывается из цикла обработки сообщений перед тем как очередное сообщение будет распределено по назначению при помощи функций TranslateMessage и DispatchMessage. В качестве параметра pMsg, передается указатель на структуру типа MSG, которая представляет очередное сообщение, полученное в цикле обработки сообщений из очереди:
virtual BOOL PreTranslateMessage(MSG* pMsg);
Таким образом метод PreTranslateMessage позволяет выполнить дополнительную обработку сообщений или даже полностью изъять их из обработки (отфильтровать).
Переопределив метод PreTranslateMessage вы должны возвращать нулевое значение, если данное сообщение должно быть обработано обычным образом или ненулевое значение, если метод PreTranslateMessage сам выполняет обработку сообщения и оно не должно передаваться приложению для дальнейшей обработки.
Во время вставки в проект компонента Splash Screen в метод PreTranslateMessage главного класса приложения добавляется вызов метода PreTranslateAppMessage класса CSplashWnd.
Если метод PreTranslateAppMessage класса CSplashWnd возвращает значение TRUE, тогда метод PreTranslateAppMessage класса CMultiApp также сразу завершается и возвращает значение TRUE. Сообщения, обработанные классом CSplashWnd более не передаются приложению для дальнейшей обработки.
Если метод PreTranslateAppMessage класса CSplashWnd возвращает значение FALSE, тогда вызывается метод PreTranslateMessage базового класса CWinApp.
Заметим, что по умолчанию метод PreTranslateMessage класса CWinApp выполняет некоторую дополнительную обработку сообщений, например выделяет сообщения от клавиатуры, соответствующие комбинациям клавиш, определенным в таблице акселераторов и преобразует их в командные сообщения. Более подробно о таблице акселераторов вы можете прочитать в разделе “Таблица акселераторов” главы “Меню, панели управления и панели состояния”, а также в 13 томе серии “Библиотека системного программиста”:
BOOL CMultiApp::PreTranslateMessage(MSG* pMsg)
{
   // CG: The following lines were added by the Splash Screen
   // component.
   if(CSplashWnd::PreTranslateAppMessage(pMsg))
      return TRUE;
   return CWinApp::PreTranslateMessage(pMsg);
}
В случае, если сообщение передается для обработки методу PreTranslateMessage базового класса CWinApp - CWinApp::PreTranslateMessage, метод PreTranslateMessage класса CMultiApp возвращает результат работы этого метода.


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


Кроме добавления новой макрокоманды к таблице сообщений класса CMultiView и соответствующего метода-обработчика OnContextMenu, компонент Pop-up Menu добавляет метод PreTranslateMessage к классу CMultiView.
В него записывается программный код, который обнаруживает нажатие комбинации клавиш или специальной клавиши на клавиатуре с дополнительными клавишами Windows 95 и напрямую вызывает метод OnContextMenu:
//////////////////////////////////////////////////////////////
// Метод PreTranslateMessage класса CMultiView
BOOL CMultiView::PreTranslateMessage(MSG* pMsg)
{
   // CG: Следующий блок добавлен компонентом Pop-up Menu
   {
   // Если нажата комбинация клавиш
      if((((pMsg->message == WM_KEYDOWN
               pMsg->message == WM_SYSKEYDOWN) &&
           (pMsg->wParam == VK_F10) &&
           (GetKeyState(VK_SHIFT) & ~1)) != 0)
           // it's Shift+F10 OR Natural keyboard key
              (pMsg->message == WM_CONTEXTMENU))
      {
   // Определяем экранные координаты клиентской части окна
         CRect rect;
         GetClientRect(rect);
         ClientToScreen(rect);
   // Записываем в объект point класса CPoint координаты
   // левого верхнего угла клиентской части окна, добавляя
   // к нему смещения в 5 пикселов по горизонтали и вертикали
         CPoint point = rect.TopLeft();
         point.Offset(5, 5);
   // Отображаем контекстное меню в позиции point
         OnContextMenu(NULL, point);
   // Возвращаем значение TRUE, так как сообщение обработано
         return TRUE;
      }
   }
   // Вызываем метод PreTranslateMessage базового класса CView
   return CView::PreTranslateMessage(pMsg);
}


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


Во время создания панели управления вы можете указать ее характеристики через параметр dwStyle метода Create. Если вам потребовалось изменить эти характеристики уже во время работы приложения - используйте метод SetBarStyle. Метод SetBarStyle определен в классе CControlBar, который является базовым для класса CToolBar:
void SetBarStyle(DWORD dwStyle);
Параметр dwStyle задает новые характеристики панели управления. В качестве этого параметра можно использовать комбинацию флагов, представленных в следующей таблице.

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



Метод SetCheck


Виртуальный метод SetCheck можно использовать для изменения состояния строки меню и кнопок панели управления:
virtual void SetCheck(int nCheck = 1);
Если вы используете метод SetCheck для управления меню и задали в качестве параметра nCheck нулевое значение, то соответствующая строка меню выделяется символом Ö, если параметр nCheck не указан или равен 1, то выделение снимается.
В случае использования метода SetCheck для управления кнопкой панели управления, параметр nCheck задает новое состояние кнопки. Если параметр nCheck равен нулю, кнопка переходит в нажатое положение, если параметр nCheck не указан или равен единице - кнопка переходит в отжатое положение, а если параметр nCheck равен 2, кнопка принимает промежуточное состояние.
Вы можете использовать метод SetCheck для управления внешним видом индикаторов панелей состояния. Если параметр nCheck равен нулю, то рамка индикатора изменяется таким образом, что он будет располагается выше общего уровня панели состояния. Если параметр nCheck равен 1, тогда индикатор переходит в нормальное состояние.
Метод Enable также можно использовать для выбора положения переключателей в диалоговых панелей управления.


Метод SetMenu класса CWnd


В качестве параметра pMenu передайте методу SetMenu указатель на объект класса CMenu, представляющий меню. Если вы желаете просто удалить текущее меню, используемое окном, передайте методу SetMenu в качестве параметра значение NULL:
BOOL SetMenu(CMenu* pMenu);
В случае успешного завершения операции метод SetMenu вернет ненулевое значение. В противном случае SetMenu вернет ноль.
После того, как вы установили меню, вызвав метод SetMenu, и до того, как соответствующий объект CMenu будет удален, надо вызвать метод Detach класса CMenu. Этот метод разорвет связь между меню и соответствующим объектом класса CMenu, после чего последний может быть удален:
HMENU Detach();
Метод Detach возвращает в случае успешного завершения идентификатор меню, а в случае ошибки - значение NULL.
Сразу отметим, что если до установки меню окно уже имело меню, надо удалить его, воспользовавшись методом DestroyMenu класса CMenu. Если с меню, подлежащим удалению, не связан объект класса CMenu, вы можете обратиться к методу Attach:
BOOL Attach(HMENU hMenu);
Для этого создайте объект класса CMenu, а затем вызовите для него метод Attach, указав в качестве параметра hMenu идентификатор меню. Метод Attach возвращает в случае успешного завершения ненулевое значение, а в случае ошибки - ноль.
Чтобы определить идентификатор меню известного окна, можно воспользоваться методом GetMenu, определенным в классе CWnd. Этот метод возвращает указатель на объект типа CMenu:
CMenu* GetMenu() const;
Вы можете получить из него идентификатор меню, если обратитесь к элементу данных m_hMenu, входящему в класс CMenu.
Мы продемонстрируем различные методы создания и управления меню в приложении MultiMenu, а сейчас сделаем несколько замечаний относительно остальных методов класса CMenu.
Класс CMenu, наследованный от базового класса CObject, содержит все необходимые методы для создания и управления меню. Используя эти методы, вы можете добавлять к меню новые строки, удалять и изменять их. Специальные методы класса CMenu позволяют выделять отдельные строки меню и даже создавать элементы меню, содержащие не только текст но и изображение.


Метод SetRadio


Виртуальный метод SetRadio, также как метод SetCheck,  можно использовать для изменения состояния строки меню и кнопок панели управления:
virtual void SetRadio(BOOL bOn = TRUE);
Если вы используете метод SetRadio для управления меню и задали в качестве параметра bOn значение TRUE, то соответствующая строка меню выделяется символом ·, если параметр nCheck равен FALSE, то выделение снимается.
В случае использования метода SetRadio для управления кнопкой панели управления, параметр bOn задает новое состояние кнопки. Если параметр bOn равен FALSE, кнопка переходит в нажатое положение, если параметр bOn не указан или равен TRUE - кнопка переходит в отжатое положение.
Вы можете использовать метод SetRadio для управления внешним видом индикаторов панелей состояния. Если параметр bOn равен FALSE, рамка индикатора изменяется таким образом, что он будет располагается выше общего уровня панели состояния. Если параметр bOn равен TRUE, тогда индикатор переходит в нормальное состояние.
Метод Enable также можно использовать для выбора положения переключателей в диалоговых панелей управления.


Метод SetText


Виртуальный метод SetText может быть использован для изменения текста, отображаемого в индикаторе панели состояния, в строке меню, в названии кнопок и некоторых органах диалоговых панелей управления. В качестве параметра lpszText надо указать текстовую строку, которую надо вывести:
virtual void SetText(LPCTSTR lpszText);
Следует отметить, что при использовании метода SetText для изменения текста в индикаторах панели состояния, вы должны отдельно позаботиться об изменении размера индикатора. Метод SetText не меняет размер индикатора, вы должны сами рассчитать ширину текста и изменить размер индикатора с помощью соответствующего метода. Более подробно об изменении параметров индикаторов панели состояния мы расскажем в разделе “Панель состояния”.


Метод ShowSplashScreen класса CSplashWnd


Метод ShowSplashScreen класса CSplashWnd создает и отображает на экране окно заставки. Перед этим он проверяет состояние флага c_bShowSplashWnd и переменной c_pSplashWnd:
if (!c_bShowSplashWnd c_pSplashWnd != NULL)
   return;
Если флаг c_bShowSplashWnd содержит значение FALSE или объект c_pSplashWnd, представляющий окно заставки уже создан, метод ShowSplashScreen немедленно завершает свою работу.
Если эти условия не выполняются, метод ShowSplashScreen создает новый объект класса CSplashWnd, который будет представлять окно заставки:
c_pSplashWnd = new CSplashWnd;
Затем вызывается метод Create для объекта c_pSplashWnd (он, кстати, переопределен в классе CSplashWnd), который и выполняет фактическое создание окна заставки:
if (!c_pSplashWnd->Create(pParentWnd))
   delete c_pSplashWnd;
else
   c_pSplashWnd->UpdateWindow();
Если окно заставки не создано, метод Create возвращает нулевое значение и объект c_pSplashWnd удаляется. Если создание окна прошло успешно, для c_pSplashWnd вызывается метод UpdateWindow, вызывающий перерисовку окна. Такая перерисовка выполняется с помощью метода OnPaint класса CSplashWnd.


Методы AssertValid и Dump класса CChildFrame


Методы AssertValid и Dump переопределяются в классе CMainFrame только для отладочной версии приложения и используются при отладке приложения:
//////////////////////////////////////////////////////////////
// Диагностические методы класса CChildFrame
#ifdef _DEBUG
void CChildFrame::AssertValid() const
{
   CMDIChildWnd::AssertValid();
}
void CChildFrame::Dump(CDumpContext& dc) const
{
   CMDIChildWnd::Dump(dc);
}


Методы AssertValid и Dump класса CDaterDoc


Методы AssertValid и Dump класса CMainFrame могут использоваться при отладке приложения.


Методы AssertValid и Dump класса CDaterSet


Методы AssertValid и Dump класса CDaterSet могут использоваться при отладке приложения.


Методы AssertValid и Dump класса CDaterView


Методы AssertValid и Dump класса CDaterView могут использоваться при отладке приложения.


Методы AssertValid и Dump класса CMainFrame


В отладочной версии приложения класс CMainFrame содержит переопределения виртуальных методов AssertValid и Dump. Эти методы определены в базовом классе CObject и используются при отладке приложения:
//////////////////////////////////////////////////////////////
// Диагностические методы класса CMainFrame
#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
   CMDIFrameWnd::AssertValid();
}
void CMainFrame::Dump(CDumpContext& dc) const
{
   CMDIFrameWnd::Dump(dc);
}

Методы AssertValid и Dump переопределяются в классе CMainFrame только для отладочной версии приложения и используются при отладке приложения:
#ifdef _DEBUG
void CMultiView::AssertValid() const
{
   CView::AssertValid();
}
void CMultiView::Dump(CDumpContext& dc) const
{
   CView::Dump(dc);
}
#endif //_DEBUG



Методы AssertValid и Dump класса CMainFrame могут использоваться при отладке приложения.


Методы AssertValid и Dump класса CMultiDoc


Методы AssertValid и Dump переопределяются в классе CMainFrame только для отладочной версии приложения и используются при отладке приложения:
//////////////////////////////////////////////////////////////
// Диагностические методы класса CMultiDoc
#ifdef _DEBUG
void CMultiDoc::AssertValid() const
{
   CDocument::AssertValid();
}
void CMultiDoc::Dump(CDumpContext& dc) const
{
   CDocument::Dump(dc);
}
#endif //_DEBUG


Методы BarCommandOne и BarCommandRange класса CMultiBarWindow


Методы BarCommandOne и BarCommandRange определены в классе главного окна приложения CMultiBarWindow и используются для обработки сообщений от кнопок панелей управления.
Фактически методы BarCommandOne и BarCommandRange не выполняют никакой полезной работы. Единственное, что делает метод BarCommandOne, - это отображает на экране сообщение о том, что выбранная вами команда не реализована. Для этого мы используем метод MessageBox:
void CMultiBarWindow::BarCommandOne()
{
   // Отображаем сообщение о том, что команда не реализована
   MessageBox("Command not implemented");       
}
Метод BarCommandRange вообще не выполняет никакой полезной работы. Однако не спешите исключать его из программы. Если вы удалите методы BarCommandOne и BarCommandRange из программы и удалите соответствующие макрокоманды из таблицы сообщений класса окна, тогда все кнопки, для обработки сообщений от которых использовались эти методы, окажутся заблокированы. MFC определит, что сообщения от кнопок не обрабатываются и запретит их использование:
void CMultiBarWindow::BarCommandRange( UINT nID )
{
   // Обработчик не выполняет никакой работы
}


Методы DockControlBar и FloatControlBar класса CFrameWnd


Чтобы пристыковать панель управления к границе окна, надо вызвать метод DockControlBar класса CFrameWnd:
void
DockControlBar(
   CControlBar * pBar,
   UINT nDockBarID = 0,
   LPCRECT lpRect = NULL
);
Панель управления, заданная параметром pBar, пристыковывается к границе окна, указанной параметром nDockBarID. В качестве nDockBarID можно использовать один или несколько флагов, перечисленных ниже.

Флаг
Описание
AFX_IDW_DOCKBAR_TOP
Панель управления присоединяется к верхней границе окна
AFX_IDW_DOCKBAR_BOTTOM
Панель управления присоединяется к нижней границе окна
AFX_IDW_DOCKBAR_LEFT
Панель управления присоединяется к левой границе окна
AFX_IDW_DOCKBAR_RIGHT
Панель управления присоединяется к правой границе окна

Если параметр nDockBarID равен нулю, то панель управления присоединяется к любой стороне окна.
Как видите, параметр nDockBarID может задавать несколько сторон окна одновременно. В этом случае панель управления присоединяется к той границе окна, которая одновременно указана методами CFrameWnd::EnableDocking и CControlBar::EnableDocking. Если таких границ несколько, то они выбираются в следующем порядке - сначала верхняя, а если к ней панель не может быть присоединена, тогда нижняя, левая, и правая стороны окна.
Последний параметр lpRect определяет, где именно будет отображаться панель управления. Положение панели задается в экранных координатах.
Если вам надо отобразить панель управления в отдельном мини-окне и не пристыковывать его к границам окна, то вместо метода DockControlBar вызовите метод FloatControlBar класса CFrameWnd:
CFrameWnd*
FloatControlBar(
   CControlBar * pBar,
   CPoint point
);
Панель управления, указанная параметром pBar, отображается в отдельном мини-окне. Расположение окна панели управления задается параметром point, который указывает координаты верхнего левого угла панели управления. Используются экранные координаты.
Метод FloatControlBar возвращает указатель на текущее окно.
По умолчанию, панель управления не имеет заголовка. Для установки и изменения текста заголовка используйте метод SetWindowText, определенный в классе CWnd:
void SetWindowText(LPCTSTR lpszString);
Параметр lpszString должен содержать указатель на объект класса CString или строку символов, закрытую двоичным нулем. В ней должен быть записан заголовок для панели управления.


Методы класса CCmdUI


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

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



Методы класса CColorDialog


Чтобы вывести диалоговую панель выбора цвета на экран, вы должны использовать метод DoModal:
virtual int DoModal();
После отображения панели на экране пользователь может выбрать из нее цвет и нажать кнопку OK или Cancel для подтверждения выбора цвета или отказа от него. Когда диалоговая панель закрывается, метод DoModal возвращает значение IDOK или IDCANCEL, в зависимости от того, какую кнопку нажал пользователь:
CColorDialog   dlgColor;
int            iResult;
iResult = dlgColor.DoModal();
На экране появится стандартная диалоговая панель выбора цвета Color (рис. 4.2). В верхней половине диалоговой панели, ниже надписи Basic colors, рассположены 48 прямоугольников, имеющих различные цвета. Они представляют так называемые основные цвета. Вы можете выбрать любой из этих цветов, и нажать кнопку OK.
Методы класса CColorDialog
Рис. 4.2. Стандартная диалоговая панель Color
После того как диалоговая панель закрыта, вы можете воспользоваться методами класса CColorDialog, чтобы узнать цвета, выбранные пользователем.
Для определения цвета, выбранного пользователем можно обратиться к методу GetColor класса CColorDialog:
COLORREF GetColor() const;
Данный метод возвращает значение COLORREF, соответствующее выбранному цвету. Если вам недостаточно основных цветов, представленных в диалоговой панели Color, вы можете выбрать до 16 дополнительных цветов. Чтобы выбрать дополнительные цвета, нажмите кнопку Define Custom Colors. Диалоговая панель Color изменит свой внешний вид (рис. 4.3).
Методы класса CColorDialog
Рис. 4.3. Выбор дополнительных цветов
С правой стороны появятся дополнительные органы управления, позволяющие выбрать любой из 16777216 цветов. Когда цвет выбран, нажмите кнопку Add Custom Colors. Выбранный вами цвет будет добавлен к дополнительным цветам. С левой стороны панели, под надписью Custom colors, один из свободных прямоугольников окрасится соответствующим цветом.
Вы можете определить дополнительные цвета, выбранные пользователем в диалоговой панели Color, при помощи метода GetSavedCustomColors класса CColorDialog:
static COLORREF * GetSavedCustomColors();
Метод GetSavedCustomColors возвращает указатель на массив из 16 элементов типа COLORREF. Каждый элемент массива описывает один дополнительный цвет.
Когда диалоговая панель Color отображается приложением первый раз, все прямоугольники, отображающие дополнительные цвета, имеют белый цвет. Соответствующие элементы массива, полученного методом GetSavedCustomColors, будут иметь значение RGB(255,255,255). Дополнительные цвета, выбранные пользователем, сохраняются во время работы приложения. После перезапуска приложения дополнительные цвета сбрасываются.


Методы класса CDialogBar


Непосредственно в состав класса CDialogBar входят всего два метода - это конструктор класса CDialogBar и метод Create.
Конструктор класса CDialogBar создает только соответствующий объект, но не саму панель управления:
CDialogBar();
Чтобы создать панель управления, следует вызвать метод Create и указать ему шаблон диалоговой панели, используемый для создания диалоговой панели управления.
Метод Create имеет два различных формата:
BOOL
Create(
   CWnd* pParentWnd,
   LPCTSTR lpszTemplateName,
   UINT nStyle,
   UINT nID
);
BOOL
Create(
   CWnd* pParentWnd,
   UINT nIDTemplate,
   UINT nStyle,
   UINT nID
);
Первый параметр CWnd задает окно, для которого создается диалоговая панель управления. Обычно в качестве такого окна выступает главное окно приложения.
Второй параметр метода Create указывает шаблон панели управления. В зависимости от того, как определена панель управления, в качестве второго параметра можно использовать либо идентификатор шаблона диалоговой панели nIDTemplate, либо его имя lpszTemplateName.
Третий параметр nStyle определяет начальное положение диалоговой панели управления в окне приложения. Вы можете указать следующие флаги: CBRS_TOP, CBRS_BOTTOM, CBRS_LEFT и CBRS_RIGHT. Они позволяют установить панель управления соответственно у верхней, нижней, левой или правой границы окна.
Форма диалоговой панели управления зависит от шаблона диалоговой панели и ее расположения в окне приложения. В случае если диалоговая панель управления отображается в верхней или нижней части окна (параметр nStyle равен CBRS_TOP или CBRS_BOTTOM), то ширина панели управления соответствует ширине окна CWnd, а высота - высоте шаблона диалоговой панели. Если же диалоговая панель управления отображается с левой или правой части окна (параметр nStyle равен CBRS_LEFT или CBRS_RIGHT), то ширина панели управления соответствует ширине шаблона диалоговой панели, а высота - высоте окна CWnd.
Последний параметр nID определяет идентификатор диалоговой панели управления.
В случае успешного создания диалоговой панели управления метод Create возвращает ненулевое значение. Если при создании панели управления обнаружены ошибки, например, вы указали несуществующий шаблон диалоговой панели, тогда метод Create возвращает нулевое значение.


Методы класса CFileDialog


Создание объекта класса CFileDialog еще не вызывает отображения соответствующей диалоговой панели. Для этого необходимо воспользоваться специальным методом DoModal класса CFileDialog.
Виртуальный метод DoModal, первоначально определенный в классе CDialog, переопределен в классе CFileDialog:
virtual int DoModal();
При вызове метода DoModal для ранее созданного объекта класса CFileDialog на экране открывается соответствующая диалоговая панель. После того, как вы закончите работать с диалоговой панелью и закроете ее, метод DoModal вернет значения IDOK или IDCANCEL в случае успешного завершения и 0 - в случае возникновения ошибок.
Если вы выберите имя файла и нажмете кнопку Open (для панели Open) или кнопку Save (для панели Save As), метод DoModal вернет значение IDOK. Если вы решите отменить выбор файла и нажмете кнопку Cancel или выберите из меню диалоговой панели строку Close, метод DoModal вернет значение IDCANCEL.
После того, как вы закроете диалоговую панель и метод DoModal вернет управление, можно воспользоваться другими методами класса CFileDialog, чтобы определить имена выбранных файлов.

Метод
Описание
GetPathName
Определяет полный путь выбранного файла
GetFileName
Определяет имя выбранного файла
GetFileExt
Определяет расширение имени выбранного файла
GetFileTitle
Позволяет определить заголовок выбранного файла
GetNextPathName
Если диалоговая панель позволяет выбрать сразу несколько файлов, то метод GetNextPathName можно использовать для определения полного пути следующего из выбранных файлов
GetReadOnlyPref
Позволяет узнать состояние атрибута “только читаемый” (read-only) выбранного файла
GetStartPosition
Возвращает положение первого элемента из списка имен файлов

 
Наиболее важный метод из представленных в таблице - GetPathName. Он получает полный путь файла, выбранного из диалоговых панелей Open или Save As:
CString GetPathName() const;
Как мы уже говорили, диалоговые панели Open и Save As можно использовать для выбора нескольких файлов. Для этого, в поле Flags структуры m_ofn должен быть установлен флаг OFN_ALLOWMULTISELECT. Тогда метод GetPathName возвращает массив строк, состоящий из нескольких строк, заканчивающихся двоичным нулем. Первая из данных строк содержит путь к каталогу, в котором расположены выбранные файлы, остальные строки содержат имена выбранных файлов. Естественно, с тем чтобы выделить путь к каталогу проблем не возникает, а чтобы получить имена выбранных файлов, вы должны использовать методы GetStartPosition и GetNextPathName.

Метод GetStartPosition  возвращает значение типа POSITION. Оно предназначено для передачи методу GetNextPathName и получения очередного имени выбранного файла. Если пользователь не выбрал ни одного файла, метод GetStartPosition возвращает значение NULL:

POSITION GetStartPosition() const;

Значение, полученное методом GetStartPosition, следует записать во временную переменную типа POSITION и передать ссылку на нее методу GetNextPathName:

CString GetNextPathName(POSITION& pos) const;

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

В отличие от метода GetPathName, метод GetFileName позволяет определить только имя выбранного файла, без пути и расширения:

CString GetFileName() const;

Метод GetFileExt возвращает только расширение файла, выбранного в диалоговой панели:

CString GetFileExt() const;

Метод GetFileTitle позволяет получить полное имя файла, включая его расширение:

CString GetFileTitle() const;

В стандартных диалоговоых панелях Open и Save As имеется переключатель Read Only. Заметим, что по умолчанию этот переключатель не отображается. Если вы желаете воспользоваться этим переключателем, флаг OFN_HIDEREADONLY должен быть сброшен.

Метод GetReadOnlyPref позволяет определить положение переключателя Read Only. Если переключатель включен, то метод GetReadOnlyPref возвращает ненулевое значение. В противном случае GetReadOnlyPref возвращает нуль:

BOOL GetReadOnlyPref() const;


Методы класса CFontDialog


Для отображения диалоговой панели Font предназначен виртуальный метод DoModal:
virtual int DoModal();
Если пользователь выбрал шрифт и нажал на кнопку OK, метод DoModal возвращает идентификатор IDOK, если пользователь отменил выбор шрифта, метод DoModal возвращает идентификатор IDCANCEL.
Остальные методы класса предназначены для определения характеристик выбранного пользователем шрифта. Полное описание этих методов содержится в документации, поставляемой с Visual C++.
Метод GetCurrentFont позволяет сразу определить все характеристики выбранного щрифта, записав их в структуру LOGFONT. Вы можете найти описание структуры LOGFONT в документации SDK.
Остальные методы класса позволяют определить только отдельные характеристики выбранного шрифта. Перечислим названия этих методов.

Метод
Описание
GetFaceName
Возвращает имя выбранного шрифта
GetStyleName
Возвращает имя стиля выбранного шрифта
GetSize
Возвращает кегль выбранного шрифта
GetColor
Возвращает цвет выбранного шрифта
GetWeight
Возвращает плотность выбранного шрифта
IsStrikeOut
Определяет является ли шрифт выделеным перечеркнутой линией
IsUnderline
Определяет является ли шрифт выделеным подчеркиванием
IsBold
Определяет является ли шрифт жирным
IsItalic
Определяет является ли шрифт наклонным



Методы класса CMultiView, предназначенные для печати


Виртуальные методы OnPreparePrinting, OnBeginPrinting и OnEndPrinting, определенные в классе CView, вызываются, если пользователь желает распечатать документ, отображенный в данном окне просмотра:
//////////////////////////////////////////////////////////////
// Методы класса CMultiView, управляющие печатью документов
BOOL CMultiView::OnPreparePrinting(CPrintInfo* pInfo)
{
   return DoPreparePrinting(pInfo);
}
void CMultiView::OnBeginPrinting(CDC* /*pDC*/,
   CPrintInfo* /*pInfo*/)
{
   // TODO:
}
void CMultiView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
   // TODO:
}
Многооконное приложение, подготовленное MFC AppWizard, уже “умеет” выводить созданные в нем документы на печатающее устройство. Методы OnPreparePrinting, OnBeginPrinting и OnEndPrinting класса CView предназначены для расширения возможностей печати и в этой книге не рассматриваются.


Методы OnMenuSwitchTEXT и OnUpdateTEXT класса CStateWindow


Методы OnMenuSwitchTEXT и OnUpdateTEXT используются в приложении совместно для управления состоянием индикатора ID_INDICATOR_TEXT.
Метод OnMenuSwitchTEXT вызывается для обработки командного сообщения с идентификатором ID_WORK_ON_SWITCH_TEXT. Это сообщение поступает в случае выбора из меню Work строки Switch TEXT:
ON_COMMAND(ID_WORK_ON_SWITCH_TEXT, OnMenuSwitchTEXT)
Единственная задача метода OnMenuSwitchTEXT заключается в изменении состояния флага bIndicatorTEXT. Если флаг bIndicatorTEXT имеет значение TRUE, тогда метод OnMenuSwitchTEXT меняет его на FALSE и наоборот:
void CStateWindow::OnMenuSwitchTEXT()
{
   bIndicatorTEXT = !bIndicatorTEXT;
}
Метод OnUpdateTEXT класса CStateWindow, вызывается макрокомандой ON_UPDATE_COMMAND_UI из таблицы сообщений класса CStateWindow:
ON_UPDATE_COMMAND_UI(ID_INDICATOR_TEXT, OnUpdateTEXT)
Мы используем этот метод, чтобы изменить текст, отображаемый в индикаторе ID_INDICATOR_TEXT. В зависимости от состояния флага bIndicatorTEXT, установленного методом OnMenuSwitchTEXT, метод OnUpdateTEXT отображает в индикаторе либо строку TEXT, либо строку PIC:
void CStateWindow::OnUpdateTEXT(CCmdUI* pCmdUI)
{
   // В зависимости от состояния флага bIndicatorTEXT
   // отображаем в индикаторе ID_INDICATOR_TEXT
   // строку TEXT или PIC
   if(bIndicatorTEXT)
      pCmdUI->SetText("TEXT"); // отображаем строку TEXT
   else
      pCmdUI->SetText("PIC");  // отображаем строку PIC
   // Разрешаем отображение текста в индикаторе
   pCmdUI->Enable();
}
В качестве параметра pCmdUI методу OnUpdateTEXT передается указатель на объект класса CCmdUI. Этот объект представляет объект интерфейса приложения (строку меню, кнопку панели управления или индикатор панели состояния). В контексте данного конкретного метода этот объект представляет индикатор панели состояния, имеющий идентификатор ID_INDICATOR_TEXT.
Для управления индикатором мы используем методы SetText и Enable класса CCmdUI. Эти методы устанавливают текст индикатора и снимают с него блокировку (если блокировка ранее была установлена).


Методы OnNewDocument и Serialize класса CMultiDoc


В классе CMultiDoc переопределены два виртуальных метода - OnNewDocument и Serialize. Виртуальный метод OnNewDocument определен в классе CDocument, от которого непосредственно наследуется класс CSingleDoc.
Метод OnNewDocument вызывается, когда надо создать новый документ для приложения. Для одноконных приложений метод OnNewDocument вызывался только один раз при запуске приложения.
Для многооконного приложения метод OnNewDocument вызывается каждый раз, когда пользователь создает новый документ. Более подробно об использовании метода OnNewDocument мы расскажем в следующих главах, когда к шаблону приложения, созданному MFC AppWizard, мы будем добавлять собственный код:
BOOL CMultiDoc::OnNewDocument()
{
   if (!CDocument::OnNewDocument())
      return FALSE;
   // TODO: Здесь можно выполнить инициализацию документа
   return TRUE;
}
Метод Serialize вызывается в тех случаях, когда надо загрузить документ из файла на диске или наоборот, записать его в файл:
//////////////////////////////////////////////////////////////
// Метод Serialize класса CMultiDoc
void CMultiDoc::Serialize(CArchive& ar)
{
   if (ar.IsStoring())
   {
      // TODO:
   }
   else
   {
      // TODO:
   }
}


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


Приложение MultiMenu имеет два меню, полное и укороченное. Вы можете выбирать, какое меню будет использоваться в данный момент, с помощью строки Restrict и Full меню Menu. Если в данный момент используется полный вариант меню, то чтобы заменить его укороченным вариантом, следует выбрать из меню Menu строку Restrict. Для обратной замены меню с укороченного варианта на полный, надо выбрать из меню Menu строку Full.
При выборе строк Restrict и Full приложению передаются командные сообщения с идентификаторами IDR_RESTRICT_MENU и IDR_FULL_MENU, соответственно. Для их обработки вызываются методы OnRestrictMenu и OnFullMenu. По сути, методы OnRestrictMenu и OnFullMenu практически идентичны. Отличие между ними заключается только в том, что метод OnRestrictMenu заменяет текущее меню укороченным вариантом меню (идентификатор меню IDR_RESTRICT_MENU), а метод OnFullMenu меняет текущее меню на полный вариант меню (идентификатор меню IDR_FULL_MENU).
Метод OnRestrictMenu работает следующим образом. Сначала он получает указатель на текущее меню окна приложения. Указатель на объект класса CMenu, представляющий это меню, записывается во временную переменную pMenu:
pMenu = this->GetMenu();
Затем текущее меню удаляется, для чего вызывается метод DestroyMenu:
pMenu->DestroyMenu();
Теперь загружается ресурс нового меню, имеющего идентификатор IDR_RESTRICT_MENU (или IDR_FULL_MENU для метода OnFullMenu):
CMenu menuRestrict;  // Новое меню
menuRestrict.LoadMenu(IDR_RESTRICT_MENU);
Загруженное меню подключается к окну приложения - вызывается метод SetMenu класса окна. В качестве параметра ему передается указатель на объект menuRestrict, представляющий новое меню:
SetMenu(&menuRestrict);
И, наконец, вызывается метод Detach, отпускающий меню в “свободное плавание”, то есть отсоединяющее его от объекта menuRestrict класса CMenu:
menuRestrict.Detach();


MFC AppWizard и базы данных


Самый короткий путь для разработки приложений, работающих с базами данных заключается в использовании MFC AppWizard. С помощью MFC AppWizard вы можете быстро создать приложение, позволяющее просматривать записи базы данных. В дальнейшем вы можете совершенствовать шаблон приложения, подготовленный MFC AppWizard, с помощью средств MFC ClassWizard и добавить другие операции по работе с базой данных, такие как добавление новых записей в таблицу, поиск нужных записей и т. д.
Создайте новый проект, присвоив ему имя Dater. Используйте для создания проекта средства MFC AppWizard. AppWizard предложит вам заполнить ряд панелей, перечислив в них свойства и характеристики создаваемого приложения.
Чтобы упростить приложние, на первом шаге определения свойств приложения выберите для него однооконный интерфейс. На втором шаге MFC AppWizard запросит у вас разрешения, чтобы включить поддержку баз данных. Соответствующая диалоговая панель MFC AppWizard представлена нами на рисунке 5.6.
MFC AppWizard и базы данных
Рис. 5.6. Диалоговая панель MFC AppWizard - Step 2 of 6
Переключатель с зависимой фиксацией, расположенный в панели MFC AppWizard - Step 2 of 6, определяет на каком уровне в приложении будет обеспечена поддержка баз данных. Следующая таблица кратко описывает этот переключатель.

Положение переключателя
Описание
None
Работа с базами данных не предусматривается
Header files only
К файлаам проекта подключаются файлы заголовков, необходимые для использования средств доступа к базам данных
Database view without file support
Обеспечивается работа с базами данных. Полученное приложение позволяет просматривать базу данных в окне просмотра. Приложения не работает с файлами документов. Меню File такого приложения содержит только строки, не имеющие отношения к работе с файлами, например строку Exit. Строки Open и Save (и некоторых других) в меню File отсутствуют
Database view with file support
Обеспечивается работа с базами данных. Полученное приложение позволяет просматривать базу данных в окне просмотра. Поддерживается работа приложения с файлами документов. Приложение имеет полное меню File
<
Мы выбрали для нашего приложения режим работы с базами данных без поддержки файлов. Переключатель надо перевести в положение Database view without file support.

Теперь надо указать MFC AppWizard какую базу данных и какую таблиицу из нее мы желаем просматривать в нашем приложении. Для этого мы должны нажать кнопку Data Source, также рассположенную в диаалоговой панели MFC AppWizard - Step 2 of 6.

На экране появится диалоговая панель Database Options (рис. 5.7). В ней находится ряд органов управления, разделенных на три группы - Datasource, Recordset type и Advanced.

MFC AppWizard и базы данных

Рис. 5.7. Диалоговая панель Database Options

Группа Datasource предназначена для выбора базы данных (источника данных). Вы можете использовать для доступа к базе данных либо драйверы ODBC, либо средства DAO. В этой книге мы рассмотрим использование только драйверов ODBC. Переведите переключатель Datasource в положение ODBC. Из списка, расположенного справой стороны от переключателя ODBC выберите имя источника данных. В нашем случае вы должны выбрать строку Address Pad.

В группе Recordset type отображается переключатель с зависимой фиксацией. Он может принимать одно из трех положений Snapshot, Dynaset или Table. Используйте этот переключатель, чтобы определить метод работы приложения с базой данных.

Переключатель Recordset type

Описание

Snapshot

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

Dynaset

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

Для нашего приложения Dater мы используем самый простой метод доступа к записям базы данных, поэтому переведите переключатель Recordset type в положение Snapshot.

В последную группу Advanced входит только один переключатель Detect dirty columns. Этот переключатель используется средствами DAO и в этой книге не рассматривается.



Когда панель Database Options заполнена, нажмите кнопку OK. На экране появится диалоговая панель Select Database Tables (рис. 5.8). Из нее вы должны выбрать имя таблицы базы данных, с которой будет работать приложение. Информация именно из этой таблицы будет отображаться нашим приложением. В нашем случае база данных (или источник данных) содержить только одну таблицу, поэтому выбор таблицы не составит труда. Просто щелкните мышью по строку TEXTBASE.TXT и нажмите кнопку OK.

MFC AppWizard и базы данных

Рис. 5.8. Диалоговая панель Select Database Tables

Если ваша база данных содержит несколько таблиц и приложение должно работать с ними со всеми, то уже после создания приложения средствами MFC AppWizard, вы можете воспользоваться ClassWizard, чтобы подключить остальные таблицы базы данных. Приложения, работающие одновременно с несколькими таблицами базы данных мы рассмотрим в одной из следующих книг серии “Библиотека системного программиста”.

После выбора источника данных, вы можете завершить создание приложения и нажать кнопку Finish. Вы также можете продолжить заполнять диалоговые панели MFC AppWizard. В этом случае заполните панели MFC AppWizard, оставляя все предложения по умолчанию. Единственное, что вы можете изменить для упрощения приложения, это отменить все особенности приложения, связанные с печатью. Для этого отключите переключатель Print and print preview в диалоговой панели MFC AppWizard - Step 4 of 6.

Когда вы дойдете до последней панели MFC AppWizard - Step 6 of 6 (рис. 5.9) вы можете просмотреть, какие классы составляют приложение Dater.

MFC AppWizard и базы данных

Рис. 5.9. Диалоговая панель MFC AppWizard - Step 6 of 6

Оказывается, в отличае от проектов, которые мы изучали ранее, класс окна просмотра приложения Dater наследуется от базового класса CRecordView, и в проект входит класс представляющий записи базы данных, который не имеет аналогов среди других изученных нами приложений. Этот класс наследуется от базового класса CRecordset.


MFC ClassWizard и команды обновления


Если приложение подготовлено с использованием MFC AppWizard, то наилучшим способом создания обработчиков команд обновления является использование средств ClassWizard. Процедура создания обработчиков команд обновления от строк меню и кнопок панелей управления практически не отличается от процедуры создания обычных обработчиков командных сообщений.
Запустите ClassWizard. На экране появится диалоговая панель MFC ClassWizard. Выберите из нее страницу Message Maps (рис. 3.2). Теперь из списка Object IDs выберите идентификатор интересующей вас строки меню или кнопки панели управления. В списке Messages появятся две строки - COMMAND и ON_UPDATE_COMMAND_UI.
Строка COMMAND позволяет создать обработчик командных сообщений, а строка ON_UPDATE_COMMAND_UI - обработчик команд обновления. О том как создавать с помощью MFC ClassWizard методы для обработки командных сообщений, мы рассказывали в 24 томе серии “Библиотека системного программиста”, посвященном библиотеке классов MFC.
Чтобы создать обработчик для команды обновления, выберите из списка Messages строку ON_UPDATE_COMMAND_UI, а из списка Class name имя класса к которому будет добавлен новый метод. Нажмите кнопку Add Function. MFC ClassWizard предложит имя для нового метода. Вы можете согласиться с предложением ClassWizard или изменить имя метода по своему усмотрению. В частности, для  нескольких разных строк меню или кнопок панели управления вы можете указать один и тот же метод обработчик.
MFC ClassWizard и команды обновления
Рис. 3.2. MFC ClassWizard
К сожалению, MFC ClassWizard не позволяет назначить один обработчик команд обновления нескольким объектам пользовательского интерфейса с помощью макрокоманды ON_UPDATE_COMMAND_UI_RANGE. Вместо одной макрокоманды ON_UPDATE_COMMAND_UI_RANGE MFC ClassWizard разместит в таблице сообщений необходимое количество макрокоманд ON_UPDATE_COMMAND_UI.
Еще одно неприятное ограничение MFC ClassWizard заключается в том, что он не дает возможности создать обработчики для команд обновления от индикаторов панели состояния. Такие обработчики вы должны будете добавлять к классам приложения вручную.


Microsoft Visual C++ версия 4.2, Enterpise Edition


По сравнению с обычной версией Microsoft Visual C++ 4.2, версия Enterpise Edition, значительно расширяет возможности программиста при работе с базами данных.
Если вы используете в своих программах базы данных Microsoft SQL Server или Oracle, то в окне Project Workspace появится новая страница - DataView. В ней представлены различные объекты базы данных - таблицы, хранимые процедуры, триггеры и т. д.
В состав Microsoft Visual C++ 4.2 Enterpise Edition включен отладчик, позволяющий отлаживать хранимые процедуры на языке SQL. Это значительно упрощает этап отладки программы и поиска возможных ошибок.
Интересные новшества введены и в текстовый редактор Microsoft Visual C++. Теперь он имеет возможность более полно работать с текстами на языке SQL, выделяя цветом ключевые слова этого языка.


значительно расширены, даже по


Возможности Microsoft Visual C++ версии 4. 2 значительно расширены, даже по сравнению с предыдущей версией Visual C++. Ниже мы перечислили только самые интересные, на наш взгляд, новшества.


Многооконный графический редактор


Доработаем приложение Multi так, чтобы оно обладало возможностями приложения Single, описанного в томе 24 серии “Библиотека системного программиста”. Приложение Single представляет собой простейший графический редактор, в котором можно рисовать изображения, содержащие маленькие квадраты, а также сохранять эти рисунки в файлах на диске.
Добавьте в определение класса CMultiDoc новый элемент pointFigCenter, который будет хранить графический документ. Как и в приложении Single, этот элемент сделан на основе шаблона CArray. Однако вместо разработанного нами класса CFigure, здесь мы используем стандартный класс CPoint, входящий в состав MFC. Тип фигуры запоминать не надо, так как приложение Multi будет рисовать фигуры только одного типа:
class CMultiDoc : public CDocument
{
// Attributes
public:
   CArray pointFigCenter;
Шаблоны классов CArray, CMap и CList определены во включаемом файле afxtempl.h. Так как мы используем класс CArray, добавьте файл afxtempl.h в конце включаемого файла stdafx.h:
#include
Добавьте обработчик сообщения от левой кнопки мыши. Для этого лучше всего воспользоваться средствами MFC ClassWizard:
//////////////////////////////////////////////////////////////
// CMultiView message handlers
void CMultiView::OnLButtonDown(UINT nFlags, CPoint point)
{
  // TODO:
  // Получаем указатель на документ (объект класса CSingleDoc)
   CMultiDoc* pDoc = GetDocument();
   // Проверяем указатель pDoc
   ASSERT_VALID(pDoc);
   // Отображаем на экране квадрат
   CClientDC dc(this);
   dc.Rectangle(
         point.x-10, point.y-10, point.x+10, point.y+10);
   // Добавляем к массиву, определяющему документ, новый
   // элемент
   pDoc->pointFigCenter.Add(point);
   // Устанавливаем флаг изменения документа
   pDoc->SetModifiedFlag();
   // Вызываем метод OnLButtonDown базового класса CView
   CView::OnLButtonDown(nFlags, point);
}
Обработчик этого сообщения рисует квадрат. Для отображения квадрата используется метод Rectangle. Первые два параметра этого метода определяют расположение левого верхнего угла параллелепипеда. Третий и четвертый параметры задают размеры по горизонтали и вертикали.

Затем добавляем к документу новый элемент, определяющий координаты верхнего левого угла квадрата. В данном случае графический документ приложения представляется массивом pointFigCenter, содержащим объекты класса CPoint.

Так как метод OnLButtonDown изменяет документ, устанавливаем флаг модификации документа, для чего вызываем метод SetModifiedFlag. Затем вызываем метод OnLButtonDown базового класса CView. На этом обработка сообщения завершается.

Приложение должно отображать документ, когда в окно просмотра поступает сообщение WM_PAINT. Для этого следует изменить метод OnDraw окна просмотра документа. MFC AppWizard определяет шаблон этого метода, вам остается только “наполнить” готовый шаблон.

Метод OnDraw должен уметь отображать документ в любой момент времени. Так как документ записан в массиве pointFigCenter класса документа, сначала надо определить указатель на документ, а потом последовательно отобразить на экране все его элементы:

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

// CMultiView drawing

void CMultiView::OnDraw(CDC* pDC)

{

   CMultiDoc* pDoc = GetDocument();

   ASSERT_VALID(pDoc);

   int i;

   for (i=0; ipointFigCenter.GetSize(); i++)

      pDC->Rectangle(

         pDoc->pointFigCenter[i].x-10,

         pDoc->pointFigCenter[i].y-10,

         pDoc->pointFigCenter[i].x+10,

         pDoc->pointFigCenter[i].y+10

      );

}

Переопределите метод DeleteContents класса CMultiDoc так, чтобы он удалял содержимое документа. Для этого достаточно удалить все элементы массива pointFigCenter, воспользовавшись методом RemoveAll класса CArray. После очистки документа необходимо вызвать метод DeleteContents базового класса CDocument.

Чтобы вставить в класс CMultiDoc метод DeleteContents используйте MFC ClassWizard, а затем модифицируйте его в соответствии со следующим фрагментом кода:

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

// CMultiDoc commands

void CMultiDoc::DeleteContents()



{

   // Очищаем документ, удаляя все элементы массива arrayFig.

   pointFigCenter.RemoveAll( );

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

   CDocument::DeleteContents();

}

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

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

// CMultiDoc serialization

void CMultiDoc::Serialize(CArchive& ar)

{

   pointFigCenter.Serialize(ar);

}

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


Многооконный интерфейс


Приложение с однооконным интерфейсом не всегда может полностью удовлетворять потребностям пользователя. Если нужно одновременно работать с несколькими документами, вы, конечно, можете одновременно запустить несколько копий одного приложения, но гораздо удобнее использовать приложения с многооконным интерфейсом.
В таких приложениях вы одновременно можете открыть несколько документов. Каждому документу будет отведено собственное окно просмотра, но тем не менее все окна просмотра документов будут расположены внутри главного окна приложения, будут иметь общее меню, а также панели управления и состояния.
В этом разделе мы рассмотрим приложение Multi с многооконным интерфейсом, созданное с использованием средств MFC AppWizard. Сначала мы создадим простое приложение, а затем покажем, как его можно изменить, добавив возможности создания новых документов, рисования в окне и сохранения изменений в файле на диске.
Далее мы усовершенствуем наше приложение так, что оно сможет работать с документами двух типов - графическими и текстовыми.
В ходе своих объяснений мы иногда будем ссылаться на однооконное приложение Single, также созданное с использованием средств MFC AppWizard. Приложение Single мы рассматривали в 24 томе “Библиотеки системного программиста”. Если у вас нет под рукой 24 тома, вы можете быстро создать приложение Single, воспользовавшись MFC AppWizard.


Национальные ресурсы


Обратите внимание, что все ресурсы, представленные на странице RecourceView в окне проекта Project Workspace, английские. К сожалению, MFC AppWizard из доступных нам версий Microsoft Visual C++ не позволяет выбрать для создаваемого приложения русские ресурсы (язык для ресурсов выбирается в первой панели MFC AppWizard - Step 1, во время определения свойств приложения). Поэтому для приложения Multi и всех других приложений, созданных с помощью MFC AppWizard, мы выбрали английский язык.
Если вы в Control Panel с помощью приложения Regional Settings выбрали русский язык, то в некоторых случаях ClassWizard может работать неправильно. Например, если вы добавите к английской диалоговой панели новые органы управления, то ClassWizard не позволит автоматически привязать к ним переменные. Возникнут также сложности при использовании русского текста в строковых ресурсах, помеченных как английские. Чтобы избежать этих проблем, измените язык, используемый для ресурсов. Для этого достаточно в окне Project Workspace щелкнуть по идентификатору ресурса правой кнопкой мыши и выбрать из открывшегося контекстного меню строку Properties. На экране появится диалоговая панель со свойствами выбранного ресурса. Измените в ней язык ресурса, выбрав из списка Language строку Russian.


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


Изучая пример приложения DOCKTOOL, поставляемого вместе с Microsoft Visual C++, мы обнаружили, что для отображения и удаления с экрана панелей управления используется метод OnBarCheck.
Метод вызывается из таблицы сообщений класса главного окна приложения CMainFrame. Для этого используется макрокоманда ON_COMMAND_EX. В случае прихода командных сообщений от строк меню, отвечающих за показ на экране панелей управления, вызывается метод OnBarCheck и ему в качестве параметра передается идентификатор соответствующей строки меню:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
   // Другие макрокоманды таблицы сообщений
   ON_COMMAND_EX(IDW_BROWSE_BAR, OnBarCheck)
   ON_COMMAND_EX(IDW_DEBUG_BAR, OnBarCheck)
   ON_COMMAND_EX(IDW_EDIT_BAR, OnBarCheck)
END_MESSAGE_MAP()
Таким образом, когда пользователь выбирает из меню View приложения строку с именем панели управления, выдается командное сообщение с идентификатором, соответствующим строке меню и самой панели управления (идентификаторы панелей управления идентичны идентификаторам соответствующих строк меню View). Командное сообщение обрабатывается таблицей сообщений класса CMainFrame. Для его обработки вызывается метод OnBarCheck, которому в качестве параметра передается идентификатор панели управления.
Если вы решите поискать описание метода OnBarCheck в справочной системе Microsoft Visual C++, вас ждет разочарование. Ни в класс CFrameWnd, ни в один из его базовых классов метод OnBarCheck не входит. Когда вы вернетесь к исходным текстам самого приложения, в них вы также не обнаружите определение этого метода.
Мы проявили настойчивость и смогли обнаружить метод OnBarCheck только в исходных текстах библиотеки классов MFC. Оказывается, несмотря на отсутствие описания метода OnBarCheck в документации библиотеки MFC, этот метод входит в хорошо известный вам класс CFrameWnd.
В файле Afxwin.h, в котором объявлен класс CFrameWnd, вы можете найти объявления входящих в него методов OnUpdateControlBarMenu и OnBarCheck:
class CFrameWnd : public CWnd

{

// ...

// Command Handlers

public:

   afx_msg void OnUpdateControlBarMenu(CCmdUI* pCmdUI);

   afx_msg BOOL OnBarCheck(UINT nID);

}

Определения исходных текстов методов OnUpdateControlBarMenu и OnBarCheck содержатся в файле Winfrm.cpp.

В файле Winfrm.cpp также можно найти обращения к методам OnUpdateControlBarMenu и OnBarCheck в таблице сообщений класса CFrameWnd. Приведем соответствующий фрагмент этой таблицы:

BEGIN_MESSAGE_MAP(CFrameWnd, CWnd)

   // turning on and off standard frame gadgetry

   ON_UPDATE_COMMAND_UI(ID_VIEW_STATUS_BAR,

                        OnUpdateControlBarMenu)

   ON_COMMAND_EX(ID_VIEW_STATUS_BAR, OnBarCheck)

   ON_UPDATE_COMMAND_UI(ID_VIEW_TOOLBAR,

                        OnUpdateControlBarMenu)

   ON_COMMAND_EX(ID_VIEW_TOOLBAR, OnBarCheck)

END_MESSAGE_MAP()

Две пары макрокоманд ON_UPDATE_COMMAND_UI и ON_COMMAND_EX вызывают методы OnUpdateControlBarMenu и OnBarCheck для обработки командных сообщений с идентификаторами ID_VIEW_STATUS_BAR и ID_VIEW_TOOLBAR. Командные сообщения с такими идентификаторами поступают при выборе строк Toolbar и Status Bar меню View.

Меню View, содержащее строки Toolbar и Status Bar, вставляется во все приложения с оконным интерфейсом, которые созданы с использованием средств MFC AppWizard.

Рассмотрим теперь сами методы OnBarCheck и OnUpdateControlBarMenu. Метод OnBarCheck класса CFrameWnd определен следующим образом:

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

// Метод OnBarCheck класса CFrameWnd

BOOL CFrameWnd::OnBarCheck(UINT nID)

{

   ASSERT(ID_VIEW_STATUS_BAR == AFX_IDW_STATUS_BAR);

   ASSERT(ID_VIEW_TOOLBAR == AFX_IDW_TOOLBAR);

   CControlBar* pBar = GetControlBar(nID);

   if (pBar != NULL)

   {

      ShowControlBar(pBar,

        (pBar->GetStyle() & WS_VISIBLE) == 0, FALSE);

      return TRUE;

   }

   return FALSE;

}

Отладочная версия метода OnBarCheck класса CFrameWnd проверяет соответствие идентификаторов ID_VIEW_STATUS_BAR, AFX_IDW_STATUS_BAR и ID_VIEW_TOOLBAR, AFX_IDW_TOOLBAR. Отметим, что эти идентификаторы определены в файле Afxres.h следующим образом:



#define AFX_IDW_TOOLBAR      0xE800

#define AFX_IDW_STATUS_BAR   0xE801

#define ID_VIEW_TOOLBAR      0xE800

#define ID_VIEW_STATUS_BAR   0xE801

Метод GetControlBar класса CFrameWnd определяет указатель на объект класса CControlBar, который представляет панель управления или панель состояния с идентификатором nID. Идентификаторы строк меню ID_VIEW_TOOLBAR и ID_VIEW_STATUS_BAR соответствуют стандартным идентификаторам панели управления AFX_IDW_TOOLBAR и панели состояния AFX_IDW_STATUS_BAR. 

При выборе из меню View строки Toolbar передается командное сообщение ID_VIEW_TOOLBAR, а при выборе строки Status bar - сообщение ID_VIEW_STATUS_BAR. Во время обработки этих сообщений, вызов метода GetControlBar определит объект класса CControlBar, соответствующий либо панели управления AFX_IDW_TOOLBAR, либо панели состояния AFX_IDW_STATUS_BAR.

Затем метод ShowControlBar отображает или закрывает соответствующую панель. Если панель была открыта, метод ShowControlBar скрывает ее и наоборот.

Аналогичным образом устроен метод OnUpdateControlBarMenu класса CFrameWnd, который обрабатывает команды обновления (по умолчанию, он обрабатывает команды обновления от строк Toolbar и Status bar меню View).

Метод OnUpdateControlBarMenu проверяет, отображается ли на экране панель управления или панель состояния с идентификатором, соответствующим идентификатору команды обновления. Если панель отображается, то строка меню отмечается символом Ö:

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

// Метод OnUpdateControlBarMenu класса CFrameWnd

void CFrameWnd::OnUpdateControlBarMenu(CCmdUI* pCmdUI)

{

   ASSERT(ID_VIEW_STATUS_BAR == AFX_IDW_STATUS_BAR);

   ASSERT(ID_VIEW_TOOLBAR == AFX_IDW_TOOLBAR);

   CControlBar* pBar = GetControlBar(pCmdUI->m_nID);

   if (pBar != NULL)

   {

      pCmdUI->SetCheck((pBar->GetStyle() & WS_VISIBLE) != 0);

      return;

   }

   pCmdUI->ContinueRouting();

}

В конце метода OnUpdateControlBarMenu класса CFrameWnd вызывается метод ContinueRouting класса CCmdUI, который направляет команду обновления для дальнейшей обработки другим классам MFC (см. раздел “Обработка командных сообщений”).


Новая кнопка в панели управления


Чтобы добавить в панели управления новую кнопку, переместите указатель мыши на крайне правую кнопку в панели управления, отображаемой в верхней части окна редактора ресурсов, и нажмите на левую кнопку мыши. Теперь вы можете нарисовать изображение, которое будет отображаться на новой кнопке.
Вместе с Microsoft Visual C++ поставляются несколько изображений кнопок панелей управления, которые можно использовать в разрабатываемых приложениях. Откройте файл ресурсов Common.res, записанный на компакт диске Microsoft Visual C++, и просмотрите записанные в нем ресурсы типа toolbar. Если вы обнаружите подходящие вам изображение, скопируйте его в обменный буфер Windows clipboard и вставьте в редактируемую панель управления. Для более полного описания файла Common.res обратитесь к разделу “Ресурсы Microsoft”.
Введите идентификатор новой кнопки панели управления. Для этого, выполните двойной щелчок по изображению этой кнопки в верхней части окна редактирования. В поле ID, открывшейся диалоговой панели Toolbar Button Properties, введите идентификатор ID_MY_BUTTON (рис. 3.8).
Теперь вы можете построить проект и запустить приложение. В панели управления появится новая кнопка, но она будет иметь серый цвет и будет заблокирована. Такая блокировка выполняется автоматически, если кнопка не имеет соответствующего обработчика сообщений.
Когда вы разрабатываете в редакторе ресурсов меню или диалоговую панель, то достаточно нажать правую кнопку мыши и откроется временное меню из которого можно запустить ClassWizard. В окне редактора панелей управления toolbar правая кнопка не работает, точнее она задействована для других целей.
Чтобы воспользоваться ClassWizard, сначала надо выбрать кнопку, к которой вы желаете привязать код, а затем нажать кнопку ClassWizard из панели Standard. ClassWizard откроет страницу Message Map и сразу предложит создать методы для выбранной кнопки (рис. 3.10).
Новая кнопка в панели управления
Рис. 3.10. Диалоговая панель ClassWizard
По умолчанию, в списке Object IDs будет отображаться идентификатор кнопки, выбранной в редакторе панели управления - ID_MY_BUTTON. В списке Class name выбран класс CMainFrame - класс главного окна приложения. Вы можете выбрать и любой другой класс приложения, если желаете чтобы он обрабатывал команды от данной кнопки.

Теперь выберите из списка Messages идентификатор сообщения, подлежащего обработке. Вам доступны два варианта - COMMAND и UPDATE_COMMAND_UIё.

Выберите из списка Messages идентификатор COMMAND и нажмите кнопку Add Function. ClassWizard добавит к таблице сообщений класса CMainFrame новую макрокоманду для обработки команды от кнопки и запросит имя метода для обработки этой команды. По умолчанию будет предложено имя OnMyButton. Вы можете согласиться с предложением и нажать кнопку OK. ClassWizard добавит к классу CMainFrame метод OnMyButton.

Чтобы сразу перейти к редактированию данного метода, достаточно нажать на кнопку Edit Code. В окне редактирования появится файл с исходными текстами методов класса CMainFrame, а курсор будет установлен на метод OnMyButton:

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

// Метод OnMyButton класса CMainFrame

void CMainFrame::OnMyButton()

{

      // TODO: Здесь вы можете добавить собственный

      // программный код

}

Как видите, метод OnMyButton не имеет параметров. Одно то, что он вызван, служит сигналом нажатия на кнопку ID_MY_BUTTON. Добавьте после комментария TODO код, который вы желаете выполнять по нажатию кнопки. Для нашего первого тестового примера достаточно добавить вызов всего одного метода MessageBox или функции AfxMessageBox:

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

// Метод OnMyButton класса CMainFrame

void CMainFrame::OnMyButton()

{

      // TODO: Здесь вы можете добавить собственный

      // программный код

   MessageBox(“Button is pressed”);

}

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

Когда вы нажимаете на кнопку ID_MY_BUTTON, вызывается метод обработчик OnMyButton из класса CMainFrame. Он отображает на экране сообщение Button is pressed.


Новые версии Visual C++


За короткий промежуток времени, прошедший с момента выхода нашей первой книги, посвященной MFC, Microsoft выпустила еще две версии Visual C++. В них делается основной упор на создание приложений для работы с базами данных и глобальной сетью Internet.


Обнаруженные опечатки


Во время подготовки этой книги мы обнаружили несколько опечаток в томе 24 из серии “Библиотека системного программиста”, посвященном библиотеке MFC. Мы приносим вам свои извенения и недеемся, что они не ввели вас в заблуждение. На дискетах, которые вы можете приобрести вместе с книгой, все перечисленные ошибки исправлены.
На странице 17 метод ConvertString, объявленный как int, неверно определен как void. Этот метод должен возвращать значение типа int.
На странице 21 метод GetPi, объявлен как static void. На самом деле метод GetPi возвращает числовое значение и должен быть объявлен как static float.
На странице 22, в определении метода SetTitle, следует указать для переменной title имя соответствующего класса - CWindow::title.
На странице 32 следует взять текстовые строки, вводимые на экран, и символы \n в двойные кавычки. Следует также исправить ошибку в определении указателей ptrRectObject и ptrFigObject. Символ * должен быть расположен перед именами этих переменных. На странице 33 предпоследняя строка, выводимая на экран программой, должна выглядеть, не как Figure PrintName, а как Rectangle PrintName.
На странице 106, в конструкторе класса CMyDialog, в качестве параметра конструктора класса CDialog, должен передаваться не идентификатор CMyDialog::IDD, а имя шаблона диалоговой панели DIALOGPANEL. Правильный конструктор класса CMyDialog, представлен на странице 116.
На рисунках 2.3, 2.4 и 2.5, представляющих деревья наследования классов CWnd, CView и CDialog, пропущен базовый класс CCmdTarget. Правильный порядок наследования следующий: CWnd <- CCmdTarget <- CObject. Правильный порядок наследования классов также представлен на рисунке 2.2.


Обработка командных сообщений


Процесс обработки командных сообщений значительно отличается от обработки других сообщений. Обычные сообщения обрабатываются тем объектом, которому они поступили. Если таблица сообщений класса объекта не содержит обработчика сообщения, будут просмотрены таблицы сообщений его базовых классов. В том случае, если ни один из базовых классов также не содержит обработчик сообщения, выполняется обработка сообщения по умолчанию.
Судьба командных сообщений гораздо сложнее. Командное сообщение, переданное для обработки объекту приложения, может последовательно передаваться другим объектам приложения. Один из объектов, класс (или базовый класс) которого содержит обработчик этого сообщения, выполняет его обработку. Так, например, командное сообщение, переданное главному окну приложения, в конечном счете может быть обработано активным окном просмотра.
Существует стандартная последовательность объектов приложения, которым передаются командные сообщения. Каждый объект в этой последовательности может обработать командное сообщение, если в его таблице сообщений или таблице сообщений базовых классов есть соответствующая макрокоманда. Необработанные сообщения передаются дальше, другим объектам приложения.
Объекты различных классов обрабатывают командные сообщения по-разному. Например, объекты, представляющие главное окно приложения, сначала предоставляют возможность обработать полученное сообщение другим объектам, в том числе активному окну просмотра и соответствующему ему документу. Только если сообщение остается необработанным, просматривается таблица сообщений класса главного окна приложения. Если и здесь сообщение не обрабатывается, оно направляется другим объектам приложения.
Подавляющее большинство приложений, созданных на основе MFC, использует ряд стандартных командных сообщений, как правило соответствующих элементам меню или кнопкам панели управления. К ним относятся командные сообщения для завершения работы приложения, создания нового документа, открытия документа, записанного на диске, сохранения документа на диске, вызова справочной системы, управления текстовым редактором и т. д. За каждым таким командным сообщением зарезервирован отдельный идентификатор.
MFC обеспечивает различный уровень обработки стандартных командных сообщений, начиная от простого резервирования идентификатора и кончая полной его обработкой. Информацию об использовании стандартных командных сообщений вы можете получить в документации Microsoft Visual C++. Мы также рекомендуем вам изучить реализацию обработчиков стандартных командных сообщений непосредственно в исходных текстах библиотеки MFC.
В некоторых случаях вам может понадобиться изменить порядок, в котором сообщения передаются для обработки объектам приложения. В этом случае вы должны переопределить виртуальный методом OnCmdMsg. Этот метод первоначально определен в классе CCmdTarget и переопределен в классах CView и CDocument.
Ниже описаны последовательности обработки командных сообщений объектами различных классов.


Окна MDI и главное окно однооконного приложения


Для приложений, имеющих однооконный интерфейс, роль главного окна приложения выполняет объект класса CFrameWnd или класса наследованного от базового класса CFrameWnd.
Главное окно однооконного приложения и дочерние MDI окна многооконного приложения обрабатывают командные сообщения одинаковым образом. Объект класса CFrameWnd или CMDIChildWnd, которому поступило командное сообщение, передает его соответствующему окну просмотра. Если окно просмотра не может обработать сообщение, проверяется таблица сообщений класса CFrameWnd или CMDIChildWnd.
Если главное окно однооконного приложения или окно MDI многооконного приложения не может обработать сообщение, оно передается объекту главного класса приложения.


Окно просмотра


В отличие от объектов, представляющих окна типа frame (объекты классов CMDIFrameWnd, CFrameWnd и CMDIChildWnd) окно просмотра в первую очередь проверяет собственную таблицу сообщений. И только в том случае, если командное сообщение не может быть обработано, оно передается документу, связанному с данным окном просмотра.


Органы диалоговых панелей управления


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


Органы управления OCX


На странице OLE Controls диалоговой панели Component Gallery представлены органы управления OCX. Количество этих компонент настолько велико, что мы не имеем возможности рассказать про них все. Поэтому мы ограничимся только несколькими органами управления, которые поставляются вместе с Microsoft Visual C++.
¨ По возможности используйте органы управления OCX. Это позволит сократить время на разработку вашего приложения и повысит его надежность.
Многие фирмы выпускают собственные органы управления OCX и распространяют их как отдельные продукты вместе с подробной документацией. Во многих случаях вместо разработки собственных органов управления вы можете приобрести уже готовые решения. Такой подход позволит сохранить много времени на разработке и отладке собственного программного кода.

Пиктограмма
Назначение соответствующего компонента
Органы управления OCX
Сетка или таблица в каждой ячейке которой можно вывести текст или изображение
Органы управления OCX
Орган управления, который позволяет обмениваться информацией через последовательный порт
Органы управления OCX
Орган управления позволяет воспроизводить видео, записанное в форматах AVI, MPEG и QuickTime 
Органы управления OCX
Орган управления, предназначенный для форматированного ввода и вывода данных
Органы управления OCX
Кнопка с рисунком
Органы управления OCX
Набор кнопок для приложений мультимедиа
Органы управления OCX
Кнопки, имеющие нестандартные формы

Для добавления к проекту новых органов управления OCX, выберите соответствующую пиктограмму на странице OLE Control окна Component Gallry и нажмите кнопку Insert. Дальнейший процесс вставки компонента зависит от него самого.
Как правило, когда вы нажимаете кнопку Insert, у вас запрашивается различная дополнительная информация. Это могут быть названия классов, файлов и т. д. Когда вы введете всю необходимую информацию, начнется модификация проекта. В проект будут добавлены один или несколько классов, представляющих новый орган управления OCX. Методы этих классов вы будете использовать для взаимодействия с объектами OCX. Описание данных классов вы можете получить, нажав кнопку ? в панели Component Gallry.

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

Когда вы добавляете новые органы управления OCX в ваш проект, то в панели инструментов Controls появляются представляющие их пиктограммы. Так на рисунке 2.5 мы представили начальный набор органов управления панели Controls. После того как в проект добавлены органы управления Anibutton Control, Grid Control, Microsoft Comm Control, Microsoft Masked Ediit Control и VideoPlay Control, внешний вид панели изменится (рис. 2.6).

Органы управления OCX

Рис. 2.5. Стандартные органы управления

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

Органы управления OCX

Рис. 2.6. Новые органы управления OCX


Отображение текста в панели состояния


Для изменения текста в самом первом индикаторе панели состояния, который имеет идентификатор ID_SEPARATOR, можно воспользоваться методом SetWindowText. Этот метод определен в классе CWnd и вы можете его использовать, так как класс панели состояния наследуется от класса CWnd. Строку, которую надо вывести в панели состояния, следует передать через параметр lpszString:
void SetWindowText(LPCTSTR lpszString);
Метод SetWindowText изменяет текст в панели состояния, передавая ему сообщение WM_SETTEXT. Самый первый индикатор панели состояния, который имеет идентификатор ID_SEPARATOR, отличается от остальных индикаторов панели состояния. В нем отображаются подсказки для выбранных строк меню, кнопок панели управления и в некоторых других случаях. Фактически, этот индикатор используется различными объектами библиотеки MFC для отображения своего состояния или кратких подсказок.
Объекты MFC устанавливают текст первого идентификатора, непосредственно передавая окну панели состояния сообщение WM_SETTEXT. Отсюда, кстати, следует не очень утешительный вывод: текст, который вы выводите методом SetWindowText, может быть изменен в любой момент без вашего ведома. Чтобы исправить такое положение вещей, можно наследовать от класса CStatusBar собственный класс, в котором определить обработчик для сообщения WM_SETTEXT. Этот обработчик, например, может полностью игнорировать сообщения WM_SETTEXT или обрабатывать их только в особых случаях.
Если вам надо изменить текст не только в самом первом индикаторе, но и в других индикаторах панели состояния, можно воспользоваться методом SetPaneText, который определен в классе CStatusBar:
BOOL
SetPaneText(
   int nIndex,
   LPCTSTR lpszNewText,
   BOOL bUpdate = TRUE
);
Метод SetPaneText выводит в индикаторе с индексом nIndex текст, указанный в параметре lpszNewText. Параметр bUpdate позволяет сразу обновить данный индикатор. Если bUpdate содержит значение TRUE, то индикатор будет перерисован.
Текст, который отображается в индикаторе в данный момент, можно определить при помощи метода GetPaneText, также входящего в класс CStatusBar.

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

Имена для методов обработчиков сообщения ON_UPDATE_COMMAND_UI обычно составляются из строки OnUpdate и имени объекта, сообщения которого обрабатываются. Формат этих методов следующий:

afx_msg void OnUpdateCtrlName(CCmdUI* pCmdUI);

Обработчику сообщения ON_UPDATE_COMMAND_UI через параметр pCmdUI передается указатель на объект класса CCmdUI. Полученный объект класса CCmdUI представляет индикатор панели состояния.

¨ Сообщение ON_UPDATE_COMMAND_UI также используется и для обновления других объектов пользовательского интерфейса, например строк меню и кнопок панелей управления

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

virtual void SetText(LPCTSTR lpszText);

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

Во время обработки сообщения ON_UPDATE_COMMAND_UI можно не только изменить текст в индикаторе, можно полностью заблокировать индикатор. В заблокированном индикаторе текст не отображается. Чтобы выполнить блокировку индикатора, при обработке соответствующего сообщения надо вызвать метод Enable.

Метод Enable, также как метод SetText, определен в классе CCmdUI и имеет следующий прототип:

virtual void Enable(BOOL bOn = TRUE);

Единственный параметр метода bOn выбирает новое состояние индикатора. Если в качестве параметра bOn указать значение TRUE или вызвать метод Enable без параметра, индикатор переходит в нормальное состояние - текст внутри индикатора отображается. Если через параметр bOn передать значение FALSE, тогда выполняется блокировка индикатора.


Панель для выбора шрифта


Стандартная диалоговая панель Font предназначена для выбора шрифта. Диалоговая панель Font отображает список шрифтов, установленных в системе, и позволяет выбрать название шрифта, его начертание, кегль, выделение, цвет, набор национальных символов (рис. 4.7).
Для управления диалоговой панелью Font в библиотеку классов MFC включен класс CFontDialog. Методы этого класса можно использовать для отображения панели Font и определения характеристик шрифта, выбранного пользователем.
Панель для выбора шрифта
Рис. 4.7. Стандартная диалоговая панель Font
Конструктор класса CFontDialog:
CFontDialog(
   LPLOGFONT lplfInitial = NULL,
   DWORD dwFlags = CF_EFFECTS | CF_SCREENFONTS,
   CDC* pdcPrinter = NULL,
   CWnd* pParentWnd = NULL
);
Все параметры конструктора CFontDialog являются необязательными. Первое время, когда вы изучаете класс CFontDialog, вы можете не задавать ни одного параметра. Настройка стандартной панели выбора шрифта, которая выполняется конструктором класса CFontDialog по умолчанию, удовлетворит большинство потребностей.
Мы не станем подробно описывать параметры конструктора, вы найдете это описание в документации Microsoft Visual C++. Сейчас мы приведем только краткий обзор этих параметров. Дополнительную информацию по использованию стандартных панелей выбора шрифта вы можете получить в 14 томе “Библиотеки системного программиста”.
Параметр lplfInitial является указателем на структуру типа LOGFONT, описывающую логический шрифт. Если используете этот параметр, то в диалоговой панели Font по умолчанию будет выбран шрифт наиболее полно соответствующий шрифту, описанному в структуре LOGFONT. По умолчанию данная возможность не используется.
Параметр dwFlags задает набор флагов, управляющих различными режимами работы панели Font.
Через параметр pdcPrinter можно передать конструктору контекст отображения принтера, шрифты которого будут представлены в диалоговой панели Font. Данный параметр используется только в том случае, если в параметре dwFlags указан флаг CF_PRINTERFONTS или CF_BOTH.
Через параметр pParentWnd можно указать родительское окно для далоговой панели Font.


Панель для выполнения поиска и замены


Класс CFindReplaceDialog предназначен для управления диалоговыми панелями Find и Replace. Диалоговая панель Find (рис. 4.11) используется для поиска известных строк в документе приложения, а панель Replace (рис. 4.12) позволяет выполнять замену одной строки на другую.
Панель для выполнения поиска и замены
Рис. 4.11. Стандартная диалоговая панель Find
Важным отличием диалоговых панелей Find и Replace от других стандартных диалоговых панелей, является то, что они представляют собой немодальные диалоговые панели. Поэтому процесс создания этих панелей значительно отличается от процесса создания стандартных панелей для выбора цвета, шрифта и имен файлов.
Панель для выполнения поиска и замены
Рис. 4.12. Стандартная диалоговая панель Replace
Мы расскажем о стандартных диалоговых панелях Find и Replace в одной из следующих книг серии “Библиотека системного программиста”, которуые мы посвятим Microsoft Visual C++ и библиотеке MFC.


Панель состояния


В нижней части большинства современных приложений Windows находится строка состояния программы, которая называется панелью состояния. В ней обычно выводится краткая контекстная подсказка для пользователя, содержание которой зависит от того, с каким элементом окна работает пользователь. В панели состояния также может отображаться описание текущего режима приложения, текущее время, объем свободного пространства на диске и т. д.
Когда вы разрабатываете приложение с оконным интерфейсом при помощи средств MFC AppWizard, вы можете указать, будет ли приложение иметь панель состояния. Этой возможностью управляет переключатель Initial status bar на одной из панелей создания приложения AppWizard. Внешний вид этой панели представлен на рисунке 3.3.
По умолчанию переключатель Initial status bar установлен, и все оконные приложения, построенные с использованием средств MFC AppWizard, имеют панель состояния. В ней отображается текущее состояние приложения или краткая подсказка для выбранных строк меню, а также текущее положение клавиш , и .


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


Обычно, панель управления располагается в верхней части главного окна приложения. Она содержит ряд кнопок, дублирующих функции некоторых строк меню приложения. Так как кнопки располагаются непосредственно в окне приложения и всегда доступны, то нажать на кнопки можно быстрее, чем открыть меню и выбрать соответствующую строку.
Если приложение имеет сложную систему управления и большое меню, то создавать для каждой строки меню отдельную кнопку в панели управления не надо. В противном случае кнопки просто не поместятся в окне приложения.
Чтобы выйти из этого положения, создают несколько отдельных панелей управления, которые можно открывать или закрывать по своему усмотрению. Такая организация панелей управления используется очень часто. Достаточно взглянуть на любое офисное приложение Microsoft - Microsoft Word или Microsoft Excel, или даже на среду Microsoft Visual C++.


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


Панель Extended демонстрирует использование в панелях управления toolbar дополнительных органов управления - поля редактирования и списка combo-box. Сразу после запуска приложения MultiBar в списке combo-box содержатся три строки - First, Second и Third. В поле редактирования можно ввести новые строки для этого списка. После того, как вы наберете строку, нажмите кнопку Панель управления Extended. Введенная строка появится в конце списка combo-box.
На рисунке 3.18 мы показали внешний вид панели управления Extended с открытым списком combo-box, после того как в нее добавлена строка Fifth.
Панель управления Extended
Рис. 3.18. Новая строка в списке combo-box
Панель управления Extended можно присоединить к любой стороне главного окна приложения или разместить в отдельном мини-окне. Когда панель управления Extended отображается в мини-окне, ее форму можно изменять. Ширина панели Extended не может быть меньше, чем самый широкий орган управления.


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


Панель управления Player состоит из девяти кнопок, сгруппированных в четыре отдельные группы. Панель Player предназначена для управления музыкальным центром. Все кнопки имеют краткие подсказки.
При нажатии на кнопки панели управления Player на экране появляется сообщение, о том, что команда не поддерживается. Обратите внимание на кнопки Pause, Type, CD и WAVE.
Кнопка Pause работает как переключатель. Если вы на нее нажмете - она остается в нажатом положении до тех пор, пока вы не нажмете на нее еще один раз. Кнопки Type, CD и WAVE работают вместе как переключатель с зависимой фиксацией. Одновременно может быть нажата только одна из этих кнопок.
Панель управления Player можно присоединить к трем из четырех сторон главного окна приложения - к верхней, левой и нижней. К правой стороне окна панель Player не присоединяется (рис. 3.17). Вы также можете использовать для панели Player отдельное мини окно, форму которого можно изменять.
Панель управления Player
Рис. 3.17. Изменение формы панели управления Player


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


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


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


Не смотря на то, что приложение имеет два меню, для него определена только одна панель управления IDR_MAINFRAME. Идентификаторы первых трех и последних двух кнопок этой панели соответствуют идентификаторам строк и меню IDR_MAINFRAME и меню IDR_MULTITYPE. А вот вторые три идентификатора имеют соответствие только в меню IDR_MULTITYPE. Пока ни один документ не открыт и отображается меню IDR_MAINFRAME, эти кнопки недоступны и отображаются серым цветом:
//////////////////////////////////////////////////////////////
// Панель управления 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 каталога проекта (рис. 1.6):
//////////////////////////////////////////////////////////////
// Изображение Bitmap, определяющее кнопки приложения
IDR_MAINFRAME   BITMAP   MOVEABLE PURE   "res\\Toolbar.bmp"
Панелям управления мы уделили в этой книге отдельную главу, которая имеет название “Меню, панели управления и панели состояния”. В ней описаны принципы устройства и работы панелей управления, приведены простые примеры создания дополнительных панелей управления, в том числе панелей управления на основе шаблонов диалоговых панелей.
Панель управления toolbar
Рис. 1.6. Панель управления


Панель выбора цвета


Чтобы отобразить на экране стандартную диалоговую панель выбора цвета, надо создать объект класса CColorDialog, а затем вызвать метод DoModal. При создании объекта класса CColorDialog используйте следующий конструктор:
CColorDialog(
   COLORREF clrInit = 0,
   DWORD dwFlags = 0,
   CWnd* pParentWnd = NULL
);
Все параметры конструктора необязательные, вы можете их опустить, однако в некоторых случаях, использование этих параметров может вам помочь.
Первый параметр clrInit позволяет указать цвет, выбранный по умолчанию сразу после открытия диалоговой панели. Если параметр не будет указан, в качестве цвета, выбранного по умолчанию будет использоваться черный цвет.
Параметр dwFlags содержит набор флагов, управляющих диалоговой панелью выбора цвета. Подробно про эти флаги вы можете прочитать в 14 томе “Библиотеки системного программиста”, в разделе, описывающем структуру CHOOSECOLOR.
Последний параметр pParentWnd можно использовать, чтобы указать родительское окно диалоговой панели.
Если при создании объекта CColorDialog вы не укажите параметр dwFlags, вы, тем не менее, можете выполнить настройку диалоговой панели, обратившись непосредственно к элементу m_cc данного класса. Параметр dwFlags, указанный в конструкторе, используется для инициализации m_cc. Изменения в элемент m_cc должны быть внесены до того, как панель будет отображена на экране.


Панель выбора файлов


Среди стандартных диалоговых панелей, для которых в библиотеке MFC создан специальный класс, есть панели для работы с файловой системой - Open и Save As (рис. 4.4). Диалоговая панель Open позволяет выбрать один или несколько файлов, расположенных на дисках компьютера, и открыть их для дальнейшего использования. Диалоговая панель Save As позволяет выбрать имя файла для записи в него документа.
Для управления диалоговыми панелями Open и Save As предназначен один единственный класс - CFileDialog. Рассмотрим конструктор класса CFileDialog более подробно:
CFileDialog(

   BOOL bOpenFileDialog,

   LPCTSTR lpszDefExt = NULL,

   LPCTSTR lpszFileName = NULL,

   DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,

   LPCTSTR lpszFilter = NULL,

   CWnd* pParentWnd = NULL

);
Объекты класса CFileDialog представляют диалоговую панель Open или Save As в зависимости от параметра bOpenFileDialog. Если параметр bOpenFileDialog содержит значение TRUE, тогда создается объект, управляющий диалоговой панелью Open, а если FALSE - диалоговой панелью Save As.
Параметр bOpenFileDialog является единственным параметром, который вы должны указать. Остольные параметры конструктора класса CFileDialog задают различные режимы работы панели и могут не указываться.
Так, чтобы создать объект класса CFileDialog, представляющий стандартную диалоговую панель для открытия файлов (mFileOpen), и объект, представляющий стандартную диалоговую панель для сохранения файлов (mFileSaveAs), можно воспользоваться следующими вызовами конструктора класса:
// Объект mFileOpen представляет стандартную

// диалоговую панель Open

CFileDialog   mFileOpen(TRUE);

// Объект mFileOpen представляет диалоговую

// панель SaveAs

CFileDialog   mFileSaveAs(TRUE);
Во многих случаях файлы, которые вы открываете или закрываете в вашем приложении, имеют определенное расширение. Параметр lpszDefExt позволяет задать расширение файлов, используемое по умолчанию. Если пользователь не укажет расширение файла явным образом, ему автоматически присваивается расширение, принятое по умолчанию. В случае если параметр lpszDefExt не указан или содержит значение NULL, то расширение файлов должно задаваться явно.

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

Вы можете изменить внешний вид и некоторые другие характеристики стандартных диалоговых панелей для открытия и сохранения файлов с помощью параметра dwFlags. В него записывается комбинация флагов, управляющих различными характеристиками этих панелей. Назначение данного параметра соответствует полю Flags структуры OPENFILENAME. Описание структуры OPENFILENAME вы можете найти в 13 томе “Библиотеки системного программиста”.

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

Вы можете указать список фильтров для диалоговых панелей Open и Save As через параметр lpszFilter. Одновременно можно указать несколько фильтров. Каждый фильтр задается двумя строками - строкой, содержащей имя фильтра, и строкой, в которой паречислены соответствующие ему расширения имен файлов. Если одному типу файлов соответствуют несколько расширений, они разделяются символом ;. Строка, содержашая имя фильтра, отделяется от строки с расширениями файлов символом |. Если используются несколько фильтров, то они также отделяются друг от друга символом |.

Диалоговые панели, представленные объектами класса CFileDialog, могут иметь или не иметь родительского окна. Чтобы указать родительское окно, передайте конструктору CFileDialog указатель на него через параметр pParentWnd.

Панель выбора файлов

Рис. 4.4. Стандартная диалоговая панель Save As


Панели для вывода документа на печать


Вы можете использовать класс CPrintDialog для создания двух видов диалоговых панелей, предназначенных для печати документов и выбора формата документов. Кроме класса CPrintDialog, вы можете также использовать класс CPageSetupDialog. Он позволяет создать диалоговую панель для выбора формата документа, имеющую несколько иной вид.
В приложения, подготовленные с использованием средств MFC AppWizard и построенные по технологии документ - окно просмотра, по умолчанию “встроена” возможность вывода редактируемого документа на печать.
Так, например, в меню File приложения Multi находятся три строки, которые управляют процессом печати документов, подготовленных в приложении. Мы привели внешний вид меню File приложения Multi на рисунке 4.10.
Панели для вывода документа на печать
Рис. 4.8. Меню File, приложения Multi
Чтобы распечатать документ, достаточно выбрать из меню File строку Print. На экране появится диалоговая панель Print, представленная нами на рисунке 4.9. В ней вы можете выбрать печатающее устройство для печати документов (группа Name), указать будет ли печататься весь документ, либо его часть (группа Print range), а также сколько копий документа будет напечатано (группа Copies). Вы также можете настроить различные характеристики печатающего устройства, если нажмете кнопку Properties в группе Printer.
Панели для вывода документа на печать
Рис. 4.9. Стандартная диалоговая панель Print
Если вам требуется только определить печатающее устройство на котором будет печататься документ и определить формат документа, выберите из меню File строку Print Setup. В этом случае на экране появится несколько другоая диалоговая панель, внешний вид которой мы привели на рисунке 4.10.
В группе Printer вы можете указать печатающее устройство для печати документов и настроить его соответствующим образом. Группа Paper задает формат бумаги на котором будет печататься документ и режим подачи бумаги в печатающее устройство. Группа Orientation включает только один переключатель, определяющий ориентацию бумаги. Он принимает положение Portrait для вертикальной ориентации изображения на бумаги (режим “портрет”) или Landscape для горизонтальной ориентации изображения на бумаги (режим “ландшафт”).

Панели для вывода документа на печать

Рис. 4.10. Стандартная диалоговая панель Print

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

Если не требуется выполнять специфическую обработку документа перед печатью, то скорее всего, вам вообще не потребуется самостоятельно добавлять программный код, отвечающий за процесс печати. Тем более маловероятно, что вы будете самостоятельно создавать диалоговые панели типа Print, Print Setup или Print Preview. По этой причине мы не станем в этой книге рассматривать классы CPrintDialog и CPageSetupDialog. Процедура создания этих панелей практически не отличается от создания панели на основе класса CFileDialog. Описание методов классов CPrintDialog и CPageSetupDialog вы можете получить из документации Microsoft Visual C++.


Панели управления и панели состояния


Если очередь сообщений приложения пуста, вызывается метод OnIdle главного класса приложения. В своем приложении вы можете переопределить метод OnIdle и выполнять с его помощью какую-либо фоновую работу.
Метод OnIdle определен в классе CWinApp и по умолчанию выполняет обновление пользовательского интерфейса - передает команды обновления для тех кнопок панелей управления и индикаторов панелей состояния, которые имеют в таблице сообщений приложения макрокоманду ON_UPDATE_COMMAND_UI. Макрокоманда ON_UPDATE_COMMAND_UI вызывает методы-обработчики, которые могут изменить состояние кнопок и индикаторов панелей управления и панелей состояния.
Как правило, ряд строк меню и кнопок панелей управления имеют одинаковые идентификаторы. В этом случае для обработки команд обновления строк меню и кнопок панели управления вызываются одни и те же методы.


Панели управления приложения MultiBar


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


Пиктограмма


В файле ресурсов приложения Multi определены две пиктограммы IDR_MULTITYPE и IDR_MAINFRAME. Каждая из этих пиктограмм содержит по два изображения размером 32х32 и 16х16 пикселов. Внешний вид пиктограмм соответствует пиктограммам, используемым приложением с однооконным интерфейсом. Для однооконного приложения пиктограмма, представляющая документ, называлась IDR_SINGLETYPE, а не IDR_MULTITYPE. Такая разница в названиях возникла исключительно из-за разницы в названиях проектов приложений:
//////////////////////////////////////////////////////////////
// Пиктограммы
IDR_MAINFRAME   ICON   DISCARDABLE   "res\\Multi.ico"
IDR_MULTITYPE   ICON   DISCARDABLE   "res\\MultiDoc.ico"
Пиктограмма IDR_MAINFRAME представляет приложение, когда оно минимизировано (рис. 1.4). Эта же пиктограмма отображается в левом верхнем углу главного окна приложения.
Пиктограмма
Рис. 1.4. Пиктограмма IDR_MAINFRAME
Пиктограмма IDR_MULTITYPE используется для представления документа с которым работает приложение (рис. 1.5). В отличие от приложения с однооконным интерфейсом, которое не использует эту пиктограмму, приложение с многооконным интерфейсом отображает пиктограмму IDR_MULTITYPE в левом верхнем углу окна документа.
Пиктограмма
Рис. 1.5. Пиктограмма IDR_MULTITYPE


Положение панели управления


Панель управления приложения Bar нельзя переместить с одной границы окна к другой и нельзя разместить ее в отдельном окне, независимом от главного окна приложения. Единственное, что вы можете сделать, так это выбрать начальное положение панели управления, установив в момент вызова метода Create флаг CBRS_TOP или CBRS_BOTTOM.
Чтобы вы смогли перемещать панель управления с одной границы окна к другой надо:
1. Разрешить перемещение панели управления для окна, которое содержит панель управления. Для этого следует вызвать метод EnableDocking данного окна. Метод EnableDocking является элементом класса CFrameWnd
2. Разрешить такое перемещение для самой панели управления. Для этого следует вызвать метод EnableDocking панели управления. Метод EnableDocking является элементом класса CControlBar
3. Переместить панель управления к одной из сторон окна приложения или вывести ее в отдельном окне. Для этого необходимо вызвать метод DockControlBar или FloatControlBar данного окна приложения. Методы DockControlBar и FloatControlBar являются элементами класса CFrameWnd
Если в одном окне отображается несколько панелей управления, то вы должны вызвать для этого окна метод EnableDocking, а затем для каждой панели управления в отдельности вызвать методы EnableDocking и DockControlBar (или FloatControlBar).
¨ Если вы не выполните хотя бы один пункт из трех, перечисленных выше, панель управления будет жестко привязана к одной из границ окна, или возникнет ошибка во время работы приложения
Параметры методов CFrameWnd::EnableDocking, CControlBar::EnableDocking, а также CFrameWnd::DockControlBar и CFrameWnd::FloatControlBar определяют границы окна, к которым можно пристыковать панель управления. Рассмотрим эти методы более подробно.


Повторное использование кода


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


Приложение DialogBar


Создайте новый проект под названием DialogBar. В качестве типа приложения выберите из списка Type строку Application. Настройте проект DialogBar, точно также как вы настраивали проекты Bar и MultiBar: укажите, что приложение будет работать с библиотекой классов MFC.
Наберите в редакторе исходный текст приложения и сохраните его в файле DialogBar.cpp (листинг 3.10). Чтобы ускорить набор исходного текста приложения, за основу вы можете взять файл Bar.cpp приложения Bar. Готовый файл DialogBar.cpp включите в проект.
Листинг 3.10. Файл DialogBar.cpp
//============================================================
// Приложение DialogBar
// (c) Frolov G.V., 1996
// E-mail: frolov@glas.apc.org
//============================================================
// Исключаем редко используемые определения из
// включаемых файлов
#define VC_EXTRALEAN                     
// Включаемый файл для MFC
#include
#include
#include
// Включаемый файл для ресурсов приложения
#include "resource.h"
//============================================================
// Класс CDlgBarApp - главный класс приложения
//============================================================
class CDlgBarApp : public CWinApp
{
public:
   // Мы будем переопределять метод InitInstance
   virtual BOOL InitInstance();
};
 
// Создаем объект приложение класса CDlgBarApp
CDlgBarApp DlgBarApp;
 
//============================================================
// Класс CDlgBarWindow - представляет главное окно
//============================================================
class CDlgBarWindow : public CFrameWnd
{
// Определяем панель управления
protected:
   // Панель управления на основе класса CDialogBar      
   CDialogBar       m_wndDialogBar;
  
protected:
   // Метод OnCreate используется для создания диалоговой
   // панели управления
   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
public:

   // Объявляем конструктор класса CDlgBarWindow

   CDlgBarWindow();

   // Объявляем методы для обработки команд от диалоговой

   // панелей управления

   afx_msg BOOL DlgBarCommand(UINT nID);

   afx_msg void DlgBarCombo();

  

   // Макрокоманда необходима, так как класс

   // CDlgBarWindow обрабатывает сообщения

   DECLARE_MESSAGE_MAP()   

};

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

// Метод BarCommand

// Обрабатывает команды, выводит на экран сообщение

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

BOOL CDlgBarWindow::DlgBarCommand(UINT nID)

{

   CString szCommandAbout;

   // Загружаем текстовую строку с идентификатором,

   // соответствующим идентификатору поступившего командного

   // сообщения и выводим ее на экран

   if(szCommandAbout.LoadString(nID))

      MessageBox(szCommandAbout);

   else

   {

      // Ошибка при загрузке строкового ресурса

      TRACE0("Failed to load string\n");

      return -1;     

   }

  

   return TRUE;

}

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

// Метод DlgBarCombo

// Обрабатывает команды, выводит на экран сообщение

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

void CDlgBarWindow::DlgBarCombo()

{

   // Отображаем сообщение о том, что сделан выбор из списка

   MessageBox("Combo-box selection changed");

}

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

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

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

BEGIN_MESSAGE_MAP(CDlgBarWindow, CFrameWnd)

   // Макрокоманда вызывает метод OnCreate

   ON_WM_CREATE()

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

   // Обработчики команд от диалоговой панели управления

   // Командные сообщения от кнопок Set и Clear

   ON_COMMAND_EX(IDC_BUTTON_SET, DlgBarCommand)

   ON_COMMAND_EX(IDC_BUTTON_CLEAR, DlgBarCommand)



   ON_COMMAND_EX(IDC_CHECK1, DlgBarCommand)

   // Командные сообщения от переключателя Alighn

   ON_COMMAND_EX(IDC_RADIO_LEFT, DlgBarCommand)

   ON_COMMAND_EX(IDC_RADIO_CENTER, DlgBarCommand)

   ON_COMMAND_EX(IDC_RADIO_RIGHT, DlgBarCommand)

  

   // Командные сообщения от списка combo-box

   ON_CBN_SELCHANGE( IDC_COMBO_COLOUR, DlgBarCombo)

  

END_MESSAGE_MAP()

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

// Метод InitInstance класса CDlgBarApp

// Создает главное окно приложения и отображает его на экране

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

BOOL CDlgBarApp::InitInstance()

{

   m_pMainWnd = new CDlgBarWindow();

   m_pMainWnd -> ShowWindow(m_nCmdShow);

   m_pMainWnd -> UpdateWindow();

   return TRUE;

}

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

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

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

CDlgBarWindow::CDlgBarWindow()

{

   // Создаем окно приложения, соответствующее

   // данному объекту класса CDlgBarWindow

   Create(NULL, "Dialog Bar", WS_OVERLAPPEDWINDOW,

          rectDefault, NULL);

}

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

// Метод OnCreate класса CDlgBarWindow

// Используется для создания панелей управления

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

int CDlgBarWindow::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

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

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

      return -1;

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

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

   if (!m_wndDialogBar.Create(this, IDD_DIALOG_BAR, CBRS_TOP,

                              IDD_DIALOG_BAR))

   {

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

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

      return -1;

   }

   return 0;



}

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

Откройте панель свойств редактируемой панели управления Dialog Properties. Откройте страницу General и присвойте диалоговой панели идентификатор IDD_DIALOG_BAR. Затем откройте страницу Styles. Выберите из списка Style стиль диалоговой панели Child. Укажите, что диалоговая панель не имеет рамки - из списка Border надо выбрать строку None. Все остальные переключатели из страницы Styles и из страниц More Styles и Extended Styles должны быть отключены.

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

Разместите в шаблоне диалоговой панели несколько органов управления. Для нашего примера вам надо создать две кнопки Set и Clear с идентификаторами IDC_BUTTON_SET и IDC_BUTTON_CLEAR, три переключателя Left, Center и Right с зависимой фиксацией, имеющие идентификаторы IDC_RADIO_LEFT, IDC_RADIO_CENTER и IDC_RADIO_RIGHT, а также список combo-box с идентификатором IDC_COMBO_COLOUR. В список IDC_COMBO_COLOUR внесите несколько начальных значений. Мы записали в него названия различных цветов - Red, Blue, Green, Yellow, Black и White. Эти строки будут содержаться в списке сразу после отображения на экране диалоговой панели.

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

Мы привели файл ресурсов, который у вас должен получиться в листинге 3.11.

Листинг 3.11. Файл DialogBar.rc

//Microsoft Developer Studio generated resource script.

//

#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS

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

//

// Generated from the TEXTINCLUDE 2 resource.

//

#include "afxres.h"

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



#undef APSTUDIO_READONLY_SYMBOLS

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

//

// Russian resources

#if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_RUS)

#ifdef _WIN32

LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT

#pragma code_page(1251)

#endif //_WIN32

#ifdef APSTUDIO_INVOKED

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

//

// TEXTINCLUDE

//

1 TEXTINCLUDE DISCARDABLE

BEGIN

    "resource.h\0"

END

2 TEXTINCLUDE DISCARDABLE

BEGIN

    "#include ""afxres.h""\r\n"

    "\0"

END

3 TEXTINCLUDE DISCARDABLE

BEGIN

    "\r\n"

    "\0"

END

#endif    // APSTUDIO_INVOKED

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

//

// Dialog

//

IDD_DIALOG_BAR DIALOG DISCARDABLE  0, 0, 227, 50

STYLE WS_CHILD

FONT 8, "MS Sans Serif"

BEGIN

    CONTROL     "Left", IDC_RADIO_LEFT, "Button",

                   BS_AUTORADIOBUTTON, 95, 10, 40,10

    CONTROL     "Center", IDC_RADIO_CENTER, "Button",

                   BS_AUTORADIOBUTTON, 95, 19, 45, 11

    CONTROL     "Right", IDC_RADIO_RIGHT, "Button",

                   BS_AUTORADIOBUTTON, 95, 30, 40, 9

    GROUPBOX    "Aligns",IDC_STATIC,85,0,80,45

    PUSHBUTTON  "Set",IDC_BUTTON_SET,175,5,45,15

    PUSHBUTTON  "Clear",IDC_BUTTON_CLEAR,175,30,45,15

    COMBOBOX    IDC_COMBO_COLOUR, 5, 5, 70, 50,

                   CBS_DROPDOWNLIST | CBS_SORT |

                   WS_VSCROLL | WS_TABSTOP

END

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

//

// DESIGNINFO

//

#ifdef APSTUDIO_INVOKED

GUIDELINES DESIGNINFO DISCARDABLE

BEGIN

    IDD_DIALOG_BAR, DIALOG

    BEGIN

        LEFTMARGIN, 7

        RIGHTMARGIN, 220

        TOPMARGIN, 7

        BOTTOMMARGIN, 43

    END

END

#endif    // APSTUDIO_INVOKED



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

//

// Dialog Info

//

IDD_DIALOG_BAR DLGINIT

BEGIN

    IDC_COMBO_COLOUR, 0x403, 4, 0

0x6552, 0x0064,

    IDC_COMBO_COLOUR, 0x403, 5, 0

0x6c42, 0x6575, "\000"

    IDC_COMBO_COLOUR, 0x403, 6, 0

0x7247, 0x6565, 0x006e,

    IDC_COMBO_COLOUR, 0x403, 7, 0

0x6559, 0x6c6c, 0x776f, "\000"

    IDC_COMBO_COLOUR, 0x403, 6, 0

0x6c42, 0x6361, 0x006b,

    IDC_COMBO_COLOUR, 0x403, 6, 0

0x6857, 0x7469, 0x0065,

    0

END

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

//

// String Table

//

STRINGTABLE DISCARDABLE

BEGIN

    IDC_RADIO_LEFT          "Left radio box"

    IDC_RADIO_CENTER        "Center radio box"

    IDC_RADIO_RIGHT         "Right radio box"

    IDC_BUTTON_SET          "Set button pressed"

END

STRINGTABLE DISCARDABLE

BEGIN

    IDC_BUTTON_CLEAR        "Clear button pressed"

END

#endif    // Russian resources

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

#ifndef APSTUDIO_INVOKED

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

// Generated from the TEXTINCLUDE 3 resource.

//

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

#endif    // not APSTUDIO_INVOKED

Идентификаторы ресурсов приложения DialogBar и идентификаторы органов управления диалоговой панели IDD_DIALOG_BAR определены в файле resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++. Исходный текст файла представлен в листинге 3.12.

Листинг 3.12. Файл resource.h

//{{NO_DEPENDENCIES}}

// Microsoft Developer Studio generated include file.

// Used by DialogBar.rc

//

#define IDD_DIALOG_BAR                  101

#define IDC_RADIO_LEFT                  1004

#define IDC_RADIO_CENTER                1005

#define IDC_RADIO_RIGHT                 1006

#define IDC_BUTTON_SET                  1007



#define IDC_BUTTON_CLEAR                1008

#define IDC_COMBO_COLOUR                1012

// Next default values for new objects

//

#ifdef APSTUDIO_INVOKED

#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NEXT_RESOURCE_VALUE        104

#define _APS_NEXT_COMMAND_VALUE         40001

#define _APS_NEXT_CONTROL_VALUE         1014

#define _APS_NEXT_SYMED_VALUE           101

#endif

#endif

Постройте приложение DialogBar и запустите его. На экране появится главное окно приложения, в верхней части которого отображается диалоговая панель управления (рис. 3.19).

Приложение DialogBar

Рис. 3.19. Приложение DialogBar

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


Приложение FileDlg


Создайте новый проект, присвоив ему имя FileDlg. В качестве типа приложения выберите из списка Type строку Application. Наберите в редакторе исходный текст приложения и сохраните его в файле FileDlg.cpp (листинг 4.1). Включите набранный файл в проект.
Выберите из меню Build строку Settings. На экране появится диалоговая панель Project Settings, предназначенная для настройки различных характеристик проекта. Откройте в этой диалоговой панели страницу General и выберите из списка Microsoft Foundation Classes строку Use MFC in a Shared Dll.
Листинг 4.1. Файл FileDlg.cpp
//============================================================
// Приложение FileDlg
// (c) Frolov G.V., 1996
// E-mail: frolov@glas.apc.org
//============================================================
// Включаемый файл для библиотеки MFC
#include
// Включаемый файл для стандартных диалоговых панелей
#include
//============================================================
// Класс CFileDlgApp
// Наследуем от базового класса CWinApp главный
// класс приложения CFileDlgApp
//============================================================
class CFileDlgApp : public CWinApp
{
public:
   // Мы будем переопределять метод InitInstance,
   // предназначенный для инициализации приложения
   virtual BOOL InitInstance();
};
 
// Создаем объект приложение класса CFileDlgApp
CFileDlgApp FileDlgApp;
 
//============================================================
// Метод InitInstance класса CFileDlgApp
// Переопределяем виртуальный метод InitInstance
// класса CWinApp. Он вызывается каждый раз при запуске
// приложения
//============================================================
BOOL CFileDlgApp::InitInstance()
{
   // Определяем объект класса CFileDialog, представляющий
   // стандартную диалоговую панель Open
   CFileDialog mFileOpen(TRUE);
   // Переменная pos будет использоваться для получения
      // полных имен файлов, выбранных в панели Open

   POSITION pos;

     

   // В строку msg будут записываться названия файлов

   CString msg;

   // Устанавливаем флаг OFN_ALLOWMULTISELECT, который

   // разрешает одновременно выбирать несколько файлов

   mFileOpen.m_ofn.Flags |= OFN_ALLOWMULTISELECT;

     

   // Отображаем диалоговую панель Open и позволяем

   // пользователю выбрать с помощью нее один или

   // несколько файлов

   int result = mFileOpen.DoModal();

   // Проверяем как была закрыта диалоговая панель Open -

   // по нажатию кнопки OK или Cancel

   if(result == IDCANCEL)

   {

      // Если пользователь отказался от выбора файлов и

      // нажал кнопку Cancel отображаем соответствующее

      // сообщение

      AfxMessageBox("File not selected");

   }

   else if(result == IDOK)

   {

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

      // начальную позицию для метода GetNextPathName

      pos = mFileOpen.GetStartPosition();

      // В цикле получаем имена файлов, выбранных в

      // диалоговой панели Open. После получения всех

      // имен, завершаем приложение

      while(pos != NULL ) {

         // Получаем имя очередного файла

         msg = mFileOpen.GetNextPathName( pos );

         // Отображаем имена файлов

         AfxMessageBox("Selected File - " + msg);

      }

   }

   return TRUE;

}

Постройте проект и запустите полученное приложение, выбрав из меню Build строку Execute FileDlg.exe. На экране появится стандартная диалоговая панель Open (рис. 4.5).

Приложение FileDlg

Рис. 4.5. Стандартная диалоговая панель Open

С помощью нее вы можете выбрать несколько файлов из любого каталога компьютера. После того, как вы выберите интересующие вас файлы, нажмите кнопку Open. Диалоговая панель Open закроется, и на экране последовательно будут отображаться сообщения, содержащие полные имена выбранных файлов (рис. 4.6).

Приложение FileDlg

Рис. 4.6. Стандартная диалоговая панель Open

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



В нашем проекте определен только один класс CFileDlgApp. В класс CFileDlgApp входит метод InitInstance. Кроме того, определен глобальный объект FileDlgApp класса CFileDlgApp. Мы не будем подробно останавливаться на том, как устроено приложение FileDlg. Соответствующую информацию вы можете получить в 24 томе из серии “Библиотека системного программиста”, который служит введением в язык программирования Си++ и библиотеку классов MFC.

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

Сначала создается объект mFileOpen класса CFileDialog. В качестве параметра конструктора CFileDialog указывается значение TRUE, которое указывает, что данный объект класса будет управлять стандартной диалоговой панелью Open:

CFileDialog mFileOpen(TRUE);

Определение объекта класса CFileDialog не вызывает автоматического отображаеия на экране соответствующей диалоговой панели. Для этого вызывается метод DoModal. Непосредственно перед обращением к методу DoModal мы устанавливаем флаг OFN_ALLOWMULTISELECT в поле Flags элемента данных m_ofn класса CFileDialog. Если данный флаг установлен, то в диалоговой панели можно будет выбрать сразу несколько файлов. В противном случае, из панели можно будет выбрать только один файл:

mFileOpen.m_ofn.Flags |= OFN_ALLOWMULTISELECT;

Метод DoModal класса CFileDialog отображает на экране соответствующую диалоговую панель (в нашем случае диалоговую панель Open) и позволяет пользователю выбрать из нее один или несколько файлов. Результат работы метода DoModal записывается в переменную result:

int result = mFileOpen.DoModal();

Если пользователь отказался от выбора файлов и нажал кнопку Cancel, тогда метод DoModal возвращает значение IDCANCEL. В этом случае приложение отображает сообщение File not selected и завершает свою работу:

AfxMessageBox("File not selected");

Если пользователь выбрал из диалоговой панели Open один или несколько файлов и нажал кнопку Open, тогда метод DoModal возвращает значение IDOK. В этом случае вызывается метод GetStartPosition, который записывает в переменную pos типа POSITION значение, необходимое методу GetNextPathName для получения всех имен выбранных файлов:

POSITION pos;

pos = mFileOpen.GetStartPosition();

Переменная pos передается методу GetNextPathName. Этот метод получает очередное имя выбранного файла и изменяет значение переменной pos. До тех пор, пока значение pos не станет равно NULL, метод GetNextPathName вызывается в цикле и получает очередное имя файла:

while(pos != NULL ) {

   // Получаем имя очередного файла

   msg = mFileOpen.GetNextPathName( pos );

   // Отображаем имена файлов

   AfxMessageBox("Selected File - " + msg);

}

Полученные имена файлов отображаются на экране с помощью функции AfxMessageBox. Обратите внимание, что в качестве параметра этой функции передается результат операции конкатенации строки Selected File и объекта класса CString.


Приложение готово


Все, приложение готово. Постройте проект и запустите полученный выполняемый файл. На экране появится диалоговая панель New, представленная на рисунке 1.15.
Приложение готово
Рис. 1.15. Выбор типа нового документа
В списке New этой панели перечислены типы документов, с которыми работает приложение. Так как наше приложение работает с документами двух типов - текстовыми и графическими, то этот список содержит всего два элемента. Если в последствии вы добавите к приложению новые типы документов, их названия также появятся в этой панели.
Выберите из списка тип документа, который вы будете создавать и нажмите на кнопку OK. Откроется главное окно приложения и окно MDI с новым документом выбранного типа.
Когда вы будете создавать новые документы через меню (строка New меню File), или панель управления (кнопка Приложение готово), вам будет предложено выбрать тип нового документа. Для этого также будет использоваться диалоговая панель New, описанная выше.
Одновременно можно открыть несколько документов различного типа, причем каждый документ может иметь несколько окон просмотра. Документы каждого типа имеют различные названия и расширения имени файлов, используемые по умолчанию.


Приложение Multi


Создайте новое приложение с многооконным интерфейсом и назовите его Multi. При определении свойств приложения оставьте все предложения по умолчанию. Приложение Multi не будет использовать технологию OLE и сетевые технологии, не будет работать с базами данных. Процедура создания приложений с использованием MFC AppWizard описана в разделе “Приложение с оконным интерфейсом” 24 тома серии “Библиотека системного программиста”, поэтому мы будем считать, что вы уже создали проект.
Постройте проект и запустите полученное приложение. На экране появится главное окно. Внутри главного окна расположены меню, панель управления и панель состояния.
Сразу после запуска приложения Multi, открывается дочернее окно, предназначенное для просмотра документа, которое получает название Multi1. Вы можете создать новые дочерние окна, выбрав из меню File строку New - открыть новый документ или строку Open - открыть файл (рис. 1.1). Для просмотра уже открытого документа можно открыть еще одно окно (рис. 1.11). В названии такого окна указывается дополнительный числовой индекс.
Если одновременно открыто несколько окон, то можно упорядочить расположение этих окон и пиктограмм, представляющих минимизированные окна. Для этого специально предназначено меню Window.
Приложение Multi
Рис. 1.1. Приложение Multi
Теперь рассмотрим внимательно сам проект Multi, подготовленный для нас MFC AppWizard. Найдите окно Project Workspace и откройте страницу FileView. Вы увидите список всех исходных файлов, входящих в проект (рис. 1.2). В отдельную папку Dependencies будут помещены названия вспомогательных файлов проекта. Эти файлы не входят в проект непосредственно, но используются либо для хранения ресурсов, либо как включаемые файлы, указанные директивой #include в одном или нескольких основных файлах проекта.
Приложение Multi
Рис. 1.2. Окно Project Workspace, файлы проекта
В следующей таблице кратко описаны основные файлы проекта Multi. Ниже мы подробно рассмотрим ресурсы приложения Multi, а также опишем составляющие его классы и их методы.

Имя файла

Описание

ChildFrm.cpp

Файле содержит определение методов класса CChildFrame

ChildFrm.h

В файле находится определение класса дочернего окна MDI - CChildFrame

MainFrm.cpp

Файл содержит определения методов класса CMainFrame

MainFrm.h

Содержит описание класса главного окна приложения, который называется CMainFrame. Класс CMainFrame наследуется от базового класса CFrameWnd, определенного в библиотеке классов MFC

Multi.cpp

Основной файл приложения. В нем определены методы основного класса приложения CMultiApp

Multi.h

В этом файле перечислены другие включаемые файлы и описан главный класс приложения CMultiApp

Multi.pch

Файл создается во время первой трансляции программы, если вы используете предварительную компиляцию включаемых файлов

Multi.rc

Файл ресурсов. В этом файле описаны все ресурсы приложения. Сами ресурсы могут быть записаны в каталоге RES, расположенном в главном каталоге проекта

MultiDoc.cpp

Включает определение методов класса CMultiDoc

MultiDoc.h

Содержит определение класса документов приложения - CMultiDoc

MultiView.cpp

Включает определение методов класса CMultiView

MultiView.h

Содержит описание класса окна просмотра приложения - CMultiView

ReadMe.txt

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

res\Multi.ico

Пиктограмма приложения

res\Multi.rc2

В этом файле определены ресурсы, которые нельзя редактировать с помощью редактора ресурсов среды Visual C++

res\MultiDoc.ico

Пиктограмма для документов приложения

res\Toolbar.bmp

Файл содержит растровое изображение кнопок панели управления

Resource.h

Файл содержит определения идентификаторов ресурсов приложения, например, идентификаторы строк меню

StdAfx.h, StdAfx.cpp

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


Приложение MultiBar


Создайте новый проект под названием MultiBar. В качестве типа приложения выберите из списка Type строку Application. Настройте проект MultiBar, точно также как вы настраивали проект Bar - укажите, что приложение будет работать с библиотекой классов MFC.
Наберите в редакторе исходный текст приложения и сохраните его в файле MultiBar.cpp (листинг 3.7). За основу вы можете взять файл Bar.cpp приложения Bar. Включите готовый файл MultiBar.cpp в проект.
Листинг 3.7. Файл MultiBar.cpp
//============================================================
// Приложение MultiBar
// (c) Frolov G.V., 1996
// E-mail: frolov@glas.apc.org
//============================================================
// Исключаем редко используемые определения из
// включаемых файлов
#define VC_EXTRALEAN
// Включаемый файл для MFC
#include
#include
#include
// Включаемый файл для ресурсов приложения
#include "resource.h"
//============================================================
// Класс CMultiBarApp - главный класс приложения
//============================================================
class CMultiBarApp : public CWinApp
{
public:
   // Мы будем переопределять метод InitInstance
   virtual BOOL InitInstance();
};
 
// Создаем объект приложение класса CMultiBarApp
CMultiBarApp MultiBarApp;
 
   //============================================================
// Класс CExtendedBar - класс панели управления Extended
   //============================================================
class CExtendedBar : public CToolBar
{
public:
   // Дополнительные органы управления панели Extended
   CEdit m_edit;          // текстовый редактор
   CComboBox m_combo_box; // список с текстовым редактором
};
   //============================================================
// Класс CMultiBarWindow - представляет главное окно
//============================================================
class CMultiBarWindow : public CFrameWnd

{

// Определяем панели управления

protected:

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

   CToolBar       m_wndPlayerBar;

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

   CToolBar       m_wndStyleBar;

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

   CExtendedBar   m_wndExtendedBar;

  

protected:

   // Метод OnCreate используется для создания

   // панелей управления

   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

public:

   // Объявляем конструктор класса CMultiBarWindow

   CMultiBarWindow();

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

   // управления

   afx_msg void BarCommandOne();

   afx_msg void BarCommandRange(UINT nID);

   afx_msg void AddStringToComboBox();

   // Объявляем метод для обработки команд от меню View

   afx_msg BOOL ShowStyle(UINT nID);

   // Макрокоманда необходима, так как класс

   // CMultiBarWindow обрабатывает сообщения

   DECLARE_MESSAGE_MAP()   

};

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

// Метод BarCommandRange

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

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

void CMultiBarWindow::BarCommandRange( UINT nID )

{

   // Обработчик не выполняет никакой работы

}

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

// Метод BarCommand

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

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

void CMultiBarWindow::BarCommandOne()

{

   // Отображаем сообщение о том, что команда не реализована

   MessageBox("Command not implemented");       

}

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

// Метод AddStringToComboBox

// Обрабатывает команду от кнопки Add панели

// управления Extended

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

void CMultiBarWindow::AddStringToComboBox()

{

   // Получаем строку, введенную в текстовом редакторе m_edit

   char  tmpStr[39];

   m_wndExtendedBar.m_edit.GetLine(0, tmpStr,40);



   // Добавляем новую строку к списку m_combo_box

   m_wndExtendedBar.m_combo_box.AddString(tmpStr);

}

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

// Метод ShowStyle

// Обрабатывает команды от меню View

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

BOOL CMultiBarWindow::ShowStyle(UINT nID)

{

   // Определяем указатель на панель управления,

   // соответствующую идентификатору nID

   CControlBar* pBar = GetControlBar(nID);

   // Определяем, отображается в данный момент панель

   // управления на экране или нет

   BOOL bShow = ((pBar -> GetStyle() & WS_VISIBLE) != 0);

  

   // Изменяем состояние панели управления: если панель

   // управления отображается на экране, удаляем ее с экрана,

   // если нет - отображаем

   ShowControlBar( pBar, !bShow, FALSE);

     

   return TRUE;

}

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

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

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

BEGIN_MESSAGE_MAP(CMultiBarWindow, CFrameWnd)

   // Макрокоманда вызывает метод OnCreate

   ON_WM_CREATE()

   // Обработчики команд от панели управления Player

   ON_COMMAND(ID_STOP, BarCommandOne)

   ON_COMMAND(ID_PLAY, BarCommandOne)

   ON_COMMAND(ID_PAUSE, BarCommandOne)

   ON_COMMAND_RANGE(ID_LEFT, ID_RIGHT, BarCommandRange)

   ON_COMMAND_RANGE(ID_TYPE, ID_WAVE,  BarCommandRange)

   // Обработчики команд от панели управления Extended

   ON_COMMAND(ID_ADD, AddStringToComboBox)

   ON_COMMAND_RANGE(ID_FOTO, ID_DISK,  BarCommandRange)

   // Обработчик команд от панели управления Style

   ON_COMMAND_RANGE(ID_UNDERLINE, ID_MARK_4, BarCommandRange)

   // Обработчики команд меню View

   ON_COMMAND_EX(ID_Style, ShowStyle)

   ON_COMMAND_EX(ID_Extended, ShowStyle)

   ON_COMMAND_EX(ID_Player, ShowStyle)

END_MESSAGE_MAP()

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



// Метод InitInstance класса CMultiBarApp

// Создает главное окно приложения и отображает его на экране

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

BOOL CMultiBarApp::InitInstance()

{

   m_pMainWnd = new CMultiBarWindow();

   m_pMainWnd -> ShowWindow(m_nCmdShow);

   m_pMainWnd -> UpdateWindow();

   return TRUE;

}

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

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

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

CMultiBarWindow::CMultiBarWindow()

{

   // Создаем окно приложения, соответствующее

   // данному объекту класса CMultiBarWindow

   Create(NULL, "Multi Bar", WS_OVERLAPPEDWINDOW,

          rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));

}

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

// Метод OnCreate класса CMultiBarWindow

// Используется для создания панелей управления

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

int CMultiBarWindow::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

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

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

      return -1;

   // Разрешаем присоединение панелей управления ко

   // всем сторонам окна CMultiBarWindow

   EnableDocking(CBRS_ALIGN_ANY);

  

   //  Переменные для изменения стилей кнопок

   UINT nBarStyle;

   int nIndex;

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

// Создаем и отображаем панель управления Player

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

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

   if(!m_wndPlayerBar.Create(this, WS_CHILD | WS_VISIBLE |

      CBRS_SIZE_DYNAMIC | CBRS_BOTTOM  |

      CBRS_TOOLTIPS, ID_Player))

   {

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

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

      return -1;

   }

  

   // Загружаем ресурс панели управления Player

   if(!m_wndPlayerBar.LoadToolBar(IDR_PLAYER))



   {

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

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

      return -1;

   }

  

   // Устанавливаем заголовок панели управления Player

   m_wndPlayerBar.SetWindowText("Player");     

  

   // Из кнопок с идентификаторами ID_TYPE, ID_CD_DRV и

   // ID_WAVE делаем трехпозиционный переключатель с зависимой

   // фиксацией. Устанавливаем для этих кнопок стиль

   // TBBS_CHECKGROUP

   nIndex = m_wndPlayerBar.CommandToIndex(ID_TYPE);

   nBarStyle = m_wndPlayerBar.GetButtonStyle(nIndex) |

               TBBS_CHECKGROUP;

   m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle);

   nIndex = m_wndPlayerBar.CommandToIndex(ID_CD_DRV);

   nBarStyle = m_wndPlayerBar.GetButtonStyle(nIndex) |

               TBBS_CHECKGROUP;

   m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle);

   nIndex = m_wndPlayerBar.CommandToIndex(ID_WAVE);

   nBarStyle = m_wndPlayerBar.GetButtonStyle(nIndex) |

               TBBS_CHECKGROUP;

   m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle);

   

   // Из кнопки с идентификатором ID_PAUSE делаем

   // переключатель. Устанавливаем для этой кнопки стиль

   // TBBS_CHECKBOX

   nIndex = m_wndPlayerBar.CommandToIndex(ID_PAUSE);

   nBarStyle = m_wndPlayerBar.GetButtonStyle(nIndex) |

               TBBS_CHECKBOX;

   m_wndPlayerBar.SetButtonStyle(nIndex, nBarStyle);

  

   // Разрешаем пристывковывать панель управления Player к

   // любой строке родительского окна

   m_wndPlayerBar.EnableDocking(CBRS_ALIGN_ANY );

   // Пристывковываем панель управления Player к

   // родительскому окну

   DockControlBar(&m_wndPlayerBar);

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

// Создаем и отображаем панель управления Style

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

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

   if(!m_wndStyleBar.Create(this, WS_CHILD | WS_VISIBLE |

      CBRS_SIZE_FIXED | CBRS_TOP | CBRS_TOOLTIPS, ID_Style))



   {

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

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

      return -1;

   }

   // Загружаем ресурс панели управления Style

   if(!m_wndStyleBar.LoadToolBar(IDR_STYLE))

   {

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

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

      return -1;

   }

   // Устанавливаем заголовок панели управления Style

   m_wndStyleBar.SetWindowText("Style");  

   // Запрещаем пристывковывать панель управления Player к

   // родительскому окну

   m_wndStyleBar.EnableDocking(0);

   // Устанавливаем для кнопки ID_SUBSCRIPT стиль TBBS_WRAPPED

   nIndex = m_wndStyleBar.CommandToIndex(ID_SUBSCRIPT);

   nBarStyle = m_wndStyleBar.GetButtonStyle(nIndex) |

               TBBS_WRAPPED;

   m_wndStyleBar.SetButtonStyle(nIndex, nBarStyle);

 

   // Устанавливаем для кнопки ID_TEXT_JUSTIFY стиль

   // TBBS_WRAPPED

   nIndex = m_wndStyleBar.CommandToIndex(ID_TEXT_JUSTIFY);

   nBarStyle = m_wndStyleBar.GetButtonStyle(nIndex) |

               TBBS_WRAPPED;

   m_wndStyleBar.SetButtonStyle(nIndex, nBarStyle);

 

   // Отображаем панель управления Style в мини-окне.

   // Начальные координаты панели управления определяются

   // значением объекта pointStyleBar класса CPoint

   CPoint pointStyleBar(100, 100);

   FloatControlBar(&m_wndStyleBar, pointStyleBar);

 

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

// Создаем и отображаем панель управления Extended

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

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

   if(!m_wndExtendedBar.Create(this,

      WS_CHILD | WS_VISIBLE | CBRS_SIZE_DYNAMIC |

      CBRS_TOP | CBRS_TOOLTIPS, ID_Extended))

   {

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

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

      return -1;

   }

  

   // Загружаем ресурс панели управления Extended

   if(!m_wndExtendedBar.LoadToolBar(IDR_EXTENDED))



   {

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

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

      return -1;

   }

   // Устанавливаем заголовок панели управления Extended

   m_wndExtendedBar.SetWindowText("Extended");     

   // Увеличиваем размер первого разделителя.

   // Этот разделитель имеет индекс 2

   m_wndExtendedBar.SetButtonInfo(2, IDW_EDIT,

      TBBS_SEPARATOR, 130);

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

   // управления, занимаемой разделителем

   CRect rectEdit;

   m_wndExtendedBar.GetItemRect(2, &rectEdit);

   // Делаем отступ с левой и правой стороны

   rectEdit.left += 6;

   rectEdit.right -= 6;

  

   // Размещаем на панели управления Extended, в области

   // rectEdit однострочный текстовый редактор

   if(!m_wndExtendedBar.m_edit.Create(WS_CHILD |

      ES_AUTOHSCROLL|WS_VISIBLE|WS_TABSTOP|WS_BORDER,

      rectEdit, &m_wndExtendedBar, IDW_EDIT))

   {

      // Ошибка при создании текстового редактора

      TRACE0("Failed to create edit-box\n");

      return FALSE;

   }

   // Увеличиваем размер второго разделителя.

   // Этот разделитель имеет индекс 4

   m_wndExtendedBar.SetButtonInfo(4, IDW_COMBO,

      TBBS_SEPARATOR, 150);

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

   // управления, занимаемой разделителем

   CRect rectComboBox;

   m_wndExtendedBar.GetItemRect(4, &rectComboBox);

   // Делаем отступ с левой и правой стороны

   rectComboBox.left += 6;

   rectComboBox.right -= 6;

   // Увеличиваем высоту прямоугольной области, чтобы в ней

   // можно было разместить список combo-box

   rectComboBox.bottom = rectComboBox.top + 80;

   // Размещаем на панели управления Extended, в области

   // rectComboBox список combo-box

   if(!m_wndExtendedBar.m_combo_box.Create(

      CBS_DROPDOWN | WS_CHILD | WS_VISIBLE | WS_VSCROLL |

      ES_AUTOHSCROLL | CBS_DISABLENOSCROLL,

      rectComboBox, &m_wndExtendedBar, IDW_COMBO))

   {

      // Ошибка при создании списка

      TRACE0("Failed to create combo-box\n");

      return FALSE;

   }

   // Добавляем три строки в список m_combo_box

   m_wndExtendedBar.m_combo_box.AddString("One");

   m_wndExtendedBar.m_combo_box.AddString("Two");

   m_wndExtendedBar.m_combo_box.AddString("Third");

  

   // Разрешаем пристывковывать панель управления Extended к

   // любой из строн родительского окна

   m_wndExtendedBar.EnableDocking(CBRS_ALIGN_ANY );

   // Пристывковываем панель управления Extended к

   // родительскому окну

   DockControlBar(&m_wndExtendedBar);

   return 0;

}


Приложение MultiMenu


Создайте новый проект под названием MultiMenu. В качестве типа приложения выберите из списка Type строку Application. Настройте проект MultiMenu, указав что приложение будет работать с библиотекой классов MFC.
Наберите в редакторе исходный текст приложения и сохраните его в файле MultiMenu.cpp (листинг 3.1). Включите готовый файл MultiMenu.cpp в проект.
Листинг 3.1. Файл MultiMenu.cpp
//============================================================
// Приложение MultiMenu
// (c) Frolov G.V., 1996
// E-mail: frolov@glas.apc.org
//============================================================
// Включаемые файлы для MFC
#include
#include
#include
// Включаемый файл для ресурсов приложения и идентификаторов
#include "resource.h"
//============================================================
// Класс CMultiMenuApp - главный класс приложения
//============================================================
class CMultiMenuApp : public CWinApp
{
public:
   // Мы будем переопределять метод InitInstance,
   // предназначенный для инициализации приложения
   virtual BOOL InitInstance();
};
 
// Создаем объект приложение класса CMultiMenuApp
CMultiMenuApp MultiMenuApp;
 
//============================================================
// Класс CMultiMenuWindow - представляет главное окно
//============================================================
class CMultiMenuWindow : public CFrameWnd
{
protected: 
   // Панель состояния
   CStatusBar  m_wndStatusBar;  
  
   // Флаг управляет строкой Prosess меню Mission
   BOOL  bEnable;
   // Флаг управляет строкой Construction меню Mission
   BOOL  bRadio; 
   // Флаг управляет строкой Restrict меню Menu
   int   nCheck; 
      
protected:
   // Метод для создания окна приложения и панели состояния
   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
   // Методы для обработки командных сообщений

   // от меню приложения

   afx_msg void CMultiMenuWindow::OnDisable();

   afx_msg void CMultiMenuWindow::OnCommand();

   afx_msg void CMultiMenuWindow::OnExit();

   afx_msg void CMultiMenuWindow::OnConstruct();

   afx_msg void CMultiMenuWindow::OnRestrictMenu();

   afx_msg void CMultiMenuWindow::OnFullMenu();

   // Методы для обновления меню

   afx_msg void OnUpdateProcess(CCmdUI* pCmdUI);

   afx_msg void OnUpdateConstruct(CCmdUI* pCmdUI);

   afx_msg void OnUpdateDisable(CCmdUI* pCmdUI);

  

public:

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

   CMultiMenuWindow();

   // Макрокоманда необходима, так как класс

   // CMultiMenuWindow обрабатывает сообщения

   DECLARE_MESSAGE_MAP()   

};

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

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

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

BEGIN_MESSAGE_MAP(CMultiMenuWindow, CFrameWnd)

  

   // Макрокоманда вызывает метод OnCreate

   ON_WM_CREATE()

   // Макрокоманда вызывает метод OnContextMenu

   ON_WM_CONTEXTMENU()

   // Макрокоманды для обработки командных сообщений

   ON_COMMAND(ID_MENU_DISABLE, OnDisable)

   ON_COMMAND(ID_MISSION_CONSTRUCT, OnConstruct)

   ON_COMMAND(ID_FILE_EXIT, OnExit)

   ON_COMMAND(ID_MISSION_PROCESS, OnCommand)

  

   ON_COMMAND(ID_MENU_RESTRICT, OnRestrictMenu)

   ON_COMMAND(ID_MENU_FULL, OnFullMenu)

   // Обработчики сообщений ON_UPDATE_COMMAND_UI

   ON_UPDATE_COMMAND_UI(ID_MISSION_PROCESS, OnUpdateProcess)

   ON_UPDATE_COMMAND_UI(ID_MISSION_CONSTRUCT,

                           OnUpdateConstruct)

   ON_UPDATE_COMMAND_UI(ID_MENU_DISABLE, OnUpdateDisable)

END_MESSAGE_MAP()

// Индикатор панели управления

UINT indicator = ID_SEPARATOR;

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

// Метод InitInstance класса CMultiMenuApp

// Создает главное окно приложения и отображает его на экране

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



BOOL CMultiMenuApp::InitInstance()

{

   m_pMainWnd = new CMultiMenuWindow();

   m_pMainWnd -> ShowWindow(m_nCmdShow);

   m_pMainWnd -> UpdateWindow();

   return TRUE;

}

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

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

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

CMultiMenuWindow::CMultiMenuWindow()

{

   // Создаем окно приложения, соответствующее

   // данному объекту класса CMultiMenuWindow

   Create(NULL, "Multi Menu Sample", WS_OVERLAPPEDWINDOW,

      rectDefault, NULL, MAKEINTRESOURCE(IDR_FULL_MENU));

   // Загружаем таблицу клавиш акселерации

   LoadAccelTable(MAKEINTRESOURCE(IDR_ACCELERATOR));

   // Инициализируем флаги

   bEnable = TRUE;        

   bRadio = TRUE;        

   nCheck = 0;

}

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

// Метод OnCreate класса CMultiMenuWindow

// Вызывается во время создания окна приложения

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

int CMultiMenuWindow::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

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

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

      return -1;

  

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

   if(!m_wndStatusBar.Create(this))

   {

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

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

      return -1;  

   }

   // Отображаем индикаторы панели состояния

   if(!m_wndStatusBar.SetIndicators(&indicator,1))

   {

      // Ошибка при установке индикатора

      TRACE0("Failed to set indicators\n");

      return -1;  

   }

  

   return 0;

}

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

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

// Изменяем состояние флагов bEnable и nCheck

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

void CMultiMenuWindow::OnDisable()

{

   // Меняем значение bEnable с TRUE на FALSE и наоборот



   bEnable = !bEnable;

  

   // Меняем значение bEnable с 1 на 0 и наоборот

   nCheck = (nCheck == 1) ? 0 : 1;

}

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

// Метод OnRestrictMenu класса CMultiMenuWindow

// Изменяем меню приложения с IDR_FULL_MENU на

// IDR_RESTRICT_MENU

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

void CMultiMenuWindow::OnRestrictMenu()

{

   CMenu menuOld;       // текущее меню

   CMenu menuRestrict;  // новое меню

   CMenu* pMenu;

   // Получаем указатель на текущее меню

   pMenu = this->GetMenu();

  

   // Связываем меню с объектом menuOld

   menuOld.Attach(pMenu->m_hMenu);

   // Удаляем меню

   menuOld.DestroyMenu();

  

   // Загружаем меню IDR_RESTRICT_MENU

   menuRestrict.LoadMenu(IDR_RESTRICT_MENU);

   // Устанавливаем загруженное меню

   SetMenu(&menuRestrict);

   // Разрываем связь меню с объектом menuRestrict

   menuRestrict.Detach();

}

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

// Метод OnFullMenu класса CMultiMenuWindow

// Изменяем меню приложения с IDR_RESTRICT_MENU на

// IDR_FULL_MENU

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

void CMultiMenuWindow::OnFullMenu()

{

   CMenu menuOld;

   CMenu menuRestrict;

   CMenu* pMenu;

   pMenu = this->GetMenu();

   menuOld.Attach(pMenu->m_hMenu);

   menuOld.DestroyMenu();

   menuRestrict.LoadMenu(IDR_FULL_MENU);

   SetMenu(&menuRestrict);

   menuRestrict.Detach();

}

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

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

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

void CMultiMenuWindow::OnCommand()

{

   MessageBox("Command not implemented");

}

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

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

// Изменяем состояние флага bRadio

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



void CMultiMenuWindow::OnConstruct()

{

   // Меняем значение bRadio с TRUE на FALSE и наоборот

   bRadio = !bRadio;

}

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

// Метод OnExit класса CMultiMenuWindow

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

void CMultiMenuWindow::OnExit()

{

   // Завершаем приложение

   DestroyWindow();

   return;

}

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

// Метод OnUpdateProcess класса CMultiMenuWindow

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

void CMultiMenuWindow::OnUpdateProcess(CCmdUI* pCmdUI)

{

   // Блокируем или разблокируем строку Process меню Mission

   pCmdUI->Enable(bEnable);

}

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

// Метод OnUpdateConstruct класса CMultiMenuWindow

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

void CMultiMenuWindow::OnUpdateConstruct(CCmdUI* pCmdUI)

{

   // Устанавливаем или снимаем пометку

   // строки Construction меню Mission

   pCmdUI->SetRadio(bRadio);

}

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

// Метод OnUpdateDisable класса CMultiMenuWindow

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

void CMultiMenuWindow::OnUpdateDisable(CCmdUI* pCmdUI)

{

   // Устанавливаем или удаляем пометку

   // у строки Disable меню Menu

   pCmdUI->SetCheck(nCheck);

}

Создайте новый файл ресурсов и включите его в проект под именем MultiMenu.rc. Включите в него два меню, присвоив им идентификаторы IDR_RESTRICT_MENU и IDR_FULL_MENU.

Введите строки этих меню в соответствии с представленным нами файлом ресурсов (листинг 3.2). Для всех строк меню введите их описания. Они будут записаны в файл ресурсов как строковые ресурсы, имеющие одинаковые идентификаторы со строками меню.

Добавьте в файл ресурсов строку Ready, выбрав для нее идентификатор AFX_IDS_IDLEMESSAGE. Эта строка будет отображаться в панели состояния во время “бездействия” приложения.



Включите в файл ресурсов таблицу акселераторов, состоящую из трех команд: ID_MENU_DISABLE, ID_MISSION_PROCESS и ID_FILE_EXIT. Присвойте им комбинации клавиш , и соответственно.

Листинг 3.2. Файл MultiMenu.rc

//Microsoft Developer Studio generated resource script.

//

#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS

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

//

// Generated from the TEXTINCLUDE 2 resource.

//

#include "afxres.h"

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

#undef APSTUDIO_READONLY_SYMBOLS

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

// Russian resources

#if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_RUS)

#ifdef _WIN32

LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT

#pragma code_page(1251)

#endif //_WIN32

#ifdef APSTUDIO_INVOKED

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

//

// TEXTINCLUDE

//

1 TEXTINCLUDE DISCARDABLE

BEGIN

    "resource.h\0"

END

2 TEXTINCLUDE DISCARDABLE

BEGIN

    "#include ""afxres.h""\r\n"

    "\0"

END

3 TEXTINCLUDE DISCARDABLE

BEGIN

    "\r\n"

    "\0"

END

#endif    // APSTUDIO_INVOKED

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

//

// Menu

//

IDR_RESTRICT_MENU MENU DISCARDABLE

BEGIN

    POPUP "&File"

    BEGIN

        MENUITEM "P&rocess\tCtrl+P",  ID_MISSION_PROCESS

        MENUITEM SEPARATOR

        MENUITEM "E&xit\tCtrl+E",     ID_FILE_EXIT

    END

    POPUP "&Menu"

    BEGIN

        MENUITEM "Full",              ID_MENU_FULL

        MENUITEM "Disa&ble\tCtrl+D",  ID_MENU_DISABLE

    END

    POPUP "&Help", HELP

    BEGIN

        MENUITEM "Help index",        ID_HELP_HELPINDEX



        MENUITEM SEPARATOR

        MENUITEM "System info",       ID_HELP_SYSTEMINFO

    END

END

IDR_FULL_MENU MENU DISCARDABLE

BEGIN

    POPUP "&File"

    BEGIN

        MENUITEM SEPARATOR

        MENUITEM "E&xit\tCtrl+E",     ID_FILE_EXIT

    END

    POPUP "&Menu"

    BEGIN

        MENUITEM "Restrict",          ID_MENU_RESTRICT

        MENUITEM "Disa&ble\tCtrl+D",  ID_MENU_DISABLE

    END

    POPUP "M&ission"

    BEGIN

        MENUITEM "P&rocess\tCtrl+P",  ID_MISSION_PROCESS

        MENUITEM SEPARATOR

        MENUITEM "Construction",      ID_MISSION_CONSTRUCT

    END

    POPUP "&Help", HELP

    BEGIN

        MENUITEM "Help index",        ID_HELP_HELPINDEX

        MENUITEM "Context help",      ID_HELP_CONTEXTHELP

        MENUITEM SEPARATOR

        MENUITEM "System info",       ID_HELP_SYSTEMINFO

    END

END

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

//

// Accelerator

//

IDR_ACCELERATOR ACCELERATORS DISCARDABLE

BEGIN

    "D",   ID_MENU_DISABLE,    VIRTKEY, CONTROL, NOINVERT

    "E",   ID_FILE_EXIT,       VIRTKEY, CONTROL, NOINVERT

    "P",   ID_MISSION_PROCESS, VIRTKEY, CONTROL, NOINVERT

END

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

//

// String Table

//

STRINGTABLE DISCARDABLE

BEGIN

    AFX_IDS_IDLEMESSAGE     "Ready"

END

STRINGTABLE DISCARDABLE

BEGIN

    ID_FILE_EXIT            "Exit application"

    ID_MISSION_PROCESS      "Process"

    ID_HELP_HELPINDEX       "Open help index"

    ID_HELP_CONTEXTHELP     "Context help"

END

STRINGTABLE DISCARDABLE

BEGIN

    ID_HELP_SYSTEMINFO      "Display system info"

    ID_MISSION_CONSTRUCT    "Construct"

    ID_MENU_RESTRICT        "Restrict menu"



    ID_MENU_FULL            "Display full menu"

    ID_MENU_DISABLE         " Disable command Process from menu Mission"

END

#endif    // Russian resources

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

#ifndef APSTUDIO_INVOKED

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

//

// Generated from the TEXTINCLUDE 3 resource.

//

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

#endif    // not APSTUDIO_INVOKED

Идентификаторы ресурсов приложения MultiMenu определены в файле resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++. Исходный текст этого файла представлен в листинге 3.3.

Листинг 3.3. Файл resource.h

//{{NO_DEPENDENCIES}}

// Microsoft Developer Studio generated include file.

// Used by MultiMenu.rc

//

#define IDR_RESTRICT_MENU               106

#define IDR_FULL_MENU                   107

#define IDR_ACCELERATOR                 108

#define ID_FILE_EXIT                    40009

#define ID_MISSION_PROCESS              40013

#define ID_HELP_HELPINDEX               40014

#define ID_HELP_CONTEXTHELP             40015

#define ID_HELP_SYSTEMINFO              40016

#define ID_MISSION_CONSTRUCT            40017

#define ID_MENU_RESTRICT                40019

#define ID_MENU_FULL                    40020

#define ID_MENU_DISABLE                 40025

// Next default values for new objects

//

#ifdef APSTUDIO_INVOKED

#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NEXT_RESOURCE_VALUE        110

#define _APS_NEXT_COMMAND_VALUE         40027

#define _APS_NEXT_CONTROL_VALUE         1000

#define _APS_NEXT_SYMED_VALUE           101

#endif

#endif

Постройте приложение MultiMenu и запустите его. На экране появится окно приложения с главным меню и панелью состояния (рис. 3.4).

В панели состояния расположен один индикатор. В нем отображается подсказка о выбранной строке меню приложения или системного меню, а если приложение “бездействует” - строка Ready.



Приложение MultiMenu

Рис. 3.4. Приложение MultiMenu

Сразу после запуска приложения MultiMenu используется меню с идентификатором IDR_FULL_MENU. Если вы выберите из меню Menu строку Restrict, то меню приложения будет заменено на меню IDR_RESTRICT_MENU. Это сокращенный вариант меню IDR_FULL_MENU, в котором отсутствуют некоторые строки, а строка Process перенесена из меню Mission в меню File.

Различные строки меню IDR_FULL_MENU и IDR_RESTRICT_MENU иллюстрируют режимы отображения строк меню. Так, при выборе из меню Menu строки Disable, блокируется строка Process в меню Mission. Около строки Disable при этом отображается символ Ö (рис. 3.3). Чтобы снять блокировку, выберите строку Disable из меню Menu еще раз. Символ Ö также исчезнет.

При выборе строки Process из меню Mission на экране появляется сообщение. Когда строка Process заблокирована, выбрать ее невозможно.

Если вы выберите из меню Mission строку Construction, то она будет выделена символом ·. Повторный выбор этой строки снимает с нее выделение.

Чтобы завершить работу приложения MultiMenu, можно выбрать из меню File строку Exit или выбрать из системного меню приложения строку Close. Все остальные строки меню приложения не работают и заблокированы.

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

Комбинация клавиш

Соответствующая строка меню



Строка Disable из меню Menu



Строка Process из меню Mission или из меню File (для сокращенного варианта меню)



Строка Exit из меню File


Приложение Status


Создайте новый проект под названием Status. В качестве типа приложения выберите из списка Type строку Application. Настройте проект Status, указав, что приложение будет работать с библиотекой классов MFC.
Наберите в редакторе исходный текст приложения и сохраните его в файле Status.cpp (листинг 3.13). Включите готовый файл DialogBar.cpp в проект.
Листинг 3.13. Файл Status.cpp
//============================================================
// Приложение Status
// (c) Frolov G.V., 1996
// E-mail: frolov@glas.apc.org
//============================================================
// Включаемые файлы для MFC
#include
#include
#include
// Включаемый файл для ресурсов приложения и идентификаторов
#include "resource.h"
//============================================================
// Класс CStateApp - главный класс приложения
//============================================================
class CStateApp : public CWinApp
{
public:
   // Мы будем переопределять метод InitInstance,
   // предназначенный для инициализации приложения
   virtual BOOL InitInstance();
};
 
// Создаем объект приложение класса CStateApp
CStateApp StateApp;
 
//============================================================
// Класс CStateWindow - представляет главное окно
//============================================================
class CStateWindow : public CFrameWnd
{
protected: 
   CStatusBar  m_wndStatusBar;   // Панель состояния
   BOOL  bIndicatorTEXT;         // Флаг для управления
                                 // индикатором
                                 // ID_INDICATOR_TEXT
    
protected:
   // Метод для создания окна приложения и панели состояния
   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
public:
   // Объявляем конструктор класса CStateWindow
   CStateWindow();
   // Объявляем методы для обработки команд меню
   afx_msg BOOL OnMenuDirectADD_SUB(UINT nID);

   afx_msg void OnMenuProcessBar();

   afx_msg void OnMenuDisableADD_SUB();

   afx_msg void OnMenuSwitchTEXT();

   afx_msg void OnMenuExit();

  

   // Метод для обработки команды ON_UPDATE_COMMAND_UI

   // от индикатора ID_INDICATOR_TEXT

   afx_msg void OnUpdateTEXT(CCmdUI* pCmdUI);

  

   // Макрокоманда необходима, так как класс

   // CStateWindow обрабатывает сообщения

   DECLARE_MESSAGE_MAP()   

};

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

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

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

BEGIN_MESSAGE_MAP(CStateWindow, CFrameWnd)

  

   // Макрокоманда вызывает метод OnCreate

   ON_WM_CREATE()

   // Обработчик сообщения ON_UPDATE_COMMAND_UI

   ON_UPDATE_COMMAND_UI(ID_INDICATOR_TEXT, OnUpdateTEXT)

  

   // Обработчики команд меню Work

   ON_COMMAND(ID_WORK_PROCESS, OnMenuProcessBar)

   ON_COMMAND(ID_WORK_DISABLE_ADDSUB, OnMenuDisableADD_SUB)

   ON_COMMAND(ID_WORK_ON_SWITCH_TEXT, OnMenuSwitchTEXT)

  

   ON_COMMAND_EX(ID_WORK_DIRECT_ADD, OnMenuDirectADD_SUB)

   ON_COMMAND_EX(ID_WORK_DIRECT_SUB, OnMenuDirectADD_SUB)

   ON_COMMAND(ID_WORK_EXIT, OnMenuExit)

  

END_MESSAGE_MAP()

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

// Индикаторы панели управления. Порядок идентификаторов

// соответствует порядку индикаторов в панели состояния

// (до тех пор, пока он не изменен методом SetPaneInfo)

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

static UINT indicators[] =

{

   ID_SEPARATOR,           // Самый первый индикатор

   ID_INDICATOR_OVR,       // Индикатор OVR

   ID_INDICATOR_PROGRESS,  // Резервирование места для

                           // progress bar

   ID_INDICATOR_CAPS,      // Индикатор клавиши

   ID_INDICATOR_NUM,       // Индикатор клавиши

   ID_INDICATOR_SCRL,      // Индикатор клавиши

   ID_INDICATOR_TEXT,      // Индикатор TEXT/PIC



   ID_INDICATOR_ADD,       // Индикатор ADD/SUB (начальное

                           // состояние START)

};

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

// Метод InitInstance класса CStateApp

// Создает главное окно приложения и отображает его на экране

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

BOOL CStateApp::InitInstance()

{

   m_pMainWnd = new CStateWindow();

   m_pMainWnd -> ShowWindow(m_nCmdShow);

   m_pMainWnd -> UpdateWindow();

   return TRUE;

}

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

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

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

CStateWindow::CStateWindow()

{

   // Создаем окно приложения, соответствующее

   // данному объекту класса CStateWindow

   Create(NULL, "Status Bar Sample", WS_OVERLAPPEDWINDOW,

      rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));

  

   // Устанавливаем значение флага bIndicatorTEXT

   bIndicatorTEXT = TRUE;

}

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

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

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

void CStateWindow::OnMenuProcessBar()

{

    // Определяем координаты индикатора ID_INDICATOR_PROGRESS

    RECT rectProgress;

   

    m_wndStatusBar.GetItemRect(

       m_wndStatusBar.CommandToIndex(ID_INDICATOR_PROGRESS),

         &rectProgress);

   // Создаем полосу progress bar. Размещаем ее

   // на месте индикатора ID_INDICATOR_PROGRESS

   CProgressCtrl ctrlProgressBar;

   if(!ctrlProgressBar.Create(WS_CHILD | WS_VISIBLE,

                          rectProgress, &m_wndStatusBar, 1))

   {

      // Ошибка при создании progress bar

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

      return;

   }

   // Устанавливаем границы для progress bar

   ctrlProgressBar.SetRange(0, 100);

   // Устанавливаем шаг приращения для progress bar



   ctrlProgressBar.SetStep(1);

 

   // Плавно увеличиваем положение progress bar

   for(int i=0;i<100;i++)

   {

      // Выполняем короткую задержку

      Sleep(10);

     

      // Выполняем шаг приращения progress bar

      ctrlProgressBar.StepIt();

   }

  

   // По завершении, отображаем текст в самом первом

   // индикаторе панели состояния

   m_wndStatusBar.SetWindowText("Process completed");

}

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

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

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

BOOL  CStateWindow::OnMenuDirectADD_SUB(UINT nID)

{

   // Определяем индекс индикатора ID_INDICATOR_ADD

   int nIndex =

          m_wndStatusBar.CommandToIndex(ID_INDICATOR_ADD);

   // Устанавливаем нормальный режим отображения индикатора

   m_wndStatusBar.SetPaneStyle(nIndex, SBPS_NORMAL);

   // Из меню Work выбрана строка Direct set ADD

   if(nID == ID_WORK_DIRECT_ADD)

   {

      // Выводим текст ADD

      m_wndStatusBar.SetPaneText(nIndex, "ADD");

   }

   // Из меню Work выбрана строка Direct set SUB

   else if(nID == ID_WORK_DIRECT_SUB)

   {

      // Изменяем внешний вид индикатора

      m_wndStatusBar.SetPaneStyle(nIndex, SBPS_POPOUT);

      // Выводим текст SUB

      m_wndStatusBar.SetPaneText(nIndex, "SUB");

   }

   return TRUE;

}  

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

// Метод OnMenuDisableADD_SUB класса OnMenuDisableADD_SUB

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

void CStateWindow::OnMenuDisableADD_SUB()

{

   // Определяем индекс индикатора ID_INDICATOR_ADD  

   int nIndex =

          m_wndStatusBar.CommandToIndex(ID_INDICATOR_ADD);

  

   // Блокируем индикатор

   m_wndStatusBar.SetPaneStyle(nIndex, SBPS_DISABLED);

}  

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

// Метод OnUpdateTEXT класса CStateWindow



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

void CStateWindow::OnMenuSwitchTEXT()

{

   // Изменяем состояние флага bIndicatorTEXT,

   // который используется методом OnUpdateTEXT

   bIndicatorTEXT = !bIndicatorTEXT;

}

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

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

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

void CStateWindow::OnMenuExit()

{

   // Завершаем приложение

   DestroyWindow();

   return;

}

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

// Метод OnCreate класса CStateWindow

// Вызывается во время создания окна приложения

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

int CStateWindow::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

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

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

      return -1;

  

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

   if(!m_wndStatusBar.Create(this))

   {

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

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

      return -1;  

   }

   // Отображаем индикаторы панели состояния

   if(!m_wndStatusBar.SetIndicators(indicators,

                         sizeof(indicators)/sizeof(UINT)))

   {

      // Ошибка при установке индикаторов

      TRACE0("Failed to set indicators\n");

      return -1;  

   }

  

   // Устанавливаем характеристики индикатора

   // ID_INDICATOR_PROGRESS

   m_wndStatusBar.SetPaneInfo(

      m_wndStatusBar.CommandToIndex(ID_INDICATOR_PROGRESS),

      ID_INDICATOR_PROGRESS,

      SBPS_DISABLED |   // Текст не отображается

      SBPS_NOBORDERS,   // Рамка вокруг индикатора отсутствует

      150 );            // Ширина индикатора 150 пикселов

   return 0;

}

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

// Метод OnUpdateTEXT класса CStateWindow

// Обрабатывает сообщение ON_UPDATE_COMMAND_UI

// от индикатора ID_INDICATOR_TEXT



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

void CStateWindow::OnUpdateTEXT(CCmdUI* pCmdUI)

{

   // В зависимости от состояния флага bIndicatorTEXT

   // отображаем в индикаторе ID_INDICATOR_TEXT

   // строку TEXT или PIC

   if(bIndicatorTEXT)

      pCmdUI->SetText("TEXT"); // отображаем строку TEXT

   else

      pCmdUI->SetText("PIC");  // отображаем строку PIC

   // Разрешаем отображение текста в индикаторе

   pCmdUI->Enable();

}

Создайте новый файл ресурсов и включите его в проект под именем Status.rc. Включите в него меню, присвоив ему идентификатор IDR_MENU. Введите строки меню IDR_MENU в соответствии с представленным нами файлом ресурсов (листинг 3.14). Для всех строк меню введите их описания. Они будут записаны в файл ресурсов как строковые ресурсы, имеющие одинаковые идентификаторы со строками меню.

Добавьте в файл ресурсов строку Ready, выбрав для нее идентификатор AFX_IDS_IDLEMESSAGE. Эта строка будет отображаться в панели состояния во время “бездействия” приложения.

Добавьте в файл ресурсов строки, представляющие индикаторы панели состояния: ID_INDICATOR_ADD, ID_INDICATOR_PROGRESS и ID_INDICATOR_TEXT.

Листинг 3.14. Файл Status.rc

//Microsoft Developer Studio generated resource script.

//

#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS

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

//

// Generated from the TEXTINCLUDE 2 resource.

//

#include "afxres.h"

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

#undef APSTUDIO_READONLY_SYMBOLS

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

// Russian resources

#if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_RUS)

#ifdef _WIN32

LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT

#pragma code_page(1251)

#endif //_WIN32

#ifdef APSTUDIO_INVOKED

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

//

// TEXTINCLUDE



//

1 TEXTINCLUDE DISCARDABLE

BEGIN

    "resource.h\0"

END

2 TEXTINCLUDE DISCARDABLE

BEGIN

    "#include ""afxres.h""\r\n"

    "\0"

END

3 TEXTINCLUDE DISCARDABLE

BEGIN

    "\r\n"

    "\0"

END

#endif    // APSTUDIO_INVOKED

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

//

// Menu

//

IDR_MENU MENU DISCARDABLE

BEGIN

    POPUP "Work"

    BEGIN

        MENUITEM "Process",          ID_WORK_PROCESS

        MENUITEM "Direct set ADD",   ID_WORK_DIRECT_ADD

        MENUITEM "Direct set SUB",   ID_WORK_DIRECT_SUB

        MENUITEM "Disable ADD SUB",  ID_WORK_DISABLE_ADDSUB

        MENUITEM "Switch TEXT",      ID_WORK_ON_SWITCH_TEXT

        MENUITEM SEPARATOR

        MENUITEM "Exit",             ID_WORK_EXIT

    END

END

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

//

// String Table

//

STRINGTABLE DISCARDABLE

BEGIN

    ID_INDICATOR_ADD        "START"

END

STRINGTABLE DISCARDABLE

BEGIN

    ID_INDICATOR_PROGRESS   "neve display"

    ID_INDICATOR_TEXT       "TEXT"

END

STRINGTABLE DISCARDABLE

BEGIN

    ID_WORK_PROCESS         " Display and play progress bar"

    ID_WORK_DIRECT_ADD      "Set indicator ID_INDICATOR_ADD

                             to ADD"

    ID_WORK_ON_SWITCH_TEXT  "Switch text in indicator

                             ID_INDICATOR_TEXT"

    ID_WORK_DIRECT_SUB      "Set indicator ID_INDICATOR_ADD

                             to SUB"

    ID_WORK_DISABLE_ADDSUB  "Disable indicator

                             ID_INDICATOR_ADD"

    ID_WORK_EXIT            "Exit application"

END

STRINGTABLE DISCARDABLE

BEGIN

    AFX_IDS_IDLEMESSAGE     "Ready"

END

#endif    // Russian resources



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

#ifndef APSTUDIO_INVOKED

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

//

// Generated from the TEXTINCLUDE 3 resource.

//

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

#endif    // not APSTUDIO_INVOKED

Идентификаторы ресурсов приложения Status определены в файле resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++. Исходный текст файла resource.h представлен в листинге 3.15.

Листинг 3.15. Файл resource.h

//{{NO_DEPENDENCIES}}

// Microsoft Developer Studio generated include file.

// Used by Status.rc

//

#define ID_INDICATOR_ADD                1

#define IDR_MENU                        101

#define ID_INDICATOR_PROGRESS           102

#define ID_INDICATOR_TEXT               103

#define ID_WORK_PROCESS                 40001

#define ID_WORK_DIRECT_ADD              40006

#define ID_WORK_ON_SWITCH_TEXT          40007

#define ID_WORK_DIRECT_SUB              40008

#define ID_WORK_DISABLE_ADDSUB          40009

#define ID_WORK_EXIT                    40010

#define ID_TIMER_CHECK                  0xE001

// Next default values for new objects

//

#ifdef APSTUDIO_INVOKED

#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NEXT_RESOURCE_VALUE        106

#define _APS_NEXT_COMMAND_VALUE         40011

#define _APS_NEXT_CONTROL_VALUE         1000

#define _APS_NEXT_SYMED_VALUE           104

#endif

#endif

Постройте приложение Status и запустите его. На экране появится главное окно приложения, в нижней части которого отображается панель состояния (рис. 3.23).

Приложение Status

Рис. 3.23. Приложение Status

В панели состояния расположены несколько индикаторов. Самый первый индикатор, размер которого зависит от размера окна приложения, отображает подсказку о выбранной строке меню приложения или системного меню, а если приложение “бездействует” в нем отображается строка Ready.

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



Вслед за идентификатором OVR следует свободное пространство, занятое индикатором. Этот индикатор не отображается на панели и предназначен только для резервирования места под линейный индикатор progress bar.

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

После того, как линейный индикатор покажет окончание процесса, он будет удален с панели состояния, а в самом первом индикаторе появится надпись Process completed.

Затем в панели состояния следуют три стандартных индикатора CAP, NUM и SCRL, которые отображают текущее состояние клавиш , и .

Следующий индикатор, который мы рассмотрим, называется TEXT. Если вы выберите из меню Work строку Switch TEXT, то надпись TEXT в индикаторе заменится на PIC. Повторный выбор данной строки меню восстановит предыдущее состояние индикатора.

Последний индикатор в панели состояния START. Этот индикатор управляет тремя строками меню Work. При выборе строки Direct set ADD в индикаторе отображается строка ADD, а при выборе строки Direct set SUB - SUB. Во втором случае также меняется оформление индикатора. Если вы выберите из меню Work строку Disable ADD SUB, то индикатор будет заблокирован.


Процедура создания диалоговой панели управления


Как и в случае разработки обычных диалоговый панелей, сначала следует подготовить шаблон диалоговой панели, которая будет использоваться в качестве панели управления. Для этого можно воспользоваться редактором ресурсов Microsoft Visual C++.
В шаблоне диалоговой панели следует установить только один стиль - WS_CHILD. Диалоговая панель управления будет выступать в качестве дочернего окна и не должна иметь ни заголовка ни рамки.
Готовый шаблон следует сохранить в файле ресурсов проекта. На этом подготовка шаблона диалоговой панели управления считается законченной и надо приступить к написанию программного кода для работы с нею.
В состав класса окна, в котором будет отображаться диалоговая панель управления, надо включить элемент данных класса CDialogBar. Этот элемент данных будет представлять диалоговую панель управления. Если диалоговая панель управления достаточно сложна и имеет много органов управления, то можно создать для нее отдельный класс на основе базового класса CDialogBar. Этот класс следует дополнить методами, обрабатывающими сообщения от органов управления панели. Таким образом вы сможете разгрузить таблицу сообщений главного окна приложения.
Затем надо переопределить метод OnCreate класса окна и добавить в нем вызов метода Create класса CDialogBar для объекта представляющего диалоговую панель управления.
Если вы добавляете диалоговую панель управления к приложению, созданному с использованием средств MFC AppWizard, то в нем уже определен метод OnCreate и вам только остается добавить в него соответствующий вызов метода Create.
Органы управления диалоговой панели передают сообщения непосредственно своему родительскому окну. Добавьте в таблицу сообщений класса окна соответствующие макрокоманды для получения сообщений и включите в класс окна методы для их обработки.
В следующем разделе мы реализуем только что описанный алгоритм создания диалоговой панели управления на примере приложения DialogBar.


Простое приложение с панелью управления


Вы уже знаете, насколько просто добавить новые кнопки к панели управления, если приложение создано с помощью средств автоматизированной разработки MFC AppWizard. Однако приложение, подготовленное MFC AppWizard, достаточно многофункционально и состоит из нескольких сотен строк программного кода.
Чтобы подробнее разобраться с устройством и возможностями панелей управления, гораздо лучше взять маленькое приложение, программный код которого можно охватить буквально одним взглядом. Возьмем за основу приложение MFStart с одним единственным окном, представленное нами в первой книге серии “Библиотека системного программиста”, посвященной программированию в Microsoft Visual C++ с применением классов MFC.
¨ К сожалению, если вы не используете для создания приложения MFC AppWizard, то вам, скорее всего, не будут доступны и возможности ClassWizard. Сейчас нам придется пожертвовать удобством в разработке приложения ради простоты его исходных текстов.
Создайте новый проект под названием Bar. В качестве типа приложения выберите из списка Type строку Application. Выберите из меню Build строку Settings или нажмите комбинацию клавиш . На экране появится диалоговая панель Project Settings. В этой панели расположены несколько страниц, позволяющих настроить различные характеристики проекта.
Откройте страницу General. Выберите из списка Microsoft Foundation Classes строку Use MFC in a Shared Dll или строку Use MFC in a Static Library. Эта настройка указывает Microsoft Visual C++, что в приложении используются классы библиотеки MFC.
Наберите в редакторе исходный текст приложения и сохраните его в файле Bar.cpp (листинг 3.4). Затем включите этот файл в проект. Для простоты мы включили в один файл весь программный код приложения.
Листинг 3.4. Файл Bar.cpp
//============================================================
// Приложение Bar
// (c) Frolov G.V., 1996
// E-mail: frolov@glas.apc.org
//============================================================
// Исключаем редко используемые определения из

// включаемых файлов

#define VC_EXTRALEAN

// Включаемый файл для MFC

#include

#include

#include

// Включаемый файл для ресурсов приложения

#include "resource.h"

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

// Класс CBarApp - главный класс приложения

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

class CBarApp : public CWinApp

{

public:

   // Мы будем переопределять метод InitInstance,

   // предназначенный для инициализации приложения

   virtual BOOL InitInstance();

};

 

// Создаем объект приложение класса CBarApp

CBarApp MyBarApp;

 

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

// Класс CBarWindow - представляет главное окно

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

class CBarWindow : public CFrameWnd

{

protected:  // control bar embedded members

   CToolBar    m_wndToolBar;

  

protected:

   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

public:

   // Объявляем конструктор класса CBarWindow

   CBarWindow();

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

   afx_msg void BarCommand();

   // Макрокоманда необходима, так как класс

   // CBarWindow обрабатывает сообщения

   DECLARE_MESSAGE_MAP()   

};

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

// Метод BarCommand

// Обрабатывает команду ID_TEST_BEEP

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

void CBarWindow::BarCommand()

{

   MessageBox("Command not implemented");       

}

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

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

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

BEGIN_MESSAGE_MAP(CBarWindow, CFrameWnd)

   ON_WM_CREATE()

   ON_COMMAND(ID_FILE_OPEN, CBarWindow::BarCommand)

   ON_COMMAND(ID_FILE_SAVE, CBarWindow::BarCommand)

   ON_COMMAND(ID_TOOL_EXIT, CBarWindow::BarCommand)



END_MESSAGE_MAP()

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

// Метод InitInstance класса CBarApp

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

BOOL CBarApp::InitInstance()

{

   // Создаем объект класса CBarWindow

   m_pMainWnd = new CBarWindow();

   // Отображаем окно на экране

   m_pMainWnd -> ShowWindow(m_nCmdShow);

   // Обновляем содержимое окна

   m_pMainWnd -> UpdateWindow();

   return TRUE;

}

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

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

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

CBarWindow::CBarWindow()

{

   // Создаем окно приложения, соответствующее

   // данному объекту класса CBarWindow

   Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW,

         rectDefault, NULL );

}

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

// Метод OnCreate класса CBarWindow

// Вызывается во время создания окна приложения

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

int CBarWindow::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

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

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

      return -1;

   // Создаем панель управления toolbar. В качестве

   // родительского окна указываем окно CBarWindow

   if (!m_wndToolBar.Create(this))

   {

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

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

      return -1;

   }

   if (!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))

   {

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

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

      return -1;

   }

   return 0;

}

Теперь необходимо создать ресурс панели управления toolbar. Вы можете сделать это несколькими способами - создать панель управления “с нуля” или скопировать уже готовую панель управления из другого приложения, например из приложения Multi, представленного в главе “Многооконное приложение”.

Перед тем как приступить к разработке панели управления, создайте файл ресурсов и включите его в проект. Для этого выберите из меню File строку New. Из открывшейся диалоговой панели New выберите строку Resource Script и нажмите на кнопку OK. Будет создан пустой файл ресурсов. Сохраните его в каталоге приложения под именем Bar.rc и включите в проект.


Редактор панели управления


Начиная с версии 4.0 Microsoft Visual C++, панель управления является отдельным ресурсом, таким же как меню или шаблон диалоговой панели. Редактор ресурсов Microsoft Visual C++ позволяет создать новую панель управления или изменить уже существующую. В редакторе вы не только можете нарисовать новые кнопки, вы сразу можете присвоить им идентификаторы и вызвать ClassWizard, чтобы добавить программный код, вызываемый при нажатии на данную кнопку. В самом простом случае процедура добавления новой кнопки к уже существующей панели управления займет у вас всего несколько минут.
Создайте с помощью MFC AppWizard новый проект или откройте проект, созданный ранее, например, проект Multi. В окне Project Workspace откройте страницу ResourceView. Откройте папку ресурсов Toolbar. В ней располагается только один ресурс этого типа, имеющий идентификатор IDR_MAINFRAME. Загрузите этот ресурс в редактор ресурсов (рис. 3.7).
Редактор панели управления
Рис. 3.7. Редактор ресурсов toolbar
В верхней части окна редактора ресурсов типа toolbar отображается редактируемая панель управления. С помощью мыши можно выбирать кнопки этой панели, которые вы желаете изменить. Редактирование изображения кнопок выполняется в нижней части окна редактора, разделенной на две области, в которых отображается кнопка в нормальном и увеличенном масштабе. Для редактирования предназначены инструменты, представленные в панели Graphics. Вы также можете задавать цвета из палитры Colors.
Редактор ресурсов позволяет присвоить идентификаторы отдельным кнопкам панели управления. Для этого сделайте двойной щелчок по изображению кнопки в верхней части окна редактора. На экране появится диалоговая панель Toolbar Button Properties (рис. 3.8).
Редактор панели управления
Рис. 3.8. Диалоговая панель Toolbar Button Properties
Идентификатор кнопки вводится в списке ID. По умолчанию в списке ID отображается идентификатор, который кнопка имеет в данный момент. Вы можете заменить его другим идентификатором, выбрав его из списка или введя его вручную. Если кнопке, идентификатор еще не присвоен и поле ID пустое, то когда вы закроете панель Toolbar Button Properties, идентификатор будет создан автоматически.
В полях Width и Height выводятся, соответственно, ширина и высота кнопок панели управления в пикселах. Эти два значения относятся ко всем кнопкам панели, а не только к кнопке, выбранной для редактирования в данный момент.
В поле Prompt отображается текстовая строка, описывающая кнопку панели управления. Строка состоит из двух частей, разделенных символом \n. Первая часть содержит описание кнопки, отображаемое в панели состояния когда кнопка выбрана (или соответствующей строки меню, если в меню есть строка с таким же идентификатором). Во второй части строки находится короткое описание кнопки. Оно отображается в окне подсказки tooltips, если вы поместите указатель мыши над кнопкой и некоторое время подождете.
Каждая кнопка в панели управления и каждый разделитель имеют свой собственный индекс. Этот индекс соответствует порядковому номеру, под которым кнопка или разделитель отображаются на экране.


Ресурсы клавиш акселераторов


Чтобы ускорить выбора строк из меню в приложениях используются таблицы клавиш акселераторов. Они задают соответствие комбинаций клавиш идентификаторам командных сообщений. Когда пользователь нажимает комбинацию клавиш, определенную в таблице акселераторов, приложению передается командное сообщение с соответствующим идентификатором. В принципе, можно определить комбинации клавиш акселерации, не только дублирующие строки меню, но и вызывающие передачу других командных сообщений.
Для создания и изменения таблиц акселераторов следует использовать редактор ресурсов Microsoft Visual C++. Он позволяет определить соответствие комбинаций клавиш и идентификаторов командных сообщений (рис. 3.3).
Ресурсы клавиш акселераторов
Рис. 3.3. Редактор таблицы клавиш ускорения
Для приложений имеющих оконный интерфейс, и созданных с использованием MFC AppWizard, таблица акселераторов создается автоматически. Таблица акселераторов загружается приложением, во время создания главного окна приложения методом LoadFrame. Мы уже рассматривали этот метод, который также используется для загрузки меню и ряда других ресурсов:
// Создаем главное окно многооконного приложения
CMainFrame* pMainFrame = new CMainFrame;
// Загружаем ресурсы с идентификатором IDR_MAINFRAME,
// в том числе и таблицу акселераторов
if(!pMainFrame->LoadFrame(IDR_MAINFRAME))
   return FALSE;
Для многооконных приложений каждый тип документа может иметь собственную таблицу акселераторов. Эта таблица будет загружена автоматически вместе с меню (и некоторыми другими ресурсами), когда пользователь откроет окно просмотра документа данного типа.
Чтобы определить таблицу акселераторов для документов данного типа, надо просто включить ее в файл ресурсов приложения, присвоив ей идентификатор данного типа документов:
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
   IDR_MULTITYPE,
   RUNTIME_CLASS(CMultiDoc),
   RUNTIME_CLASS(CChildFrame),
   RUNTIME_CLASS(CMultiView));
AddDocTemplate(pDocTemplate);
Если приложение создается без использования средств MFC AppWizard и модели документ - окно просмотра, вы можете загрузить таблицу акселераторов, с помощью метода LoadAccelTable, входящего в состав класса CFrameWnd:
BOOL LoadAccelTable(LPCTSTR lpszResourceName);
В качестве параметра lpszResourceName следует указать имя ресурса таблицы акселераторов. Если таблица акселераторов вместо строкового имени имеет числовой идентификатор, то вы должны воспользоваться макрокомандой MAKEINTRESOURCE.
Как и многие другие методы классов MFC, метод LoadAccelTable возвращает в случае успешного завершения ненулевое значение и нуль в случае ошибки. Ошибка во время загрузки таблицы акселераторов может случиться, если вы неправильно укажите идентификатор (или имя) ресурса таблицы.


Ресурсы Microsoft


Вместе с Microsoft Visual C++ поставляется файл Common.res, содержащий различные ресурсы - пиктограммы, курсоры, изображения bitmap. Вы можете свободно использовать эти ресурсы в своих приложениях и распространять их.
¨ Вместо того, чтобы самому рисовать картинки для пиктограмм, панелей управления и курсоров или заказывать их у художника, обратите внимание на файл Common.res. Возможно, в нем есть то, что вам надо.
Во время установки Microsoft Visual C++ не происходит копирования файла ресурсов Common.res на жесткий диск компьютера. Поэтому вы должны скопировать его отдельно или использовать непосредственно с компакт диска. На компакт дисках Microsoft Visual C++ этот файл расположен в каталоге Msdev\Samples\Mfc\General\Clipart\.
Чтобы включить некоторые ресурсы из файла Common.res в файл ресурсов вашего приложения, откройте в Microsoft Visual C++ файл Common.res и файл ресурсов приложения. Затем скопируйте интересующие вас ресурсы и сохраните изменения на диске.


Ресурсы приложений и панель состояния


К сожалению, нет специальных ресурсов, предназначенных для разработки панелей состояния. Редактор ресурсов вам не поможет - панель состояния создается “вручную”. Каждый элемент панели состояния должен быть описан в специальном массиве.


Ресурсы приложения


Рассмотрим ресурсы, которые MFC AppWizard создал для нашего приложения. Откройте страницу RecourceView в окне проекта Project Workspace. В нем отображается полный список всех ресурсов приложения (рис. 1.3).
Ресурсы приложения
Рис. 1.3. Окно Project Workspace, ресурсы приложения
Сравните эти ресурсы с ресурсами приложения с однооконным интерфейсом (том 24 серии “Библиотека системного программиста”). Вы заметите, что в состав приложения с многооконным интерфейсом входит больше ресурсов. Так, например, для многооконного приложения определены два меню, две пиктограммы, больше размер таблицы текстовых строк.


Ресурсы приложения Dater


В файле ресурсов приложения Dater определены меню, панель управления и таблица клавиш акселераторов IDR_MAINFRAME, шаблон диалоговой панели IDD_DATER_FORM, который используется окном просмотра и шаблон информационной панели IDD_ABOUTBOX. В файле ресурсов также расположены строковые ресурсы, описывающие строки меню, кнопки панелей управления и индикаторы панели состояния. Мы привели исходный текст файла Dater.rc в листинге 5.3.
Листинг 5.3. Файл Dater.rc
//Microsoft Developer Studio generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
// English (U.S.) resources
//#if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_ENU)
//#ifdef _WIN32
//LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//#pragma code_page(1252)
//#endif //_WIN32
#ifdef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
    "resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
   "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
   "#define _AFX_NO_OLE_RESOURCES\r\n"
   "#define _AFX_NO_TRACKER_RESOURCES\r\n"
   "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
   "\r\n"
   "#if !defined(AFX_RESOURCE_DLL)defined(AFX_TARG_ENU)\r\n"
   "#ifdef _WIN32\r\n"
   "LANGUAGE 9, 1\r\n"
   "#pragma code_page(1252)\r\n"
   "#endif\r\n"
   "#include ""res\\Dater.rc2""  // non-Microsoft Visual C++

                                 // edited resources\r\n"

   "#include ""afxres.rc""       // Standard components\r\n"

   "#include ""afxdb.rc""        // Database resources\r\n"

   "#endif\0"

END

#endif    // APSTUDIO_INVOKED

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

//

// Icon

//

IDR_MAINFRAME   ICON   DISCARDABLE   "res\\Dater.ico"

IDR_DATERTYPE   ICON   DISCARDABLE   "res\\DaterDoc.ico"

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

//

// Bitmap

//

IDR_MAINFRAME   BITMAP  MOVEABLE PURE   "res\\Toolbar.bmp"

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

//

// Toolbar

//

IDR_MAINFRAME TOOLBAR DISCARDABLE  16, 15

BEGIN

    BUTTON      ID_EDIT_CUT

    BUTTON      ID_EDIT_COPY

    BUTTON      ID_EDIT_PASTE

    SEPARATOR

    BUTTON      ID_FILE_PRINT

    SEPARATOR

    BUTTON      ID_RECORD_FIRST

    BUTTON      ID_RECORD_PREV

    BUTTON      ID_RECORD_NEXT

    BUTTON      ID_RECORD_LAST

    SEPARATOR

    BUTTON      ID_APP_ABOUT

END

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

//

// Menu

//

IDR_MAINFRAME MENU PRELOAD DISCARDABLE

BEGIN

   POPUP "&File"

   BEGIN

       MENUITEM "E&xit",                ID_APP_EXIT

   END

   POPUP "&Edit"

   BEGIN

      MENUITEM "&Undo\tCtrl+Z",         ID_EDIT_UNDO

      MENUITEM SEPARATOR

      MENUITEM "Cu&t\tCtrl+X",          ID_EDIT_CUT

      MENUITEM "&Copy\tCtrl+C",         ID_EDIT_COPY

      MENUITEM "&Paste\tCtrl+V",        ID_EDIT_PASTE

   END

   POPUP "&Record"

   BEGIN

      MENUITEM "&First Record",         ID_RECORD_FIRST

      MENUITEM "&Previous Record",      ID_RECORD_PREV

      MENUITEM "&Next Record",          ID_RECORD_NEXT



      MENUITEM "&Last Record",          ID_RECORD_LAST

   END

   POPUP "&View"

   BEGIN

      MENUITEM "&Toolbar",              ID_VIEW_TOOLBAR

      MENUITEM "&Status Bar",           ID_VIEW_STATUS_BAR

   END

   POPUP "&Help"

   BEGIN

      MENUITEM "&About Dater...",       ID_APP_ABOUT

   END

END

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

//

// Accelerator

//

IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE

BEGIN

    "Z",            ID_EDIT_UNDO,           VIRTKEY, CONTROL

    "X",            ID_EDIT_CUT,            VIRTKEY, CONTROL

    "C",            ID_EDIT_COPY,           VIRTKEY, CONTROL

    "V",            ID_EDIT_PASTE,          VIRTKEY, CONTROL

    VK_BACK,        ID_EDIT_UNDO,           VIRTKEY, ALT

    VK_DELETE,      ID_EDIT_CUT,            VIRTKEY, SHIFT

    VK_INSERT,      ID_EDIT_COPY,           VIRTKEY, CONTROL

    VK_INSERT,      ID_EDIT_PASTE,          VIRTKEY, SHIFT

    VK_F6,          ID_NEXT_PANE,           VIRTKEY

    VK_F6,          ID_PREV_PANE,           VIRTKEY, SHIFT

END

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

//

// Dialog

//

IDD_ABOUTBOX DIALOG DISCARDABLE  0, 0, 217, 55

STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU

CAPTION "About Dater"

FONT 8, "MS Sans Serif"

BEGIN

   ICON            IDR_MAINFRAME,IDC_STATIC,11,17,20,20

   LTEXT           "Dater Version 1.0",

                   IDC_STATIC,40,10,119,8,SS_NOPREFIX

   LTEXT           "Copyright © 1996",IDC_STATIC,40,25,119,8

   DEFPUSHBUTTON   "OK",IDOK,178,7,32,14,WS_GROUP

END

IDD_DATER_FORM DIALOG DISCARDABLE  0, 0, 201, 101

STYLE WS_CHILD

FONT 8, "MS Sans Serif"

BEGIN

    LTEXT           "E-Mail",IDC_STATIC,5,35,20,8

    LTEXT           "Priority",IDC_STATIC,5,86,22,8



    EDITTEXT        IDC_NAME,35,5,160,15,ES_AUTOHSCROLL

    LTEXT           "Name",IDC_STATIC,5,10,20,8

    EDITTEXT        IDC_ADDRESS,35,30,160,15,ES_AUTOHSCROLL

    EDITTEXT        IDC_PRIORITY,35,80,80,15,ES_AUTOHSCROLL

    LTEXT           "Phone",IDC_STATIC,5,60,22,8

    EDITTEXT        IDC_PHONE,35,55,80,15,ES_AUTOHSCROLL

END

#ifndef _MAC

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

//

// Version

//

VS_VERSION_INFO VERSIONINFO

 FILEVERSION 1,0,0,1

 PRODUCTVERSION 1,0,0,1

 FILEFLAGSMASK 0x3fL

#ifdef _DEBUG

 FILEFLAGS 0x1L

#else

 FILEFLAGS 0x0L

#endif

 FILEOS 0x4L

 FILETYPE 0x1L

 FILESUBTYPE 0x0L

BEGIN

    BLOCK "StringFileInfo"

    BEGIN

        BLOCK "040904B0"

        BEGIN

            VALUE "CompanyName", "\0"

            VALUE "FileDescription", "DATER MFC Application\0"

            VALUE "FileVersion", "1, 0, 0, 1\0"

            VALUE "InternalName", "DATER\0"

            VALUE "LegalCopyright", "Copyright © 1996\0"

            VALUE "LegalTrademarks", "\0"

            VALUE "OriginalFilename", "DATER.EXE\0"

            VALUE "ProductName", "DATER Application\0"

            VALUE "ProductVersion", "1, 0, 0, 1\0"

        END

    END

    BLOCK "VarFileInfo"

    BEGIN

        VALUE "Translation", 0x409, 1200

    END

END

#endif    // !_MAC

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

//

// DESIGNINFO

//

#ifdef APSTUDIO_INVOKED

GUIDELINES DESIGNINFO DISCARDABLE

BEGIN

    IDD_ABOUTBOX, DIALOG

    BEGIN

        LEFTMARGIN, 7

        RIGHTMARGIN, 210

        TOPMARGIN, 7

        BOTTOMMARGIN, 48

    END

    IDD_DATER_FORM, DIALOG

    BEGIN

        LEFTMARGIN, 7

        RIGHTMARGIN, 194



        TOPMARGIN, 7

        BOTTOMMARGIN, 94

    END

END

#endif    // APSTUDIO_INVOKED

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

//

// String Table

//

STRINGTABLE DISCARDABLE

BEGIN

    IDP_FAILED_OPEN_DATABASE "Cannot open database."

END

STRINGTABLE PRELOAD DISCARDABLE

BEGIN

   IDR_MAINFRAME "Dater\n\nDater\n\n\n

                  Dater.Document\nDater Document"

END

STRINGTABLE PRELOAD DISCARDABLE

BEGIN

    AFX_IDS_APP_TITLE       "Dater"

    AFX_IDS_IDLEMESSAGE     "Ready"

END

STRINGTABLE DISCARDABLE

BEGIN

    ID_INDICATOR_EXT        "EXT"

    ID_INDICATOR_CAPS       "CAP"

    ID_INDICATOR_NUM        "NUM"

    ID_INDICATOR_SCRL       "SCRL"

    ID_INDICATOR_OVR        "OVR"

    ID_INDICATOR_REC        "REC"

END

STRINGTABLE DISCARDABLE

BEGIN

    ID_APP_ABOUT   "Display program information, version

                    number and copyright\nAbout"

    ID_APP_EXIT    " Quit the application; prompts to save

                    documents\nExit"

END

STRINGTABLE DISCARDABLE

BEGIN

    ID_FILE_MRU_FILE1       "Open this document"

    ID_FILE_MRU_FILE2       "Open this document"

    //...

    ID_FILE_MRU_FILE16      "Open this document"

END

STRINGTABLE DISCARDABLE

BEGIN

    ID_NEXT_PANE   "Switch to the next window pane\nNext Pane"

    ID_PREV_PANE   "Switch back to the previous window pane\n

                    Previous Pane"

END

STRINGTABLE DISCARDABLE

BEGIN

   ID_WINDOW_SPLIT "Split the active window into panes\nSplit"

END

STRINGTABLE DISCARDABLE

BEGIN

   ID_EDIT_CLEAR        "Erase the selection\nErase"

   ID_EDIT_CLEAR_ALL    "Erase everything\nErase All"

   ID_EDIT_COPY         "Copy the selection and put it on the



                         Clipboard\nCopy"

   ID_EDIT_CUT          " Cut the selection and put it on the

                         Clipboard\nCut"

   ID_EDIT_FIND         "Find the specified text\nFind"

   ID_EDIT_PASTE        "Insert Clipboard contents\nPaste"

   ID_EDIT_REPEAT       "Repeat the last action\nRepeat"

   ID_EDIT_REPLACE      "Replace specific text with different

                         text\nReplace"

   ID_EDIT_SELECT_ALL   "Select the entire document\n

                         Select All"

   ID_EDIT_UNDO      "Undo the last action\nUndo"

   ID_EDIT_REDO      "Redo the previously undone action\nRedo"

END

STRINGTABLE DISCARDABLE

BEGIN

   ID_VIEW_TOOLBAR     "Show or hide the toolbar\n

                        Toggle ToolBar"

    ID_VIEW_STATUS_BAR "Show or hide the status bar\n

                        Toggle StatusBar"

END

STRINGTABLE DISCARDABLE

BEGIN

   ID_RECORD_FIRST  "Move to first record\nFirst Record"

   ID_RECORD_LAST   "Move to final record\nLast Record"

   ID_RECORD_NEXT   "Move to next record\nNext Record"

   ID_RECORD_PREV   "Move to previous record\nPrevious Record"

END

STRINGTABLE DISCARDABLE

BEGIN

   AFX_IDS_SCSIZE        "Change the window size"

   AFX_IDS_SCMOVE        "Change the window position"

   AFX_IDS_SCMINIMIZE    "Reduce the window to an icon"

   AFX_IDS_SCMAXIMIZE    "Enlarge the window to full size"

   AFX_IDS_SCNEXTWINDOW  "Switch to the next document window"

   AFX_IDS_SCPREVWINDOW  "Switch to the previous document

                          window"

   AFX_IDS_SCCLOSE       "Close the active window and prompts

                          to save the documents"

END

STRINGTABLE DISCARDABLE

BEGIN

    AFX_IDS_SCRESTORE    "Restore the window to normal size"



    AFX_IDS_SCTASKLIST   "Activate Task List"

END

//#endif    // English (U.S.) resources

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

#ifndef APSTUDIO_INVOKED

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

//

// Generated from the TEXTINCLUDE 3 resource.

//

#define _AFX_NO_SPLITTER_RESOURCES

#define _AFX_NO_OLE_RESOURCES

#define _AFX_NO_TRACKER_RESOURCES

#define _AFX_NO_PROPERTY_RESOURCES

#if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_ENU)

#ifdef _WIN32

LANGUAGE 9, 1

#pragma code_page(1252)

#endif

#include "res\Dater.rc2"  // non-Microsoft Visual C++ edited resources

#include "afxres.rc"         // Standard components

#include "afxdb.rc"          // Database resources

#endif

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

#endif    // not APSTUDIO_INVOKED

Идентификаторы ресурсов приложения Dater определены в файле resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++. Исходный текст файла resource.h представлен в листинге 3.15.

Листинг 5.4. Файл resource.h

//{{NO_DEPENDENCIES}}

// Microsoft Developer Studio generated include file.

// Used by Dater.rc

//

#define IDD_ABOUTBOX                    100

#define IDD_DATER_FORM                  101

#define IDP_FAILED_OPEN_DATABASE        103

#define IDR_MAINFRAME                   128

#define IDR_DATERTYPE                   129

#define IDC_NAME                        1000

#define IDC_ADDRESS                     1001

#define IDC_PRIORITY                    1002

#define IDC_PHONE                       1003

// Next default values for new objects

//

#ifdef APSTUDIO_INVOKED

#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_3D_CONTROLS                1

#define _APS_NEXT_RESOURCE_VALUE        130

#define _APS_NEXT_COMMAND_VALUE         32771

#define _APS_NEXT_CONTROL_VALUE         1004

#define _APS_NEXT_SYMED_VALUE           101

#endif



#endif

Наибольший интерес в файле ресурсов приложения Dater представляют строки меню Record и соответствующие им кнопки панели управления. Эти строки и кнопки позволяют просматривать в окне приложения все записи базы данных.

Строка меню Record

Идентификатор

Описание

First Record

ID_RECORD_FIRST

Перейти к первой записи

Previous Record

ID_RECORD_PREV

Перейти к предыдущей записи

Next Record

ID_RECORD_NEXT

Перейти к следующей записи

Last Record

ID_RECORD_LAST

Перейти к последней записи

Командные сообщения с идентификаторами ID_RECORD_FIRST, ID_RECORD_PREV, ID_RECORD_NEXT и ID_RECORD_LAST обрабатываются виртуальным методом OnMove класса окна просмотра CRecordView.

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


Ресурсы приложения MultiBar


Создайте новый файл ресурсов и включите его в проект под именем MultiBar.rc. Теперь надо создать три панели управления toolbar и включить их в файл ресурсов. Изображения кнопок панелей управления вы можете нарисовать самостоятельно или скопировать их из файла ресурсов Common.res, поставляемого вместе с Microsoft Visual C++. Более подробная информация о файле Common.res представлена в разделе “Ресурсы Microsoft”.
При создании панелей управления руководствуйтесь информацией из файла ресурсов приложения MultiBar (листинг 3.3) и изображениями кнопок панелей управления (рис. 3.3, 3.4, 3.5).
Первая панель управления должна иметь идентификатор IDR_PLAYER и содержать девять кнопок с идентификаторами ID_LEFT, ID_PLAY, ID_RIGHT, ID_STOP, ID_PAUSE, ID_EJECT, ID_TYPE, ID_CD_DRV и ID_WAVE. Между кнопками ID_RIGHT и ID_STOP, ID_PAUSE и ID_EJECT, ID_EJECT и ID_TYPE вставьте разделители. Для каждой из кнопок вы также можете ввести их текстовые описания.
Вторая панель управления должна иметь идентификатор IDR_STYLE и содержать двенадцать кнопок с идентификаторами ID_UNDERLINE, ID_2_UNDERLINE, ID_SUPERSCRIPT, ID_SUBSCRIPT, ID_TEXT_LEFT, ID_ID_TEXT_CENTER, ID_TEXT_RIGHT, ID_TEXT_JUSTIFY, ID_MARK_1, ID_MARK_2, ID_MARK_3 и ID_MARK_4. Для первых шести кнопок введите их текстовые описания.
Третья панель управления должна иметь идентификатор IDR_EXTENDED. Определите в ней четыре кнопки с идентификаторами ID_FOTO, ID_PRINTER, ID_ADD и ID_DISK. Между кнопками ID_PRINTER и  ID_ADD, а также ID_ADD и ID_DISK вставьте разделители. Введите текстовые описания кнопок.
В файл ресурсов, кроме панелей управления toolbar, включите меню IDR_MENU с тремя строками Style, Extended и Player, которые имеют идентификаторы ID_Style, ID_Extended и ID_Player. Обратите внимание, что идентификаторы строк меню соответствуют идентификаторам панелей управления.
Файл ресурсов приложения MultiBar мы привели в листинге 3.8. В нем определены ресурсы панелей управления toolbar с идентификаторами IDR_PLAYER, IDR_STYLE и IDR_EXTENDED, ресурсы изображений этих панелей управления с соответствующими идентификаторами, меню приложения, а также несколько строковых ресурсов с описаниями кнопок панелей управления. Идентификаторы строковых ресурсов соответствуют идентификаторам кнопок панелей управления, которые они описывают.

Листинг 3.8. Файл MultiBar.rc

// Microsoft Developer Studio generated resource script.

//

#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS

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

//

// Generated from the TEXTINCLUDE 2 resource.

//

#include "afxres.h"

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

#undef APSTUDIO_READONLY_SYMBOLS

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

//

// Russian resources

#if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_RUS)

#ifdef _WIN32

LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT

#pragma code_page(1251)

#endif //_WIN32

#ifdef APSTUDIO_INVOKED

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

//

// TEXTINCLUDE

//

1 TEXTINCLUDE DISCARDABLE

BEGIN

    "resource.h\0"

END

2 TEXTINCLUDE DISCARDABLE

BEGIN

    "#include ""afxres.h""\r\n"

    "\0"

END

3 TEXTINCLUDE DISCARDABLE

BEGIN

    "\r\n"

    "\0"

END

#endif    // APSTUDIO_INVOKED

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

//

// Toolbar

//

IDR_PLAYER TOOLBAR DISCARDABLE  16, 15

BEGIN

    BUTTON      ID_LEFT

    BUTTON      ID_PLAY

    BUTTON      ID_RIGHT

    SEPARATOR

    BUTTON      ID_STOP

    BUTTON      ID_PAUSE

    SEPARATOR

    BUTTON      ID_EJECT

    SEPARATOR

    BUTTON      ID_TYPE

    BUTTON      ID_CD_DRV

    BUTTON      ID_WAVE

END

IDR_STYLE TOOLBAR DISCARDABLE  16, 15

BEGIN

    BUTTON      ID_UNDERLINE

    BUTTON      ID_2_UNDERLINE

    BUTTON      ID_SUPERSCRIPT

    BUTTON      ID_SUBSCRIPT

    BUTTON      ID_TEXT_LEFT

    BUTTON      ID_ID_TEXT_CENTER

    BUTTON      ID_TEXT_RIGHT

    BUTTON      ID_TEXT_JUSTIFY

    BUTTON      ID_MARK_1

    BUTTON      ID_MARK_2

    BUTTON      ID_MARK_3

    BUTTON      ID_MARK_4

END

IDR_EXTENDED TOOLBAR DISCARDABLE  16, 15



BEGIN

    BUTTON      ID_FOTO

    BUTTON      ID_PRINTER

    SEPARATOR

    BUTTON      ID_ADD

    SEPARATOR

    BUTTON      ID_DISK

END

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

//

// Bitmap

//

IDR_PLAYER              BITMAP  DISCARDABLE     "player.bmp"

IDR_STYLE               BITMAP  DISCARDABLE     "style.bmp"

IDR_EXTENDED            BITMAP  DISCARDABLE     "extended.bmp"

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

//

// Menu

//

IDR_MENU MENU DISCARDABLE

BEGIN

    POPUP "View"

    BEGIN

        MENUITEM "Style",                       ID_Style

        MENUITEM "Extended",                    ID_Extended

        MENUITEM "Player",                      ID_Player

    END

END

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

//

// String Table

//

STRINGTABLE DISCARDABLE

BEGIN

    ID_LEFT              "Rewind to begin\nRewind to begin"

    ID_RIGHT             "Rewind to end\nRewind to end"

    ID_PLAY              "Play\nPlay"

    ID_STOP              "Stop\nStop"

    ID_PAUSE             "Pause\nPause"

    ID_EJECT             "Eject\nEject"

    ID_TYPE              "Type drive\nType"

    ID_CD_DRV            "CD drive\nCD"

    ID_BUTTON40010       "Wave/nWave File"

    ID_WAVE              "Wave file\nWAWE"

    ID_UNDERLINE         "Underline\nUnderline"

    ID_2_UNDERLINE       "Double underline\nDouble underline"

    ID_SUPERSCRIPT       "Superscript\nSuperscript"

    ID_SUBSCRIPT         "Subscript\nSubscript"

END

STRINGTABLE DISCARDABLE

BEGIN

    ID_ADD               "Add from edit-box item to combo-

                          box\nAdd item to list"

END

STRINGTABLE DISCARDABLE

BEGIN



    ID_TEXT_LEFT         "Left text\nLeft"

    ID_ID_TEXT_CENTER    "Center text\nCenter"

    ID_TEXT_RIGHT        "Right text\nRight"

    ID_TEXT_JUSTIFY      "Justify text\nJustify"

    ID_FOTO              "Foto\nFoto"

    ID_PRINTER           "Printer\nPrinter"

    ID_DISK              "Disk\nDisk"

END

STRINGTABLE DISCARDABLE

BEGIN

    IDW_EDIT             "Edit\nEdit"

    IDW_COMBO            "Combo box\nCombo box"

END

#endif    // Russian resources

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

#ifndef APSTUDIO_INVOKED

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

// Generated from the TEXTINCLUDE 3 resource.

//

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

#endif    // not APSTUDIO_INVOKED

Изображения кнопок панелей управления располагаются в отдельных файлах player.bmp (рис. 3.14), style.bmp  (рис. 3.15) и extended.bmp (рис. 3.13) в главном каталоге проекта. Файлы изображений панелей управления содержат только изображения кнопок. В них не представлены разделители и дополнительные органы управления.

В принципе, изображения панели управления можно редактировать в любом графическом редакторе, который работает с 16-цветными изображениями в формате BMP. Примером такого приложения является графический редактор Microsoft Paint, поставляемый вместе с операционными системами Windows 95 и Windows NT версии 4.0. Однако, лучше использовать редактор ресурсов среды Microsoft Visual C++, так как он не только разрешает редактировать существующие кнопки, но также позволяет добавлять новые кнопки вместе с соответствующими идентификаторами и строковыми ресурсами.

Ресурсы приложения MultiBar

Рис. 3.13. Изображение кнопок панели управления Extended

Ресурсы приложения MultiBar

Рис. 3.14. Изображение кнопок панели управления Player

Ресурсы приложения MultiBar

Рис. 3.15. Изображение кнопок панели управления Style

Идентификаторы всех ресурсов приложения MultiBar и идентификаторы дополнительных органов управления панели Extended определены в файле resource.h. Этот файл автоматически создается редактором ресурсов Microsoft Visual C++.



Мы привели исходный текст файла resource.h в листинге 3.9.

Листинг 3.9. Файл resource.h

//{{NO_DEPENDENCIES}}

// Microsoft Developer Studio generated include file.

// Used by MultiBar.rc

//

#define IDW_EDIT                        101

#define IDW_COMBO                       102

#define IDR_PLAYER                      103

#define IDR_STYLE                       105

#define IDR_EXTENDED                    107

#define IDR_MENU                        109

#define ID_LEFT                         40001

#define ID_RIGHT                        40002

#define ID_PLAY                         40003

#define ID_STOP                         40004

#define ID_PAUSE                        40005

#define ID_EJECT                        40007

#define ID_TYPE                         40008

#define ID_CD_DRV                       40009

#define ID_WAVE                         40011

#define ID_UNDERLINE                    40012

#define ID_2_UNDERLINE                  40013

#define ID_SUPERSCRIPT                  40014

#define ID_SUBSCRIPT                    40015

#define ID_TEXT_LEFT                    40017

#define ID_ID_TEXT_CENTER               40018

#define ID_TEXT_RIGHT                   40019

#define ID_TEXT_JUSTIFY                 40020

#define ID_MARK_1                       40021

#define ID_MARK_2                       40022

#define ID_MARK_3                       40023

#define ID_MARK_4                       40024

#define ID_FOTO                         40025

#define ID_PRINTER                      40026

#define ID_DISK                         40027

#define ID_Style                        40029

#define ID_Extended                     40030

#define ID_Buttons                      40031

#define ID_Player                       40031

#define ID_ADD                          40032

// Next default values for new objects

//

#ifdef APSTUDIO_INVOKED

#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NEXT_RESOURCE_VALUE        110

#define _APS_NEXT_COMMAND_VALUE         40033

#define _APS_NEXT_CONTROL_VALUE         1000

#define _APS_NEXT_SYMED_VALUE           103

#endif

#endif


Ресурсы текстовых документов


Откройте редактор ресурсов приложения. Вам требуется создать меню, пиктограмму и строковый ресурс с идентификатором IDR_EDITORTYPE. Самый простой путь для этого заключается в копировании и изменении идентификатора ресурсов, относящихся к графическим документам. Идентификатор этих ресурсов IDR_MULTITYPE. Чтобы скопировать ресурс, вы можете выбрать интересующий вас ресурс из окна проекта, записать его в обменный буфер clipboard, вставить его, а затем изменить идентификатор на IDR_EDITORTYPE.
На первом этапе разработки приложения скопированные меню и пиктограмму вы можете оставить без изменения. Потом вы можете изменить их по своему усмотрению.
Строковый ресурс IDR_EDITORTYPE, описывающий документ, желательно изменить сразу. Для графического документа строковый ресурс IDR_MULTITYPE выглядит следующим образом:
\nGraph\nGraph\nGraph Files (*.LIS)\n.LIS\nGraph.Document\n
Graph Document
Чтобы текстовый документ имел другое название типа документа, расширение файлов принятое по умолчанию, измените строковый ресурс с идентификатором IDR_EDITORTYPE:
\nText\nTexti\nTexti Files (*.TXT)\n.TXT\nText.Document\n
Text Document


Сеть Internet


В состав версии 4.2 включены абсолютно новые классы для работы с сетью Internet, основанные на программном интерфейсе Wininet. С их помощью вы легко сможете создавать клиентские приложения для работы с серверами WWW, FTP и Goufer.
Microsoft Visual C++ версии 4.2 позволяет реализовать новую технологию ActiveX, предложенную Microsoft для использования в сети Internet. В MFC AppWizard добавлены средства, ускоряющие разработку органов управления ActiveX.


Синхронизация окон просмотра документа


Как правило, многооконные приложения позволяют открыть для одного документа несколько окон просмотра. Наше приложение тоже не составляет исключения. Чтобы открыть дополнительное окно для просмотра уже открытого документа, выберите из меню Window строку New.
Откроется новое окно. Заголовки окон просмотра одного документа будут одинаковыми за исключением того, что каждое такое окно имеет дополнительный числовой идентификатор, означающий номер окна. На рисунке 1.14 мы показали как будет выглядеть приложение Multi, если в нем создать два новых документа Multi1 и Multi2, а затем открыть два дополнительных окна для просмотра документа Multi2.
Синхронизация окон просмотра документа
Рис. 1.14. Окно Project Workspace, класс CMultiView
К сожалению, окна просмотра документа несогласованны. Если вы внесете в документ изменения через одно окно, они не появятся во втором до тех пор, пока содержимое окна не будет перерисовано. Чтобы избежать рассогласования между окнами просмотра одного и того же документа, необходимо сразу после изменения документа в одном окне вызвать метод UpdateAllViews, определенный в классе CDocument:
void UpdateAllViews(
   CView* pSender,
   LPARAM lHint = 0L,
   CObject* pHint = NULL
);
Метод UpdateAllViews вызывает метод CView::OnUpdate для всех окон просмотра данного документа, за исключением окна просмотра, указанного параметром pSender. Как правило, в качестве pSender используют указатель того окна просмотра через который был изменен документ. Его состояние изменять не надо, так как оно отображает последние изменения в документе.
Если изменение документа вызвано другими причинами, не связанными с окнами просмотра, в качестве параметра pSender можно указать значение NULL. В этом случае будут вызваны методы OnUpdate всех окон просмотра без исключения.
Параметры lHint и pHint могут содержать дополнительную информацию об изменении документа. Методы OnUpdate получают значения lHint и pHint и могут использовать их, чтобы сократить перерисовку документа.
Мы изменяем документ только в методе OnLButtonDown. Поэтому добавьте вызов UpdateAllViews в нем. Разместите его после добавления нового элемента в массив pointFigCenter и установки флага модификации документа (метод UpdateAllViews следует вызывать после метода SetModifiedFlag):

void CMultiView::OnLButtonDown(UINT nFlags, CPoint point)

{

   // ...

   // Устанавливаем флаг изменения документа

   pDoc->SetModifiedFlag();

   // Сообщаем всем окнам просмотра кроме данного об

   // изменении документа

   pDoc->UpdateAllViews(this);

  

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

   CView::OnLButtonDown(nFlags, point);

}

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

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

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

Рассмотрим более подробно, как работает метод OnUpdate:

virtual void

OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint);

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

Второй и третий параметры - lHint и pHint содержат дополнительную. информацию, указанную во время вызова метода UpdateAllViews. Если дополнительная информация не определена, тогда эти параметры содержат значения 0L и NULL соответственно.

Метод OnUpdate вызывается, когда после изменения документа вызывается метод CDocument::UpdateAllViews. Метод также вызывается методом OnInitialUpdate (если вы не переопределите метод OnInitialUpdate).

Реализация OnUpdate из класса CView, определяет, что вся внутренняя область окна просмотра подлежит обновлению и передает данному окну сообщение WM_PAINT (для этого вызывается функция InvalidateRect). Это сообщение обрабатывается методом OnDraw.



Параметр lHint имеет тип LPARAM и может содержать любое 32-битное значение. В нашем случае мы можем передавать через этот параметр индекс элемента массива документа, который надо перерисовать.

Параметр pHint является указателем на объект типа CObject. Поэтому если вы желаете его использовать, вы должны определить собственный класс, наследованный от базового класса CObject, создать объект этого класса и передать указатель на него методу UpdateAllViews.

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

Когда вы будете разрабатывать метод OnUpdate, вы должны проверять тип объекта, передаваемого через параметр pHint. Для этого можно воспользоваться методом IsKindOf класса CObject. Метод IsKindOf позволяет узнать тип объекта уже на этапе выполнения приложения.

В нашем приложении новые фигуры добавляются в документ во время обработки сообщения WM_LBUTTONDOWN методом OnLButtonDown класса окна просмотра. Модифицируем этот метод так, чтобы после изменения документа метод UpdateAllViews передавал остальным окнам просмотра индекс добавленного элемента в массиве pointFigCenter редактируемого документа:

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

// Метод для обработки сообщения WM_LBUTTONDOWN

void CMultiView::OnLButtonDown(UINT nFlags, CPoint point)

{

   // ...

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

   // элемент

   pDoc->pointFigCenter.Add(point);

   // Устанавливаем флаг изменения документа

   pDoc->SetModifiedFlag();

   // Записываем в переменную nNewFig индекс последнего

   // элемента массива pointFigCenter

   int nNewFig;

   nNewFig = pDoc->pointFigCenter.GetUpperBound();

   // Сообщаем всем окнам просмотра кроме данного об

   // изменении документа, указывая индекс нового элемента

   // массива, представляющего документ



   pDoc->UpdateAllViews(this, nNewFig);

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

   CView::OnLButtonDown(nFlags, point);

}

Теперь мы должны переопределить метод OnUpdate так, чтобы при вызове через метод UpdateAllViews он отображал на экране только последний элемент массива pointFigCenter.

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

Когда вы переопределяете метод OnUpdate, вы должны иметь в виду, что этот метод вызывается не только методом UpdateAllViews. В некоторых случаях он может быть вызван, если надо перерисовать все изображение в окне просмотра. В этом случае параметр lHint содержит 0, а параметр pHint - NULL Вы должны обрабатывать эту ситуацию отдельно, вызывая метод InvalidateRect и обновляя все окно целиком:

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

// CMultiView

void CMultiView::OnUpdate(

   CView* pSender, LPARAM lHint, CObject* pHint)

{

   // Получаем указатель на документ, относящийся к данному

   // окну просмотра

   CMultiDoc* pDoc = GetDocument();

   ASSERT_VALID(pDoc);

   // Если lHint равен нулю, выполняем обработку по умолчанию

   if (lHint==0L)

      CView::OnUpdate(pSender, lHint, pHint);

   // В противном случае отображаем заданный элемент документа

   else

   {

      // Получаем контекст отображения окна просмотра

      CClientDC dc(this);

      // Отображаем фигуру, определенную элементом массива

      // pointFigCenter с индексом lHint

      dc.Rectangle(

            pDoc->pointFigCenter[lHint].x-10,

            pDoc->pointFigCenter[lHint].y-10,

            pDoc->pointFigCenter[lHint].x+10,

            pDoc->pointFigCenter[lHint].y+10

      );

   }

}

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

В нашем примере мы отображаем новый объект непосредственно из метода OnUpdate. Однако лучше если метод OnUpdate только укажет методу OnDraw область окна просмотра, которую надо перерисовать. Для этого можно воспользоваться методом CWnd::InvalidateRect.


Создание базы данных


Итак, мы решили создать базу данных на основе обычного текстового файла. Запустите любой текстовый редактор, например Notepad. Вы также можете воспользоваться текстовым редактором среды Microsoft Visual C++. Наберите в нем файл TextBase.txt, представленный в листинге 5.1.
Листинг 5.1. Файл TextBase.txt
NAME;ADDRESS;PRIORITY;PHONE
Фролов Григорий Вячеславович;frolov@glas.apc.org;1;(не известен)
Фролов Александр Вячеславович;frolov@glas.apc.org;1;(не известен)
Евсеев Святослав Олегович;sun@power.com;4;8783-77-35
Николаев Петр Иванович;lis@nikol.com;4;1242-09-09
Петров Евгений Николаевич;petr@power.com;7;5453-59-05
Файл TextBase.txt содержит шесть записей (строк). Каждая запись состоит из четырех полей, разделенных символами ;. Самая первая строка отличается от остальных. Она содержит названия полей таблицы, котоорые мы будем использовать далее.
После того, как файл создан, запишите его в каталоге TEXTBASE. Мы разместили каталог TEXTBASE на диске E:, но вы можете записать его на любом другом диске.
Следующим шагом является создание так называемого источника данных, который заключается в подключении текстового драйвера ODBC к файлу TextBase.txt. Обраащаясь к этому источнику данных, программа получает доступ к базе даанных через соответствующий драйвер ODBC.

Создание базы данных
Для подключения к базе данных драйвера ODBC и создания источника данных используйте приложение 32bit ODBC. Пиктограмма приложения 32bit ODBC находится в окне Control Panel

Откройте Control Panel и запустите приложение 32bit ODBC. На экране появится диалоговая панель Data Source (рис. 5.1).
Создание базы данных
Рис. 5.1. Диалоговая панель Data Source
Нажмите кнопку Add в диалоговой панели Data Source. На экране появится диалоговая панель Add Data Source (рис. 5.2). В ней вы должны выбрать драйвер ODBC, который будет использоваться для доступа к базе данных.
Если драйвер ODBC для текстовых файлов отсутствует в списке Installed ODBC Drivers, значит он не установлен на вашем компьютере. Чтобы подключить этот драйвер (а также другие драйверы ODBC) повторите установку Microsoft Visual C++ и укажите драйверы ODBC с которыми вы будете работать.

Для первого приложения, использующего драйвера ODBC, мы используем базу данных, представляющую собой обычный текстовый файл. Поэтому выберите из списка Installed ODBC Drivers строку Microsoft Text Driver, представляющую текстовый драйвер ODBC. Нажмите кнопку OK.

Создание базы данных

Рис. 5.2. Диалоговая панель Add Data Source

Откроется диалоговая панель ODBC Text Setup (рис. 5.3). Эта панель позволяет выбрать базу данных, для дооступа к которой будет использоваться текстовый драйвер ODBC.

В поле Data Source Name введите имя базы данных, под которым она будет использоваться. В поле Description можно занести текстовое описание базы данных.

Так как мы знаем место расположения файла базы данных, убедитесь, что переключатель Use Current Directory выключен и нажмите кнопку Select Directory. На экране появится стандартная диалоговая панель для выбора файлов, но список файлов в ней будет заблокирован. Выберите из нее каталог, в котором записан файл базы данных TextBase.txt. В нашем примере этот файл расположен в каталоге TEXTBASE на диске E:.

Создание базы данных

Рис. 5.3. Диалоговая панель ODBC Text Setup

Нажмите кнопку OK. Стандартная диалоговая панель выбора файлов закроется. Теперь в поле Directory диалоговой панели ODBC Text Setup будет отображаться имя каталога нашей базы данных.

Как только текстовая база данных (точнее каталог с файлами этой базы) будет выбрана, вы сможете воспользоваться кнопкой Options, расположенной в правом нижнем углу диаалоговой панели ODBC Text Setup. Когда вы нажмете на эту кнопку, внешний вид диалоговой панели ODBC Text Setup изменится (рис. 5.4). В нижней части панели появится новая группа органов управления, которая имеет название Files.

Создание базы данных

Рис. 5.4. Расширенный вариант панели ODBC Text Setup

В группе Extension List вы должны указать расширения файлов, которые входят в базу данных. Вы можете ввести расширение *.txt или использовать маску *.*. Так как в нашем примере каталог TEXTBASE содержит единственный файл TextBase.txt, то это не имеет значения. Заметим, что маска *.* используется по умолчанию, когда переключатель Default (*.*) установлен.



Теперь надо определить формат таблииц, входящих в базу данных. Нажмите кнопку Define Format. На экране откроется диалоговая панель Define Text Format (рис. 5.5).

Создание базы данных

Рис. 5.5. Диалоговая панель Define Text Format

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

Выберите из списка Tables имя файла TextBase.txt. Теперь надо определить формат этого файла.

Из списка Format выберите строку, соответствующую типу разделителей, которыми вы отделяете отдельные поля таблицы. Мы использовали в нашем примере разделитель ;. Поэтому выберите из списка Format строку Custom Delimited и введите в поле Delimiter символ ;.

В зависимости от того, какой набор символов используется в вашей таблице, установите переключатель Characters в положение ANSI или OEM. Мы заполнили файл TextBase.txt в формате ANSI, поэтому переведите переключатель в соответствующее положение.

В поле Rows to Scan определите количество строк таблицы, которые будут проверяться при выборе формата. Оставьте это значение без изменения.

Если первая строка файла содержит названия полей соответствующей таблицы, установите переключатель Column Name Header. Обратите внимание на листинг 5.1 файла TextBase.txt. Первая строка этого файла как раз содержит названия полей таблицы. Поэтому переключатель Column Name Header надо установить.

А теперь нажмите кнопку Guess в группе Columns и, ...о чудо: программа установки сама определит формат полей таблицы. Названия этих полей, взятые из первой строки файла TextBase.txt появятся в списке группы Columns. Последовательно выбитите из этого списка названия всех полей таблицы. В полях Data Type, Name и Width будут отображаться тип, имя и максимальная ширина выбранного поля. В случае необходимости вы можете изменить эти значения.

Поля NAME, ADDRESS и PHONE будут определены как символьные строки, имеющие максимальную длинну 255 символов. Поле PRIORITY будет определено как число Integer.



Если первая строка файла таблицы не содержит имена полей, то переключатель Column Name Header должен быть выключен. Нажмите кнопку Guess в группе Columns. Программа установки определит формат полей таблицы и присвоит им имена F1, F2, F3 и т. д. В последствии вы можете изменить названия полей, изменив их в поле Name и нажав кнопку Modify.

После того, как формат файла определен, вы можете закрыть все диалоговые панели приложения 32bit ODBC. Обратите внимание, что в диалоговой панели Data Source появится еще один источник данных - Address Pad (Microsoft Text Driver (*.txt; *.csv)).

В каталоге TEXTBASE, содержащем файл базы данных TextBase.txt появится еще один файл - schema.ini. Этот файл содержит информацию о таблицах (в нашем случае об единственной таблице) базы данных. В принципе, вы можете изменять характеристики источника данных Address Pad через этот файл, но лучше использовать приложение 32bit ODBC из Control Panel. Для этого запустите приложение 32bit ODBC, выберите из списка имя источника и нажмие кнопку Setup. Откроется панель ODBC Text Setup, через которую можно полностью управлять всеми параметрами базы данных.

Листинг 5.2. Файл schema.ini

[textbase.txt]

ColNameHeader=True

Format=Delimited(;)

MaxScanRows=25

CharacterSet=ANSI

Col1=NAME Char Width 255

Col2=ADDRESS Char Width 255

Col3=PRIORITY Integer

Col4=PHONE Char Width 255


Создание нового класса документа и класса окна просмотра


Создайте два новых класса - класс документа и класс окна отображения для хранения и отображения на экране текстовой информации. Для создания новых классов используйте MFC ClassWizard.
Сначала откройте панель ClassWizard и нажмите кнопку Add Class. Откроется новая диалоговая панель Create New Class. Введите в поле Name имя нового класса - CEditorDoc, а в поле Base Class выберите имя базового класса CDocument. Нажмите кнопку Create. ClassWizard создаст класс CEditorDoc, определение которого он разместит в файле CEditorDoc.h, а реализацию методов класса в файле CEditorDoc.cpp.
Не закрывая ClassWizard, создайте класс окна просмотра текстового документа. Нажмите кнопку Add Class. В поле Name диалоговой панели Create New Class введите имя класса окна просмотра CEditorView, а в поле Base Class выберите имя его базового класса CEditView. Нажмите кнопку Create. ClassWizard создаст класс CEditorView, определение которого он разместит в файле CEditorView.h, а реализацию методов класса в файле CEditorView.cpp.
Теперь вы можете определить, как записывать и считывать текстовый документ из файла на диске. Когда ClassWizard создает для вас класс документа, наследованный от базового класса CDocument, он сразу создает шаблон метода Serialize. К сожалению, этот шаблон придется переделать.
Мы уже изучали класс CEditView в предыдущей книге, посвященной MFC, и вы должны знать, что объекты этого класса сами хранят данные редактируемого документа. Поэтому для записи и чтения документа метод Serialize класса документа должен вызвать соответствующий метод класса окна просмотра. Измените метод Serialize следующим образом:
//////////////////////////////////////////////////////////////
// Метод класса Serialize CEditorDoc
void CEditorDoc::Serialize(CArchive& ar)
{
   ((CEditView*)m_viewList.GetHead())->SerializeRaw(ar);
}
На этом реализация классов для хранения и отображения текстового документа закончена и можно перейти к самому интересному - к созданию шаблона нового документа.


Создание новой панели управления


Выберите из меню Insert строку Resource, а затем из открывшейся диалоговой панели Insert Resource выберите строку Toolbar и нажмите на кнопку OK.
Запустится редактор ресурсов и в нем будет загружена новая панель управления. Создайте панель управления, постепенно добавляя к ней по одной кнопке. Для каждой кнопки нарисуйте ее изображение, а также присвойте ей уникальный идентификатор и строку описания (см. раздел “Редактор панели управления”).
При создании панелей управления руководствуйтесь информацией из файла ресурсов приложения Bar (листинг 3.5) и изображениями кнопок панелей управления (рис. 3.9).
Панель управления приложения Bar должна иметь идентификатор IDR_MAINFRAME. В ней следует расположить девять кнопок с идентификаторами ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_SAVE, ID_EDIT_CUT, ID_EDIT_COPY, ID_EDIT_PASTE, ID_FILE_PRINT, ID_APP_ABOUT и ID_TOOL_EXIT.
Между кнопками ID_FILE_SAVE и ID_EDIT_CUT, ID_EDIT_PASTE и ID_FILE_PRINT, вставьте разделители. Для каждой из кнопок вы также можете ввести их текстовые описания.
Сохраните измененный файл ресурсов.


Создание шаблона текстовых документов


Шаблоны документов, с которыми работает приложение определяют все характеристики данного типа документа. Они включают информацию о классе документа, классе окна просмотра, классе окна рамки, а также о ресурсах, связанных с данным типом документов.
Шаблоны документов создаются объектом приложения во время его инициализации. Откройте метод InitInstance главного класса приложения CMultiApp. В нем создается только один объект класса CMultiDocTemplate, который представляет графический документ и средства для работы с ним.
Так как наше приложение должно работать не только с графическими, но и с текстовыми документами, вы должны создать еще один объект шаблона документов, представляющий текстовый документ. Класс текстового документа называется CEditorDoc, класс окна для его просмотра CEditorView. В качестве окна рамки мы используем класс CChildFrame. Ниже представлен соответствующий фрагмент метода InitInstance:
//////////////////////////////////////////////////////////////
// CMultiApp initialization
BOOL CMultiApp::InitInstance()
{
   // ...
   // Регистрируем шаблоны документов приложения
   CMultiDocTemplate* pDocTemplate;
   pDocTemplate = new CMultiDocTemplate(
      IDR_MULTITYPE,
      RUNTIME_CLASS(CMultiDoc),
      RUNTIME_CLASS(CChildFrame),
      RUNTIME_CLASS(CMultiView));
   AddDocTemplate(pDocTemplate);
   pDocTemplate = new CMultiDocTemplate(
      IDR_EDITORTYPE,
      RUNTIME_CLASS(CEditorDoc),
      RUNTIME_CLASS(CChildFrame),
      RUNTIME_CLASS(CEditorView));
   AddDocTemplate(pDocTemplate);
   // ...
   return TRUE;
}
При создании шаблона документов указывается идентификатор, который определяет меню, пиктограмму и некоторую другую полезную информацию, связанную с документами данного типа. Мы указали для шаблона текстового документа идентификатор IDR_EDITORTYPE. Здесь мы несколько забежали вперед, так как такой идентификатор еще не определен. Мы вернемся к ресурсам текстового документа в следующем разделе.
Чтобы созданный шаблон текстовых документов добавить к списку шаблонов документов приложения, надо вызвать метод AddDocTemplate, указав ему адрес объекта шаблона.


Стандартная библиотека языка Си++


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


Стандартные диалоговые панели


В 24 томе серии “Библиотека системного программиста”, посвященном библиотеке MFC, вы уже сталкивались со стандартными диалоговыми панелями, предназначенными для открытия файлов документов и сохранения измененных документов на диске. Эти диалоговые панели используются всеми приложениями, построенными с использованием средств MFC AppWizard и имеющими оконный пользовательский интерфейс.
Исходный текст, полученный с помощью MFC AppWizard, не содержит прямых обращений к классам стандартных диалоговых панелей. Процедуры создания этих панелей, и отображения их на экране скрыты в других классах библиотеки MFC. В этом разделе мы выведем классы управления стандартными диалоговыми панелями на чистую воду и расскажем, как вы можете их использовать.
В состав библиотеки классов MFC входят ряд классов, представляющих стандартные диалоговые панели. Эти классы позволяют легко реализовать такие часто используемые операции, как открытие и сохранение файла, выбор цвета, выбор шрифта и т. д. Схема наследования классов стандартных диалоговых панелей представлена на рисунке 4.1.

Класс
Описание
CColorDialog
Панель для выбора цвета
CFileDialog
Панель выбора файлов для открытия и сохранения на диске
CFindReplaceDialog
Панель для выполнения операции поиска и замены
CFontDialog
Панель для выбора шрифта
CPrintDialog
Панель для вывода документа на печать
CPageSetupDialog
Панель выбора формата документа

Кроме перечисленных в таблице, существует класс COleDialog, который определяет стандартные панели диалогов, предназначенные для управления технологией OLE. В этой книге мы не рассматриваем программирование OLE и поэтому не будем останавливаться на классе COleDialog.
Стандартные диалоговые панели
Рис. 4.1. Классы стандартных диалоговых панелей
Классы, управляющие стандартными диалоговыми панелями, определены в файле afxdlgs.h. Если вы используете в своем приложении эти классы, вы должны включить файл afxdlgs.h в исходные тексты при помощи директивы препроцессора #include:
// Включаемый файл для стандартных диалоговых панелей
#include
Класс COleDialog и другие классы, которые используются для управления стандартными диалоговыми панелями, связанными с технологией OLE, определены в другом файле - afxodlgs.h.


Таблица акселераторов


Таблица акселераторов IDR_MAINFRAME приложения полностью соответствует таблице акселераторов, добавленной MFC AppWizard к ресурсам приложения с однооконным интерфейсом:
//////////////////////////////////////////////////////////////
// Таблица акселераторов
IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE
BEGIN
   "N",            ID_FILE_NEW,            VIRTKEY,CONTROL
   "O",            ID_FILE_OPEN,           VIRTKEY,CONTROL
   "S",            ID_FILE_SAVE,           VIRTKEY,CONTROL
   "P",            ID_FILE_PRINT,          VIRTKEY,CONTROL
   "Z",            ID_EDIT_UNDO,           VIRTKEY,CONTROL
   "X",            ID_EDIT_CUT,            VIRTKEY,CONTROL
   "C",            ID_EDIT_COPY,           VIRTKEY,CONTROL
   "V",            ID_EDIT_PASTE,          VIRTKEY,CONTROL
   VK_BACK,        ID_EDIT_UNDO,           VIRTKEY,ALT
   VK_DELETE,      ID_EDIT_CUT,            VIRTKEY,SHIFT
   VK_INSERT,      ID_EDIT_COPY,           VIRTKEY,CONTROL
   VK_INSERT,      ID_EDIT_PASTE,          VIRTKEY,SHIFT
   VK_F6,          ID_NEXT_PANE,           VIRTKEY
   VK_F6,          ID_PREV_PANE,           VIRTKEY,SHIFT
END
Мы расскажем вам подробнее о таблице акселераторов в разделе “Таблица акселераторов”. Дополнительную информацию вы сможете получить в 13 томе серии “Библиотека системного программиста”.

Класс CMultiApp может получать сообщения


Класс CMultiApp может получать сообщения и имеет таблицу сообщений. Таблицу сообщений класса CMultiApp расположена в файле Multi.cpp. Она содержит четыре макрокоманды для обработки командных сообщений от меню приложения:
//////////////////////////////////////////////////////////////
// Таблица сообщений класса CMultiApp
BEGIN_MESSAGE_MAP(CMultiApp, CWinApp)
   //{{AFX_MSG_MAP(CMultiApp)
   ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
   //}}AFX_MSG_MAP
   ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
   ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
   ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
Только для одного командного сообщения, имеющего идентификатор ID_APP_ABOUT, вызывается метод обработчик OnAppAbout, определенный в классе CMultiApp. Остальные три командных сообщения ID_FILE_NEW, ID_FILE_OPEN и ID_FILE_PRINT_SETUP передаются для обработки методам класса CWinApp, который является базовым классом для CMultiApp.
Метод-обработчик OnAppAbout вызывается объектом главного класса приложения, когда пользователь выбирает из меню Help строку About. OnAppAbout создает объект класса CAboutDlg, представляющий модальную диалоговую панель About и вызывает для него метод DoModal, отображающий панель на экране (рис. 1.9):
void CMultiApp::OnAppAbout()
{
   CAboutDlg aboutDlg;
   aboutDlg.DoModal();
}
Класс CMultiApp может получать сообщения
Рис. 1.9. Окно Project Workspace, класс CMainFrame

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


Таблица сообщений класса CChildFrame не содержит обработчиков сообщений:
// Объекты класса CChildFrame создаются динамически
IMPLEMENT_DYNCREATE(CChildFrame, CMDIChildWnd)
// Таблица сообщений
BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd)
   //{{AFX_MSG_MAP(CChildFrame)
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()


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


Таблица сообщений класса CDlgBarWindow обрабатывает сообщения от диалоговой панели управления. В ней также располагается макрокоманда ON_WM_CREATE, которая вызывает метод OnCreate во время создания окна:
ON_WM_CREATE()
Когда пользователь работает с диалоговой панелью управления, в ее родительское окно поступают сообщения. От кнопок Set и Clear и переключателя Alighn поступают командные сообщения, которые обрабатываются при помощи макрокоманд ON_COMMAND_EX:
// Командные сообщения от кнопок Set и Clear
ON_COMMAND_EX(IDC_BUTTON_SET, DlgBarCommand)
ON_COMMAND_EX(IDC_BUTTON_CLEAR, DlgBarCommand)
ON_COMMAND_EX(IDC_CHECK1, DlgBarCommand)
// Командные сообщения от переключателя Alighn
ON_COMMAND_EX(IDC_RADIO_LEFT, DlgBarCommand)
ON_COMMAND_EX(IDC_RADIO_CENTER, DlgBarCommand)
ON_COMMAND_EX(IDC_RADIO_RIGHT, DlgBarCommand)
Для обработки всех этих командных сообщений вызывается один и тот же метод DlgBarCommand, которому передается идентификатор кнопки или переключателя, вызвавшего сообщение.
От списка combo-box, расположенного в диалоговой панели управления, мы обрабатываем только одно сообщение с кодом извещения CBN_SELCHANGE. Это сообщение передается, когда пользователь выбирает из списка новую строку:
// Командные сообщения от списка combo-box
ON_CBN_SELCHANGE( IDC_COMBO_COLOUR, DlgBarCombo)


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


Таблица сообщений класса CMainFrame содержит только одну макрокоманду ON_WM_CREATE, которая устанавливает для обработки сообщения WM_CREATE метод OnCreate. Сообщения WM_CREATE приходит во время создания главного окна приложения.
Непосредственно перед таблицей сообщений класса CMainFrame располагается макрокоманда IMPLEMENT_DYNAMIC. Она указывает, что объекты класса CMainFrame могут создаваться динамически во время работы приложения:
// Объекты класса CMainFrame могут создаваться автоматически
IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd)
// Таблица сообщений
BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
   //{{AFX_MSG_MAP(CMainFrame)
   ON_WM_CREATE()
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

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


Таблица сообщений класса CMultiDoc не содержит ни одного обработчика сообщений:
// Объекты класса CMultiDoc могут создаваться динамически
IMPLEMENT_DYNCREATE(CMultiDoc, CDocument)
// Таблица сообщений класса CMultiDoc
BEGIN_MESSAGE_MAP(CMultiDoc, CDocument)
   //{{AFX_MSG_MAP(CMultiDoc)
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()


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


Таблица сообщений класса CMultiMenuWindow обрабатывает командные сообщения и команды обновления от меню приложения, а также содержит макрокоманду ON_WM_CREATE.
Макрокоманда ON_WM_CREATE вызывает метод OnCreate во время создания окна:
ON_WM_CREATE()
Для обработки командных сообщений от меню приложения в таблицу сообщений класса CMultiMenuWindow включены шесть макрокоманд ON_COMMAND. Они вызывают обработчики OnDisable, OnConstruct, OnCommand, OnFullMenu, OnRestrictMenu и OnMenuExit:
ON_COMMAND(ID_MENU_DISABLE, OnDisable)
ON_COMMAND(ID_MISSION_CONSTRUCT, OnConstruct)
ON_COMMAND(ID_FILE_EXIT, OnExit)
ON_COMMAND(ID_MISSION_PROCESS, OnCommand)
ON_COMMAND(ID_MENU_RESTRICT, OnRestrictMenu)
ON_COMMAND(ID_MENU_FULL, OnFullMenu)
Для обработки команд обновления в таблицу сообщений класса включены три макрокоманды ON_UPDATE_COMMAND_UI. Они вызывают методы OnUpdateProcess, OnUpdateConstruct и OnUpdateDisable:
ON_UPDATE_COMMAND_UI(ID_MISSION_PROCESS, OnUpdateProcess)
ON_UPDATE_COMMAND_UI(ID_MISSION_CONSTRUCT, OnUpdateConstruct)
ON_UPDATE_COMMAND_UI(ID_MENU_DISABLE, OnUpdateDisable)
Соответствие методов, вызываемых макрокомандами ON_COMMAND и ON_UPDATE_COMMAND_UI, строкам меню приложения вы можете посмотреть в файле ресурсов приложения, представленном в листинге 3.3.


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


Таблица сообщений класса CMultiView располагается в файле MultiView.cpp. Непосредственно перед ней расположена макрокоманда IMPLEMENT_DYNCREATE:
// Объекты класса CMultiView создаются динамически
IMPLEMENT_DYNCREATE(CMultiView, CView)
// Таблица сообщений класса CMultiView
BEGIN_MESSAGE_MAP(CMultiView, CView)
   //{{AFX_MSG_MAP(CMultiView)
   //}}AFX_MSG_MAP
   ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
   ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
   ON_COMMAND(ID_FILE_PRINT_PREVIEW,
                             CView::OnFilePrintPreview)
END_MESSAGE_MAP()

При добавлении контекстного меню к окну класса CMultiView, в таблицу сообщений класса CMultiView добавляется новая макрокоманда ON_WM_CONTEXTMENU:
//////////////////////////////////////////////////////////////
// Таблица сообщений класса CMultiView
// Объекты класса CMultiView создаются динамически
IMPLEMENT_DYNCREATE(CMultiView, CView)
// Таблица сообщений класса CMultiView. В нее добавлена
// макрокоманда ON_WM_CONTEXTMENU
BEGIN_MESSAGE_MAP(CMultiView, CView)
   ON_WM_CONTEXTMENU()
   //{{AFX_MSG_MAP(CMultiView)
   //}}AFX_MSG_MAP
   // Стандартные команды
   ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
   ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
   ON_COMMAND(ID_FILE_PRINT_PREVIEW,CView::OnFilePrintPreview)
END_MESSAGE_MAP()

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


Таблица сообщений класса CSplashWnd содержит всего три макрокоманды, которые расположены внутри блока //{{AFX_MSG_MAP и поэтому управляются через ClassWizard:
BEGIN_MESSAGE_MAP(CSplashWnd, CWnd)
   //{{AFX_MSG_MAP(CSplashWnd)
   ON_WM_CREATE()
   ON_WM_PAINT()
   ON_WM_TIMER()
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()
Две макрокоманды таблицы сообщений класса CSplashWnd уже давно вам знакомы. Это макрокоманда ON_WM_CREATE, которая вызывает метод OnCreate во время создания окна заставки (обработка сообщения WM_CREATE), и макрокоманда ON_WM_PAINT, которая вызывает метод OnPaint, когда надо перерисовать окно (обработка сообщения WM_PAINT).
Таблицы сообщений класса CSplashWnd содержит также ранее не описанную нами макрокоманду ON_WM_TIMER. Эта макрокоманда предназначена для обработки сообщений WM_TIMER от таймера и вызывает метод OnTimer.
Первоначально метод OnTimer определен в классе CWnd следующим образом. При вызове метода OnTimer для обработки сообщения WM_TIMER параметр nIDEvent содержит идентификатор таймера, вызвавшего это сообщение:
afx_msg void OnTimer(UINT  nIDEvent);
Для установки (“запуска”) таймера используется метод SetTimer, также входящий в состав класса CWnd. Мы расскажем об этом методе более подробно, когда будем описывать метод OnCreate класса CSplashWnd, так как именно он устанавливает таймер для отображения заставки.


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


Таблица сообщений класса CStateWindow обрабатывает командные сообщения от меню Work, а также содержит макрокоманды ON_UPDATE_COMMAND_UI и ON_WM_CREATE.
Макрокоманда ON_UPDATE_COMMAND_UI вызывает метод OnUpdateTEXT для обновления состояния индикатора ID_INDICATOR_TEXT панели состояния:
ON_UPDATE_COMMAND_UI(ID_INDICATOR_TEXT, OnUpdateTEXT)
Макрокоманда ON_WM_CREATE вызывает метод OnCreate во время создания окна:
ON_WM_CREATE()
Для обработки командных сообщений от меню Work в таблицу сообщений класса CStateWindow включены несколько макрокоманд ON_COMMAND и ON_COMMAND_EX. Они вызывают обработчики OnMenuProcessBar, OnMenuDisableADD_SUB, OnMenuSwitchTEXT, OnMenuDirectADD_SUB и OnMenuExit:
ON_COMMAND(ID_WORK_PROCESS, OnMenuProcessBar)
ON_COMMAND(ID_WORK_DISABLE_ADDSUB, OnMenuDisableADD_SUB)
ON_COMMAND(ID_WORK_ON_SWITCH_TEXT, OnMenuSwitchTEXT)
ON_COMMAND(ID_WORK_EXIT, OnMenuExit)
ON_COMMAND_EX(ID_WORK_DIRECT_ADD, OnMenuDirectADD_SUB)
ON_COMMAND_EX(ID_WORK_DIRECT_SUB, OnMenuDirectADD_SUB)


Ресурсы приложения содержат несколько блоков,


Ресурсы приложения содержат несколько блоков, описывающих таблицы текстовых строк. Эти таблицы практически совпадают с таблицами текстовых строк, включенных MFC AppWizard в ресурсы приложения с однооконным интерфейсом.
Блоки текстовых строк, описывающие тип документов приложения, и основные характеристики главного окна приложения совпадают с соответствующими блоками однооконного приложения, за исключением строки с названием проекта:
//////////////////////////////////////////////////////////////
// Таблица текстовых строк
STRINGTABLE PRELOAD DISCARDABLE
BEGIN
   IDR_MAINFRAME   "Multi"
   IDR_MULTITYPE   "\nMulti\nMulti\n\n\nMulti.Document\nMulti
                    Document"
END
STRINGTABLE PRELOAD DISCARDABLE
BEGIN
   AFX_IDS_APP_TITLE     "Multi"
   AFX_IDS_IDLEMESSAGE   "Ready"
END
Блок текстовых строк, которые используются в панели состояния ststus bar, полностью совпадает с аналогичным блоком в ресурсах однооконного приложения:
STRINGTABLE DISCARDABLE
BEGIN
   ID_INDICATOR_EXT    "EXT"
   ID_INDICATOR_CAPS   "CAP"
   ID_INDICATOR_NUM    "NUM"
   ID_INDICATOR_SCRL   "SCRL"
   ID_INDICATOR_OVR    "OVR"
   ID_INDICATOR_REC    "REC"
END
В блоке текстовых строк, описывающих элементы меню, добавлен ряд текстовых строк, которые относятся к меню Window. Для однооконного приложения эти строки не определены, так как меню Window есть только у многооконных приложений:
STRINGTABLE DISCARDABLE
BEGIN
 ID_FILE_NEW         "Create a new document\nNew"
 ID_FILE_OPEN        "Open an existing document\nOpen"
 ...
 ID_PREV_PANE        "Switch back to the previous window
                      pane\nPrevious Pane"
 ID_WINDOW_NEW       "Open another window for the active
                      document\nNew Window"
 ID_WINDOW_ARRANGE   "Arrange icons at the bottom of the
                      window\nArrange Icons"


 ID_WINDOW_CASCADE   "Arrange windows so they overlap\nCascade
                      Windows"
 ID_WINDOW_TILE_HORZ "Arrange windows as non-overlapping
                      tiles\nTile Windows"
 ID_WINDOW_TILE_VERT "Arrange windows as non-overlapping
                      tiles\nTile Windows"
 ID_WINDOW_SPLIT     " Split the active window into panes\nSplit"
 ...
 ID_EDIT_CLEAR       "Erase the selection\nErase"
 ID_VIEW_TOOLBAR     "Show or hide the toolbar\nToggle ToolBar"
 ID_VIEW_STATUS_BAR  "Show or hide the status bar\nToggle StatusBar"
END
По сравнению с ресурсами однооконного приложения, для приложений с многооконным пользовательским интерфейсом, добавлен еще один блок текстовых строк. В нем содержатся строки, имеющие отношение к многооконному интерфейсу приложения:
STRINGTABLE DISCARDABLE
BEGIN
 AFX_IDS_SCSIZE        "Change the window size"
 AFX_IDS_SCMOVE        "Change the window position"
 AFX_IDS_SCMINIMIZE    "Reduce the window to an icon"
 AFX_IDS_SCMAXIMIZE    "Enlarge the window to full size"
 AFX_IDS_SCNEXTWINDOW  "Switch to the next document
                        window"
 AFX_IDS_SCPREVWINDOW  "Switch to the previous document
                        window"
 AFX_IDS_SCCLOSE       "Close the active window and
                        prompts to save the documents"
 AFX_IDS_SCRESTORE     "Restore the window to normal size"
 AFX_IDS_SCTASKLIST    "Activate Task List"
 AFX_IDS_MDICHILD      "Activate this window"
 AFX_IDS_PREVIEW_CLOSE "Close print preview mode\nCancel
                        Preview"
END

Управление компонентами


Сразу после установки среды Microsoft Visual C++ в диалоговой панели Component Gallery отображается две станицы, содержащие компоненты - Microsoft и OLE Controls. Каждая страница представляет разные категории компонентов.
В диалоговой панели Component Gallery вы можете создавать и удалять страницы с компонентами, добавлять и удалять компоненты, перемещать их между категориями, переименовывать и выполнять над ними другие простые действия.
Для управления страницами компонентов и отдельными компонентами достаточно нажать кнопку Customize. Вы сможете выполнить все настройки в открывшейся диалоговой панели Customize Component Gallery.
Вся информация о компонентах и страницах Component Gallery записана в специальной базе данных. Эта база данных расположена в файле gallery.dat, в каталоге Msdev\Template. Если вы случайно удалили нужные компоненты с панели Component Gallery, вы можете восстановить их. Для этого надо удалить файл gallery.dat. Во время очередного запуска Microsoft Visual C++ заново создаст файл gallery.dat и восстановит все компоненты, поставляемые с Visual C++.


Версия приложения


Приложение Multi включает ресурс, описывающий версию приложения.  В этом ресурсе содержится информация о приложении и ее версии, данные о фирме-разработчике, авторские права:
//////////////////////////////////////////////////////////////
// Версия
VS_VERSION_INFO     VERSIONINFO
 FILEVERSION       1,0,0,1
 RODUCTVERSION    1,0,0,1
 ILEFLAGSMASK 0x3fL
#ifdef _DEBUG
   FILEFLAGS 0x1L
#else
   FILEFLAGS 0x0L
#endif
   FILEOS 0x4L
   FILETYPE 0x1L
   FILESUBTYPE 0x0L
BEGIN
   BLOCK "StringFileInfo"
   BEGIN
      BLOCK "040904B0"
      BEGIN
         VALUE "CompanyName", "Solaris\0"
         VALUE "FileDescription", "MULTI MFC Application\0"
         VALUE "FileVersion", "1, 0, 0, 1\0"
         VALUE "InternalName", "MULTI\0"
         VALUE "LegalCopyright",
               "Copyright © 1996 Frolov G.V.\0"
         VALUE "OriginalFilename", "MULTI.EXE\0"
         VALUE "ProductName", "MULTI Application\0"
         VALUE "ProductVersion", "1, 0, 0, 1\0"
      END
   END
   BLOCK "VarFileInfo"
   BEGIN
      VALUE "Translation", 0x409, 1200
   END
END


Виртуальные методы класса CFileDialog


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

Метод
Описание
OnShareViolation
Вызывается в случае нарушения прав доступа к файлу
OnFileNameOK
Вызывается для проверки имени файла, введенного в диалоговой панели
OnLBSelChangedNotify
Вызывается при изменении выделения в списке файлов диалоговой панели
OnInitDone
Вызывается для обработки сообщения WM_NOTIFY CDN_INITDONE
OnFileNameChange
Вызывается для обработки сообщения WM_NOTIFY CDN_SELCHANGE
OnFolderChange
Вызывается для обработки сообщения WM_NOTIFY CDN_FOLDERCHANGE
OnTypeChange
Вызывается для обработки сообщения WM_NOTIFY CDN_TYPECHANGE

Прототипы перечисленных выше виртуальных методов вы можете найти в справочной системе Microsoft Visual C++.


Visual C++ и Visual J++


Летом 1996 года Microsoft выпустила тестовую версию нового продукта, получившего название Visual J++. Его название перекликается с названием Visual C++, и можно подумать, что “Жи++” стал приемником Си++. Спешим вас успокоить, Жи++ не предназначен для замены Си++. Это совершенно другой язык программирования, основанный на языке Java, лицензию на который Microsoft приобрела у известной фирмы Sun Microsystems.
Предусмотрена интеграция Visual J++ в уже знакомую вам среду разработчика Microsoft Developer Studio. Более того, можно организовать взаимодействие Visual C++ и Visual J++.


Win32s посвящается


Microsoft Visual C++ версии 2.0 и все старшие версии этого продукта не позволяют разрабатывать 16-разрядные приложения для операционных систем Windows 3.хх.
Начиная с версии 4.2, приложения, подготовленные в Microsoft Visual C++ и MFC 4.2, также не будут работать в среде Windows 3.хх с установленной поддержкой 32-разрядных приложений Win32s.
Если вам все же необходимо создать приложение, способное работать в среде Win32s, следует воспользоваться более ранними версиями MFC, например MFC 4.1. К сожалению, в этом случае, последние новшества, введенные в библиотеку MFC 4.2, уже не будут вам доступны.


Заставка для приложения


Многие приложения Windows при запуске отображают на экране временную заставку. В ней, как правило, на фоне рисунка представлены название приложения, номер версии, другая справочная информация. Примером таких приложений могут служить Microsoft Word, Microsoft Excel и другие офисные приложения Microsoft. Microsoft Visual C++ также отображает заставку во время запуска.
С помощью Component Gallry и компонента Splash Screen вы затратите всего нескольких минут, чтобы добавить к приложению заставку.
Откройте проект приложения к которому надо добавить заставку. Выберите на панели Component Gallry из страницы Microsoft, пиктограмму Splash Screen. Затем нажмите кнопку Insert.
На экране появится диалоговая панель Splash Screen (рис. 2.2). В ней вы должны указать ряд характеристик. В поле Class Name отображается имя класса CSplashWnd, который будет отвечать за отображение заставки. Вы можете изменить это имя по своему усмотрению или оставить его без изменения. Во втором поле диалоговой панели - Bitmap resource ID отображается идентификатор изображения bitmap, который будет служить заставкой для приложения. По умолчанию в качестве идентификатора предлагается использовать изображение bitmap с идентификатором IDB_SPLASH.
Заставка для приложения
Рис. 2.2. Диалоговая панель Splash Screen
Компонент Splash Screen добавит к вашему приложению заготовку изображения bitmap с идентификатором IDB_SPLASH. Поэтому вам останется только отредактировать ее на свой вкус. Мы привели уже отредактированное нами изображение IDB_SPLASH на рисунке 2.3.
Заставка для приложения
Рис. 2.3. Изображение IDB_SPLASH
Определение класса CSplashWnd, управляющего окном заставки, и его методы записываются в файлы с именами Splash.cpp и Splash.h. Эти файлы будут размещены в каталоге вместе с остальными исходными файлами приложения. Если вам надо изменить названия данных файлов, нажмите на кнопку Change.
Когда вы заполните диалоговую панель Splash Screen, нажмите на кнопку OK. Начнется процесс встраивания компонента в проект. После его завершения закройте диалоговую панель Component Gallry, для чего нажмите на кнопку Close.
Вы можете сразу встроить в проект несколько различных компонент. Однако, на наш взгляд, после встраивания очередного компонента стоит, как минимум, перестроить проект и убедиться в его работоспособности.


Немного о C++


Несмотря на все многообразие средств, предоставляемых Си++, совершенно необязательно использовать их все сразу. Первым шагом при переходе от Си к Си++ может стать изменение расширений имен исходных файлов ваших программ. Вместо традиционного расширения C в языке Си++ принято использовать расширение CPP. Теперь ваша программа будет транслироваться, как программа, написанная на языке Си++.
Затем вы можете использовать все новые и новые особенности Си++, постепенно отходя от стандартного Си к Си++. На момент написания книги окончательный стандарт Си++ еще не был разработан. Компиляторы различных фирм, например Microsoft и Borland имеют различия в реализации Си++. Наша книга ориентирована в первую очередь на компиляторы Microsoft Visual C++ версий 1.5, 2.0, 4.0 и 4.1.
Конечно, в одной главе мы не можем рассказать обо всех возможностях языка Си++. Поэтому мы изучим только основные особенности языка Си++. Более подробную информацию вы можете получить из справочников или учебников по языку Си++.


Абстрактные классы


Виртуальные методы могут быть объявлены как чисто виртуальные. Для этого после описания метода указывается специальный спецификатор (= 0). Он означает, что описанные методы не определены.
Класс в котором определен хотя бы один чисто виртуальный метод называется абстрактным. Нельзя создавать объекты абстрактного класса. Абстрактный класс может использоваться только в качестве базового класса для построения других классов.
Класс, порожденный от абстрактного класса, должен переопределять описанные в нем чисто виртуальные методы. В противном случае этот класс также будет абстрактным.
В качестве примера абстрактного класса мы приведем класс Abstract, в котором описан чисто виртуальный метод PureFunc. Обратите внимание, что этот метод не определен в классе Abstract. Определение метода содержится только в порожденном классе Fact.
// Абстрактный класс Abstract
class      Abstract
{
public:
      // Чисто виртуальный метод, не имеет определения
      virtual int    PureFunc(void) = 0;
      void      SetValue(int i) {iValue = i;}
      int  iValue;
};
// Класс Fact
class      Fact : public Abstract
{
      int  PureFunc(void) {return iValue * iValue;}
};


Архивный класс (класс CArchive)


Класс CArchive используется для сохранения и восстановления состояния объектов в файлах на диске. Перед использованием объекта класса CArchive он должен быть привязан к файлу - объекту класса CFile.
Более подробно о процессе сохранения и восстановления объектов вы можете прочитать в разделе “Сохранение и восстановление объектов”. Пример использования класса CArchive для записи и восстановления документов в файлах представлен в разделе “Простейший графический редактор” главы “Однооконный интерфейс”.


Базы данных (классы для работы с базами данных)


В MFC включены несколько классов, обеспечивающую поддержку приложений, работающих с базами данных. В первую очередь это классы ориентированные на работу с ODBC драйверами - CDatabase и CRecordSet. Поддерживаются также новые средства для работы с базами данных DAO (Data Access Object). Для этого предназначены классы CDaoDatabase, CDaoRecordSet, . CDaoQueryDef, CDaoTableDef, CDaoWorkspace и CLongBinary.
Для работы с базами данных также предназначены классы CFieldExchange и CDaoFieldExchange. Это самостоятельные классы, они не наследуются от базового класса CObject.
Классы CFieldExchange и CDaoFieldExchange работают с процедурами обмена данными RFX (Record Field Exchange) для классов управляющих базами данных.


Блокировка


В состав класса CFile включены методы LockRange и UnlockRange, позволяющие заблокировать один или несколько фрагментов данных файла для доступа других процессов. Если приложение пытается повторно блокировать данные, уже заблокированные раньше этим или другим приложением, вызывается исключение. Блокировка представляет собой один из механизмов, позволяющих нескольким приложениям или процессам одновременно работать с одним файлом, не мешая друг другу.
Установить блокировку можно с помощью метода LockRange:
virtual void LockRange(DWORD dwPos, DWORD dwCount);

      throw(CFileException);
Параметр dwPos указывает на начало фрагмента данных внутри файла, который надо заблокировать. Параметр dwCount определяет количество байт для блокировки. В одном файле можно установить несколько блокировок на различные, не перекрывающиеся фрагменты данных.
Чтобы снять установленные блокировки, надо воспользоваться методом UnlockRange. Если в одном файле установлено несколько блокировок, то каждая из них должна сниматься отдельным вызовом метода UnlockRange:
virtual void UnlockRange(DWORD dwPos, DWORD dwCount);

      throw(CFileException);
Как и для метода LockRange, параметры dwPos и dwCount должны указывать начало фрагмента и его размер. Размер фрагмента должен соответствовать размеру фрагмента, указанному при вызове метода LockRange.


Чтение и запись файлов


Для доступа к файлам предназначены пять различных методов класса CFile: Read, ReadHuge, Write, WriteHuge, Flush. Методы Read и ReadHuge предназначены для чтения данных из предварительно открытого файла. В 16-разрядной операционной системе Windows функция Read может считать не больше, чем 65535 байт. На метод ReadHuge такие ограничения не накладываются. В 32-разрядных операционных системах оба метода могут одновременно считать из файла больше, чем 65535 байт.
virtual UINT Read(void* lpBuf, UINT nCount);

      throw(CFileException);
Данные, прочитанные из файла, записываются в буфер lpBuf. Параметр nCount определяет количество байт, которое надо считать из файла. Фактически из файла может быть считано меньше байт, чем запрошено параметром nCount. Это происходит, если во время чтения достигнут конец файла. Метод Read возвращает количество байт, прочитанных из файла:
DWORD ReadHuge(void* lpBuffer, DWORD dwCount);

      throw(CFileException);
Назначение параметров метода ReadHuge аналогично назначению параметров метода Read. Спецификация ReadHuge считается устаревшей и оставлена только для обратной совместимости с 16-разряднымми операционными системами.
Для записи в файл предназначены методы Write и WriteHuge. Метод WriteHuge не накладывает ограничения на количество одновременно записываемых байт. В 16-разрядных версиях операционной системы Windows метод Write позволяет записать не больше 65535 байт. Это ограничение снято в операционных системах Windows NT и Windows 95.
virtual void Write(const void* lpBuf, UINT nCount);

      throw(CFileException);
Метод Write записывает в открытый файл nCount байт из буфера lpBuf. В случае возникновения ошибки записи, например, переполнения диска, метод Write вызывает исключение.
Не смотря на то, что спецификация WriteHuge считается устаревшей, вы можете использовать ее для создания 16-разрядных приложений в среде Visual C++ версии 1.5х.


Чтение из архивного файла


В предыдущем разделе мы рассказали как сохранить объект вашего класса в архивном файле. Теперь опишем, как восстановить записанное ранее состояние объекта класса из архивного файла. Когда приложение желает восстановить состояние объекта данного класса, оно вызывает для него метод Serialize. В качестве параметра этому методу передается указатель на объект класса CArchive, связанного с файлом, открытым для чтения.
Реализация метода Serialize должна восстановить из файла все элементы данных, которые были в него записаны. Для этого можно воспользоваться оператором >> или методами ReadString и Read, определенными в классе CArchive.
Вы должны считывать данные из архивного файла именно в том порядке, в котором они были в него записаны.
Оператор >> можно использовать для чтения из архивного файла переменных простых типов, например long, int, char и объектов других классов, которые наследованы от класса CObject. В одной строке программы можно использовать оператор >> несколько раз.
Для чтения из архивного файла массивов, записанных в него методом Write, надо использовать метод Read класса CArchive. Он позволяет прочитать из файла определенное количество байт и записать его в указанный буфер:
UINT Read(void* lpBuf, UINT nMax);

      throw(CFileException);
Параметр lpBuf определяет буфер памяти, в который будут записаны считанные из файла данные. Параметр nMax указывает максимальное количество байт, которое можно считать из архивного файла и записать в буфер lpBuf. Метод Read возвращает количество прочитанных байт.
Если требуется прочитать из архивного файла строку, записанную в него методом WriteString, воспользуйтесь методом ReadString. В состав класса CArchive входят два метода ReadString, которые предназначены для записи прочитанной из файла строки в объект класса CString или в строку Си.
Первый метод имеет более простой прототип. Единственный параметр rString определяет объект класса CString, в который будет записана полученная информация. Если метод ReadString успешно завершит чтение, он возвращает значение TRUE, в противном случае - значение FALSE:

BOOL ReadString(CString& rString);

Если вам надо записать прочитанную из архивного файла строку в массив символов, воспользуйтесь другим прототипом метода ReadString:

LPTSTR ReadString(LPTSTR lpsz, UINT nMax);

      throw(CArchiveException);

Параметр lpsz должен указывать на буфер памяти, в который будет записана информация. Параметр nMax определяет максимальное количество символов, которое метод может прочитать из файла. Размер буфера lpsz должен быть как минимум на единицу больше, чем значение nMax (в конец строки lpsz будет записан символ \0).

Когда метод Serialize завершит работу по восстановлению или записи объекта из архивного файла, вы должны закрыть используемый для этого объект класса CArchive. Для этого необходимо вызвать метод Close:

void Close();

      throw(CArchiveException, CFileException);

После вызова этого метода закройте файл, связанный с объектом CArchive, вызвав метод CFile::Close, и удалите сам объект класса CFile.


Диагностика


Класс CObject содержит методы AssertValid и Dump, которые могут помочь на этапе отладки приложения. Оба эти методы определены как виртуальные. Вы можете переопределить их в своем классе.


Диалоговая панель


Очень удобным средством для организации взаимодействия пользователя и приложения являются диалоговые панели. Более того, многие приложения могут успешно работать и без главного окна, взаимодействуя с пользователем только через диалоговые панели. Примером такого приложения может служить приложение Scandisk, входящее в состав операционной системы Windows 95.
Библиотека классов MFC содержит класс CDialog, специально предназначенный для управления диалоговыми панелями. Как мы рассказывали в предыдущих томах серии “Библиотека системного программиста”, посвященных программированию для операционных систем Windows и Windows 95, диалоговые панели бывают двух типов - модальные и немодальные.
После отображения модальных диалоговых панелей блокируется родительское окно приложения и все его дочерние окна. Пользователь не может продолжить работу с приложением, пока не закроет модальную диалоговую панель.
Немодальные диалоговые панели не блокируют работу остальных окон приложения. Поэтому, открыв такую панель вы можете продолжить работать с приложением - использовать меню, открывать другие дочерние окна и диалоговые панели.
Как ни странно, и модальные и немодальные диалоговые панели обслуживаются одним (общим) классом CDialog, наследованным от базового класса CWnd (рис. 2.27).
Диалоговая панель
Рис. 2.27. Класс CDialog
В библиотеке MFC версии 1.0 для немодальных диалоговых панелей был предназначен отдельный класс CModalDialog. Однако, начиная с MFC версии 2.0 он включен в класс CDialog. Для совместимости класс CModalDialog также оставлен, но он определен макрокомандой #define как CDialog (файл Afxwin.h):
#define CModalDialog CDialog
Как создать и отобразить на экране диалоговую панель? В первую очередь необходимо добавить в файл ресурсов приложения шаблон новой диалоговой панели и при помощи редактора ресурсов изменить его по своему усмотрению.
Следующим этапом создается класс для управления диалоговой панелью. Этот класс наследуется непосредственно от базового класса CDialog.
Каждая диалоговая панель обычно содержит несколько органов управления. Работая с диалоговой панелью, пользователь взаимодействует с этими органами управления - нажимает кнопки, вводит текст, выбирает элементы списков. В результате генерируются соответствующие сообщения, которые должны быть обработаны классом диалоговой панели.
Так как класс диалоговой панели обрабатывает сообщения, то он содержит таблицу сообщений и соответствующие методы обработчики сообщений.
Чтобы создать модальную диалоговую панель, сначала необходимо создать объект определенного вами класса диалоговой панели, а затем вызвать метод DoModal, определенный в классе CDialog.
Процедура создания немодальной диалоговой панели несколько другая. Для этого используется метод Create класса CDialog. Мы рассмотрим создание немодальных диалоговых панелей позже.

В ресурсах приложения определена только одна диалоговая панель с идентификатором IDD_ABOUTBOX. Она содержит краткую информацию о приложении и отображается на экране, когда пользователь выбирает из меню Help строку About Single.
//////////////////////////////////////////////////////////////
// Диалоговая панель
IDD_ABOUTBOX DIALOG DISCARDABLE  0, 0, 217, 55
CAPTION "About Single"
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
FONT 8, "MS Sans Serif"
BEGIN
      ICON          IDR_MAINFRAME,IDC_STATIC,11,17,20,20
      LTEXT             "Single Version 1.0",IDC_STATIC,40,10,119,8,
                         SS_NOPREFIX
      LTEXT             "Copyright \251 1996",IDC_STATIC,40,25,119,8
      DEFPUSHBUTTON          "OK",IDOK,178,7,32,14,WS_GROUP
END



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


Диалоговые панели приложения Dialog


Диалоговые панели имеют идентификаторы IDD_DIALOG_DIALOG и IDD_ABOUTBOX. Диалоговая панель IDD_DIALOG_DIALOG - это и есть главная диалоговая панель приложения. Она будет отображаться на экране монитора сразу после запуска приложения.
Просмотрите внешний вид диалоговой панели IDD_DIALOG_DIALOG в редакторе ресурсов. Для этого сделайте двойной щелчок по ее названию в окне Project Workspace. Изначально эта панель содержит только две кнопки OK и Cancel, а также короткую текстовую строку. Впоследствии вы можете изменять эту панель, добавляя к ней новые органы управления.
Вторая диалоговая панель IDD_ABOUTBOX содержит информацию о приложении - его название, авторские права, год разработки и пиктограмму. Эта панель будет отображаться на экране, когда пользователь выберет строку About из системного меню главной диалоговой панели приложения.
Вы можете изменить диалоговую панель IDD_ABOUTBOX по своему усмотрению. Так например, вы можете добавить к ней вашу фамилию и адрес электронной почты.
Ниже мы привели фрагменты из файла ресурсов приложения Dialog, в которых определяются шаблоны диалоговых панелей IDD_ABOUTBOX и IDD_DIALOG_DIALOG.
//////////////////////////////////////////////////////////////
// Шаблоны диалоговых панелей приложения
IDD_ABOUTBOX DIALOG DISCARDABLE  0, 0, 217, 55
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "About Dialog"
FONT 8, "MS Sans Serif"
BEGIN
      ICON          IDR_MAINFRAME,IDC_STATIC,11,17,20,20
      LTEXT      "Dialog Version 1.0",
                         IDC_STATIC,40,10,119,8,SS_NOPREFIX
      LTEXT             "Copyright © 1996",IDC_STATIC,40,25,119,8
      DEFPUSHBUTTON          "OK",IDOK,178,7,32,14,WS_GROUP
END
IDD_DIALOG_DIALOG DIALOGEX 0, 0, 185, 92
STYLE DS_MODALFRAME|WS_POPUP|WS_VISIBLE|WS_CAPTION|WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "Dialog"
FONT 8, "MS Sans Serif"
BEGIN
      PUSHBUTTON                                 "Cancel",IDCANCEL,128,23,50,14
      DEFPUSHBUTTON          "OK",IDOK,128,7,50,14
      LTEXT                                                             "TODO: Place dialog controls here.",
                                                                IDC_STATIC,5,34,113,8
END


Для забывчивых пользователей


В ряде случаев пользователь может забыть сохранить внесенные им изменения документа в файле. Попробуйте отредактировать ранее сохраненный документ приложения Single, а затем создайте новый файл. Изменения документа сохранены не будут.
Класс CDocument и все классы, для которых он является базовым, позволяют установить специальный флаг модификации, означающий что документ изменен. В этом случае, перед закрытием документа пользователю будет предложено его сохранить. Для установки этого флага предназначен метод SetModifiedFlag. Вот прототип метода SetModifiedFlag:
void SetModifiedFlag(BOOL bModified = TRUE);
Если документ изменен, установите флаг модификации, вызвав метод SetModifiedFlag с параметром bModified, равным TRUE или без параметра. В случае необходимости вы можете убрать установленный флаг. Для этого надо вызвать метод SetModifiedFlag с параметром bModified, равным FALSE.
Мы должны добавить вызов метода SetModifiedFlag в методах OnLButtonDown и OnRButtonDown, выполняющих модификацию документа. Вызов метода можно разместить в любом месте, например, сразу после добавления к массиву arrayFig, представляющему документ, нового элемента.
//////////////////////////////////////////////////////////////
// Метод OnLButtonDown класса CSingleView
void CSingleView::OnLButtonDown(UINT nFlags, CPoint point)
{
      // ...
      // Добавляем к массиву, определяющему документ, новый
      // элемент
      pDoc->arrayFig.Add(OneFigure);
      // Устанавливаем флаг изменения документа
      pDoc->SetModifiedFlag();
      CView::OnLButtonDown(nFlags, point);
}
//////////////////////////////////////////////////////////////
// Метод OnRButtonDown класса CSingleView
void CSingleView::OnRButtonDown(UINT nFlags, CPoint point)
{
      // ...
      // Добавляем к массиву, определяющему документ, новый
      // элемент
      pDoc->arrayFig.Add(OneFigure);
      // Устанавливаем флаг изменения документа
      pDoc->SetModifiedFlag();
     
      CView::OnRButtonDown(nFlags, point);
}


Добавление к классу нового элемента данных


Процедура добавления в класс новых данных сходна с только что описанной процедурой добавления метода. Для этого выберите из меню строку Add Variable. На экране появится диалоговая панель Add Member Variable, представленная на рисунке 2.16.
Добавление к классу нового элемента данных
Рис. 2.16. Диалоговая панель Add Member Variable
В поле Variable Type надо ввести тип данных, а в поле Variable Declaration - название соответствующей переменной. Область видимости переменной определяется переключателем Access.
С помощью ClassView можно просмотреть список названий файлов в которых используется данный класс. Для этого надо выбрать из временного меню строку References. На экране появится диалоговая панель Definitions and References.


Добавление к классу нового метода


Чтобы добавить в класс новый метод, выберите из временного меню строку Add Function. На экране появится диалоговая панель Add Member Function, представленная на рисунке 2.15.
Добавление к классу нового метода
Рис. 2.15. Диалоговая панель Add Member Function
В поле Function Type следует ввести тип значения, возвращаемого методом. Объявление метода запишите в поле Function Declaration. В этой же диалоговой панели можно определить область видимости метода. Для этого предназначен переключатель с зависимой фиксацией Access. Он может находиться в трех положениях: Public, Protected и Private. Переключатели Static и Virtual позволяют определить, что добавляемый метод должен быть объявлен, соответственно, как статический или виртуальный.
Когда все поля диалоговой панели заполнены, нажмите кнопку OK. В объявлении класса будет добавлен новый метод. Название метода также появится в списке элементов класса в окне ClassView.


Документ приложения (класс CDocument)


Большинство приложений работают с данными или документами, хранимыми на диске в отдельных файлах. Класс CDocument, наследованный от базового класса CCmdTarget, служит для представления документов приложения.


Документ


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


Долгий путь сообщения


В предыдущем примере мы изучили основы метода обработки сообщений, рассмотрели принципы построения таблицы сообщений класса. Теперь мы приступим к более детальному рассмотрению механизма обработки сообщений для приложений MFC.
Создайте новый проект под названием MFMessage. В качестве типа приложения выберите из списка Type строку Application (рис. 4.1). Наберите в редакторе исходный текст приложения и сохраните его в файле MFMessage.cpp (листинг 2.10). Чтобы быстрее набрать текст приложения, вы можете модифицировать исходный текст приложения MFMenu.
Листинг 2.10. Файл MFMessage.cpp
// Включаемый файл для MFC
#include
#include "resource.h"
//=====================================================
// Класс CMFMessageApp - главный класс приложения
//=====================================================
class CMFMessageApp : public CWinApp
{
public:
      // Мы будем переопределять метод InitInstance,
      // предназначенный для инициализации приложения
      virtual BOOL InitInstance();
      afx_msg void AppMessageCommand();
      // Макрокоманда необходима, так как класс
      // CMFMessageWindow обрабатывает сообщения
      DECLARE_MESSAGE_MAP()   
};
 
// Создаем объект приложение класса CMFMessageApp
CMFMessageApp MFMessageApp;
 
//=====================================================
// Класс CMFMessageWindow - представляет главное окно
//=====================================================
class CMFMessageWindow : public CFrameWnd
{
public:
      // Объявляем конструктор класса CMFMessageWindow
      CMFMessageWindow();
      // Объявляем методы для обработки команд меню
      afx_msg void FrameMessageCommand();
      afx_msg void ExitApp();
      // Макрокоманда необходима, так как класс
      // CMFMessageWindow обрабатывает сообщения
      DECLARE_MESSAGE_MAP()   
};
//=====================================================
// Метод MessageCommand

// Обрабатывает команду ID_TEST_BEEP

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

void CMFMessageWindow::FrameMessageCommand()

{

      ::MessageBox(NULL,

             " Command received in CMFMessageWindow Message Map",

             "Message", MB_OK);

}

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

// Метод MessageCommand

// Обрабатывает команду ID_TEST_BEEP

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

void CMFMessageApp::AppMessageCommand()

{

      ::MessageBox(NULL,

             "Command received in CMFMessageApp Message Map",

             "Message", MB_OK);

}

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

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

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

BEGIN_MESSAGE_MAP(CMFMessageWindow, CFrameWnd)

      ON_COMMAND(ID_TEST_INFRAMECLASS, FrameMessageCommand)

      ON_COMMAND(ID_TEST_INBOTHCLASS,  FrameMessageCommand)

END_MESSAGE_MAP()

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

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

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

BEGIN_MESSAGE_MAP(CMFMessageApp, CWinApp)

      ON_COMMAND(ID_TEST_INAPPCLASS,     AppMessageCommand)

      ON_COMMAND(ID_TEST_INBOTHCLASS,  AppMessageCommand)

END_MESSAGE_MAP()

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

// Метод InitInstance класса CMFMessageApp

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

BOOL CMFMessageApp::InitInstance()

{

      // Создаем объект класса CMFMessageWindow

      m_pMainWnd = new CMFMessageWindow();

      // Отображаем окно на экране

      m_pMainWnd -> ShowWindow(m_nCmdShow);

      // Обновляем содержимое окна

      m_pMainWnd -> UpdateWindow();

      return TRUE;

}

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

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

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



CMFMessageWindow::CMFMessageWindow()

{

      // Создаем окно приложения, соответствующее

      // данному объекту класса CMFMessageWindow

      Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW,

                   rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));

}

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

Строка меню Test

Идентификатор

In Frame Class

ID_TEST_INFRAMECLASS

In App Class

ID_TEST_INAPPCLASS

In Both Class

ID_TEST_INBOTHCLASS

Exit

ID_APP_EXIT

В листинге 2.11 представлен фрагмент файла ресурсов MFMessage.rc, в котором определяется меню приложения. Чтобы ускорить разработку меню, вы можете скопировать меню приложения MFMenu и изменить его соответствующим образом.

Листинг 2.11. Фрагмент файла MFMessage.rc

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

// Меню

//

IDR_MENU MENU DISCARDABLE

BEGIN

      POPUP "Test"

      BEGIN

             MENUITEM "In Frame Class",   ID_TEST_INFRAMECLASS

             MENUITEM "In App Class",      ID_TEST_INAPPCLASS

             MENUITEM "In Both Class",     ID_TEST_INBOTHCLASS

             MENUITEM "Exit",                   ID_APP_EXIT

      END

END

Идентификаторы, необходимые для файла ресурсов, записываются в файл resource.h, показанный в листинге 2.12. Этот файл создается автоматически редактором ресурсов. Все что вы должны сделать - это включить его в исходный текст приложения - файл MFMessage.cpp.

Листинг 2.12. Файл resource.h

//{{NO_DEPENDENCIES}}

// Включаемый файл, созданный Microsoft Developer Studio

// Используется в файле ресурсов MFMessage.rc

//

#define IDR_MENU                        101

#define ID_TEST_BEEP                    40001

#define ID_TEST_EXIT                    40002

#define ID_TEST_INAPPCLASS              40003



#define ID_TEST_INFRAMECLASS            40004

#define ID_TEST_INBOTHCLASS             40006

// Следующие значения идентификаторов используются по

// умолчанию для новых объектов

#ifdef APSTUDIO_INVOKED

      #ifndef APSTUDIO_READONLY_SYMBOLS

             #define _APS_3D_CONTROLS                1

             #define _APS_NEXT_RESOURCE_VALUE        102

             #define _APS_NEXT_COMMAND_VALUE         40007

             #define _APS_NEXT_CONTROL_VALUE         1000

             #define _APS_NEXT_SYMED_VALUE           101

      #endif

#endif

Укажите в проекте MFMessage, что приложение использует библиотеку классов MFC и постройте проект. Запустите полученный выполнимый файл. На экране появится главное окно приложение, имеющее меню Test (рис. 2.26).

Долгий путь сообщения

Рис. 2.26. Приложение MFMessage

Когда вы выбираете строки из меню Test, приложению передаются команды с соответствующими идентификаторами. Обратите внимание, что команда ID_TEST_INFRAMECLASS обрабатывается в классе окна CMFMessageWindow, команда ID_TEST_INAPPCLASS в главном классе приложения CMFMessageApp. Команда ID_TEST_INBOTHCLASS содержит два обработчика - один в классе окна, другой в классе приложения, зато команда ID_APP_EXIT не имеет обработчика совсем.

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

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

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

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


Дружественные функции и дружественные классы


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


Дружественные функции


В Си++ вы можете определить для класса так называемую дружественную функцию, воспользовавшись ключевым словом friend. В классе содержится только объявление дружественной функции. Ее определение расположено вне класса. Вы можете объявить дружественную функцию в любой секции класса - public, private или protect.
Дружественная функция не является элементом класса, но может обращаться ко всем его элементам, включая private и protect. Одна и та же функция может быть дружественной для двух или более классов.
В следующем примере определена функция Clear, дружественная для класса point. Дружественная функция Clear используется для изменения значения элементов данных m_x и m_y, объявленных как private:
//==========================================================
// Класс point
class point
{
public:
      // Функция Clear объявляется дружественной классу point
      friend void point::Clear(point*);
      // Интерфейс класса...
private:
      int  m_x;
      int  m_y;
};
//==========================================================
// Функция Clear
void Clear(point* ptrPoint)
{
      // Обращаемся к элементам класса, объявленным как private
      ptrPoint->m_x = 0;
      ptrPoint->m_y = 0;
      return;
}
//==========================================================
// Главная функция
void main()
{
      point pointTestPoint;
      // Вызываем дружественную функцию
      Clear(&pointTestPoint);
}
С помощью ключевого слова friend вы можете объявить некоторые методы одного класса дружественными для другого класса. Такие методы могут обращаться ко всем элементам класса, даже объявленным как private и protect, несмотря на то, что сами они входят в другой класс.
В следующем примере мы определяем два класса - line и point. В классе point определяем метод Set и объявляем его в классе line как дружественный. Дружественный метод Set может обращаться ко всем элементам класса line:
// Предварительное объявление класса line

class line;

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

// Класс point

class point

{

public:

      // Метод Set класса point

      void Set(line*);

      // ...

};

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

// Класс line

class line

{

public:

      // Метод Set класса point объявляется дружественной

      // классу point

      friend void point::Set(line*);

private:

      int  begin_x, begin_y;

      int  end_x, end_y;

};

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

// Функция Clear

void point::Set(line* ptrLine)

{

      // Обращаемся к элементам класса line, объявленным как

      // private

      ptrLine->begin_x = 0;

      ptrLine->begin_y = 0;

      // ...

     

      return;

}

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

// Главная функция

void main()

{

      point           pointTestPoint;

      line       lineTestPoint;

      // Вызываем дружественный метод

      pointTestPoint.Set(&lineTestPoint);

}


Дружественные классы


По аналогии с дружественными функциями и методами, можно объявить дружественный класс. Все методы дружественного класса, могут обращаться ко всем элементам класса, включая элементы, объявленные как private и protect.
Так, например, в предыдущем примере вы могли бы определить, что класс point является дружественным классу line. Все методы класса point могут обращаться к любым элемента класса line.
//==========================================================
// Класс point
class point
{
      // ...
};
//==========================================================
// Класс line
class line
{
public:
      // Класс point объявляется дружественным классу line
      friend class point;
};


Единичное наследование


В случае единичного наследования порожденный класс наследуется только от одного базового класса. Рисунок 1.1 отражает единичное наследование классов. Единичное наследование является наиболее распространенным методом наследования. Библиотека классов MFC использует только единичное наследование.
Чтобы указать, что класс наследуется от другого базового класса, имя базового класса необходимо указать после имени класса перед открывающей фигурной скобкой определения класса. Непосредственно перед именем базового класса необходимо поставить знак двоеточия:
class [[:]]

{

     

} [];
Перед названием базового класса может быть указан спецификатор доступа public, private или protect. Назначение этих спецификаторов мы рассмотрим в разделе “Разграничение доступа к элементам базового класса”. Сейчас же мы скажем только, что если вы не укажите спецификатор доступа, то по умолчанию будет подразумеваться спецификатор private.
Ниже мы определили базовый класс Base, содержащий несколько элементов, а затем наследовали от него два новых класса DerivedFirst и DerivedSecond. В каждом из порожденных классов мы определили различные дополнительные методы и элементы данных.
// Класс Base
class      Base
{
      // Элементы класса Base
};
// Класс DerivedFirst, наследованный от базового класса Base
class      DerivedFirst : Base
{
      // Элементы класса DerivedFirst
};
// Класс DerivedSecond, наследованный от базового класса Base
class      DerivedSecond : Base
{
      // Элементы класса DerivedSecond
};
Классы DerivedFirst и DerivedSecond сами могут выступать в качестве базовых классов.
Вы можете определять в пороженном классе элементы, имена которых совпадают с именами элементов базовых классов. Если вы выполнили такое переопределение, вы можете обратиться к элементу базового класса, если укажете его полное имя. Полное имя должно состоять из имени класса, к которому относится элемент, оператора :: и имени самого элемента.
В качестве примера приведем базовый класс Base и производный от него класс Derived. В обоих классах определен элемент данных iNumber. Чтобы получить доступ из порожденного класса к элементу iNumber базового класса указывается его полное имя Base::iNumber.
// Класс Base
class      Base
{
public:
      int  iNumber;
      // Другие элементы класса
};
// Класс Derived, наследованный от базового класса Base
class      Derived : Base
{
public:
      // Это объявление скрывает элемент iNumber базового класса
      int  iNumber;
      int  GetNumber(void) {return iNumber + Base::iNumber; }
};
Указатель на объект базового класса можно присвоить указатель на объект класса порожденного от него. Эта возможность будет широко использоваться в библиотеке классов MFC.


Файловая система (класс CFile)


Библиотека MFC включает класс для работы с файловой системой компьютера. Он называется CFile и также наследуется от базового класса CObject. Непосредственно от класса CFile наследуются еще несколько классов - CMemFile, CStdioFile, CSocketFile.
При работе с файловой системой вам может потребоваться получить различную информацию о некотором файле - например, дату создания, размер и т. д. Для хранения этих данных предназначен специальный класс CFileStatus. Класс CFileStatus один из немногих классов, которые не наследуются от базового класса CObject.

Библиотека MFC включает класс CFile, предназначенный для обеспечения работы с файлами. Он позволяет упростить использование файлов, представляя файл как объект, который можно создать, читать, записывать и т. д. Класс CFile наследуется непосредственно от класса CObject:
CFile <- CObject     
Чтобы получить доступ к файлу, сначала надо создать объект класса CFile. Конструктор класса CFile позволяет сразу после создания такого объекта открыть файл. Но мы воспользуется более общим способом и откроем файл позже, вызвав метод Open.


Файловая система - класс CStdioFile


Если вы привыкли пользоваться функциями потокового ввода/вывода из стандартной библиотеки трансляторов Си и Си++, обратите внимание на класс CStdioFile, наследованный от базового класса CFile. Этот класс позволяет выполнять буферизованный ввод/вывод в текстовом и двоичном режиме.
CStdioFile <- CFile <- CObject
В текстовом режиме выполняется специальная обработка символов возврата каретки и перевода строки. Когда в файл, открытый в текстовом режиме, записывается символ перевода строки \n (код 0x0A), он преобразуется в два символа - символ перевода строки (код 0x0A) и символ возврата каретки (код 0x0D). И наоборот, когда из файла считывается пара символов перевода строки и возврата каретки, они преобразуются в один символ перевода строки.
Для объектов класса CStdioFile можно вызывать все методы его базового класса CFile, кроме методов Duplicate, LockRange и UnlockRange. Напомним, что класс CMemFile, также наследованный от базового класса CFile, тоже работает с этими методами.
В класс CStdioFile входит элемент данных m_pStream, который содержит указатель на открытый файл. Если объект CStdioFile создан, но файл либо еще не открыт, либо закрыт, тогда m_pStream содержит константу NULL.
Класс CStdioFile имеет три различных конструктора. Первый конструктор класса CStdioFile не имеет параметров:
CStdioFile();
Этот конструктор только создает объект класса, но не открывает никаких файлов. Чтобы открыть файл, надо вызвать метод Open базового класса CFile.
Второй конструктор класса CStdioFile можно вызывать, если файл уже открыт и вам надо создать новый объект класса и связать с ним открытый файл:
CStdioFile(FILE* pOpenStream);
Этот конструктор можно использовать, если файл был открыт стандартной функцией fopen.
Параметр pOpenStream должен содержать указатель на файл, полученный вызовом стандартной функции fopen.
Третий, последний конструктор можно использовать, если надо создать объект класса CStdioFile, открыть новый файл и связать его с только что созданным объектом:

CStdioFile(LPCTSTR lpszFileName, UINT nOpenFlags);

      throw(CFileException);

Если указанный файл не может быть открыт, вызывается исключение CFileException.

Параметр lpszFileName должен содержать указатель на строку с именем файла. Можно указать полный путь файла, а не только его имя. Параметр nOpenFlags определяет режим, в котором будет открыт файл. Возможные значения этого параметра были описаны ранее (см. Метод Open класса CFile).

Для чтения и записи в текстовый файл класс CStdioFile включает два новых метода ReadString и WriteString. Метод ReadString позволяет прочитать из файла строку символов, а метод WriteString - записать.

Метод ReadString имеет две формы. Первая используется для чтения строк из файла в буфер памяти, а вторая для чтения строк и записи их в объект класса CString.

Вот описание первой формы метода ReadString:

virtual LPTSTR ReadString(LPTSTR lpsz, UINT nMax);

      throw(CFileException);

Из открытого файла считывается текстовая строка и записывается в буфер lpsz. Считается, что строка файла оканчивается символами перевода строки и возврата каретки. В конец строки, записанной в буфер lpsz, заносится символ двоичного нуля (\0).

Максимальное количество считываемых символов определяется параметром nMax. Если в строке файла больше, чем nMax - 1 байт, то остальные символы не будут прочитаны. Метод ReadString возвращает указатель на прочитанную строку или NULL, если достигнут конец файла.

Вторая форма метода ReadString не намного отличается от первой. Вместо двух параметров lpsz и nMax указывается один параметр rString, указывающий на объект класса CString, в который будет записана строка, прочитанная из файла:

BOOL ReadString(CString& rString);

      throw(CFileException);

Еще одно отличие второй формы метода ReadString заключается в типе возвращаемого им значения. Если достигнут конец файла, то вторая форма метода ReadString возвращает константу FALSE.

Для записи в файл текстовой строки предназначен метод WriteString:

virtual void WriteString(LPCTSTR lpsz);

      throw(CFileException);

В качестве параметра lpsz этого метода надо указать адрес буфера с текстовой строкой, закрытой символом \0. Символ \0 не записывается в файл. Если в текстовой строке lpsz есть символы перевода строки, они записываются как пара символов возврата каретки и перевода строки.

Метод WriteString может вызвать исключение, если во время записи в файл произойдет ошибка, например переполнение диска.


Файловая система - классы CMemFile и CStdioFile


В библиотеку MFC входит класс CMemFile, наследуемый от базового класса CFile. Класс CMemFile представляет файл, размещенный в оперативной памяти. Вы можете работать с объектами класса CMemFile также, как с объектами класса CFile. Отличие заключается в том, что файл, связанный с объектом CMemFile, на самом деле расположен не на магнитном диске, а в оперативной памяти компьютера. За счет этого операции с таким файлом происходят значительно быстрее, чем с обычными файлами.
CMemFile <- CFile <- CObject
Работая с объектами класса CMemFile, можно использовать все методы класса CFile, которые мы уже описали в предыдущей главе. Вы можете записывать данные в такой файл и считывать их. К сожалению, для класса CMemFile не реализованы методы блокировки LockRange и UnlockRange и метод для копирования Duplicate. Кроме этих методов, в состав класса CMemFile включены дополнительные методы.
Для создания объектов класса CMemFile предназначены два различных конструктора. Первый конструктор CMemFile имеет всего один необязательный параметр nGrowBytes:
CMemFile(UINT nGrowBytes = 1024);
Этот конструктор создает в оперативной памяти пустой файл. После создания файл автоматически открывается. Вы не должны специально вызывать метод Open.
Когда вы начинаете запись в такой файл, автоматически выделяется блок памяти. Для получения памяти методы класса CMemFile вызывают стандартные функции malloc, realloc и free. Если выделенного блока памяти недостаточно, его размер увеличивается. Увеличение блока памяти файла происходит частями по nGrowBytes байт. После удаления объекта класса CMemFile используемая им память автоматически возвращается системе.
Второй конструктор класса CMemFile имеет более сложный прототип. Этот конструктор используется в тех случаях, когда вы сами выделяете память для файла:
CMemFile(BYTE* lpBuffer, UINT nBufferSize,
      UINT nGrowBytes = 0);
Параметр lpBuffer указывает на буфер, который будет использоваться для файла. Размер буфера определяется параметром nBufferSize.

Необязательный параметр nGrowBytes используется более комплексно, чем в первом конструкторе класса. Если nGrowBytes содержит нуль, то созданный файл будет содержать данные из буфера lpBuffer. Длина такого файла будет равна nBufferSize.

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

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

BYTE * Detach();

Перед эти полезно определить длину файла и соответственно, размер буфера памяти, вызвав метод GetLength.

Метод Detach закрывает данный файл и возвращает указатель на используемый им блок памяти. Если вам требуется опять открыть файл и связать с ним блок оперативной памяти, вызовите метод Attach:

void

Attach(BYTE* lpBuffer, UINT nBufferSize, UINT nGrowBytes = 0);

Параметры метода Attach соответствуют параметрам второго конструктора класса CMemFile, рассмотренному выше. Параметр lpBuffer указывает на буфер размера nBufferSize, который будет связан с файлом.

Если необязательный параметр nGrowBytes равен нулю, то созданный файл будет содержать данные из буфера lpBuffer. Если nGrowBytes больше нуля, то содержимое буфера lpBuffer игнорируется. Если вы запишете в такой файл больше данных, чем помещается в отведенном вами буфере, его размер автоматически увеличивается на nGrowBytes байт. Для управления буфером файла класс CMemFile вызывает стандартные функции malloc, calloc и free. Поэтому, чтобы не нарушать механизм управления памяти, буфер lpBuffer должен быть создан функциями malloc или calloc.


Файлы StdAfx.cpp и StdAfx.h


Самый маленький файл проекта StdAfx.cpp. Исходный текст файла StdAfx.cpp представлен в листинге 4.5.
Листинг 4.5. Файл StdAfx.cpp
#include "stdafx.h"
Фактически файл StdAfx.cpp содержит только директиву #include, предназначенную для подключения файла StdAfx.h. Включаемый файл StdAfx.h, представлен нами в листинге 4.6.
Включаемый файл StdAfx.h предназначен для включения стандартных системных включаемых файлов afxwin.h, afxext.h и afxcmn.h. Если в вашем проекте определены редко изменяемые включаемые файлы, которые используются во многих модулях приложения, вы можете также подключить их в этом файле.
Листинг 4.6. Файл StdAfx.h
// Исключить редко используемые директивы из файлов windows.h
// и afxv_w32.h
#define VC_EXTRALEAN     
// Файл afxwin.h необходим при использовании MFC
#include         
// Файл afxwin.h определяет некоторые расширения MFC
#include         
#ifndef _AFX_NO_AFXCMN_SUPPORT
      // Файл afxcmn.h используется для органов управления
      // операционной системы Windows 95
      #include                                         
#endif // _AFX_NO_AFXCMN_SUPPORT


Главное окно однооконного приложения


Большинство командных сообщений передаются главному окну приложения. Для приложений, имеющих однооконный интерфейс, роль главного окна приложения выполняет объект класса CFrameWnd или объект класса, наследованного от базового класса CFrameWnd.
Получив сообщение, главное окно приложения сначала предоставляет возможность обработать сообщение окну просмотра. И только если окно просмотра не может обработать сообщение, проверяется таблица сообщений класса главного окна приложения.
Если главное окно приложения также не может обработать командное сообщение, оно передается объекту главного класса приложения. Напомним, что главный класс приложения наследуется от базового класса CWinApp и приложение имеет только один объект этого класса.


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


Главный класс приложения CDialogApp, наследованный от базового класса CWinApp, определен во включаемом файле Dialog.h. Исходный текст этого файла содержится в листинге 4.1.
Первые строки файла содержат директиву #ifndef, которая проверяет, определен ли символ __AFXWIN_H__. Символ __AFXWIN_H__ определен в файле afxwin.h. Если на этапе обработки файла Dialog.h символ не определен, то при построении проекта выдается сообщение об ошибке. Это гарантирует, что включаемый файл afxwin.h будет обработан до Dialog.h.
Следующая директива #include включает файл resource.h. Этот файл создается MFC AppWizard и содержит определение идентификаторов, задействованных для ресурсов приложения.
Листинг 4.1. Файл Dialog.h
// Dialog.h : Главный включаемый файл для приложения Dialog
//
#ifndef __AFXWIN_H__
      #error include 'stdafx.h' before including this file for PCH
#endif
#include "resource.h"   // включаемый файл содержащий
                        // идентификаторы ресурсов приложения
//////////////////////////////////////////////////////////////
// Класс CDialogApp:
// Методы класса CDialogApp определены в файле Dialog.cpp
//
class CDialogApp : public CWinApp
{
public:
      CDialogApp();
// Overrides
      // В следующем блоке ClassWizard помещает описания
      // переопределенных виртуальных методов класса
      //{{AFX_VIRTUAL(CDialogApp)
public:
      virtual BOOL InitInstance();
      //}}AFX_VIRTUAL
// Implementation
      //{{AFX_MSG(CDialogApp)
             // В этом блоке ClassWizard размещает описания методов
             // класса. Не редактируйте содержимое этого блока вручную
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
};
Для класса CDialogApp описан конструктор CDialogApp, не имеющий параметров. Этот конструктор будет использоваться в момент запуска приложения для создания объекта класса CDialogApp.
Кроме конструктора, в классе CDialogApp, переопределяется виртуальный метод InitInstance базового класса CWinApp. Как видите, метод InitInstance находится, после комментария // Overrides, который обозначает секцию переопределения виртуальных методов.

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

MFC AppWizard поместил объявление метода InitInstance внутри блока комментариев AFX_VIRTUAL. Первая и последняя строка, обозначающая этот блок, не влияют на работу программы, так как они являются комментариями (расположены после символов комментария //). Блок AFX_VIRTUAL нужен ClassWizard, чтобы выделить методы класса, которые им управляются. Вы не должны вручную вносить изменения в этот блок и другие блоки AFX_.

Более подробно о блоках AFX_ и других комментариях, вставляемых MFC AppWizard и ClassWizard, мы рассказывали в разделе “Имена, используемые MFC” данной главы книги.

Основной файл приложения имеет имя, совпадающее с именем проекта - Dialog.cpp. Текст этого файла приведен нами в листинге 4.2. Файл содержит реализацию методов главного класса приложения CDialogApp.

Листинг 4.2. Файл Dialog.cpp

// Dialog.cpp : Определяет главный класс приложения

//

// Включаемые файлы

#include "stdafx.h"

#include "Dialog.h"

#include "DialogDlg.h"

// Для отладочной версии приложения включается дополнительные

// определения

#ifdef _DEBUG

      #define new DEBUG_NEW

      #undef THIS_FILE

      static char THIS_FILE[] = __FILE__;

#endif

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

// CDialogApp

BEGIN_MESSAGE_MAP(CDialogApp, CWinApp)

      //{{AFX_MSG_MAP(CDialogApp)

      // ClassWizard размещает в данном блоке макрокоманды для

      // обработки сообщений. Не изменяйте содержимое этого блока

      // вручную

      //}}AFX_MSG

      ON_COMMAND(ID_HELP, CWinApp::OnHelp)

END_MESSAGE_MAP()

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

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



CDialogApp::CDialogApp()

{

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

}

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

// Создаем один объект класса CDialogApp. Это будет главный

// объект приложения

CDialogApp theApp;

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

// Инициализация приложения

BOOL CDialogApp::InitInstance()

{

// Стандартная инициализация приложения. Вы можете сократить

// размер выполняемого модуля приложения, если удалите

// инициализацию, которая вам не нужна

#ifdef _AFXDLL

      Enable3dControls();

#else

      Enable3dControlsStatic();

#endif

      CDialogDlg dlg;

      m_pMainWnd = &dlg;

      int nResponse = dlg.DoModal();

      if (nResponse == IDOK)

      {

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

             // который вызывается, если пользователь нажмет кнопку OK

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

      }

      else if (nResponse == IDCANCEL)

      {

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

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

             // Cancel в диалоговой панели приложения

      }

      // Так как диалоговая панель закрыта, возвращаем значение

      // FALSE чтобы завершить приложение

      return FALSE;

}

В начале файла Dialog.cpp подключены три файла stdafx.h, Dialog.h и DialogDlg.h. Файл stdafx.h будет описан нами ниже. Сейчас отметим, что он содержит определения, необходимые для библиотеки классов MFC.

Файл Dialog.h содержит описание главного класса приложения CDialogApp. Файл DialogDlg.h включает описание класса диалоговой панели приложения. Именно эта панель будет представлять пользовательский интерфейс нашего приложения.

Далее директива #ifdef проверяет, был ли определен символ _DEBUG. Вы не найдете определение _DEBUG ни в одном исходном файле проекта. Этот символ определяется самой средой VIsual C++, если вы создаете отладочную версию приложения.



#ifdef _DEBUG

      #define new DEBUG_NEW

      #undef THIS_FILE

      static char THIS_FILE[] = __FILE__;

#endif

Для отладочной версии приложения определяется символ DEBUG_NEW и переопределяется статическая переменная THIS_FILE. Если такой символ уже был определен, он предварительно отменяется  директивой #undef, а затем определяется снова.

THIS_FILE определяется как символьная строка, в которую записывается имя исходного файла данного модуля, определенное специальным символом __FILE__ (то, есть Dialog.cpp).

Исходные тексты приложений, подготовленных с использованием средств автоматизированного проектирования MFC AppWizard и ClassWizard, активно используют символы, определенные в среде VIsual C++. Чтобы просмотреть их список, выберите из меню Build строку Settings. На экране появится диалоговая панель Project Settings, содержащая несколько страниц. Выберите страницу C/C ++ (рис. 4.9).

В поле Settings For приложение Dialog представлено двумя строками. Одна выбирает параметры проекта для отладочной версии приложения, а вторая для законченной, не отладочной, версии.

В поле Preprocessor definitions отображается список символов, определенных для данного проекта. Вы можете добавить в этот список новые символы, или убрать символы, которые уже определены.

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

Рис. 4.9. Настройка проекта

Далее в исходном файле располагается таблица сообщений главного класса приложения. На первый взгляд внешний вид таблицы сообщений соответствует таблице сообщений приложения MFMessage.


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


Теперь приступим к рассмотрению исходного текста приложения, представленного в листинге 1.1.
Сначала мы определяем главный класс приложения CMFDialogApp, наследованный от базового класса CWinApp. Класс CMFDialogApp и его методы используются аналогично приложению MFHello. Отличие заключается в том, что переопределенный метод InitInstance вместо отображения на экране сообщения при помощи функции AfxMessageBox, отображает полноценную модальную диалоговую панель.
//=====================================================
// Метод InitInstance класса CMFDialogApp
//=====================================================
BOOL CMFDialogApp::InitInstance()
{
      // Создаем объект класса CMyDialog
      CMyDialog dlgTest;
      m_pMainWnd = &dlgTest;
      // Отображаем на экране модельную диалоговую панель
      dlgTest.DoModal();
      // Отображаем на экране значение переменной m_Text,
      // входящей в класс CMyDialog
      AfxMessageBox(dlgTest.m_Text);
      return FALSE;
}
Сначала создается объект dlgTest класса CMyDialog, который будет представлять диалоговую панель. Когда объект dlgTest создан, диалоговая панель еще не появляется на экране, для этого надо воспользоваться методом DoModal, определенным в классе CDialog.
Следующим оператором мы присваиваем адрес объекта диалоговой панели элементу данных m_pMainWnd, входящему в класс CWinApp. Элемент данных m_pMainWnd определяет главное окно приложения. В нашем случае главное окно как таковое отсутствует и вместо него выступает диалоговая панель.
Чтобы отобразить диалоговую панель на экране, мы вызываем для объекта dlgTest метод DoModal. На этом выполнение метода InitInstance приостанавливается, пока пользователь не закроет диалоговую панель.
Когда диалоговая панель закрыта, мы отображаем на экране состояние переменной dlgTest.m_Text, которая соответствует полю ввода Edit диалоговой панели. Последний оператор метода return возвращает значение FALSE и приложение завершается.


Главный объект приложения


В файле Dialog.cpp объявляется глобальный объект главного класса приложения. Именно с создания этого объекта и начинается работа приложения.
CDialogApp theApp;
Объект класса CWinApp обязательно входит во все приложения, созданные с использованием MFC AppWizard, вне зависимости от пользовательского интерфейса этого приложения. Приложения с однооконным, многооконным интерфейсом и с интерфейсом, основанном на диалоговой панели, имеют единственный объект класса CWinApp.

В приложении создается всего один объект класса CSingleApp. Этот объект определяется как статический, поэтому его конструктор получает управление сразу после запуска приложения.
CSingleApp theApp;


Характеристики открытого файла


Чтобы определить расположение открытого файла на диске, надо вызвать метод GetFilePath. Этот метод возвращает объект класса CString, в котором содержится полный путь файла, включая имя диска, каталоги, имя диска и его расширение:
virtual CString GetFilePath() const;
Если требуется определить только имя и расширение открытого файла, можно воспользоваться методом GetFileName. Он возвращает объект класса CString, в котором находится имя файла:
virtual CString GetFileName() const;
В случае, когда надо узнать только имя открытого файла без расширения, воспользуйтесь методом GetFileTitle:
virtual CString GetFileTitle() const;
Последний метод класса CFile позволяет установить путь файла. Этот метод не создает, не копирует и не изменяет имени файла, он только заполняет соответствующий элемент данных в объекте класса CFile:
virtual void SetFilePath(LPCTSTR lpszNewName);
Единственный параметр метода lpszNewName должен содержать указатель на строку символов, содержащую путь файла. Эта строка должна оканчиваться двоичным нулем.


Идентификатор открытого файла


В состав класса CFile входит элемент данных m_hFile типа UINT. В нем хранится идентификатор открытого файла. Если вы создали объект класса CFile, но еще не открыли никакого файла, то в m_hFile записана константа hFileNull.
Обычно нет необходимости непосредственно использовать идентификатор открытого файла. Методы класса CFile позволяют выполнять практически любые операции с файлами и не требуют указывать идентификатора файла. Так как m_hFile является элементом класса, то реализация его методов всегда имеет свободный доступ к нему.


Имена, используемые MFC


Библиотека классов содержит огромное количество классов, структур, констант и т. д. Чтобы сделать исходный текст приложений MFC более легким для понимания, принято использовать ряд соглашений для используемых имен и комментариев.
Названия всех классов и шаблонов классов библиотеки MFC начинаются с заглавной буквы C. Например, CWnd, CMenu, CArray - это классы. Когда вы наследуете собственные классы от классов MFC, вы можете давать им любые имена. Рекомендуется называть их как и классы MFC с заглавной буквы C. Это сделает исходный текст приложения более ясным для понимания.
Чтобы отличать элементы данных, входящие в класс, от простых переменных, их имена принято начинать с префикса  m_. Названия методов классов, как правило специально не выделяются, но их обычно  пишут с заглавной буквы.
Библиотека MFC включает помимо классов, набор служебных функций. Названия этих функций начинаются с символов Afx, например AfxGetApp, AfxGetAppName, AfxGetMainWnd. Символы AFX являются сокращением от словосочетания Application FrameworkX, означающего основу приложения, его внутреннее устройство.
Символы AFX встречаются не только в названии функций MFC. Многие константы, макрокоманды и другие символы начинаются с этих символов. В общем случае Afx является признаком, по которому вы можете определить принадлежность того или иного объекта (функции, переменной, ключевого слова или символа) к библиотеке MFC.
В процессе создания приложения MFC AppWizard и ClassWizard могут определять идентификаторы ресурсов, идентификаторы для справочной системы и т. д. При этом, для идентификаторов различного типа используются разные префиксы:

Префикс
Представляет идентификаторы
HID_
Контекстной подсказки для команд
HIDD_
Контекстной подсказки для диалоговых панелей
ID_
Строк меню и кнопок панелей управления
IDB_
Растровых изображений bitmap
IDC_
Курсоров
IDC_
Органов управления диалоговых панелей
IDD_
Шаблонов диалоговых панелей
IDI_
Пиктограмм
IDP_
Строковых ресурсов, используемые в диалоговых панелях message box для отображения приглашения
IDR_
Приложение может иметь несколько ресурсов различного типа с одинаковыми идентификаторами. Для таких идентификаторов используют префикс IDR_
IDS_
Строковых ресурсов
IDOK, IDCANCEL
Стандартные идентификаторы для кнопок OK и Cancel диалоговых панелей
<
Когда приложение разрабатывается средствами MFC AppWizard и ClassWizard, они размещают в исходном тексте приложения комментарии следующего вида:

//{{AFX_

  ...

//}}AFX_

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

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

Блок

Включает

//{{AFX_DATA 

//}}AFX_DATA

Объявление элементов данных класса. Используется в описании классов диалоговых панелей

//{{AFX_DATA_INIT 

//}}AFX_DATA_INIT

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

//{{AFX_DATA_MAP 

//}}AFX_DATA_MAP

Макрокоманды DDX, предназначенные для связывания элементов данных класса и органов управления диалоговых панелей. Используется в файле реализации классов диалоговых панелей

//{{AFX_MSG

//}}AFX_MSG

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

//{{AFX_MSG_MAP

//}}AFX_MSG_MAP

Макрокоманды таблицы сообщений класса. Используются совместно с AFX_MSG

//{{AFX_VIRTUAL

//}}AFX_VIRTUAL

Описание переопределенных виртуальных методов класса. Блок AFX_VIRTUAL используется при описании класса

Мы перечислили далеко не все блоки //{{AFX_. Существует еще целый ряд блоков, относящихся к реализации технологии OLE и использованию баз данных.

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

Комментарий

После комментария размещаются

// Constructors

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

// Attributes

Элементы данных класса, и методы для доступа к ним (свойства класса). Как правило, элементы класса размещенные в этой секции определены с ключевым словом public

// Operations

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

// Overridables

Здесь расположены виртуальные методы, которые вы можете переопределить в порожденных классах. Как правило, элементы класса размещенные в этой секции определены с ключевым словом protected. В большинстве случаев, названия виртуальных методов класса начинается с символов On

// Implementation

Методы и элементы данных, относящиеся к внутреннему устройству класса - реализации класса. Как правило, элементы класса размещенные в этой секции определены с ключевым словом protected или private

// Dialog Data

Элементы данных, класса диалоговой панели, связанные с органами управления

Для некоторых классов, используются и другие комментарии, например, // Advanced Overridables и т. д.

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


Информация о классе объекта (структура CRuntimeClass)


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


Информация о классе


Класс CObject содержит два метода: GetRuntimeClass и IsKindOf, позволяющих получить информацию о классе объекта.


Исходные тексты приложения


Рассмотрим исходные тексты приложения более подробно. Из главы “Введение в MFC” вы уже знаете, что приложения, созданные на основе библиотеки MFC, как правило, не имеют главной функции приложения WinMain.
Функция WinMain скрыта от программиста внутри методов класса CWinApp. Каждое приложение должно иметь один объект класса, наследованного от базового класса CWinApp. Поэтому свой рассказ мы начнем с главного класса приложения.


Исключения (класс CException)


Для реализации механизма исключений в MFC определен специальный класс CException, наследованный от базового класса CObject. Все исключения, определенные в MFC, наследуются от этого класса. Вот список классов, наследованных от CException и их краткое описание. Более полное описание классов, связанных с исключениями, вы можете найти в разделе “Исключения - класс CException” главы “Вспомогательные классы MFC”.

Класс
Описание
CArchiveException
Исключение, вызванное ошибкой при использовании объекта класса CArchive. Класс CArchive применяется для сохранения и загрузки документа из файла на диске
CDaoException
Ошибка при работе с базами данных (при использовании классов DAO)
CDBException
Ошибка при работе с базами данных (при использовании ODBC)
CFileException
Ошибка, связанная с файловой системой
CMemoryException
Недостаточно оперативной памяти
CNotSupportedException
Попытка выполнить неопределенную операцию
COleDispatchException, COleException
Ошибка OLE
CResourceException
Не найден ресурс
CUserException
Ошибка приложения, вызванная действиями пользователя


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

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

Класс

Исключение вызывается

CMemoryException

При распределении оперативной памяти

CFileException

При работе с файлами

CArchiveException

Во время записи или восстановления объектов (Archive/Serialization)

CNotSupportedException

При обращении к неизвестный метод, который не поддерживается данным классом

CResourceException

Ошибка при работе с ресурсами Windows

CDaoException

Ошибка при работе с базами данных, через средства DAO

CDBException

Ошибка при работе с базами данных, через средства ODBC

COleException

Ошибка при работе OLE

COleDispatchException

Ошибка при работе OLE

CUserException

При обработке этого исключения на экране отображается сообщение, а затем вызывается исключение CException

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


Изменение порядка обработки сообщений


Порядок обработки командных сообщений определяется виртуальным методом OnCmdMsg. Этот метод первоначально определен в классе CCmdTarget и переопределен в классах CFrameWnd, CView, CDocument и некоторых других классах MFC.
В ряде случаев может понадобиться изменить порядок, в котором командные сообщения передаются объектам приложения или ввести новый объект в цепочке стандартных объектов. В этом случае вы должны переопределить виртуальный метод OnCmdMsg соответствующим образом. В качестве примера использования метода OnCmdMsg вы можете взять класс CView. Если вы установили Visual C++ с исходными текстами библиотеки MFC, вы можете найти определение этого метода в файле Viewcore.cpp.


Как связаться с авторами


Авторы имеют почтовый адрес в сети GlasNet. Все свои замечания и предложения по содержанию книг серий "Библиотека системного программиста", а также "Персональный компьютер - шаг за шагом" вы можете присылать нам по следующему адресу:
frolov@glas.apc.org
Наш почтовый адрес доступен не только пользователям сети GlasNet. Абоненты других компьютерных сетей также могут передавать нам сообщения. Ниже мы приводим наш адрес в различных сетях:

Глобальная сеть
Наш адрес
CompuServe
>internet:frolov@glas.apc.org
GlasNet
frolov@glas.apc.org
Internet
frolov@glas.apc.org
Relcom
frolov@glas.apc.org
UUCP
uunet!cdp!glas!frolov

Вы также можете присылать свои пожелания почтой по адресу:
Издательский отдел АО "ДИАЛОГ-МИФИ".
Индекс 115409, город Москва, улица Москворечье, дом 31, корпус 2.
Приносим свои извинения за то, что не можем ответить на каждое письмо. Мы также не занимаемся рассылкой дискет и исходных текстов к нашим книгам. По этому вопросу обращайтесь непосредственно в издательство “Диалог-МИФИ”.


Класс CArchiveException


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

Константа
Причина ошибки
CArchiveException:: none
Без ошибки
CArchiveException:: generic
Неопределенная ошибка
CArchiveException:: readOnly
Попытка записи в архивный объект, открытый для чтения
CArchiveException:: endOfFile
Обнаружен конец файла при чтении объекта
CArchiveException:: writeOnly
Попытка читать из архивного объекта, открытого для записи
CArchiveException:: badIndex
Неправильный формат файла
CArchiveException:: badClass
Попытка прочитать объект в объект неправильного типа
CArchiveException:: badSchema
Попытка чтения объекта с несоответствующей версией класса

Чтобы создать объект CArchiveException и вызвать исключение воспользуйтесь функцией AfxThrowArchiveException:
void AfxThrowArchiveException(int cause);
Параметр cause должен определять причину вызова исключения. Возможный список значений этого параметра представлен в таблице выше (см. элемент данных m_cause класса CArchiveException).


Класс CException


Класс CException включает два виртуальных метода GetErrorMessage и ReportError. Эти методы позволяют получить словесное описание причины, которая привела к вызову исключения. Заметим, что методы GetErrorMessage и ReportError чисто виртуальные, поэтому они должны быть переопределены в наследуемом классе:
virtual BOOL

GetErrorMessage(LPTSTR lpszError, UINT nMaxError,
      PUINT pnHelpContext = NULL);
Когда вы вызываете в обработчике исключения метод GetErrorMessage, он записывает в буфер lpszError сообщение об ошибке, вызвовшей исключение. Размер буфера надо указать в параметре nMaxError. В конце сообщения всегда записывается символ двоичного нуля. Если сообщение не помещается в буфер lpszError (оно больше чем nMaxError - 1 байт), тогда в буфер записываются только nMaxError - 1 символов сообщения. В последний байт записывается двоичный нуль.
Необязательный параметр pnHelpContext может содержать указатель на переменную типа UINT, в которую будет записан идентификатор контекстной подсказки (help context ID).
Метод GetErrorMessage возвращает ненулевое значение, если сообщение об ошибке доступно, и нуль в противном случае.
Вы можете вызывать метод ReportError из обработчика исключений:
virtual int

ReportError(UINT nType = MB_OK, UINT nMessageID = 0);
Метод ReportError отображает в диалоговой панели на экране сообщение об ошибке, вызвавшей данное исключение. Параметр nType определяет внешний вид диалоговой панели сообщения. В качестве параметра nType можно указать любую комбинацию стилей панелей сообщения, таких как MB_OK,  MB_OKCANCEL,  MB_RETRYCANCEL, MB_ICONEXCLAMATION, MB_ICONINFORMATION, MB_ICONQUESTION, MB_ICONSTOP. Если вы не укажите этот параметр, тогда подразумевается стиль MB_OK, то есть панель сообщения с одной кнопкой OK.
Иногда исключение может не иметь текстового описания. Вы можете указать методу ReportError, чтобы он отображал в этом случае определенное сообщение. Текст этого сообщения надо сохранить в строковом ресурсе, а соответствующий идентификатор передать методу ReportError в качестве второго параметра nMessageID. Если вы не укажите этот параметр, тогда отображается сообщение “No error message is available”.
Метод ReportError возвращает значение типа AfxMessageBox. Оно определяет, какая кнопка была нажата в диалоговой панели с сообщением.
Методы GetErrorMessage и ReportError используют данные из ресурсов, созданных AppWizard. Поэтому они могут работать неправильно, если приложение создано без использования AppWizard.


Класс CFileException


Класс CFileException предназначен для обработки исключительных ситуаций, возникающих во время создания или вызова методов класса CFile и порожденных от него классов. Этот класс описан нами в разделе “Класс CFile” и предназначается для работы с файловой системой. Естественно, при работе с файловой системой могут возникнуть самые разнообразные ошибки (исключительные ситуации): попытка открыть несуществующий файл, переполнение диска во время операции записи, ошибка чтения с диска и т. д.
Наибольший интерес для нас представляет элемент данных m_cause из класса CFileException. В него заносится код, по которому можно определить причину исключения.

Константа
Причина ошибки
CFileException:: none
Без ошибки
CFileException:: generic
Неопределенная ошибка
CFileException:: fileNotFound
Файл не найден
CFileException:: badPath
Задан несуществующий путь
CFileException:: tooManyOpenFiles
Открыто слишком много файлов
CFileException:: accessDenied
Доступ к файлу закрыт
CFileException:: invalidFile
Использование неправильного идентификатора (дескриптора) файла
CFileException:: removeCurrentDir
Попытка удалить текущий каталог
CFileException:: directoryFull
Переполнение структуры каталогов. Невозможно создать новый каталог
CFileException:: badSeek
Ошибка во время перемещения указателя файлов
CFileException:: hardIO
Ошибка аппаратного обеспечения компьютера
CFileException:: sharingViolation
Программа SHARE.EXE не загружена или общая область заблокирована (locked)
CFileException:: lockViolation
Попытка заблокировать область файла, которая уже была заблокирована ранее
CFileException:: diskFull
Нет свободного пространства на диске
CFileException:: endOfFile
Достигнут конец файла

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


Класс CMainFrame


Класс CMainFrame представляет главное окно нашего приложения. Внутри этого окна отображаются окно просмотра документа, а также панели управления и состояния. Определение класса CMainFrame расположено в файле MainFrm.h.
Класс CMainFrame наследуется от базового класса CFrameWnd, определенного в библиотеке MFC. Как правило у программиста не возникает необходимости как-либо дорабатывать класс главного окна приложения, созданный MFC AppWizard.
class CMainFrame : public CFrameWnd
{
protected:
      CMainFrame();
      DECLARE_DYNCREATE(CMainFrame)
// Attributes
public:
// Operations
public:
// Overrides
      //{{AFX_VIRTUAL(CMainFrame)
      virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
      //}}AFX_VIRTUAL
// Implementation
public:
      virtual ~CMainFrame();
#ifdef _DEBUG
      virtual void AssertValid() const;
      virtual void Dump(CDumpContext& dc) const;
#endif
protected: 
      // Панель управления и панель состояния
      CStatusBar  m_wndStatusBar;
      CToolBar    m_wndToolBar;
// Методы, предназначенные для обработки сообщений
protected:
      //{{AFX_MSG(CMainFrame)
      afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
};
Шаблон документов приложения создает объекты класса CMainFrame динамически. Для этого в определении класса указана макрокоманда DECLARE_DYNCREATE, объявлен конструктор, не имеющий параметров, а в файле реализации добавлена макрокоманда IMPLEMENT_DYNCREATE.
// Объекты класса CMainFrame могут создаваться динамически
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)


Класс CMemoryException


Когда приложение заказывает у операционной системы новый блок оперативной памяти, может случиться, что вся память уже используется и больше памяти отдано приложению быть не может.
Когда приложение пытается создать новую переменную или объект, вызывая оператор new, то в том случае, если память под него не может быть выделена, создается объект класса CMemoryException и вызывается соответствующее исключение.
Функции malloc не вызывают исключение CMemoryException, но вы можете проверять значение, возвращаемое функцией malloc, и если оно равно нулю, вызывать его сами.
Чтобы самому вызвать исключение, воспользуйтесь функцией AfxThrowMemoryException:
void AfxThrowMemoryException();
Эта функция не пытается получить у операционной системы дополнительную память, а использует память полученную ранее. В противном случае, возможно, вы не смогли бы создать даже объекта CMemoryException, так как свободная память уже кончилась.
Приведем небольшой пример использования этой функции. Допустим, вы пытаетесь получить область оперативной памяти для хранения данных с помощью функции GlobalAlloc. Если операционная система не может выделить область памяти такого размера, она возвращает NULL и вы можете вызвать функцию AfxThrowMemoryException:
if(GlobalAlloc(GMEM_FIXED, 1000000) == NULL)
      AfxThrowMemoryException();


Класс CNotSupportedException


Если приложение пытается вызвать несуществующий метод класса, то вызывается исключение CNotSupportedException. Конструктор класса CNotSupportedException имеет следующий вид:
CNotSupportedException();
Однако если вы сами желаете вызвать из своего кода исключение этого типа, то вместо того, чтобы создавать объект класса CNotSupportedException вручную и передавать его оператору throw, воспользуйтесь функцией AfxThrowNotSupportedException:
void AfxThrowNotSupportedException();


Класс CObject - основной класс MFC


Подавляющее большинство классов из библиотеки MFC наследуются от основного класса CObject. Практически все классы, которые используются в ваших приложениях, например CView или CWinApp, унаследованы от класса CObject.
Класс CObject обеспечивает наиболее общие свойства всех других классов. Так, класс CObject позволяет сохранять и восстанавливать состояние объектов, порожденных из данного класса. Например, текущее состояние объекта можно записать в файл на диске, а затем восстановить объект из этого файла. Этот процесс иногда называют преобразованием в последовательную форму (serialization).
Для объектов, порожденных от CObject, можно получить информацию во время работы программы. Такая возможность используется при динамическом создании объектов. MFC версии 4.0 позволяет также определить имя типа объекта во время работы программы. Эта особенность называется Run-Time Type Information (RTTI). Кроме того возможности класса CObject используются для вывода диагностических сообщений объектами, порожденными от этого класса.
Класс CObject не позволяет осуществлять множественное наследование. Ваш класс должен иметь только один базовый класс CObject.
Чтобы приложение смогло воспользоваться всеми возможностями класса CObject, необходимо включить в описание и определение класса специальные макрокоманды.
Например, чтобы получить возможность во время выполнения приложения определять название класса и его базовый класс, следует включить в объявление и определение вашего класса макрокоманды DECLARE_DYNAMIC  и IMPLEMENT_DYNAMIC. А если вам надо, чтобы состояние объектов класса можно было сохранять и восстанавливать из архивного класса, следует включить в объявление и определение класса макрокоманды DECLARE_SERIAL и IMPLEMENT_SERIAL.


Класс CPoint - точка на плоскости


В предыдущих томах серии “Библиотека системного программиста” мы рассматривали структуру POINT, используемую средствами разработки приложений Windows. Структура POINT позволяет сохранить координаты точки в двумерном пространстве.
Библиотека классов MFC включает в себя класс CPoint, который можно использовать вместо структуры POINT. Класс CPoint имеет несколько конструкторов, которые вы можете использовать.
Первый конструктор класса не имеет параметров:
CPoint();
Вы можете создать объект класса CPoint и сразу присвоить ему значения. Если известны отдельные координаты точки, воспользуйтесь конструктором с двумя параметрами:
CPoint(int initX, int initY);
Первый параметр initX определяет х-координату точки, а параметр initY - y-координату точки. Если надо создать объект CPoint и записать в него координаты из переменной типа POINT или другого объекта класса CPoint, используйте другой конструктор:
CPoint(POINT initPt);
Можно создать объект CPoint и записать в него данные из объекта класса CSize или структуры SIZE:
CPoint(SIZE initSize);
Если у вас есть переменная типа DWORD, в младшем слове которой записана x-координата точки, а в старшем слове y-координата, то можно создать объект класса CPoint и записать в него данные из этой переменной:
CPoint(DWORD dwPoint);
Объекты класса CPoint можно сравнивать друг с другом, пользуясь обычными операторами равенства == (равно) и != (не равно). Результатом действия этих операторов является значение типа BOOL. Если условие определенное операторами равенства выполняется, возвращается ненулевое значение. В противном случае результат равен нулю.


Класс CResourceException


Если в процессе работы возникают проблемы с ресурсами, например приложение пытается загрузить несуществующий ресурс, тогда вызывается исключение CResourceException. Вы можете вызвать это исключение сами. Для этого воспользуйтесь функцией AfxThrowResourceException:
void AfxThrowResourceException();


Класс CSingleApp


Главный класс приложения CSingleApp наследуется от базового класса CWinApp. Вы можете просмотреть определение класса, если выполните двойной щелчок левой клавишей мыши по его названию в окне Project Workspace. Откроется окно редактора и в него загрузится файл Single.h. Курсор будет автоматически установлен на описание класса CSingleApp.
//////////////////////////////////////////////////////////////
// Класс CSingleApp:
class CSingleApp : public CWinApp
{
public:
      CSingleApp();
// Overrides
      //{{AFX_VIRTUAL(CSingleApp)
public:
      virtual BOOL InitInstance();
      //}}AFX_VIRTUAL
// Implementation
      //{{AFX_MSG(CSingleApp)
      afx_msg void OnAppAbout();
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
};


Класс CSingleDoc


Следующий класс который мы рассмотрим - это класс документа нашего приложения CSingleDoc. В качестве базового класса для него используется класс CDocument библиотеки MFC.
Класс CSingleDoc, несколько сложнее главного класса приложения, рассмотренного выше.
class CSingleDoc : public CDocument
{
protected:
      CSingleDoc();
      DECLARE_DYNCREATE(CSingleDoc)
// Attributes
public:
// Operations
public:
// Overrides
      //{{AFX_VIRTUAL(CSingleDoc)
public:
      virtual BOOL OnNewDocument();
      virtual void Serialize(CArchive& ar);
      //}}AFX_VIRTUAL
// Implementation
public:
      virtual ~CSingleDoc();
#ifdef _DEBUG
      virtual void AssertValid() const;
      virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Методы, предназначенные для обработки сообщений
protected:
      //{{AFX_MSG(CSingleDoc)
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
};
Просмотрите исходные тексты приложения. Вы не обнаружите кода, который бы явно создавал объекты этого класса. Объект класса CSingleDoc создается динамически шаблоном документа, во время работы приложения. Шаблон документа также динамически создает еще два объекта - класса окна и класса окна просмотра.
Для того чтобы объекты любого класса, наследованного от базового класса CObject, в том числе и CSingleDoc, можно было создавать динамически, необходимо выполнить следующее:
  • в описании класса надо поместить макрокоманду DECLARE_DYNCREATE. В качестве параметра этой макрокоманды необходимо указать имя данного класса;

  • определить конструктор класса, который не имеет параметров;

  • разместить макрокоманду IMPLEMENT_DYNCREATE в файле реализации. Макрокоманда IMPLEMENT_DYNCREATE имеет два параметра. В первом указывается имя класса, а во втором имя его базового класса

  • MFC AppWizard автоматически выполняет все эти требования для класса документа приложения CSingleDoc, класса окна приложения CMainFrame и класса окна просмотра CSingleView.


    Класс CSingleView


    Следующий класс, который мы рассмотрим, является классом окна просмотра документа CSingleView. Этот класс наследуется от базового класса CView библиотеки MFC. Определения класса CSingleView вы можете найти в файле SingleView.h.
    Окно просмотра и связанный с ним класс окна просмотра документа создается шаблоном документа в процессе работы приложения. Поэтому необходимо, чтобы объекты класса CSingleView можно было создавать динамически.
    Для этого определяется конструктор класса, не имеющий параметров, в определении класса указывается макрокоманда DECLARE_DYNCREATE, а в файле реализации макрокоманда IMPLEMENT_DYNCREATE.
    class CSingleView : public CView
    {
    protected:
          CSingleView();
          DECLARE_DYNCREATE(CSingleView)
    // Attributes
    public:
          CSingleDoc* GetDocument();
    // Operations
    public:
    // Overrides
          //{{AFX_VIRTUAL(CSingleView)
    public:
          virtual void OnDraw(CDC* pDC);
          virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    protected:
          virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
          virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
          virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
          //}}AFX_VIRTUAL
    // Implementation
    public:
          virtual ~CSingleView();
    #ifdef _DEBUG
          virtual void AssertValid() const;
          virtual void Dump(CDumpContext& dc) const;
    #endif
    protected:
    // Методы, предназначенные для обработки сообщений
    protected:
          //{{AFX_MSG(CSingleView)
          //}}AFX_MSG
          DECLARE_MESSAGE_MAP()
    };
    Секция Overrides в описании класса CSingleView содержит описания переопределяемых виртуальных методов базового класса CView. Два метода описаны в ней как public - OnDraw и PreCreateWindow и три как protected - OnPreparePrinting, OnBeginPrinting, OnEndPrinting. Поэтому методы OnDraw и PreCreateWindow можно вызывать и из других классов приложения, а методы OnPreparePrinting, OnBeginPrinting, OnEndPrinting только из класса CSingleView.


    Класс CSize - относительные координаты


    Класс CSize создан на основе структуры SIZE, предназначенной для определения относительных координат точек в двумерном пространстве. Так как CSize наследуется от структуры SIZE, он включает все ее поля. Вы можете непосредственно обращаться к элементам SIZE. Элементы данных cx и cy объявлены как доступные. Объекты класса CSize можно использовать вместо переменных типа SIZE - их можно указывать в качестве параметров функций. Вы можете передавать объекты CSize функциям, которые принимают параметры типа SIZE. Объявление класса CSize расположено во включаемом файле #include .


    Класс CString - текстовые строки


    Практически ни одно приложение не обходится без использования текстовых строк. Язык Си и Си++ не имеет специального строкового типа. Обычно для хранения строк используют массивы символов. MFC предоставляет программисту более удобное средство для организации и работы со строками. Для этого предназначен специальный класс CString.
    Строки CString состоят из символов, типа TCHAR. Тип TCHAR определяется по разному в зависимости от того, определен или нет символ _UNICODE. Если символ _UNICODE не определен, TCHAR соответствует обычному типу char. В противном случае он представляет символы, определяемые двумя байтами.
    При использовании класса CString отпадает необходимость постоянно следить за тем, чтобы длина строки не превысила объем выделенной для нее памяти. Строки CString автоматически расширяются и сокращаются в зависимости от фактической длины строки.
    В класс CString входят методы, выполняющие все основные операции над строками - копирование, сравнение, присваивание и т. д. Над объектами класса CString определены такие операции как +, -, облегчающие работу со строками. Строки, представленные объектами CString, можно сравнивать, используя обычные операторы сравнения <, >, <=, >=, ==. Объекты класса CString можно записывать на диск - для них работает механизм сохранения и восстановления объектов класса. Фактически вы можете работать со стоками как с другими простыми типами данных.
    Строки CString можно использовать совместно с строками языка Си (массивами символов, заканчивающихся знаком двоичного нуля). Вы можете обращаться к объектам класса CString как к обычным массивам символов, то есть использовать их как строки символов языка Си. Вы можете указывать строки в качестве параметра функций и методов, которые принимают параметры типа const char* или LPCTSTR.
    Класс CString содержит большое количество различных методов, предназначенных для работы со строками. Среди них есть методы для доступа к отдельным элементам строки, сравнения строк между собой, поиска подстрок, сохранения строк в файле и их последующего восстановления. Мы оставляем класс CString для вашего самостоятельного изучения и ограничимся описанием только самых основным методов.


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


    Для работы с календарными датами и временем в состав библиотеки классов MFC включен класс CTime. Класс основан на элементе типа time_t, в котором будет храниться дата и время. Элемент типа time_t объявлен как private, поэтому вы не можете обращаться непосредственно к этому элементу. Для этого в состав класса CTime входит набор методов.
    Класс CTime включает несколько конструкторов, которые можно использовать для создания и инициализации объектов данного класса. Выберите тот конструктор, который наиболее удобен в каждом конкретном случае.
    Если вам требуется создать массив, состоящий из объектов класса CTime, используйте конструктор без указания параметров:
    CTime();
    Объекты, созданные этим конструктором не инициализированы. Перед тем как ими пользоваться, запишите в них дату и время:
    CTime(const CTime& timeSrc);
    Параметр timeSrc определяет уже существующий объект класса CTime. Новый объект класса инициализируется значением даты и времени, взятым из существующего объекта.
    Если в программе уже существует переменная типа time_t, в которой хранится значение, то можно создать новый объект класса CTime, основываясь на этой переменной:
    CTime(time_t time);
    Параметр time определяет переменную типа time_t, значение которой будет записано в создаваемый объект.
    Если существует несколько переменных, в которых отдельно хранятся год, месяц, день, часы, минуты и секунды, то можно создать объект класса CTime, сразу же записав в него значения из этих переменных:
    CTime(int nYear, int nMonth, int nDay,
                 int nHour, int nMin, int nSec, int nDST = -1);
    Первые 6 параметров метода определяют время и дату. Параметр nYear должен содержать год, nMonth - месяц, nDay - день, nHour - час, nMin - минуты, nSec - секунды. NYear может принимать значения от 1900 до 2038, nMonth - от 1 до 12 и nDay от 1 до 31.
    Параметр nDST управляет режимом перехода на летнее время. Если nDST содержит нуль, то время представляется в стандартном виде, режим перехода на летнее время не используется. Чтобы включить этот режим, параметр nDST должен содержать значение, больше нуля. Параметр nDST является необязательным. Если его не указать, считается, что он равен нулю. В этом случае конструктор самостоятельно определяет режим перехода на летнее время.

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

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

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

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

    typedef struct _SYSTEMTIME {

          WORD wYear;                 // год

          WORD wMonth;                                // месяц

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

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

          WORD wHour;                  // часы

          WORD wMinute;                               // минуты

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

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

    } SYSTEMTIME;

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

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

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

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

    typedef struct _FILETIME {

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

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

    } FILETIME, *PFILETIME, *LPFILETIME;

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

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


    Класс CUserException


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


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


    Второй класс, который мы используем в своем приложении, наследуется от базового класса CDialog и носит название CMyDialog. Этот класс предназначен для управления диалоговой панелью приложения.
    //=====================================================
    // Класс CMyDialog - класс диалоговой панели
    //=====================================================
    class CMyDialog : public CDialog
    {
    public:
          CMyDialog();
          CString m_Text;
    protected:
          virtual void DoDataExchange(CDataExchange* pDX);
         
          // Обработчики сообщений от кнопок диалоговой панели
          afx_msg void OnDefault();
          virtual void OnCancel();
          virtual void OnOK();
          DECLARE_MESSAGE_MAP()
    };
    Обратите внимание на то, как мы определяем конструктор класса CMyDialog. После названия конструктора стоит символ двоеточие и название конструктора класса CDialog. При этом в качестве параметра, конструктору CDialog передается идентификатор диалоговой панели "DIALOGPANEL":
    // Конструктор класса CMyDialog
    CMyDialog::CMyDialog() : CDialog("DIALOGPANEL")
    {
          // Инициализируем переменную m_Text
          m_Text = "";
    }
    Основное назначение конструктора CMyDialog - вызвать конструктор класса CDialog, указав ему в качестве параметра идентификатор диалоговой панели "DIALOGPANEL". Именно конструктор класса CDialog выполняет создание диалоговой панели.
    В конструкторе также инициализируется переменная m_Text, входящая в класс CMyDialog. В нее записывается пустая строка.


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


    Большой интерес представляет файл DialogDlg.h, показанный в листинге 4.3. Этот класс содержит объявление класса главной диалоговой панели приложения CDialogDlg.
    Класс CDialogDlg наследуется от базового класса CDialog, определенного в библиотеке классов MFC. Конструктор класса имеет один необязательный параметр pParent, используемый для передачи индекса главного окна приложения. Приложение Dialog не имеет главного окна. Роль главного окна выполняет сама диалоговая панель, поэтому параметр pParent не используется.
    Непосредственно после объявления конструктора класса следует объявление элементов данных класса, которые добавлены средствами MFC AppWizard или ClassWizard. Они расположены между двумя комментариями AFX_DATA. Не рекомендуется вручную изменять код приложения, расположенный между этими комментариями.
    После блока AFX_DATA следует блок AFX_VIRTUAL. В этом блоке MFC AppWizard и ClassWizard добавляют объявления переопределенных виртуальных методов базового класса.
    Сначала в этом блоке объявлен только один метод DoDataExchange, переопределенный в нашем проекте. Этот метод применяется для связывания с органами управления диалоговой панели элементов управляющего ей класса.
    Практически со всеми приложениями связана пиктограмма, которая будет отображаться при минимизации приложения. Обычно эта пиктограмма определяется на этапе регистрации класса главного окна приложения. Приложение Dialog не имеет настоящего главного окна. Вместо него используется диалоговая панель. Поэтому отображение пиктограммы приложения не происходит автоматически и мы должны управлять этим сами. Идентификатор пиктограммы m_hIcon определен в классе CDialogDlg после блока AFX_VIRTUAL.
    Диалоговая панель CDialogDlg будет обрабатывать ряд сообщений. Объявления обработчиков сообщений созданных средствами MFC AppWizard или ClassWizard располагаются между двумя комментариями AFX_MSG, образующими блок AFX_MSG.
    После создания проекта в классе CDialogDlg объявлены 4 обработчика сообщений OnInitDialog, OnSysCommand, OnPaint и OnQueryDragIcon. Эти методы определены в файле DialogDlg.cpp, описанном ниже.

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

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

          //{{AFX_VIRTUAL(CAboutDlg)

    protected:

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

          virtual void DoDataExchange(CDataExchange* pDX);

          //}}AFX_VIRTUAL

    // Implementation

    protected:

          //{{AFX_MSG(CAboutDlg)

          //}}AFX_MSG

          DECLARE_MESSAGE_MAP()

    };

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

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

    {

          //{{AFX_DATA_INIT(CAboutDlg)

          //}}AFX_DATA_INIT

    }

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

    void CAboutDlg::DoDataExchange(CDataExchange* pDX)

    {

          CDialog::DoDataExchange(pDX);

          //{{AFX_DATA_MAP(CAboutDlg)

          //}}AFX_DATA_MAP

    }

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

    BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)

          //{{AFX_MSG_MAP(CAboutDlg)

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

          //}}AFX_MSG_MAP

    END_MESSAGE_MAP()

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

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

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

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

          : CDialog(CDialogDlg::IDD, pParent)

    {

          //{{AFX_DATA_INIT(CDialogDlg)

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

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

          //}}AFX_DATA_INIT

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

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

          // Win32

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

    }

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

    void CDialogDlg::DoDataExchange(CDataExchange* pDX)

    {

          CDialog::DoDataExchange(pDX);

          //{{AFX_DATA_MAP(CDialogDlg)

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

          //}}AFX_DATA_MAP

    }

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

    BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)

          //{{AFX_MSG_MAP(CDialogDlg)



          ON_WM_SYSCOMMAND()

          ON_WM_PAINT()

          ON_WM_QUERYDRAGICON()

          //}}AFX_MSG_MAP

    END_MESSAGE_MAP()

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

    BOOL CDialogDlg::OnInitDialog()

    {

          CDialog::OnInitDialog();

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

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

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

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

          ASSERT(IDM_ABOUTBOX < 0xF000);

          CMenu* pSysMenu = GetSystemMenu(FALSE);

          CString strAboutMenu;

          strAboutMenu.LoadString(IDS_ABOUTBOX);

          if (!strAboutMenu.IsEmpty())

          {

                 pSysMenu->AppendMenu(MF_SEPARATOR);

                 pSysMenu->AppendMenu(MF_STRING,

                                                                                    IDM_ABOUTBOX, strAboutMenu);

          }

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

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

          // не нужен

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

                                                                          // размера

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

                                                                          // размера

         

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

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

         

          return TRUE; 

    }

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

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

    {

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

          {

                 CAboutDlg dlgAbout;

                 dlgAbout.DoModal();

          }

          else

          {

                 CDialog::OnSysCommand(nID, lParam);

          }

    }

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

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

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

    void CDialogDlg::OnPaint()



    {

          if (IsIconic())

          {

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

                 SendMessage(WM_ICONERASEBKGND,

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

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

                 int cxIcon = GetSystemMetrics(SM_CXICON);

                 int cyIcon = GetSystemMetrics(SM_CYICON);

                 CRect rect;

                 GetClientRect(&rect);

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

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

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

                 dc.DrawIcon(x, y, m_hIcon);

          }

          else

          {

                 CDialog::OnPaint();

          }

    }

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

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

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

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

    HCURSOR CDialogDlg::OnQueryDragIcon()

    {

          return (HCURSOR) m_hIcon;

    }


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


    Библиотека классов MFC содержит большое количество разнообразных классов. Так MFC версии 4.0 включает немного меньше 200 классов. Каждый класс, как правило, содержит от нескольких единиц до нескольких десятков различных методов и элементов данных. Конечно это значительно усложняет освоение программирования с помощью MFC. Но не стоит расстраиваться и опускать руки. Вам вовсе не обязательно знать как устроены все 200 классов. Большинство приложений можно построить на основе нескольких основных классов. Более того, самое простое приложение, которое мы предложим вашему вниманию, будет использовать только один класс из библиотеки MFC.
    Мы не станем приводить здесь всю иерархию классов MFC. Вы можете изучить ее, воспользовавшись документацией или справочной системой среды Visual C++. Чтобы просмотреть иерархию классов в справочной системе Visual C++, выберите из окна Project Workspace страницу InfoView, откройте описание MFC 4.0, а затем из раздела Class Library Reference выберите статью Hierarcy Chart (рис. 2.1).
    Классы библиотеки MFC
    Рис. 2.1. Просмотр иерархии классов MFC
    Сейчас мы кратко рассмотрим назначение основных классов библиотеки MFC и их связь друг с другом.


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


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


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


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

    Класс приложения
    Базовый класс
    Описание
    CSingleApp
    CWinApp
    Главный класс приложения
    CMainFrame
    CFrameWnd
    Класс главного окна приложения
    CSingleDoc
    CDocument
    Класс документа приложения
    CSingleView
    CView
    Класс окна просмотра документа

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


    Классы


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

    {

         

    } [];    
    Когда вы определяете класс, то сначала указывается ключевое слово class, а затем в качестве аргумента имя самого класса. Это имя должно быть уникальным среди имен других классов, определенных в вашей программе.
    Затем в фигурных скобках следует список элементов класса . В качестве элементов класса могут фигурировать данные (переменные), битовые поля, функции, вложенные классы, а также некоторые другие объекты. Вы можете включить качестве элемента класса указатель на другие объекты этого класса.
    Классы образуют собственное пространство имен. Имена элементов одного класса могут совпадать с именами элементов другого класса и даже с именами других переменных и функций определенных в программе.
    Функции входящие в класс, называются функциями-элементами, или следуя терминологии объектно-ориентированного подхода, методами. Далее мы будем называть такие функции методами. Внутри класса вы можете свободно обращаться ко всем его элементам функциям и данным, без указания имени класса или имени объекта этого класса.
    После закрывающей фигурной скобки в аргументе можно объявить один или несколько объектов данного класса. Объекты класса можно объявить и позже, точно так же как объявляются переменные простых типов:
    [class] tag declarators;
    Ключевое слово class перед именем класса можно опустить.
    Для динамического создания и удаления объектов классов можно пользоваться операторами new и delete. Эти операторы позволяют легко строить списки классов, деревья и другие сложные структуры.


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


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


    Коллекции


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


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


    Большинство командных сообщений от элементов меню и панели управления поступают для обработки главному окну приложения, представленному объектом класса CMainFrame.
    Главное окно приложения сразу передает командное сообщение для обработки окну просмотра. Окно просмотра представлено объектом класса CSingleView.
    Если класс окна просмотра не имеет метода для обработки сообщения, оно передается документу. Документ приложения Single является объектом класса CSingleDoc.
    Если документ приложения также не может обработать командное сообщение, оно передается объекту, представляющему шаблон документа.
    В случае, если шаблон документа также не содержит обработчика сообщения, проверяется таблица сообщений класса главного окна приложения.
    Если и главное окно приложения не может обработать поступившее командное сообщение, оно направляется объекту главного класса приложения CSingleApp.


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


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

    Идентификатор командного сообщения
    Описание
    ID_APP_EXIT
    Завершить приложение.
    Данное командное сообщение обрабатывается методом OnAppExit класса CWinApp. Метод OnAppExit передает сообщение WM_CLOSE главному окну приложения
    ID_APP_ABOUT
    Отобразить на экране краткую справку о программе - диалоговую панель About.
    Ни один из классов MFC не выполняет обработки этого сообщения по умолчанию, но MFC AppWizard автоматически создает необходимый для этого программный код



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


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

    Идентификатор командного сообщения
    Описание
    ID_EDIT_CLEAR
    Удалить выделенный объект
    ID_EDIT_CLEAR_ALL
    Удалить содержимое документа
    ID_EDIT_COPY
    Скопировать выделенный объект в универсальный буфер обмена clipboard
    ID_EDIT_CUT
    Удалить выделенный объект и записать его в clipboard
    ID_EDIT_FIND
    Отобразить на экране диалоговую панель для поиска заданного объекта в документе
    ID_EDIT_PASTE
    Вставить в документ содержимое Clipboard
    ID_EDIT_REPEAT
    Повторить последнюю операцию
    ID_EDIT_REPLACE
    Отобразить диалоговую панель для поиска и замены текста
    ID_EDIT_SELECT_ALL
    Выбрать (выделить) весь документ
    ID_EDIT_UNDO
    Отменить последнюю операцию
    ID_EDIT_REDO
    Выполнить последнюю отмененную операцию



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


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

    Идентификатор командного сообщения
    Описание
    ID_FILE_NEW
    Создать новый документ. Класс CWinApp содержит стандартный обработчик этого сообщения - метод OnFileNew. Если вы желаете его использовать, необходимо поместить в таблицу сообщений главного класса приложения соответствующую макрокоманду (см. приложение Single)
    ID_FILE_OPEN
    Открыть документ, записанный на диске. Класс CWinApp содержит стандартный обработчик этого сообщения - метод OnFileOpen. Если вы желаете его использовать, необходимо поместить в таблицу сообщений главного класса приложения соответствующую макрокоманду (см. приложение Single)
    ID_FILE_CLOSE
    Закрыть текущий документ. Класс CDocument содержит метод OnFileClose, предназначенный для обработки этого командного сообщения. Метод OnFileClose вызывает метод SaveModified, если документ приложения был изменен, а затем вызывает метод OnCloseDocument
    ID_FILE_SAVE
    Сохранить текущий документ. За обработку этого командного сообщения отвечает метод OnSaveDocument класса CDocument
    ID_FILE_SAVE_AS
    Сохранить текущий документ под новым именем. За обработку этого командного сообщения отвечает метод OnSaveDocument класса CDocument
    ID_FILE_SAVE_COPY_AS
    Сохранить копию текущего документа под новым именем
    ID_FILE_PAGE_SETUP
    Вызывает диалоговую панель выбора формата документа
    ID_FILE_PRINT_SETUP
    Вызвать диалоговую панель для настройки принтера
    ID_FILE_PRINT
    Выполнить печать текущего документа
    ID_FILE_PRINT_PREVIEW
    Перейти в режим предварительного просмотра документа перед печатью
    ID_FILE_MRU_FILE1...

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



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


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

    Идентификатор командного сообщения
    Описание
    ID_HELP_INDEX
    Отобразить список статей из справочной базы данных, записанной в HLP-файле
    ID_HELP_USING
    Отобразить подсказку об использовании справочной системы
    ID_CONTEXT_HELP
    Перейди в режим контекстной подсказки. Передается также при нажатии комбинации клавиш
    ID_HELP
    Получить справочную информацию по данному контексту
    ID_DEFAULT_HELP
    Получить справочную информацию определенную по умолчанию для данного контекста



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


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

    Идентификатор командного сообщения
    Описание
    ID_VIEW_TOOLBAR
    Отобразить или скрыть панель управления toolbar
    ID_VIEW_STATUS_BAR
    Отобразить или скрыть панель состояния status bar

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


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


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

    Идентификатор командного сообщения
    Описание
    ID_WINDOW_NEW
    Открыть новое окно с текущим документом
    ID_WINDOW_ARRANGE
    Выровнять пиктограммы в нижней части окна MDI
    ID_WINDOW_CASCADE
    Выполнить каскадное размещение окон
    ID_WINDOW_TILE_HORZ
    Расположить окна рядом по горизонтали
    ID_WINDOW_TILE_VERT
    Расположить окна рядом по вертикали
    ID_WINDOW_SPLIT
    Разделить окно на две части



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


    Сообщения WM_COMMAND от меню, кнопок панели управления и клавиш акселераторов. В отличие от оконных сообщений и сообщений от органов управления, командные сообщения могут быть обработаны более широким спектром объектов. Эти сообщения обрабатывают не только объекты, представляющие окна, но также объекты классов, представляющих приложение, документы и шаблон документов.
    Характерной особенностью командных сообщений является идентификатор. Идентификатор командного сообщения определяет объект, который вырабатывает (посылает) данное сообщение.
    В приложении MFMenu строка Beep меню Test имеет идентификатор ID_TEST_BEEP. Когда пользователь выберет данную строку, в очередь приложения поступит сообщение WM_COMMAND с идентификатором ID_TEST_BEEP или другими словами, командное сообщение ID_TEST_BEEP.


    Константы


    В Си++ существует удобное средство определения констант. Если в Си вы должны были пользоваться директивой препроцессора #define, то теперь введено новое ключевое слово const, позволяющее создавать константы. Преимущество в использовании ключевого слова const перед директивой #define состоит в том, что компилятор проверяет тип этих констант.
    Ключевое слово const указывают перед объявлением переменной. Такая переменная не может быть модифицирована. Попытки изменить ее вызывают ошибку на этапе компиляции.
    В программе, приведенной ниже, объявляются две константы. Одна типа int, другая типа char:
    // Включаемый файл для потокового ввода/вывода
    #include
    int main(void)
    {
          // Объявляем две константы
          const int     max_nuber = 256;
          // Выводим текстовую строку на экран
          printf("Const Number is %d \n", max_nuber);
          return 0;
    }
    Ключевое слово const можно указывать при объявлении постоянных указателей, которые не могут менять своего значения. Заметим, что объект (переменная), определяемый постоянным указателем, может быть изменен:
    int  iNumber;
    int  *const ptrNumber = &iNumber;


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


    MFC AppWizard определяет для вас конструктор и деструктор класса CMainFrame. Вы можете расположить в конструкторе и деструкторе CMainFrame код для инициализации объектов класса.
    // Конструктор класса CMainFrame
    CMainFrame::CMainFrame()
    {
          // TODO:
    }
    // Деструктор класса CMainFrame
    CMainFrame::~CMainFrame()
    {
    }


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


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


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


    Конструктор класса CSingleView, созданный MFC AppWizard, не содержит команд. Вы можете доработать его, чтобы конструктор выполнял инициализацию, связанную с окном просмотра.
    // Конструктор класса CSingleView
    CSingleView::CSingleView()
    {
          // TODO:
    }
    Вместе с конструктором класса CSingleView, MFC AppWizard определяет деструктор ~ CSingleView. Сразу после создания проекта деструктор не выполняет никаких действий. В дальнейшем вы можете использовать его совместно с конструктором CSingleView.
    // Деструктор класса CSingleView
    CSingleView::~CSingleView()
    {
    }


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


    Конструктор класса CDialogDlg вызывает конструктор базового класса CDialog. При этом ему передается идентификатор диалоговой панели IDD и идентификатор главного окна приложения pParent. При создании объекта класса CDialogDlg не указываются никакие параметры. Поэтому pParent по умолчанию принимается равным NULL.
    //////////////////////////////////////////////////////////////
    // Конструктор класса CDialogDlg
    CDialogDlg::CDialogDlg(CWnd* pParent /*=NULL*/)
          : CDialog(CDialogDlg::IDD, pParent)
    {
          //{{AFX_DATA_INIT(CDialogDlg)
                 // В этом блоке ClassWizard размещает инициализацию
                 // элементов данных класса
          //}}AFX_DATA_INIT
          // Вызов LoadIcon не требует последующего вызова
          // DestroyIcon, если вы используете программный интерфейс
          // Win32
          m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    }
    В теле конструктора расположен блок AFX_DATA_INIT. В него ClassWizard будет добавлять код инициализации элементов данных класса CDialogDlg. Конструктор также инициализирует m_hIcon, записывая в него идентификатор пиктограммы IDR_MAINFRAME.
    Функция AfxGetApp возвращает указатель на объект главного класса приложения. Такой объект для данного приложения всегда один. В нашем случае AfxGetApp определяет указатель на объект theApp. Вот прототип этой функции:
    CWinApp* AfxGetApp();


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


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


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


    Класс CString имеет несколько различных конструкторов, позволяющих создавать строки на основе различных данных.
    Конструктор класса CString, используемый по умолчанию, не имеет параметров. Он создает пустую строку. В последствии вы можете записать в нее любой текст.
    CString();
    Для класса CString определен конструктор копирования. Он позволяет создать строку на основе другой, ранее определенной строки. Этот и все остальные конструкторы могут вызвать исключение CMemoryException, если для создания строки недостаточно оперативной памяти.
    CString(const CString& stringSrc);

          throw(CMemoryException);
    Конструктор, представленный ниже, позволяет создать строку из nRepeat символов ch. Параметр nRepeat можно не указывать. В этом случае строка будет содержать единственный символ ch.
    CString(TCHAR ch, int nRepeat = 1);

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

          throw(CMemoryException);
    Новую строку можно создать и скопировать в нее данные из массива символов, закрытого двоичным нулем. В этом случае количество символов, которые будут записаны в новую строку не указывается. Копируются все символы до двоичного нуля.
    Для создания строки вы можете воспользоваться одним из трех конструкторов, представленных ниже. Как видите, конструкторы отличаются только типом параметра:
    CString(const unsigned char* psz);

          throw(CMemoryException);
    CString(LPCWSTR lpsz);

          throw(CMemoryException);
    CString(LPCSTR lpsz);

          throw(CMemoryException);


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


    Обычно при создании объекта класса необходимо провести начальную инициализацию объекта, например выделить участок памяти для размещения каких-либо данных, связанных с этим объектом. После окончания использования объекта выделенную память надо освободить и отдать обратно операционной системе.
    Язык С++ предоставляет удобное средство для инициализации и удаления объектов класса. Для этого предусмотрены специальные методы. Они называются конструкторами и деструкторами.
    Функция конструктор имеет такое же имя как имя класса и позволяет выполнить инициализацию объекта класса в момент его создания. Конструктор может иметь параметры. Их надо будет указать при определении объекта данного класса. Класс может иметь несколько конструкторов с разными параметрами, то есть конструкторы могут быть перегружены.
    Класс BookList, представленный ниже, имеет два конструктора BookList. Первый конструктор не имеет параметров, второй конструктор имеет один параметр типа int:
    class BookList

    {
          // Конструкторы класса
          void      BookList();
          void      BookList(int);
          // Остальные члены класса
    };
    // Первый конструктор класса
    BookList::BookList(void)
    {
    }
    // Второй конструктор класса
    BookList::BookList(int iList)
    {
    }
    Когда вы создаете объекты класса, вы можете указать параметры для конструктора. Ниже создаются два объекта класса BookList - FirstBook и SecondBook:
    BookList FirstBook;
    BookList SecondBook(100);
    При создании первого объекта параметры конструктора не указываются, поэтому используется первый конструктор класса. А вот при создании второго объекта мы указали числовой параметр, поэтому в этом случае используется второй конструктор.
    Имя деструктора также соответствует имени класса, но перед ним должен стоять символ тильда. Деструктор вызывается автоматически, когда объект уничтожается. Например, если определить объект данного класса внутри блока, то при выходе из блока для объект будет вызвана функция деструктор. Функция деструктор не имеет параметров, поэтому она не может быть перегружена, а значит у данного класса может быть только один деструктор.
    Ниже представлен класс Object, для которого определен деструктор ~Object:
    class Object

    {
          void      ~Object();
          // Остальные члены класса
    };
    Object::~Object(void)
    {
    }


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


    Для класса CObject определены два конструктора. Первый конструктор используется по умолчанию и не имеет параметров. Именно этот конструктор вызывается конструкторами классов, наследованных от CObject:
    CObject();
    Второй конструктор класса CObject называется конструктором копирования, потому что в качестве параметра objectSrc он принимает ссылку на другой объект этого же класса. Конструктор копирования создает новый объект и выполняет копирование всех элементов объекта, указанного в качестве параметра.
    Для класса CObject конструктор копирования описан как private и не имеет определения. Если вы попытаетесь передать по значению объект класса, наследованного от CObject, и не определите конструктор копирования в своем классе, вы получите ошибку на этапе компиляции.
    В случае, если бы конструктор копирования класса CObject не был описан, компилятор создал бы его автоматически. Таким образом описание конструктора копирования как private, принуждает вас явно определить конструктор копирования для порожденных классов (конечно если конструктор копирования необходим):
    private:
          CObject(const CObject& objectSrc);
    Для динамического создания и удаления объектов класса CObject вы можете воспользоваться операторами new и delete. Эти операторы переопределены в классе CObject, однако назначение и основные принципы их работы остались прежними.


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


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

    Класс
    Описание
    CClientDC
    Контекст отображения, связанный с внутренней областью окна (client area). Для получения контекста конструктор класса вызывает функцию программного интерфейса GetDC, а деструктор - функцию ReleaseDC
    CMetaFileDC
    Класс предназначен для работы с метафайлами
    CPaintDC
    Конструктор класса CPaintDC для получения контекста отображения вызывает метод CWnd::BeginPaint, деструктор метод CWnd::EndPaint. Объекты данного класса могут использовать только при обработке сообщения WM_PAINT. Это сообщение обычно обрабатывает метод OnPaint
    CWindowDC
    Контекст отображения, связанный со всем окном. Для получения контекста конструктор класса вызывает функцию программного интерфейса GetWindowDC, а деструктор - функцию ReleaseDC



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


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


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


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


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


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


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


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


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


    Макрокоманды ON_ предназначены для обработки сообщений от органов управления. Такие сообщения могут передаваться органами управления диалоговой панели. Сообщения от органов управления не имеют обработчиков, используемых по умолчанию. При необходимости вы должны определить их самостоятельно.
    Все макрокоманды ON_ имеют два параметра. В первом параметре id указывается идентификатор органа управления. Сообщения от этого органа управления будут обрабатываться методом memberFxn.
     ON_BN_CLICKED(id, memberFxn)


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


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


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


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


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


    Макрокоманда ON_UPDATE_COMMAND_UI обрабатывает сообщения, предназначенные для обновления пользовательского интерфейса, например меню, панелей управления и позволяет менять их состояние.
    Параметр id указывает идентификатор сообщения, а параметр memberFxn имя метода для его обработки.
    ON_UPDATE_COMMAND_UI(id, memberFxn)
    Методы, предназначенные для обработки данного класса сообщений, должны быть определены с ключевым словом afx_msg и иметь один параметр - указатель на объект класса CCmdUI. Для удобства, имена методов, предназначенных для обновления пользовательского интерфейса, начинаются с OnUpdate:
    afx_msg void OnUpdate<имя_обработчика>(CCmdUI* pCmdUI);
    В качестве параметра pCmdUI методу передается указатель на объект класса CCmdUI. В нем содержится информация о объекте пользовательского интерфейса, который надо обновить - строке меню или кнопке панели управления. Класс CCmdUI также включает методы, позволяющие изменить внешний вид представленного им объекта пользовательского интерфейса.
    Сообщения, предназначенные для обновления пользовательского интерфейса передаются, когда пользователь открывает меню приложения и во время цикла ожидания приложения, когда очередь сообщений приложения становится пуста.
    При этом посылается несколько сообщений, по одному для каждой строки меню. С помощью макрокоманд ON_UPDATE_COMMAND_UI можно определить методы-обработчики, ответственные за обновление внешнего вида каждой строки меню и соответствующей ей кнопке на панели управления. Эти методы могут изменять состояние строки меню - отображать ее серым цветом, запрещать ее выбор, отображать около нее символ v и т. д.
    Если вы не определите метод для обновления данной строки меню или кнопки панели управления, выполняется обработка по умолчанию. Выполняется поиск обработчика соответствующего командного сообщения и если он не обнаружен, выбор строки меню запрещается.
    Дополнительную информацию об обновлении пользовательского интерфейса приложения вы можете получить в разделе “Однооконный интерфейс”.


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


    Обрабатывает стандартные сообщения операционной системы Windows. Вместо указывается имя сообщения без префикса WM_. Так, например для обработки сообщения WM_SIZE предназначена макрокоманда ON_WM_SIZE.
    Для обработки сообщений, определенных в таблице сообщений макрокомандами ON_WM_, вызываются одноименные методы. Имя метода обработчика соответствует названию сообщения, без учета префикса WM_.
    В классе CWnd определены обработчики для стандартных сообщений. Эти обработчики будут использоваться по умолчанию. Вот некоторые из таких методов.

    Сообщение
    Макрокоманда
    Метод обработчик
    WM_CHAR
    ON_WM_CHAR()
    afx_msg void
    OnChar(UINT, UINT, UINT);
    WM_CREATE
    ON_WM_CREATE()
    afx_msg int
    OnCreate(LPCREATESTRUCT );
    WM_HSCROLL
    ON_WM_HSCROLL()
    afx_msg void
    OnHScroll(UINT, UINT, CWnd*);
    WM_KEYDOWN
    ON_WM_KEYDOWN()
    afx_msg void
    OnKeyDown(UINT, UINT, UINT);
    WM_KEYUP
    ON_WM_KEYUP()
    afx_msg void
    OnKeyUp(UINT, UINT, UINT);
    WM_LBUTTONDOWN
    ON_WM_LBUTTONDOWN()
    afx_msg void
    OnLButtonDown(UINT, CPoint);
    WM_LBUTTONUP
    ON_WM_LBUTTONUP()
    afx_msg void
    OnLButtonUp(UINT, CPoint);
    WM_PAINT
    ON_WM_PAINT()
    afx_msg void
    OnPaint();
    WM_SIZE
    ON_WM_SIZE()
    afx_msg void
    OnSize(UINT, int, int);
    WM_TIMER
    ON_WM_TIMER()
    afx_msg void
    OnTimer(UINT);
    WM_VSCROLL
    ON_WM_VSCROLL()
    afx_msg void
    OnVScroll(UINT, UINT, CWnd*);

    Все методы-обработчики определены с ключевым словом afx_msg. Оно позволяет отличить эти методы от остальных методов класса. На этапе препроцессорной обработки ключевое слово afx_msg удаляется. Определение afx_msg вы можете найти в файле afxwin.h:
    #define afx_msg
    Макрокоманды ON_WM_ не имеют параметров. Однако методы, которые вызываются для обработки соответствующих сообщений, имеют параметры, количество и назначение которых зависит от обрабатываемого сообщения.
    Когда вы определяете обработчик стандартного сообщения Windows в своем классе, он будет использоваться вместо обработчика определенного в классе CWnd (или другом базовом классе). В любом случае вы можете вызвать метод обработчик базового класса из своего метода обработчика.


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


    Библиотека MFC включает классы для организации массивов байт, слов, двойных слов, указателей, объектов класса CString и указателей на объекты класса CObject. В MFC версии 4.0 добавлен шаблон класса массива, позволяющий создавать на его основе собственные массивы переменных любых типов и массивы объектов любых классов. Для доступа к элементам массива можно использовать оператор [].
    Ниже представлен прототип шаблона CArray.
    template 
          class CArray : public CObject
    Параметр шаблона TYPE определяет тип элементов массива. В качестве TYPE можно указывать не только простые типы, например int, char, но также типы, являющиеся классами. Второй параметр шаблона ARG_TYPE определяет тип параметра массива, который используется для доступа к его элементам.
    Мы будем использовать шаблон CArray для организации массива в приложении Single (см. раздел “Простейший графический редактор” главы “Однооконный интерфейс”).


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


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

    Класс
    Массив содержит
    CByteArray
    Байты
    CDWordArray
    Двойные слова
    CObArray
    Указателей на объекты класса CObject
    CPtrArray
    Указателей типа void
    CStringArray
    Объекты класса CString
    CUIntArray
    Элементы класса unsigned integer или UINT
    CWordArray
    Слова

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

    Класс
    Список содержит элементы
    CObList
    Указатели на объекты класса CObject
    CPtrList
    Указатели типа void
    CStringList
    Объекты класса CString

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

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

    Класс

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

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

    CMapPtrToPtr

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

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

    CMapPtrToWord

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

    Слово

    CMapStringToOb

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

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

    CMapStringToPtr

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

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

    CMapStringToString

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

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

    CMapWordToOb

    Слово

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

    CMapWordToPtr

    Слово

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

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


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


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


    Метод DoDataExchange


    Диалоговая панель приложения содержит только две кнопки и не имеет связанных с ними переменных. Однако метод DoDataExchange переопределен. Фактически он не выполняет ни какой работы. Единственное что он делает, это вызывает метод DoDataExchange базового класса CDialog.
    Если вы добавите к диалоговой панели новые органы управления и свяжете их средствами ClassWizard с элементами данных класса CDialogDlg, то в блоке AFX_DATA_MAP будут размещены вызовы методов DDE и DDV, необходимые для выполнения обмена данными.
    void CDialogDlg::DoDataExchange(CDataExchange* pDX)
    {
          CDialog::DoDataExchange(pDX);
          //{{AFX_DATA_MAP(CDialogDlg)
                 // Здесь ClassWizard размещает вызовы методов DDX и DDV
          //}}AFX_DATA_MAP
    }


    Метод Flush


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

          throw(CFileException);


    Метод GetDocument


    В секции атрибутов класса CSingleView после комментария Attributes объявлен метод GetDocument. Этот метод возвращает указатель на документ, связанный с данным окном просмотра. Если окно просмотра не связано ни с каким документом, метод возвращает значение NULL.
    Интересно, что метод GetDocument имеет две реализации. Одна используется для отладочной версии приложения, а другая для окончательной.
    Окончательная версия GetDocument определена непосредственно после самого класса окна просмотра CSingleView как встраиваемый (inline) метод. Когда вы используете страницу ClassView окна Project Workspace, чтобы просмотреть определение метода GetDocument, вы увидите именно этот код.
    // Окончательная версия приложения
    #ifndef _DEBUG    
    inline CSingleDoc* CSingleView::GetDocument()
          { return (CSingleDoc*)m_pDocument; }
    #endif
    Отладочная версия GetDocument расположена в файле реализации класса окна просмотра SingleView.cpp. Откройте этот файл вручную, выбрав его название из страницы FileView окна Project Workspace.
    // Отладочная версия приложения
    #ifdef _DEBUG      
    CSingleDoc* CSingleView::GetDocument()
    {
          ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSingleDoc)));
          return (CSingleDoc*)m_pDocument;
    }
    #endif //_DEBUG
    Макрокоманда RUNTIME_CLASS возвращает указатель на структуру CRuntimeClass, содержащую информацию о классе CSingleDoc. Метод IsKindOf, определенный в классе CObject, проверяет, принадлежит ли объект, на который указывает m_pDocument, к классу CSingleDoc или классу наследованному от CSingleDoc. Если в приложении есть ошибка и m_pDocument не указывает на документ приложения, макрокоманда ASSERT отображает соответствующее сообщение и прерывает работу приложения.


    Метод InitInstance


    Каждый раз, когда запускается очередная копия приложения, вызывается метод InitInstance главного класса приложения. Это единственный метод главного класса приложения, который должен быть переопределен в любом приложении.
    Когда вы разрабатываете приложение с помощью MFC AppWizard, он переопределяет метод InitInstance, упрощая вам работу. В последствии вы можете изменить этот метод по своему усмотрению. Как MFC AppWizard переопределит метод InitInstance, зависит в первую очередь от того, какой тип пользовательского интерфейса вы выбрали для своего приложения и какие дополнительные характеристики приложения указали в диалоговых панелях MFC AppWizard.
    Самая простая реализация InitInstance у приложений, пользовательский интерфейс которых основан на диалоговой панели.
    BOOL CDialogApp::InitInstance()
    {
          // ...
    }
    Если вы указали, что ваше приложение должно иметь трехмерный графический интерфейс, то метод InitInstance вызывает метод Enable3dControls или Enable3dControlsStatic, определенные в классе CWinApp. Эти методы разрешают использование трехмерных органов управления. Какой из этих методов будет использоваться определяется на этапе работы препроцессора в зависимости от того, определен или нет символ _AFXDLL.
    // Использовать для приложения трехмерный интерфейс
    #ifdef _AFXDLL
          Enable3dControls();
    #else
          Enable3dControlsStatic();
    #endif
    Символ _AFXDLL определяется средой Visual C++, если вы используете библиотеку классов MFC как библиотеку DLL. Если же код MFC подключается к приложению как статическая библиотека, этот символ не определен.
    Вы можете удалить вызов методов Enable3dControls, если уже после создания проекта решите, что приложение должно иметь простой “плоский” интерфейс. И наоборот, вы можете добавить вызов этого метода, если на этапе разработки приложения забыли указать на необходимость использования трехмерного интерфейса.
    Затем метод InitInstance создает диалоговую панель, которая и будет выполнять роль пользовательского интерфейса приложения. Для этого сначала создается объект dlg класса CDialogDlg, который управляет диалоговой панелью. Затем адрес этого объекта присваивается элементу данных m_pMainWnd главного класса приложения.

    CDialogDlg dlg;

    m_pMainWnd = &dlg;

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

    int nResponse = dlg.DoModal();

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

    if (nResponse == IDOK)

    {

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

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

    }

    else if(nResponse == IDCANCEL)

    {

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

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

    }

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

    return FALSE;



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

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

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

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

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

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


    Метод IsKindOf


    Метод IsKindOf определяет отношение объекта и класса, представленного указателем pClass на структуру CRuntimeClass. Метод правильно работает только для классов, в объявлении которых указаны макрокоманды DECLARE_DYNAMIC или DECLARE_SERIAL.
    BOOL IsKindOf(const CRuntimeClass* pClass) const;
    Метод возвращает ненулевое значение, если объект, для которого он вызван, принадлежит классу заданному параметром pClass или классу наследованному от него. В противном случае метод возвращает нулевое значение.


    Метод IsSerializable


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


    Метод OnAppAbout


    Метод OnAppAbout вызывается для обработки командного сообщения ID_APP_ABOUT. Это сообщение поступает в очередь приложения, когда пользователь выбирает из меню Help строку About. Он создает объект класса CAboutDlg, представляющий модальную диалоговую панель и отображает ее на экране.
    // Метод OnAppAbout
    void CSingleApp::OnAppAbout()
    {
          CAboutDlg aboutDlg;
          aboutDlg.DoModal();
    }
    Описание класса CAboutDlg, а также определение его методов содержится в файле Single.cpp. Класс CAboutDlg приложения Single полностью соответствует классу CAboutDlg приложения Dialog, описанного в предыдущей главе. Мы не будем повторять описание класса CAboutDlg, вы можете самостоятельно найти его в листинге 4.4.


    Метод OnCreate


    Метод OnCreate определен в классе CWnd следующим образом.
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    Параметр lpCreateStruct содержит указатель на объект CREATESTRUCT, содержащий характеристики создаваемого окна. Эта структура уже была нами описана ранее.
    При нормальной работе OnCreate должен вернуть значение 0, чтобы продолжить создание окна. Если OnCreate возвращает –1, окно будет удалено (уничтожено).
    MFC AppWizard переопределяет метод OnCreate в классе CMainFrame. Поэтому для обработки сообщения WM_CREATE будет вызван именно переопределенный метод OnCreate класса CMainFrame.
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
          // Вызываем метод OnCreate базового класса
          if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
                 return -1;
          // Создаем панель управления toolbar
          if (!m_wndToolBar.Create(this)
                 !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
          {
                 // Ошибка при создании панели управления toolbar
                 TRACE0("Failed to create toolbar\n");
                 return -1;     
          }
          // Создаем панель состояния status bar
          if (!m_wndStatusBar.Create(this)
                 !m_wndStatusBar.SetIndicators(indicators,
                   sizeof(indicators)/sizeof(UINT)))
          {
                 // Ошибка при создании панели состояния status bar
                 TRACE0("Failed to create status bar\n");
                 return -1;
          }
          // TODO: вы можете изменить характеристики панели
          // управления, убрав некоторые флаги CBRS_
          m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
                 CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
          // TODO: вы можете запретить перемещение панели управления,
          // если удалите следующие три строки программы
          m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
          EnableDocking(CBRS_ALIGN_ANY);
          DockControlBar(&m_wndToolBar);
          return 0;
    }
    Основное назначение метода OnCreate заключается в том, что он сначала вызывает метод OnCreate базового класса CFrameWnd, а затем создает и отображает внутри главного окна панель управления toolbar и панель состояния status bar.
    Метод OnCreate базового класса CFrameWnd выполняет обработку сообщения WM_CREATE по умолчанию и возвращает нулевое значение если обработка прошла без ошибок.
    В случае возникновения ошибок при обработке сообщения в базовом классе возвращается -1. Метод CMainFrame::OnCreate при этом также прерывает дальнейшую обработку и возвращает -1, вызывая удаление окна.


    Метод OnDraw


    Метод OnDraw вызывается, когда надо отобразить документ в окне. В качестве параметра pDC методу OnDraw передается указатель на контекст устройства, используя который надо отобразить документ. В зависимости от ситуации, метод OnDraw вызывается для отображения документа в окне просмотра, вывода на печать и предварительного просмотра документа перед печатью. Контекст устройства в каждом случае используется разный.
    Используя контекст устройства, переданный параметром pDC, вы можете вызывать различные методы графического интерфейса, чтобы отображать информацию в окне.
    В том случае, если внешний вид документа при выводе его на печать должен отличаться от вывода в окно, вы можете вызвать метод IsPrinting контекста устройства, чтобы определить, для чего вызывается OnDraw.
    BOOL IsPrinting() const;
    Метод IsPrinting возвращает ненулевое значение, если объект контекста устройства, для которого он вызывается, является контекстом принтера. Если контекст представляет другое устройства отображения, например окно, тогда метод IsPrinting возвращает нуль.
    MFC AppWizard переопределяет для вас метод OnDraw класса CView следующим образом:
    void CSingleView::OnDraw(CDC* pDC)
    {
          CSingleDoc* pDoc = GetDocument();
          ASSERT_VALID(pDoc);
          // TODO: Здесь вы можете расположить код, для отображения
          // данных в контексте устройства pDC
    }
    Первые две строки метода OnDraw служат для получения указателя pDoc на документ, связанный с данным окном просмотра. Предполагается, что используя указатель pDoc, вы получите данные из документа и отобразите их на экране.


    Метод OnInitDialog


    Когда вы отображаете диалоговую панель на экране, вызывая методы DoModal, Create или CreateIndirect, функции диалоговой панели передается сообщение WM_INITDIALOG. Вы не имеете доступа непосредственно в функции диалога. Ее реализация содержится в базовом классе CDialog.
    В ответ на сообщение WM_INITDIALOG вызывается метод OnInitDialog, объявленный как виртуальный метод класса CDialog. Метод OnInitDialog вызывается непосредственно перед выводом панели на экран.
    Таблица сообщений класса CDialogDlg не содержит макрокоманд для обработки сообщения WM_INITDIALOG. Метод OnInitDialog вызывается непосредственно MFC.
    Чтобы реализовать собственную обработку сообщения WM_INITDIALOG, нужно просто переопределить метод OnInitDialog. Переопределенный метод должен сразу вызвать метод OnInitDialog базового класса CDialog.
    Для приложения Dialog MFC AppWizard уже переопределил метод OnInitDialog. В реализации метода добавляется новая строка к системному меню диалоговой панели для вызова краткой справки о приложении. Затем вызывая метод SetIcon, определенный в базовом классе CWnd, мы выбираем пиктограммы для приложения.
    Метод OnInitDialog возвращает значение TRUE. Это означает, что фокус ввода будет установлен на первый орган управления диалоговой панели. Первый орган диалоговой панели можно выбрать в редакторе диалоговой панели, выбрав из меню Layout строку Tab Order.
    Если во время инициализации диалоговой панели метод OnInitDialog устанавливает фокус ввода другому органу управления, метод должен вернуть значение FALSE.
    BOOL CDialogDlg::OnInitDialog()
    {
          CDialog::OnInitDialog();
          // Добавление строки "About..." к системному меню приложения
          // Проверяем, что идентификатор IDM_ABOUTBOX относится к
          // системным командам
          ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
          ASSERT(IDM_ABOUTBOX < 0xF000);
          CMenu* pSysMenu = GetSystemMenu(FALSE);
          CString strAboutMenu;
          strAboutMenu.LoadString(IDS_ABOUTBOX);
          if (!strAboutMenu.IsEmpty())
          {
                 pSysMenu->AppendMenu(MF_SEPARATOR);
                 pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX,
                       strAboutMenu);
          }
          // Выбираем пиктограмму для диалоговой панели. Если главное
          // окно приложения не является диалоговой панелью этот код
          // не нужен
          SetIcon(m_hIcon,TRUE);  // Выбираем пиктограмму большого
                                                                          // размера
          SetIcon(m_hIcon,FALSE); // Выбираем пиктограмму маленького
                                                                          // размера
         
          // TODO: Здесь вы можете выполнить дополнительную
          // инициализацию
          return TRUE;
    }


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


    Диалоговая панель может иметь кнопку минимизации. Нажав эту кнопку или выбрав строку Minimaze из системного меню, пользователь может свернуть диалоговую панель (приложение) до размера пиктограммы.
    К сожалению диалоговая панель, в отличие от обычного окна, не содержит встроенных средств отображения пиктограммы приложения. Поэтому приложение должно само заботиться об отображении пиктограммы. MFC AppWizard облегчает эту задачу. Если вы выберите приложение с интерфейсом на основе диалоговой панели, то в исходный текст добавляется определение метода OnPaint. Рассмотрим метод OnPaint подробнее.
    void CDialogDlg::OnPaint()
    {
          // Определяем размер диалоговой панели
          if (IsIconic())
          {
          // Если диалоговая панель минимизирована, отображаем
          // пиктограмму
                 CPaintDC dc(this); // получаем контекст устройства
                 SendMessage(WM_ICONERASEBKGND,
                       (WPARAM) dc.GetSafeHdc(), 0);
                 int cxIcon = GetSystemMetrics(SM_CXICON);
                 int cyIcon = GetSystemMetrics(SM_CYICON);
                 // Определяем размеры внутренней области окна
                 CRect rect;
                GetClientRect(&rect);
                 // Выравниваем пиктограмму по центру
                 int x = (rect.Width() - cxIcon + 1) / 2;
                 int y = (rect.Height() - cyIcon + 1) / 2;
                 // Отображаем пиктограмму
                 dc.DrawIcon(x, y, m_hIcon);
          }
          else
          {
                 // Выполняем обработку по умолчанию
                 CDialog::OnPaint();
          }
    }
    После вызова метода OnPaint он проверяет состояние диалоговой панели. Для этого вызывается метод IsIconic, определенный в классе CWnd. Если окно, или в нашем случае диалоговая панель, связанная с объектом, для которого вызывается метод IsIconic, минимизировано, возвращается ненулевое значение, в противном случае - нуль.
    BOOL IsIconic() const;
    В случае если диалоговая панель имеет нормальный размер, управление передается методу OnPaint базового класса CDialog для выполнения обработки по умолчанию.
    Если вызов OnPaint произошел в следствии минимизации диалоговой панели, тогда определяются размеры внутренней области минимизированного окна и размеры пиктограммы. Затем пиктограмма отображается в центре минимизированного окна.


    Метод OnQueryDragIcon


    Пользователь может перетащить пиктограмму минимизированного приложения с места на место. При этом, как правило, форма курсора меняется. Если пользователь перетаскивает пиктограмму окна для класса которого не определена пиктограмма, вызывается метод OnQueryDragIcon. Этот метод должен вернуть идентификатор курсора, который будет отображаться в момент перетаскивания пиктограммы окна.
    MFC AppWizard определяет метод OnQueryDragIcon для класса CDialogDlg, просто возвращая идентификатор пиктограммы приложения.
    HCURSOR CDialogDlg::OnQueryDragIcon()
    {
          // Возвращаем идентификатор пиктограммы
          return (HCURSOR) m_hIcon;
    }


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


    Разрабатывая приложение с помощью MFC AppWizard, мы указали, что оно должно иметь возможность отображения краткой справочной информации. Для этого в системное меню приложения была добавлена строка About.
    Когда пользователь выбирает строки системного меню любого окна, в том числе и диалоговой панели, или нажимает кнопки максимизации и минимизации, в функцию данного окна поступает сообщение WM_SYSCOMMAND. Для обработки этого сообщения вызывается виртуальный метод OnSysCommand, определенный в классе CWnd.
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    Параметр nID содержит идентификатор строки системного меню, вызвавшего сообщение. Младшие четыре бита параметра nID используются операционной системой и могут принимать любые значения. Параметр nID (без учета четырех младших бит) может принимать одно из следующих значений.

    Параметр nID
    Описание
    SC_CLOSE
    Закрывает объект CWnd
    SC_HOTKEY
    Активизирует объект CWnd, связанный с комбинацией клавиш, определенной приложением. Младшее слово параметра lParam содержит идентификатор активизируемого окна
    SC_HSCROLL
    Свертка по горизонтали
    SC_KEYMENU
    Выбор из меню при помощи комбинации клавиш
    SC_MAXIMIZE,

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

    SC_ICON
    Минимизировать объект CWnd
    SC_MOUSEMENU
    Выбор из меню при помощи мыши
    SC_MOVE
    Перемещение окна CWnd
    SC_NEXTWINDOW
    Переключение на следующее окно
    SC_PREVWINDOW
    Переключение на предыдущее окно
    SC_RESTORE
    Восстановление нормального расположения и размера окна
    SC_SCREENSAVE
    Запустить приложение, предохраняющее экран монитора, указанное в секции [boot] файла SYSTEM.INI
    SC_SIZE
    Изменить размер окна CWnd
    SC_TASKLIST
    Запустить или активизировать приложение Task Manager
    SC_VSCROLL
    Свертка по вертикали

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

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

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

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

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

    {

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

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

          {

                 CAboutDlg dlgAbout;

                 dlgAbout.DoModal();

          }

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

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

          else

          {

                 CDialog::OnSysCommand(nID, lParam);

          }

    }

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

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

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


    Метод PreCreateWindow


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


    Методы AssertValid и Dump


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


    Методы OnNewDocument и Serialize


    В классе 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


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

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


    В конце 1995 года появилась


    В конце 1995 года появилась новая версия Microsoft Visual C++ - 4.0. Этот компилятор интегрирован в среду Microsoft Developer Studio, в состав которого могут входить другие программные  продукты Microsoft.

    В конце 1995 года появилась

    Интегрированная среда разработчика объединяет одной оболочкой Microsoft Visual C++ версии 4.0, Microsoft Visual Test, Microsoft Fortran PowerStation, Microsoft Development Library и Microsoft Visual SourceSafe

    Для программистов, работающих в среде Visual C++, наиболее интересна возможность интегрирования с базой данных Microsoft Development Library. После того как вы установите в компьютере Microsoft Development Library, вы сможете осуществлять поиск интересующей вас информации не только в справочной базе Visual C++, но и в базе данных Microsoft Development Library. При этом вам не придется переключаться на другое приложение, достаточно выбрать справочную систему в панели Info Viewer.


    Microsoft Visual C++


    Существует несколько версий компилятора 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.


    Многозадачные приложения


    Когда вы запускаете приложение в среде операционной системы Windows, начинает работать новый процесс. Если запустить это же приложение еще один раз, будет запущен новый процесс. Обычно такие процессы никак не связаны друг с другом. Когда приложение завершается, оканчивается соответствующий процесс.
    Каждый процесс может состоять из нескольких нитей (thread) или задач. В рамках одного процесса его задачи выполняются параллельно. До сих пор мы рассматривали только приложения, имеющие единственную задачу. Однако, часто бывает полезно, чтобы внутри одного приложения одновременно выполнялись несколько задач. Например, одна задача может отвечать за взаимодействие с пользователем - принимать от него команды, данные, отображать информацию на экране. Вторая задача может выполнять дополнительную фоновую работу, например печать документа, обмен данными через модем, проводить вычисления.
    Библиотека классов MFC позволяет создавать многозадачные приложения.
    Приложения Windows 95 и Windows NT могут быть многозадачными. Такие приложения состоят из нескольких независимых задач. Дополнительные задачи могут использоваться, чтобы выполнять фоновую работу. Класс CWinApp представляет основную задачу приложения. Если вам надо создать дополнительные задачи, вы должны воспользоваться классом CWinThread.

    Многозадачные приложения
    Приложение Pview 95 позволяет просмотреть список процессов, загруженных в памяти и остановить любой процесс



    Множественное наследование


    Множественное наследование выполняется подобно единичному наследованию. В отличие от единичного наследования у порожденного класса может быть несколько базовых классов. На рисунке 1.2 представлен пример множественного наследования классов. Класс DerivedClaass имеет два базовых класса BaseClassOne и BaseClassSecond. Класс DerivedClaass и еще один класс BaseClass используются при множественном наследовании класса DerivedClaassSecond.
    Множественное наследование
    Рис. 1.2. Множественное наследование
    Вместо имени единственного базового класса указывается список имен базовых классов, разделенный запятыми. Непосредственно перед названиями базовых классов могут быть указаны спецификаторы доступа public, private и protect. Их назначение мы рассмотрим в разделе “Разграничение доступа к элементам базового класса”.
    class [[:[]]

    {

         

    } [];
    Порядок, в котором вы перечислите базовые классы влияет только на последовательность в которой вызываются конструкторы и деструкторы базовых классов. Конструкторы базовых классов вызываются в том порядке, в котором они перечислены (слева на право). Деструкторы базовых классов вызываются в обратном порядке.
    Один и тот же класс нельзя указывать два или более раза в качестве базового класса (за исключением тех случаев, когда он является непрямым базовым классом).
    В следующем примере определены два базовых класса 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 собственные классы. При этом вы можете реализовать свой механизм выделения памяти для файла, чтения и записи данных. Для этого в состав 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, который обеспечивает наиболее общие свойства унаследованных от него классов.
    Можно выделить две основных формы наследования. Когда производный класс наследуется от единственного базового класса - единичное наследование. Наследование, при котором производный класс наследуется сразу от нескольких базовых классов - это множественное наследование.


    Некоторые классы MFC


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


    Объекты графического интерфейса (класс CGdiObject)


    Для отображения информации используются различные объекты графического интерфейса - GDI объекты. Для каждого из этих объектов библиотека MFC содержит описывающий его класс, наследованный от базового класса CGdiObject (рис. 2.7).
    Объекты графического интерфейса (класс CGdiObject)
    Рис. 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_. Эти функции позволяют гарантировать, что данные, введенные пользователем в диалоговой панели, соответствуют определенным условиям.

    Если вы используете функцию DDV_ для проверки ввода в данном органе управления, вы должны вызвать ее сразу после вызова функции DDX_ для этого же органа управления.

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

    В отличие от функций DDX_, функции DDV_, в зависимости от их предназначения, имеют различное количество параметров. Первый параметр, как и в случае функций DDX_, содержит указатель на объект класса CDataExchange. Остальные параметры имеют различное назначение в зависимости от функции. Так, например, прототип функции, которая проверяет максимальную длину введенной текстовой строки, выглядит следующим образом:

    void AFXAPI

    DDV_MaxChars(CDataExchange* pDX,

                                    CString const& value, int nChars);

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

    Другие функции DDV_ имеют больше параметров. Так, функция DDV_MinMaxInt, проверяющая, что введенное значение находится в определенных границах, имеет 4 параметра:

    void AFXAPI

    DDV_MinMaxInt(CDataExchange* pDX,

                                 int value, int minVal, int maxVal);

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

    Приложение не должно напрямую вызывать метод DoDataExchange. Он вызывается через метод UpdateData, определенный в классе CWnd (рис. 2.34). Если вам надо выполнить обмен данными, между органами управления и элементами данных класса диалоговой панели, используйте метод UpdateData. Приведем прототип метода UpdateData:

    BOOL UpdateData(BOOL bSaveAndValidate = TRUE);

    Обмен данными

    Рис. 2.34. Вызов метода DoDataExchange



    Необязательный параметр bSaveAndValidate, определяет, как будет происходить обмен данными. Если метод UpdateData вызывается с параметром FALSE, выполняется инициализация диалоговой панели. Информация из элементов данных класса отображается в органах управления диалоговой панели.

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

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

    При создании модальной диалоговой панели перед тем как панель появится на экране, вызывается виртуальный метод OnInitDialog класса CDialog. По умолчанию OnInitDialog вызывает метод UpdateData и выполняет инициализацию органов управления. Если вы переопределили метод OnInitDialog в своем классе диалоговой панели, в первую очередь вызовите метод OnInitDialog класса CDialog.

    Метод UpdateData также вызывается некоторыми другими методами класса CDialog. Так, метод UpdateData вызывается, когда пользователь закрывает модальную диалоговую панель, нажимая кнопку OK. Заметим, что кнопка OK должна иметь идентификатор IDOK. Если пользователь нажмет на кнопку Cancel, имеющую идентификатор IDCANCEL, то диалоговая панель также закрывается, но метод UpdateData не вызывается и обмен данными не происходит.

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

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

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

    void CMyDialog::DoDataExchange(CDataExchange* pDX)

    {

          CDialog::DoDataExchange(pDX);

          DDX_Text(pDX, IDC_EDIT, m_Text);

    }

    Методу DoDataExchange передается указатель pDX на объект класса CDataExchange. Этот объект создается, когда вы инициируете процесс обмена данными, вызывая метод UpdateData. Элементы данных класса CDataExchange определяют процедуру обмена данными, в том числе определяют, в каком направлении будет происходить этот обмен. Обратите внимание, что указатель pDX передается функциям DDX_ и DDV_.



    В библиотеку MFC входит большое количество функций DDX_ и DDV_. Чтобы облегчить задачу написания метода DoDataExchange для класса вашей диалоговой панели, используйте ClassWizard. Он позволяет привязать к органам диалоговой панели элементы данных класса. При этом метод DoDataExchange создается ClassWizard автоматически. ClassWizard сам выбирает, какие функции DDX_ и DDV_ надо использовать для данного органа управления и связанного с ним элемента данных класса диалоговой панели. Подробно об использовании ClassWizard для разработки диалоговых панелей вы можете прочитать в главе “Приложение с главной диалоговой панелью”.

    Класс диалоговой панели должен обрабатывать сообщения от своих органов управления, поэтому он должен иметь таблицу сообщений. В заголовке таблицы сообщений указывается имя класса CMyDialog и имя базового класса CDialog:

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

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

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

    BEGIN_MESSAGE_MAP(CMyDialog, CDialog)

          ON_BN_CLICKED(IDC_DEFAULT, OnDefault)

    END_MESSAGE_MAP()

    Таблица сообщений содержит только одну строку, в которой обрабатывается сообщение с кодом извещения ON_BN_CLICKED от кнопки “Default”. Когда пользователь нажимает кнопку, вырабатывается данное сообщение и вызывается его обработчик - метод OnDefault, определенный в классе CMyDialog:

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

    // Метод OnDefault класса CMyDialog

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

    void CMyDialog::OnDefault()

    {

          // TODO:

          m_Text = "Start Text";

          UpdateData(FALSE);

          MessageBeep(0);

    }

    Две другие кнопки диалоговой панели "DIALOGPANEL", OK и Cancel не представлены в таблице сообщений, но тем не менее в приложении определены методы OnOK и OnCancel, которые вызываются при нажатии на них. Оказывается для диалоговых панелей определены две стандартные кнопки OK и Cancel, которым присвоены специальные идентификаторы IDOK и IDCANCEL.



    Базовый класс CDialog, также как и класс CMyDialog, содержит таблицу сообщений. Если вы установили библиотеку MFC вместе с исходными текстами, вы можете изучить реализацию класса CDialog в файле Dlgcore.cpp. Сам класс CDialog определен во включаемом файле Afxwin.h. Ниже представлена выдержка из таблицы сообщений, определенной в файле Dlgcore.cpp:

    BEGIN_MESSAGE_MAP(CDialog, CWnd)

          //{{AFX_MSG_MAP(CDialog)

          ON_WM_CTLCOLOR()

          ON_COMMAND(IDOK, OnOK)

          ON_COMMAND(IDCANCEL, OnCancel)

          ON_MESSAGE(WM_COMMANDHELP, OnCommandHelp)

          ON_MESSAGE(WM_HELPHITTEST, OnHelpHitTest)

          ON_MESSAGE(WM_QUERY3DCONTROLS, OnQuery3dControls)

          ON_MESSAGE(WM_INITDIALOG, HandleInitDialog)

          ON_MESSAGE(WM_SETFONT, HandleSetFont)

          //}}AFX_MSG_MAP

    END_MESSAGE_MAP()

    Среди прочих сообщений, в этой таблице определены командные сообщения с идентификаторами IDOK и IDCANCEL. Для обработки этих командных сообщений определены виртуальные методы OnOK и OnCancel. Данные методы также определены в MFC (файл Dlgcore.cpp). Поэтому когда диалоговая панель содержит кнопки с идентификаторами IDOK и IDCANCEL, как правило нет необходимости создавать для них обработчики.

    Так как в таблице сообщений класса CMyDialog отсутствуют макрокоманды для обработки сообщений от кнопок OK и Cancel, они передаются для обработки базовому классу CDialog. Здесь они обрабатываются виртуальными методами OnOK и OnCancel.

    Метод OnOK, определенный в классе CDialog, копирует данные из полей диалоговой панели в связанные с ними переменные. Для этого вызывается метод UpdateData с параметром TRUE. Затем выполняется вызов метода EndDialog, который закрывает диалоговую панель и возвращает значение IDOK. После того как диалоговая панель закрыта, метод DoModal возвращает значение IDOK и продолжает работать метод InitInstance.

    void CDialog::OnOK()

    {

          if(!UpdateData(TRUE))

          {

                 // В процессе обмена данными произошла ошибка

                 TRACE0("UpdateData failed during dialog termination.\n");



                 return;

          }

          EndDialog(IDOK);

    }

    Метод OnCancel, определенный в классе CDialog, еще проще, чем OnOK. Он только закрывает диалоговую панель и возвращает значение IDCANCEL. Копирование данных из полей диалоговой панели не происходит, так как пользователь отменил изменения, нажав кнопку Cancel.

    void CDialog::OnCancel()

    {

          EndDialog(IDCANCEL);

    }

    Так как методы OnOK и OnCancel определены в классе CDialog как виртуальные, вы можете переназначить их в своем классе CMyDialog. В этом случае управление получат переопределенные вами методы, а не методы класса CDialog. Методы базового класса CDialog можно вызвать, явно указав класс.

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

    // Метод OnCancel класса CMyDialog

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

    void CMyDialog::OnCancel()

    {

          // Подаем звуковой сигнал

          MessageBeep(0);

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

          CDialog::OnCancel();

    }

    Переопределенный нами метод OnCancel вызывает функцию программного интерфейса MessageBeep, которая подает звуковой сигнал, а затем вызываем метод OnCancel базового класса CDialog. Метод OnCancel базового класса CDialog вызывается в конце, так как он закрывает саму диалоговую панель.

    Аналогично методу OnCancel мы переопределили метод OnOK.

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

    // Метод OnOK класса CMyDialog

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

    void CMyDialog::OnOK()

    {

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

          CDialog::OnOK();

          // Подаем звуковой сигнал

          MessageBeep(0);

    }

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


    Обработка исключительных ситуаций


    При написании настоящих приложений большую роль играет обработка всевозможных ошибочных ситуаций, возникающих во время работы приложения. В качестве примера таких ошибочных ситуаций может служить неудачная попытка получить блок оперативной памяти, попытка открыть несуществующий файл и т. д.
    От того насколько внимательно ваше приложение обрабатывает все ошибочные ситуации, зависит насколько надежно и устойчиво оно будет работать. Например, если приложение по мере своей работы должно получать у операционной системы большие блоки памяти и вы не учтете возможности, что оперативной памяти окажется недостаточно, приложение может вызвать срабатывание защиты памяти.
    В языке Си++ реализованы специальные операторы 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)

          {

          HDC                  hdc ;

          PAINTSTRUCT                ps ;

          RECT                rect ;

          switch(message)

          {

                 case WM_PAINT:

                       hdc = BeginPaint(hWnd, &ps) ;

                       GetClientRect(hWnd, &rect) ;

                       DrawText(hdc, "Hello, Windows!", -1, &rect,

                             DT_SINGLELINE | DT_CENTER) ;

                       EndPaint(hWnd, &ps) ;

                       return 0 ;

                 case WM_DESTROY:

                       PostQuitMessage(0) ;

                       return 0 ;

          }

          return DefWindowProc(hwnd, message, wParam, lParam) ;

    }

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

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

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

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

  • оконные сообщения;


  • сообщения от органов управления;


  • команды



  • Общие члены объектов класса


    Иногда удобно, чтобы все объекты данного класса имели общие элементы данных, которые используются совместно. За счет этого можно существенно сократить количество глобальных переменных, улучшая структуру программы.
    Общие элементы данных класса следует объявить с ключевым словом 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)


    Практически все приложения имеют пользовательский интерфейс, построенный на основе окон. Это может быть диалоговая панель, одно окно или несколько окон, связанных вместе. Основные свойства окон представлены классом CWnd, наследованным от класса CCmdTarget.
    Вы очень редко будете создавать объекты класса CWnd. Класс CWnd сам является базовым классом для большого количества классов, представляющих разнообразные окна. На рисунке 2.3 представлена только небольшая часть дерева наследования класса CWnd.
    Окна (класс 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
    Орган управления, который позволяет просматривать иерархические структуры данных
    <
  • Управляющие панели (классы CControlBar, CStatusBar, CDialogBar)


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

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

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

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

  • Блокнот (класс CPropertySheet)


  • Класс CPropertySheet представляет блокнот - диалоговую панель, содержащую несколько страниц. Отдельные страницы такого блокнота управляются объектами другого класса - CPropertyPage. Класс CPropertyPage наследуется от базового класс CDialog, который мы рассмотрим ниже.

  • Окна просмотра (класс CView и классы наследованные от него)


  • Большой интерес представляет класс CView и классы, наследуемые от него (рис. 2.4). Эти классы представляют окно просмотра документов приложения. Именно окно просмотра используется для вывода на экран документа, с которым работает приложения. Через это окно пользователь может изменять документ.

    Разрабатывая приложение, вы будете наследовать собственные классы просмотра документов либо от базового класса CView, либо от одного из нескольких порожденных классов, определенных в библиотеке MFC.

    Классы, наследованные от CCtrlView, используют для отображения документа готовые органы управления. Например, класс CEditView использует орган управления edit (редактор). Более подробно эти классы будут описаны позже, когда мы будем рассказывать о средствах автоматизированного программирования MFC AppWizard и ClassWizard.



    Класс CScrollView  представляет окно просмотра, которое имеет полосы свертки. В классе определены специальные методы, управляющие полосами просмотра

    Класс CFormView позволяет создать окно просмотра документа, основанное на диалоговой панели. От этого класса наследуются еще два класса CRecordView и CDaoRecordView. Эти классы используются для просмотра записей баз данных.

    Окна (класс CWnd)

    Рис. 2.4. Класс CView

  • Диалоговые панели (класс CDialog и классы наследованные от него)


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

    Вместе с диалоговыми панелями обычно используется класс CDataExchange. Класс CDataExchange обеспечивает работу процедур обмена данными DDX (Dialog Data Exchange) и проверки данных DDV (Dialog Data Validation) используемых для диалоговых панелей. В отличие от класса CDialog, класс CDataExchange не наследуется от какого-либо другого класса.

    Когда вы создаете блокнот, состоящий из нескольких страниц, то каждая такая страница является объектом класса, наследованного от CPropertyPage.

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

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

    Окна (класс CWnd)

    Рис. 2.5. Класс CDialog


    Окно Project Workspace


    Microsoft Visual C++ версии 4.0 имеет удобные средства для просмотра исходных текстов файлов проекта, кодов классов приложения, ресурсов, а также для получения справочной информации.
    Откройте окно Project Workspace. Обычно оно расположено в левой части экрана, но вы можете переместить его в другое место и даже закрыть. Если окно закрыто, откройте его. Для этого выберите из меню View строку Project Workspace. Окно Project Workspace состоит из нескольких страниц. Вы можете открыть их, нажимая на соответствующие закладки. Количество страниц зависит от того, установлена ли справочная система и открыт ли проект.

    Закладка
    Описание
    Окно Project Workspace
    ClassView. Средство для просмотра и редактирования классов приложения
    Окно Project Workspace
    ResourceView. Позволяет просматривать и редактировать ресурсы приложения
    Окно Project Workspace
    FileView. Выполняет просмотр файлов приложения
    Окно Project Workspace
    InfoView. Справочная система Microsoft Visual C++. В нее включена информация о языке Си, Си++, библиотеки классов MFC, функций програмного интерфейса Windows

    Откройте окно Project Workspace и выберите страницу FileView. Так как сначала в проекте нет ни одного файла, вы увидите пустую папку проекта. Теперь надо создать новый текстовый файл и набрать в нем исходный текст нашего первого приложения.
    Чтобы создать новый файл, вы можете нажать кнопку New Source File (Окно Project Workspace) в стандартной панели управления или выбрать из меню File строку New, а затем из списка в открывшейся панели New выбрать строку Text File. Откроется новое окно текстового редактора. В нем надо набрать исходный текст приложения, представленного листингом 2.1. Сохраните набранный текст в файле под именем MFHello.cpp в каталоге проекта. Для этого выберите из меню File строку Save As.
    Листинг 2.1. Файл MFHello.cpp
    // Включаемый файл для MFC
    #include
    //=====================================================
    // Класс CMFHelloApp
    // Наследуем от базового класса CWinApp главный
    // класс приложения CMFHelloApp
    //=====================================================

    class CMFHelloApp : public CWinApp

    {

    public:

          // Мы будем переопределять метод InitInstance,

          // предназначенный для инициализации приложения

          virtual BOOL InitInstance();

    };

     

    // Создаем объект приложение класса CMFHelloApp

    CMFHelloApp MFHelloApp;

     

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

    // Метод InitInstance класса CMFHelloApp

    // Переопределяем виртуальный метод InitInstance

    // класса CWinApp. Он вызывается каждый раз при запуске

    // приложения

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

    BOOL CMFHelloApp::InitInstance()

    {

          AfxMessageBox("Hello, MFC!");

          return FALSE;

    }

    Единственный файл с исходным текстом приложения создан и его надо включить в проект. Выберите из меню Insert строку Files into Project. На экране появится диалоговая панель Insert Files into Project. Выберите файл MFHello.cpp и нажмите кнопку Add. Диалоговая панель закроется. Просмотрите еще раз папку с файлами проекта. Теперь в ней расположен файл MFHello.cpp (рис. 2.10).

    Окно Project Workspace

    Рис. 2.10. Файлы проекта MFHello

    Откройте страницу ClassView в окне Project Workspace. В ней отображаются все классы, определенные в приложении и все глобальные переменные. Для каждого класса приложения можно видеть входящие в него элементы (рис. 2.11).

    На странице ClassView отображается древовидная структура классов вашего приложения. Когда вы в первый раз открываете ClassView, структура классов отображается в виде закрытой папки Окно Project Workspace. Чтобы ее открыть, щелкните два раза левой кнопкой мыши по изображению папки или один раз по символу Окно Project Workspace, расположенному левее папки. В открытой папке символом Окно Project Workspace представлены классы приложения и еще одним символом папки глобальные объекты приложения. Папку с глобальными объектами можно открыть также как папку с классами. Вы можете открыть и сами классы. Так вы сможете просмотреть элементы класса - методы и данные. Методы обозначаются символом Окно Project Workspace, а данные символом Окно Project Workspace. Если методы или данные объявлены как protected, перед ними отображается символ Окно Project Workspace, а если как private - символ Окно Project Workspace.



    В нашем проекте определен только один класс CMFHelloApp. В класс CMFHelloApp входит метод InitInstance. Кроме того, определена одна глобальная переменная MFHelloApp.

    Окно Project Workspace

    Рис. 2.11. Классы проекта MFHello

    Если вы используете в приложении классы или функции библиотеки классов MFC, то проект надо настроить. Выберите из меню Build строку Settings или нажмите комбинацию клавиш . На экране появится диалоговая панель Project Settings. В этой панели расположены несколько страниц, позволяющих настроить различные характеристики проекта.

    Откройте страницу General. Мы привели внешний вид этой страницы на рисунке 2.12. Обратите внимание на список Microsoft Foundation Classes. По умолчанию из этого списка выбрана строка No Using MFC. Она означает, что приложение не будет использовать библиотеку MFC. Так как в приложении MFHello и всех остальных приложениях, описанных в этой книге, задействованы классы MFC, выберите из списка Microsoft Foundation Classes строку Use MFC in a Shared Dll (mfc40(d).dll) или строку Use MFC in a Static Library.

    Что же выбрать - Use MFC in a Shared Dll или Use MFC in a Static Library? Оказывается программные коды библиотеки классов MFC могут использоваться приложением двумя способами. Код библиотеки MFC либо непосредственно записывается в выполнимый файл приложения, либо вызывается по мере необходимости из отдельной dll-библиотеки.

    Использование для приложения dll-библиотеки немного ускоряет процесс построения проекта и позволяет создавать выполнимые файлы значительно меньшего размера. Однако сам по себе такой выполнимый файл работать не будет. Для него необходима dll-библиотека. Поэтому если приложение устанавливается и на других компьютерах, его надо распространять вместе с dll-библиотекой.

    Для MFC версии 4.0 dll-библиотека хранится в файлах Mfc40d.dll и Mfc40.dll. В файле Mfc40d.dll находится отладочная версия MFC, а в файле Mfc40.dll отладочная информация отсутствует. В ходе установки Visual C++ эти dll-библиотеки записываются в системный каталог операционной системы.



    Если вы забудете указать, что приложение использует MFC, то во время построения проекта на этапе установления связи (Linking...) будут обнаружены ошибки:

    error LNK2001: unresolved external symbol __endthreadex

    error LNK2001: unresolved external symbol __beginthreadex

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

    Окно Project Workspace

    Рис. 2.12. Диалоговая панель Project Settings

    Если вы все сделали, постройте проект. Для этого вы можете выбрать из меню Build строку Build MFHello.exe - создать выполнимый файл MFHello.exe или строку Rebuild All - заново оттранслировать все исходные файлы проекта. Если в ходе построения выполнимого файла приложения нашего проекта возникли ошибки, исправьте их и заново оттранслируйте проект. После успешного построения проекта запустите полученное приложение, выбрав из меню Build строку Execute MFHello.exe.

    На экране появится маленькая диалоговая панель (рис. 2.13). Название этой диалоговой панели соответствует названию выполнимого файла приложения MFHELLO. В диалоговой панели отображается текстовое сообщение “Hello, MFC!”, пиктограмма с восклицательным знаком внутри треугольника и кнопка OK. Как только вы нажмете кнопку OK, диалоговая панель закрывается и приложение завершит свою работу.

    Окно Project Workspace

    Рис. 2.13. Приложение MFHello

    Посмотрим, как работает приложение MFHello на уровне исходного текста. Первая строка, не считая строки комментария, содержит директиву препроцессора #include, которая включает файл afxwin.h:

    // Включаемый файл для MFC

    #include

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



    В предыдущих томах серии “Библиотека системного программиста”, посвященных программированию в среде операционных систем Windows и Windows 95, мы рассказывали о функции WinMain, которая является главной функцией приложения. Эта функция вызывается, когда пользователь или операционная система запускает приложение. Все приложения, которые мы рассматривали до сих пор содержали функцию WinMain:

    // Самое простое приложение Windows

    #include

    int WINAPI WinMain(

     

        HINSTANCE  hInstance,

        HINSTANCE  hPrevInstance,

        LPSTR  lpCmdLine,            

        int  nShowCmd     

       )

    {

          // Отображаем на экране небольшую панель с сообщением

          MessageBox(NULL,"Hello, world", "Text Message", MB_OK);

         

          // Завершаем приложение

          return 0;

    }

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

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

    Класс CWinApp выполняет все действия, которые обычно выполняла функция WinMain - инициализирует приложение, обрабатывает сообщения и завершает приложение. Для этого класс CWinApp включает виртуальные методы InitApplication, InitInstance, Run и ExitInstance.

    Чтобы выполнить инициализацию приложения, функция WinMain вызывает методы InitApplication и InitInstance для объекта главного класса приложения. Метод InitApplication выполняет инициализацию на уровне приложения. Вы можете переопределить этот метод в своем приложении. Метод InitInstance выполняет инициализацию каждой запущенной копии приложения. Обычно метод InitInstance создает главное окно приложения. Вы обязательно должны переопределить этот метод в своем приложении. Остальные методы, например Run, можно не переопределять.



    Затем функция WinMain начинает обрабатывать цикл сообщений. Для этого вызывается метод Run. Вы можете переопределить этот метод, чтобы реализовать собственный цикл обработки сообщений.

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

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

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

    // Класс CMFHelloApp

    // Наследуем от базового класса CWinApp главный

    // класс приложения CMFHelloApp

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

    class CMFHelloApp : public CWinApp

    {

    public:

          // Мы будем переопределять метод InitInstance,

          // предназначенный для инициализации приложения

          virtual BOOL InitInstance();

    };

    Мы наследуем класс CMFHelloApp от базового класса CWinApp. При этом базовый класс указан как public. Это означает, что в программе доступны все элементы базового класса CWinApp, объявленные как public. Мы можем вызывать методы класса CWinApp для объектов класса CMFHelloApp и обращаться к элементам данных класса CWinApp.

    В определении класса CMFHelloApp мы объявляем виртуальный метод InitInstance. Он будет нами переопределен. Изначально метод InitInstance определен в классе CWinApp. Метод InitInstance отвечает за инициализацию приложения. Он вызывается каждый раз, когда пользователь запускает приложение. Если пользователь запустит приложение несколько раз, то метод InitInstance будет вызываться каждый раз.

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

    Сразу после объявления главного класса приложения мы создаем объект этого класса - MFHelloApp. Вы должны определить объект главного класса приложения как глобальный. В этом случае он будет создан сразу при запуске приложения и сможет управлять всей работой приложения. После создания глобальных объектов вызывается функция WinMain, определенная в классе CWinApp. Она выполняет свои обычные функции - регистрирует классы окон, создает окно, и т. д.



    Нельзя создавать два и более объектов класса, наследованного от базового класса CWinApp. Каждое приложение должно иметь один и только один объект главного класса приложения:

    // Создаем объект приложение класса CMFHelloApp

    CMFStartApp MFHelloApp;

    Метод InitInstance главного класса приложения CMFHelloApp служит для инициализации. Он вызывается автоматически каждый раз, когда запускается очередная копия приложения. Мы используем метод InitInstance чтобы вывести на экран компьютера диалоговую панель с сообщением “Hello, MFC!”. Для этого вызываем функцию AfxMessageBox:

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

    // Метод InitInstance класса CMFHelloApp

    // Переопределяем виртуальный метод InitInstance

    // класса CWinApp. Он вызывается каждый раз при запуске

    // приложения

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

    BOOL CMFHelloApp::InitInstance()

    {

          AfxMessageBox("Hello, MFC!");

          return FALSE;

    }

    Функция AfxMessageBox определена в библиотеке классов MFC. Вместо функции AfxMessageBox вы можете воспользоваться хорошо известной вам функцией MessageBox программного интерфейса Windows.

    Когда вы вызываете из приложения, созданного с использованием классов MFC, функции программного интерфейса Windows, необходимо указывать перед именем функции символы ::. Так, вызов функции MessageBox мог бы выглядеть следующим образом:

    ::MessageBox(NULL,"Hello, world!", "Message", MB_OK);

    В конце метода InitInstance мы вызываем оператор return и возвращаем значение FALSE. Приложение сразу завершается. Если метод InitInstance вернет значение TRUE, приложение продолжит работу и приступит к обработке очереди сообщений. Более подробно о методе InitInstance и свойствах базового класса CWinApp вы узнаете позже, когда мы будем рассматривать средства автоматизированной разработки приложений.


    Окно просмотра


    В отличие от объектов, представляющих окна типа 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 имеет элементы, описанные в следующей таблице:

    Поле структуры CFileStatus

    Описание

    CTime m_ctime

    Дата и время создания файла. Описание класса CTime представлено нами в главе “Дата и время”

    CTime m_mtime

    Дата и время последней модификации файла

    CTime m_atime

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

    LONG m_size

    Размер файла в байтах

    BYTE m_attribute

    Атрибуты файла

    char m_szFullName[_MAX_PATH]

    Полное имя файла в стандарте операционной системы Windows. Виртуальная версия метода не заполняет это поле

    Атрибуты файла, указанные в поле m_attribute структуры CFileStatus, определяются как переменная перечислимого типа Attribute. Этот тип определен в классе CFile следующим образом:

    enum Attribute {

          normal =    0x00,

          readOnly =  0x01,

          hidden =    0x02,

          system =    0x04,

          volume =    0x08,

          directory = 0x10,

          archive =   0x20

    };

    Атрибут

    Описание

    normal

    Нормальный файл

    readOnly

    Файл, который можно открыть только для чтения

    hidden

    Скрытый файл

    system

    Системный файл

    volume

    Метка тома

    directory

    Каталог

    archive

    Архивный

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


    Оператор присваивания


    Для класса CObject описан оператор присваивания. Он описан с ключевым словом private и не имеет реализации:
    private:
          void operator =( const CObject& src );
    Таким образом для классов, наследованных от CObject запрещается выполнение операции копирования по умолчанию. Если такая операция необходима, вы должны явно определить ее в своем классе. Если вы не определите в своем классе оператор присваивания, но попытаетесь им воспользоваться, компилятор выдаст сообщение об ошибке.


    Операторы new и delete


    Оператор 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


    Оператор 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” и завершит работу.

    Листинг 1.1. Файл Exception.cpp

    #include

    int main()

    {

          long      num = 7;

          long      res = 0;

         

          // Введите число num

          cout << "Input number: ";

          cin >> num;

          // Блок try, из которого можно вызвать исключение

          try {

                 if((num == 7) (num == 0))

          // Если переменная num содержит значение 0 или 7,

          // тогда вызываем исключение типа float

                       throw num;

          // Значения num равные 0 или 7 вызовут ошибку

          // деления на нуль в следующем выражении

                 res = 100 / (num * (num - 7));

          // Отображаем на экране результат вычисления

                 cout << "Result = " << res << endl;

          }

          // Обработчик исключения типа float

          catch(long num)

          {

                 // Отображаем на экране значение переменной num

                 cout << "Exception, num = " << num << endl;

          }

          cout << "Stop program" << endl;

          return 0;

    }

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

    catch(long)

    {

          // Отображаем на экране значение переменной num

          cout << "Exception, num = " << num << endl;

    }


    Основа структуры приложения (класс CCmdTarget)


    Непосредственно от класса CObject наследуются ряд классов, которые сами являются базовыми для остальных классов MFC. В первую очередь это класс CCmdTarget, представляющий основу структуры любого приложения. Основной особенностью класса CCmdTarget и классов, наследованных от него является то, что объекты этих классов могут получать от операционной системы сообщения и обрабатывать их.
    Структура классов, связанных с классом CCmdTarget представлена на рисунке 2.2.
    Основа структуры приложения (класс CCmdTarget)
    Рис. 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)


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

          ID_INDICATOR_CAPS,

          ID_INDICATOR_NUM,

          ID_INDICATOR_SCRL,

    };

    Сразу после создания панели состояния вызывается метод SetIndicators. В первом параметре ему передается массив indicators, а во втором - количество элементов в этом массиве.

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

    После создания панели управления toolbar метод SetBarStyle класса CControlBar устанавливает различные стили этой панели. Стили определяются одной или несколькими константами CBRS_.

    Константа

    Описание

    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 и выше

    По умолчанию MFC AppWizard устанавливает для панели управления стили CBRS_TOOLTIPS, CBRS_FLYBY, CBRS_SIZE_DYNAMIC. Вы можете изменить их по своему усмотрению.

    m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |

          CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);

    Чтобы панель управления могла отображаться в собственном маленьком окне, не прикрепленном ни к одной стороне родительского окна, два раза вызываются метод EnableDocking - для панели управления и для главного окна приложения. Затем вызывается метод DockControlBar для главного окна приложения.

    m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);

    EnableDocking(CBRS_ALIGN_ANY);

    DockControlBar(&m_wndToolBar);


    Панель управления и панель состояния


    Наиболее интересная задача, которую выполняет метод OnCreate, заключается в отображении панелей управления и состояния. Для панели управления и панели состояния в библиотеке классов MFC предусмотрены два класса CStatusBar и CToolBar.
    Классы CStatusBar и CToolBar имеют длинную цепочку наследования.
    CStatusBar <- | <- CControlBar <- CWnd <- CCmdTarget <- CObject
    CToolBar   <- |
    Полное описание этих классов заняло бы слишком много места, поэтому мы ограничимся рассказом о самых главных методах, необходимых для работы с панелями управления и состояния.


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


    Многие современные приложения, в том числе все приложения имеющие оконный интерфейс и созданные с использованием средств 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"


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


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


    Печать документа (класс CPrintInfo)


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

    Существует ряд ограничений, которые вы должны учитывать при переопределении операторов:

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


  • Нельзя изменять старшинство операторов


  • Нельзя определять новые операторы


  • Нельзя переопределять операторы принимающие в качестве параметров стандартные типы данных языка, такие как int или char


  • Переопределенные операторы не могут иметь параметров, используемых по умолчанию


  • Нельзя переопределять следующие операторы: (.), (.*), (::), (?:), а также символы, обрабатываемые препроцессором (символы комментария и т. д.).


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


    Переопределение методов базового класса


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


    Первое приложение MFC


    Первое приложение 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


    Подзадачи приложения (классы CWinThread и CWinApp)


    От класса 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();


          throw(CFileException);

    Фактически вызов метода SeekToBegin эквивалентен вызову метода Seek с параметром lOff, содержащим нуль и параметром nFrom, содержащим константу CFile::begin.

    Метод SeekToEnd имеет почти такой же прототип как метод SeekToBegin, но перемещает указатель в конец файла:

    DWORD SeekToEnd();

          throw(CFileException);

    Метод SeekToEnd возвращает длину файла в байтах. Если вам надо определить длину открытого файла, совсем не обязательно перемещать его указатель. Можно воспользоваться методом GetLength. Этот метод также возвращает длину открытого файла в байтах:

    virtual DWORD GetLength() const;

          throw(CFileException);

    Метод SetLength позволяет изменить длину открытого файла:

    virtual void SetLength(DWORD dwNewLen);

          throw(CFileException);

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

    Вы можете определить текущую позицию указателя файла с помощью метода GetPosition. Возвращаемое методом GetPosition 32-разрядное значение определяет смещение указателя от начала файла:

    virtual DWORD GetPosition() const;

          throw(CFileException);


    Приложение Dialog


    Названия файлов, входящих в проект 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


    Приложение 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",

                             MB_OK | MB_ICONSTOP);

                 else if(ptrException -> m_cause ==

                                                                                    CFileException::diskFull)

                       MessageBox(NULL,"The disk is full", "Exception",

                             MB_OK | MB_ICONSTOP);

                 else MessageBox(NULL,"File Exception", "Exception",

                                    MB_OK | MB_ICONSTOP);

                 ptrException -> Delete();

          }

          // Обработчик для исключений класса CException и

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

          catch(CException* ptrException)

          {

                 MessageBox(NULL,"Exception", "Exception",

                       MB_OK | MB_ICONSTOP);

                 ptrException -> Delete();

          }

         

          // Все остальные исключения обрабатываются здесь

          catch(...)

          {

                 MessageBox(NULL,"Another Exception", "Exception",

                       MB_OK | MB_ICONSTOP);

          }

          return 0;

    }

    В блоке try мы пытаемся открыть для чтения файл с именем This file is absent. Длинные имена файлов, содержащие символы пробелов, разрешены в операционных системах Windows 95 и Windows NT. Если файла This file is absent нет на диске, тогда создается объект класса CFileException и вызывается исключение.

    Обработчик исключений, связанных с ошибками при работе с файловой системой, проверяет, вызвано ли оно тем, что приложение пытается открыть несуществующий файл. Если это так,  на экране отображается сообщение File Not Found.

    После обработки исключения, управление передается первому оператору за последним блоком catch. В нашем примере это оператор return. Он завершает работу приложения.

    Вы можете сами создать объект класса CFileException и вызвать исключение. Для этого рекомендуется использовать функцию AfxThrowFileException:

    void AfxThrowFileException(int cause, LONG lOsError = –1);

    Параметр cause должен определять причину исключения. В качестве этого параметра можно задавать возможные значения для элемента данных m_cause из класса CFileException (см. таблицу выше). Необязательный параметр lOsError может содержать код ошибки, определенной операционной системой.


    Приложение MFMenu


    Приложения, которые мы рассматривали до сих пор, фактически никак не могли взаимодействовать с пользователем. Они не имели ни меню, ни панели управления. И самое главное - они не содержали обработчиков сообщений.
    Сейчас мы рассмотрим приложение MFMenu, которое имеет меню и содержит обработчики сообщений, передаваемых приложению, когда пользователь открывает меню и выбирает из него строки. Для создания приложения MFMenu мы также не станем пользоваться средствами автоматизированной разработки MFC AppWizard, и наберем текст приложения вручную. Этот позволит вам лучше разобраться с механизмом сообщений.
    Создайте новый проект под названием MFMenu. В качестве типа приложения выберите из списка Type строку Application (рис. 4.1 из главы “Приложение с главной диалоговой панелью”). Наберите в редакторе исходный текст приложения и сохраните его в файле MFMenu.cpp (листинг 2.7). Чтобы быстрее набрать текст приложения, вы можете получить его, изменив исходный текст приложения MFStart. Затем включите этот файл в проект.
    Листинг 2.7. Файл MFMenu.cpp
    // Включаемый файл для MFC
    #include
    #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();

          // Макрокоманда необходима, так как класс

          // CMFMenuWindow обрабатывает сообщения

          DECLARE_MESSAGE_MAP()   

    };

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

    // Метод MenuCommand

    // Обрабатывает команду ID_TEST_BEEP

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

    void CMFMenuWindow::MenuCommand()

    {

          MessageBeep(0);               

    }

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

    // Метод ExitApp

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

    void CMFMenuWindow::ExitApp()

    {

          DestroyWindow();

    }

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

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

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

    BEGIN_MESSAGE_MAP(CMFMenuWindow, CFrameWnd)

          ON_COMMAND(ID_TEST_BEEP, MenuCommand)

          ON_COMMAND(ID_TEST_EXIT, ExitApp)

    END_MESSAGE_MAP()

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

    // Метод InitInstance класса CMFMenuApp

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

    BOOL CMFMenuApp::InitInstance()

    {

          // Создаем объект класса CMFMenuWindow

          m_pMainWnd = new CMFMenuWindow();

          // Отображаем окно на экране

          m_pMainWnd -> ShowWindow(m_nCmdShow);

          // Обновляем содержимое окна

          m_pMainWnd -> UpdateWindow();

          return TRUE;

    }

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

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

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

    CMFMenuWindow::CMFMenuWindow()

    {

          // Создаем окно приложения, соответствующее

          // данному объекту класса CMFMenuWindow

          Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW,

                       rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));

    }


    Приложение с единственным окном


    Первое созданное нами приложение не имело главного окна. Для вывода сообщения на экран мы использовали функцию AfxMessageBox, которая очень похожа на функцию MessageBox программного интерфейса операционной системы Windows.
    Следующее приложение, которое мы будем рассматривать, немного сложнее. При запуске оно будет отображать на экране компьютера обычное окно, имеющее заголовок, системное меню и кнопки управления.
    Точно так же как в приложении MFHello, в нашем втором приложении мы будем использовать класс CWinApp в качестве главного класса приложения. Для управления окном приложения мы создадим еще один класс, наследуемый от базового класса CFrameWnd, входящего в библиотеку MFС.
    Создайте новый проект, как мы рассказывали выше, и назовите его именем MFStart. В качестве типа приложения выберите из списка Type строку Application (рис. 4.1 из главы “Приложение с главной диалоговой панелью”). Автоматизированные средства разработки приложений MFC AppWizard мы рассмотрим позже.
    Наберите в редакторе исходный текст приложения и сохраните его в файле MFStart.cpp (листинг 2.2). Наш пример без учета строк комментариев состоит всего из двадцати строк исходного текста, поэтому набор текста не займет у вас много времени. Включите набранный файл в проект.
    Листинг 2.2. Файл MFStart.cpp
    // Включаемый файл для MFC
    #include
    //=====================================================
    // Класс CMFStartApp
    // Наследуем от базового класса CWinApp главный
    // класс приложения CMFStartApp
    //=====================================================
    class CMFStartApp : public CWinApp
    {
    public:
          // Мы будем переопределять метод InitInstance,
          // предназначенный для инициализации приложения
          virtual BOOL InitInstance();
    };
     
    // Создаем объект приложение класса CMFStartApp
    CMFStartApp MFStartApp;
     
    //=====================================================
    // Класс CMFStartWindow
    // Наследуем от базового класса CFrameWnd класс
    // CMFStartWindow. Он будет представлять главное

    // окно нашего приложения

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

    class CMFStartWindow : public CFrameWnd

    {

    public:

          // Объявляем конструктор класса CMFStartWindow

          CMFStartWindow();

    };

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

    // Метод InitInstance класса CMFStartApp

    // Переопределяем виртуальный метод InitInstance

    // класса CWinApp. Он вызывается каждый раз при запуске

    // приложения

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

    BOOL CMFStartApp::InitInstance()

    {

          // Создаем объект класса CMFStartWindow

          m_pMainWnd = new CMFStartWindow();

          // Отображаем окно на экране. Параметр m_nCmdShow

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

          m_pMainWnd -> ShowWindow(m_nCmdShow);

          // Обновляем содержимое окна

          m_pMainWnd -> UpdateWindow();

          return TRUE;

    }

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

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

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

    CMFStartWindow::CMFStartWindow()

    {

          // Создаем окно приложения, соответствующее

          // данному объекту класса CMFStartWindow

          Create(NULL, "Hello MFC");

    }

    Просмотрите папку с файлами проекта. Теперь в ней расположен файл MFStart.cpp. Затем откройте страницу ClassView в окне Project Workspace (рис. 2.20). В ней отображаются два класса CMFStartApp и CMFStartWindow. В класс CMFStartApp входит метод InitInstance, а в класс CMFStartWindow конструктор CMFStartWindow. Кроме того, определена глобальная переменная MFStartApp.

    Приложение с единственным окном

    Рис. 2.20. Классы проекта MFStart

    Постройте проект и запустите полученное приложение, выбрав из меню Build строку Execute MFStart.exe. На экране появится главное окно приложения, представлене нами на рисунке 2.21. Оно имеет стандартный заголовок с надписью Hello MFC, системное меню и кнопки для изменения размера окна. Чтобы завершить приложение, вы можете выбрать строку Close из системного меню главного окна или нажать на кнопку Приложение с единственным окном.



    Приложение с единственным окном

    Рис. 2.21. Приложение MFStart

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

    Так же как и у приложения MFHello, первая строка исходного текста приложения MFStart, не считая строки комментария, содержит директиву препроцессора #include, которая включает файл afxwin.h. Этот файл включается в исходные тексты всех приложений, использующих библиотеку классов MFC.

    Затем мы определяем главный класс приложения, который наследуется от базового класса CWinApp. Главный класс приложения MFHello, который называется CMFStartApp, определяется следующим образом:

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

    // Класс CMFStartApp

    // Наследуем от базового класса CWinApp главный

    // класс приложения CMFStartApp

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

    class CMFStartApp : public CWinApp

    {

    public:

          // Мы будем переопределять метод InitInstance,

          // предназначенный для инициализации приложения

          virtual BOOL InitInstance();

    };

    Как видите главный класс приложения MFStart определяется точно так же как у приложения MFHello. Класс CMFStartApp наследуется от базового класса CWinApp. При этом базовый класс указан как public. Мы можем вызывать методы класса CWinApp для объектов класса CMFStartApp и обращаться к элементам данных класса CWinApp. В определении класса CMFStartApp объявлен виртуальный метод InitInstance. Он будет переопределен нами несколько позже, после объявления класса окна приложения.

    После объявления главного класса приложения мы создаем объект этого класса - MFStartApp:

     // Создаем объект приложение класса CMFStartApp

    CMFStartApp MFStartApp;

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



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

    // Класс CMFStartWindow

    // Наследуем от базового класса CFrameWnd класс

    // CMFStartWindow. Он будет представлять главное

    // окно нашего приложения

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

    class CMFStartWindow : public CFrameWnd

    {

    public:

          // Объявляем конструктор класса CMFStartWindow

          CMFStartWindow();

    };

    Метод InitInstance главного класса приложения CMFStartApp служит для инициализации. Он вызывается автоматически каждый раз, когда запускается очередная копия приложения.

    Мы используем метод InitInstance, чтобы отобразить на экране окно приложения. Для этого мы создаем объект класса CMFStartWindow и записываем указатель на этот объект в элемент данных m_pMainWnd класса CWinThread (класс CWinThread является базовым для класса CWinApp). Таким образом, объект приложения и объект окна приложения связываются вместе.

    Для создания объекта класса CMFStartWindow мы используем оператор new. Он создает новый объект указанного класса, отводит память и возвращает указатель на него. При создании нового объекта оператором new для него автоматически вызывается конструктор, описанный ниже.

    Окно появится на экране только после того, как будет вызван метод ShowWindow. В качестве параметра методу ShowWindow передается параметр m_nCmdShow. Переменная m_nCmdShow является элементом класса CWinApp. Его назначение соответствует параметру nCmdShow функции WinMain, то есть определяет, как должно отображаться главное окно приложения сразу после его запуска.

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

    В конце метода InitInstance мы вызываем оператор return и возвращаем значение TRUE, означающее, что инициализация приложения завершилась успешно и можно приступать к обработке очереди сообщений.



    Если метод InitInstance вернет значение FALSE, приложение немедленно завершится. Мы использовали эту возможность в приложении MFHello, описанном выше.

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

    // Метод InitInstance класса CMFStartApp

    // Переопределяем виртуальный метод InitInstance

    // класса CWinApp. Он вызывается каждый раз при запуске

    // приложения

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

    BOOL CMFStartApp::InitInstance()

    {

          // Создаем объект класса CMFStartWindow

          m_pMainWnd = new CMFStartWindow();

          // Отображаем окно на экране. Параметр m_nCmdShow

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

          m_pMainWnd -> ShowWindow(m_nCmdShow);

          // Обновляем содержимое окна

          m_pMainWnd -> UpdateWindow();

          return TRUE;

    }

    Чтобы создать окно, мы создаем объект класса CMFStartWindow. Такой объект не является собственно окном, которое пользователь видит на экране компьютера, а представляет собой внутреннее представление окна. Для создания окна предназначается метод Create, определенный в классе CFrameWnd. Он создает окно и связывает его с объектом Си++, в нашем случае с объектом класса CMFStartWindow:

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

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

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

    CMFStartWindow::CMFStartWindow()

    {

          // Создаем окно приложения, соответствующее

          // данному объекту класса CMFStartWindow

          Create(NULL, "Hello MFC");

    }

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

    Например, мы могли бы поместить описание классов CMFStartApp и CMFStartWindow в файлы MFStartApp.h и MFStartWindow.h. Метод InitInstance класса CMFStartApp и определение глобальной переменной MFStartApp можно поместить в файл MFStartApp.cpp, а определение конструктора класса CMFStartWindow - в файл MFStartWindow.cpp.



    Так как в методе InitInstance класса CMFStartApp мы создаем новый объект класса CMFStartWindow, то мы должны включить в файл MFStartApp.cpp не только файл MFStartApp.h но еще и файл MFStartWindow.h. В проект MFStart мы должны записать оба программных файла MFStartApp.cpp и MFStartWindow.cpp. Листинги, представленные ниже содержат проект MFStart, разделенный на несколько файлов. Для того, чтобы сократить размер файлов, мы убрали из них комментарии.

    Файл MFStartApp.h содержит описание главного класса приложения CMFStartApp. Этот файл представлен в листинге 2.3.

    Листинг 2.3. Файл MFStartApp.h

    #include

    class CMFStartApp : public CWinApp

    {

    public:

          virtual BOOL InitInstance();

    };

    Виртуальный метод InitInstance переопределен нами в файле MFStartApp.cpp. В этом же файле создается объект класса CMFStartApp, представляющий само приложение. Файл MFStartApp.cpp показан в листинге 2.4.

    Листинг 2.4. Файл MFStartApp.cpp

    #include

    #include "MFStartApp.h"

    #include "MFStartWindow.h"

    CMFStartApp MFStartApp;

    BOOL CMFStartApp::InitInstance()

    {

          m_pMainWnd = new CMFStartWindow();

          m_pMainWnd -> ShowWindow(m_nCmdShow);

          m_pMainWnd -> UpdateWindow();

          return TRUE;

    }

    Класс окна приложения CMFStartWindow определяется в файле MFStartWindow.h, представленном листингом 2.5. Мы наследуем класс CMFStartWindow от базового класса CFrameWnd.

    Листинг 2.5. Файл MFStartWindow.h

    #include

    class CMFStartWindow : public CFrameWnd

    {

    public:

          CMFStartWindow();

    };

    И наконец, последний файл MFStartWindow.cpp модифицированного проекта MFStart показан в листинге 2.6. В этом файле определяется конструктор класса CMFStartWindow.

    Листинг 2.6. Файл MFStartWindow.cpp

    #include

    #include "MFStartWindow.h"

    CMFStartWindow::CMFStartWindow()

    {

          Create(NULL, "Hello MFC");

    }


    Приложение с главной диалоговой панелью


    В состав компиляторов 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
    #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 = "";

    }

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

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

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

    void CMyDialog::DoDataExchange(CDataExchange* pDX)

    {

          CDialog::DoDataExchange(pDX);

          DDX_Text(pDX, IDC_EDIT, m_Text);

    }

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

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

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

    BEGIN_MESSAGE_MAP(CMyDialog, CDialog)

          ON_BN_CLICKED(IDC_DEFAULT, OnDefault)

    END_MESSAGE_MAP()

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

    // Метод OnDefault класса CMyDialog

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

    void CMyDialog::OnDefault()

    {

          // TODO:

          m_Text = "Start Text";

          UpdateData(FALSE);

          MessageBeep(0);

    }

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

    // Метод OnCancel класса CMyDialog

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

    void CMyDialog::OnCancel()

    {

          // Подаем звуковой сигнал

          MessageBeep(0);

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

          CDialog::OnCancel();

    }

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

    // Метод OnOK класса CMyDialog

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

    void CMyDialog::OnOK()

    {

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

          CDialog::OnOK();

         

          // Подаем звуковой сигнал

          MessageBeep(0);

    }

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

    // Метод InitInstance класса CMFDialogApp

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

    BOOL CMFDialogApp::InitInstance()

    {

          // Создаем объект класса CMyDialog

          CMyDialog dlgTest;

          m_pMainWnd = &dlgTest;

          // Отображаем на экране модельную диалоговую панель

          dlgTest.DoModal();

          // Отображаем на экране значение переменной m_Text,

          // ввходящей в класс CMyDialog



          AfxMessageBox(dlgTest.m_Text);

          return FALSE;

    }

    Создайте файл ресурсов MFDlgRes.rc и добавьте в него новую диалоговую панель. На экране откроется окно редактора диалоговой панели и панель с инструментами Controls (рис. 2.28). По умолчанию новая диалоговая панель называется Dialog и содержит две кнопки OK и Cancel.

    Вы можете добавлять в диалоговую панель другие органы управления - кнопки, переключатели, поля редактирования, статические текстовые поля, рисунки. Более того в Visual C++ версии 4.0 вам становятся доступны новые органы управления - многостраничные диалоговые панели, поля для просмотра видеоинформации и т. д.

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

    Рис. 2.28. Создание диалоговой панели

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

    Кнопка

    Название

    Описание

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

    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

    Орган управления, внешний вид и назначение которого определяется пользователем

    <


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

    Сначала добавьте кнопку. Для этого щелкните по изображению кнопки в панели Controls. Затем переместите указатель мыши в то место диалоговой панели, где вы желаете разместить кнопку и нажмите левую клавишу мыши. В диалоговой панели появится изображение кнопки, названное по умолчанию Button1. Выполните по ней двойной щелчок левой клавишей мыши. На экране появится панель Push Button Propeties, определяющая различные характеристики кнопки. В первую очередь вас будут интересовать поля Caption и ID. В поле Caption введите название кнопки Default, а в поле ID ее идентификатор IDC_DEFAULT. Остальные характеристики кнопки оставьте без изменения.

    Находясь в редакторе ресурсов вы можете сразу попробовать как работает диалоговая панель. Найдите диалоговую панель Dialog (рис. 2.29). Если ее нет на экране, выберите из меню View строку Toolbars и в открывшейся диалоговой панели Toolbars установите переключатель Dialog. Диалоговая панель Dialog содержит ряд кнопок. Первая кнопка, на которой нарисован тумблер, позволяет проверить, как будет работать диалоговая панель.

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

    Рис. 2.29. Панель управления Dialog

    Остальные кнопки диалоговой панели Dialog позволяют задавать выравнивание органов управления друг относительно друга и относительно границ панели. Последние две кнопки устанавливают разметку на диалоговой панели. Разметка поможет вам ровнее разместить органы управления.

    Вы можете изменить заголовок диалоговой панели, ее идентификатор и многие другие параметры, если сделаете двойной щелчок левой кнопкой мыши по заголовку диалоговой панели. На экране появится панель Dialog Properties, содержащая несколько страниц. Выберите страницу General (рис. 2.30).

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

    Рис. 2.30. Характеристики диалоговой панели

    В поле ID вы можете изменить идентификатор диалоговой панели, в поле Caption - заголовок диалоговой панели. В полях Font name и Font size отображается тип и размер шрифта, который используется для всех текстов в диалоговой панели. Изменить параметры шрифта можно, нажав кнопку Font. Поля X Pos и Y Pos позволяют указать начальное положение диалоговой панели на экране. Если X Pos и Y Pos содержат нулевые значения, начальное положение диалоговой панели определяется операционной системой.



    Диалоговая панель может иметь собственное меню. Это меню будет отображаться непосредственно под заголовком диалоговой панели. Если вы желаете подключить к диалоговой панели меню, сначала разработайте меню и запишите его в файл ресурсов. Затем выберите в диалоговой панели Dialog Properties идентификатор данного меню из списка Menu.

    Для приложения MFDialog вам надо поменять только идентификатор диалоговой панели и ее название. Измените идентификатор диалоговой панели с IDD_DIALOG1 на “DIALOGPANEL”, а ее заголовок - с Dialog на My Dialog. Остальные характеристики диалоговой панели оставьте без изменения.

    Итак, диалоговая панель “DIALOGPANEL” приложения MFDialog содержит три кнопки, одно статическое текстовое поле и одно поле редактирования. В листинге 2.14 представлен фрагмент файла ресурсов в котором определяется диалоговая панель приложения.

    Листинг 2.14. Фрагмент файла MFDlgRes.rc

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

    // Диалоговая панель

    //

    DIALOGPANEL DIALOG DISCARDABLE  0, 0, 186, 46

    STYLE DS_MODALFRAME|DS_CENTER|WS_POPUP|WS_CAPTION|WS_SYSMENU

    CAPTION "My Dialog"

    FONT 8, "MS Sans Serif"

    BEGIN

        DEFPUSHBUTTON   "OK",IDOK,129,7,50,14

        PUSHBUTTON      "Cancel",IDCANCEL,129,24,50,14

        PUSHBUTTON      "Default",IDC_DEFAULT,70,7,50,14

        EDITTEXT        IDC_EDIT,7,24,113,14,ES_AUTOHSCROLL

        LTEXT           "Line Editor",IDC_STATIC,9,10,34,8

    END

    Идентификаторы, задействованные в файле ресурсов приложения по умолчанию, определяются во включаемом файле resource.h. Мы привели этот файл в листинге 2.15. Вы можете изменить название включаемого файла, выбрав из меню View строку Resource Includes.

    Листинг 2.15. Файл resource.h

    //{{NO_DEPENDENCIES}}

    // Включаемый файл, созданный Microsoft Developer Studio

    // Используется в файле ресурсов MFDlgRes.rc

    //

    #define IDR_MENU               101

    #define IDC_DEFAULT            1000

    #define IDC_EDIT               1001



    #define ID_TEST_DIALOG         40001

    #define ID_TEST_EXIT           40002

    // Следующие значения идентификаторов используются по

    // умолчанию для новых объектов

    #ifdef APSTUDIO_INVOKED

          #ifndef APSTUDIO_READONLY_SYMBOLS

                 #define _APS_NEXT_RESOURCE_VALUE        103

                 #define _APS_NEXT_COMMAND_VALUE         40003

                 #define _APS_NEXT_CONTROL_VALUE         1003

                 #define _APS_NEXT_SYMED_VALUE           101

          #endif

    #endif

    Обратите внимание, что включаемый файл resource.h содержит не только определения идентификаторов, но также дополнительную служебную информацию. Она расположена после директивы #ifdef APSTUDIO_INVOKED и представляет собой ряд макроопределений. Данные макроопределения используются редактором ресурсов при создании новых идентификаторов.

    Откройте страницу ClassView в окне Project Workspace. Обратите внимание, что если вы переместите окно Project Workspace к границе главного окна Visual C++, его заголовок пропадет и окно станет похоже на обычную панель управления. Если горизонтальный размер окна Project Workspace уменьшится, тогда исчезнут названия в закладках страниц и останутся только маленькие пиктограммы (рис. 2.31).

    В ней отображаются два класса CMFDialogApp и CMyDialog. В главный класс приложения CMFDialogApp входит метод InitInstance. В класс CMyDialog входит конструктор CMyDialog, метод DoDataExchange, предназначенный для обмена данными между органами управления диалоговой панели и привязанных к ним переменных, а также методы OnOK, OnCancel и OnDefault. Последние три метода вызываются когда пользователь нажимает на кнопки OK, Cancel и Default, расположенные в диалоговой панели. Кроме того, определена глобальная переменная MFDialogApp.

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

    Рис. 2.31. Классы проекта MFDialog

    Страница ResourceView окна Project Workspace показывает все ресурсы, входящие в проект (рис. 2.32). В приложении MFDialog определен только один ресурс - диалоговая панель, имеющая идентификатор “DIALOGPANEL”.



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

    Рис. 2.32. Ресурсы проекта MFDialog

    Постройте проект и запустите полученный выполнимый файл. На экране появится диалоговая панель My Dialog, определенная в файле ресурсов нашего проекта (рис. 2.33). Вначале поле редактирования Line Editor пустое. В нем вы можете ввести любой текст. Если вы нажмете на кнопку Default, тогда все, что вы ввели в поле редактирования Line Editor, пропадает и в нем отображается строка Start Text.

    Кроме кнопки Default в диалоговой панели My Dialog находятся еще две кнопки OK и Cancel. Если вы нажмете кнопку OK, тогда диалоговая панель закрывается, а на экране появляется текстовое сообщение, содержащее текст, введенный вами в поле Line Editor. Если нажать кнопку Cancel, то диалоговая панель My Dialog также закроется, но сообщение с введенным вами текстом не появится.

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

    Рис. 2.33. Приложение MFDialog

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


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


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


    Приложение Single


    В этом разделе мы рассмотрим однооконное приложение, созданное с использованием средств 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, на экране открывается стандартная диалоговая панель для выбора файла. Вы можете выбрать из этой панели любой файл и открыть его. Однако от этого изменится только заголовок окна приложения - в нем появится название открытого файла. Содержимое файла будет недоступно. Чтобы вы смогли просматривать и изменять содержимое открытого файла, необходимо добавить специальный код. Мы займемся этим в разделе “Простейший графический редактор” данной главы.
    Приложение Single
    Рис. 5.10. Приложение Single


    Приложение TestFile


    Теперь мы представим вам приложение, которое использует класс 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. Эта константа не может быть использована вместе с другими
    <
    Параметр lpFileSystemNameBuffer содержит указатель на буфер, в который будет записан тип файловой системы. В зависимости от типа файловой системы, установленной на данном томе, это может быть FAT, NTFS или HPFS.

    Размер буфера определяется параметром nFileSystemNameSize. Если вас не интересует тип файловой системы, запишите в параметр lpFileSystemNameBuffer значение NULL.

    Если функция GetVolumeInformation завершилась без ошибок, она возвращает значение TRUE. В противном случае возвращается значение FALSE, а для получения дополнительной информации вы можете вызвать функцию GetLastError.

    В листинге 3.1 представлен исходный текст приложения TestFile. В этом приложении используются всего несколько классов библиотеки классов MFC. Для организации самого приложения мы наследовали от базового класса CWinApp главный класс приложения CMFStartApp. Класс CWinApp был описан нами ранее в разделе “Приложение MFStart”.

    В классе CMFStartApp определены два метода: InitInstance и FileSystemInfo. Метод InitInstance является переопределением виртуального метода InitInstance базового класса CWinApp. Этот метод вызывается каждый раз при запуске приложения. Мы используем метод InitInstance для того, чтобы вызвать метод FileSystemInfo и сразу завершить работу приложения.

    Метод FileSystemInfo получает данные о файловой системе текущего тома и записывает их в файл fsystem.dat в текущем каталоге. Для получения информации о файловой системе используется функция GetVolumeInformation, описанная выше.

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

    Для записи в файл мы создали объект класса CStdioFile. В конструкторе мы сразу указали имя файла, который надо создать и открыть. Все операции с файлом, включая его создание, выполняются в блоке try. В случае возникновении ошибки во время создания или других операций с файлом вызывается исключение, которое будет обработано блоком catch.



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

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

    Чтобы закрыть файл, мы используем метод Close базового класса CFile. В случае успешного завершения на экране отображается сообщение с именем файла fsystem.dat и управление возвращается методу InitInstance.

    Если при работе с файлом возникла ошибка, управление передается блоку catch. В нем отображается сообщение об ошибке, а затем управление сразу возвращается методу InitInstance. Невыполненная часть блока try игнорируется.

    Листинг 3.1. Файл TestFile.cpp

    // Включаемый файл для MFC

    #include

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

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

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

    class CMFStartApp : public CWinApp

    {

    public:

          virtual BOOL     InitInstance();

          void                                                   FileSystemInfo();

    };

     

    // Создаем объект приложение класса CMFStartApp

    CMFStartApp MFStartApp;

     

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

    // Переопределяем виртуальный метод InitInstance

    // класса CWinApp

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

    BOOL CMFStartApp::InitInstance()

    {

          // Определяем характеристики файловой системы

          FileSystemInfo();              

          // Завершаем приложение

          return FALSE;

    }

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

    // Метод FileSystemInfo главного класса приложения

    // Определяет характеристики текущего тома и записывает



    // их в файл

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

    void CMFStartApp::FileSystemInfo()

    {

          // Метка тома

          CString VolumeNameBuffer;

          // Максимальная длина метки тома

          DWORD      nVolumeNameSize = 100;             

          // Серийный номер тома

          DWORD      VolumeSerialNumber;

          // Максимальная длина имени файла

          DWORD      MaximumComponentLength;

          // Характеристики файловой системы

          DWORD      FileSystemFlags;

          // Тип файловой системы

          CString FileSystemNameBuffer;

          // Максимальная длина строки типа файловой системы

          DWORD      nFileSystemNameSize = 100;

          // Получаем данные о файловой системе и текущем устройстве

          GetVolumeInformation(

                 NULL,

                 VolumeNameBuffer.GetBuffer(nVolumeNameSize),

                 nVolumeNameSize,

                 &VolumeSerialNumber,

                 &MaximumComponentLength, &FileSystemFlags,

                 FileSystemNameBuffer.GetBuffer(nFileSystemNameSize),

                 nFileSystemNameSize );

          // Снимаем блокировку буферов

          VolumeNameBuffer.ReleaseBuffer();

          FileSystemNameBuffer.ReleaseBuffer();

          // Обрабатываем ошибочные ситуации, которые могут

          // возникнуть при работе с файлами

          try

          {

                 // Создаем файл fsystem.dat и открываем его для записи

                 CStdioFile file("fsystem.dat",

                       CFile::modeCreate |

                       CFile::modeWrite |

                       CFile::typeText);

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

                 CString strTmpOut;

                 // Увеличиваем размер буфера до 512 байт

                 strTmpOut.GetBuffer(512);

                 // Записываем в файл метку тома

                 strTmpOut.Format("Метка тома: %s \n", VolumeNameBuffer);

                 file.WriteString(strTmpOut);



                 // Записываем в файл серийный номер

                 strTmpOut.Format("Серийный номер: %X \n",

                       VolumeSerialNumber);

                 file.WriteString(strTmpOut);

                 // Записываем в файл тип файловой системы

                 strTmpOut.Format("Тип файловой системы: %s \n",

                       FileSystemNameBuffer);

                 file.WriteString(strTmpOut);

         

                 // Записываем в файл максимальную длину имени файла

                 strTmpOut.Format("Максимальная длина имени файла: %d \n",

                       MaximumComponentLength);

                 file.WriteString(strTmpOut);

                

                 // Записываем в файл свойства файловой системы

                 strTmpOut = "Свойства файловой системы \n";

                 if(FileSystemFlags & FS_CASE_IS_PRESERVED)

                       strTmpOut += "   FS_CASE_IS_PRESERVED\n";

                 if(FileSystemFlags & FS_CASE_SENSITIVE)

                       strTmpOut += "   FS_CASE_SENSITIVE\n";

         

                 if(FileSystemFlags & FS_UNICODE_STORED_ON_DISK)

                       strTmpOut += "   FS_UNICODE_STORED_ON_DISK\n";

         

                 if(FileSystemFlags & FS_PERSISTENT_ACLS)

                       strTmpOut += "   FS_PERSISTENT_ACLS\n";

         

                 if(FileSystemFlags & FS_FILE_COMPRESSION)

                       strTmpOut += "   FS_FILE_COMPRESSION\n";

                 if(FileSystemFlags & FS_VOL_IS_COMPRESSED)

                       strTmpOut += "   FS_VOL_IS_COMPRESSED\n";

                 file.WriteString(strTmpOut);       

         

                 // Закрываем файл

                 file.Close();

                 // Отображаем сообщение об успешном завершении приложения

                 MessageBox(NULL, "File fsystem.dat", "Message", MB_OK);

          }

          // Обработчик исключения. Вызывается при ошибках

          // работы с файлами

          catch(...)

          {

                 // Отображаем сообщение о возникшей ошибке

                 MessageBox(NULL, "File I/O Error", "Error", MB_OK);

          }

          return;

    }

    Файл fsystem.dat, созданный приложением, можно просмотреть в любом текстовом редакторе, например Notepad или WordPad. В листинге 3.2 приведен пример файла, полученного при помощи приложения TestFile на нашем компьютере, на котором установлена операционная система Windows 95.

    Листинг 3.2. Файл fsystem.dat

    Метка тома:  LIBRARY

    Серийный номер: 1D794E8D

    Тип файловой системы: FAT

    Максимальная длина имени файла: 255

    Свойства файловой системы

       FS_CASE_IS_PRESERVED

       FS_UNICODE_STORED_ON_DISK


    Процедура создания однооконного приложения


    Сначала вы должны создать новый проект и выбрать тип проекта 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
    <
    Следующий переключатель в диалоговой панели называется Would you like support for OLE compound files?. Положение переключателя определяет в каком формате будет сохранятся документ, подготовленный в приложении. Если переключатель находится в положении Yes, please, и документ содержит несколько OLE объектов, то остается возможность индивидуального доступа к этим объектам.

    В нижней части диалоговой панели расположены еще два переключателя - OLE automation и OLE controls. Переключатель OLE automation следует включить, если вы желаете, чтобы приложение было доступно для других приложений, поддерживающих OLE automation. Переключатель OLE controls надо включить, если вы предполагаете использовать в приложение органы управления OLE.

    Установив все переключатели как вам надо, нажмите кнопку Next >. На экране появится следующая диалоговая панель MFC AppWizard (рис. 5.3).

    Процедура создания однооконного приложения

    Рис. 5.3. Третий шаг MFC AppWizard

    Переключатели в верхней части панели определяют основные особенности приложения, такие как использование панели управления (toolbar), справочной системы, трехмерных органов управления.

    Переключатель Docking toolbar определяет, будет ли приложение иметь панель управления. Если вы включите этот переключатель, то приложение будет иметь панель управления с кнопками (рис. 5.4).

    Процедура создания однооконного приложения

    Рис. 5.4. Панель управления

    По умолчанию в нем размещаются три группы кнопок. Вы можете потом добавлять или удалять кнопки из панели управления по своему усмотрению.

    Первая группа из трех кнопок предназначена для управления документами: они позволяют создавать новый документ, открывать существующий документ и  сохранять открытый документ в файле.

    Вторая группа из трех кнопок может быть использована для редактирования документа. Эти кнопки позволяют удалять, копировать в clipboard и вставлять из clipboard выделенный текст.

    В последней, третьей группе находится кнопка, позволяющая распечатать документ и кнопка для вызова справочной системы.

    Переключатель Initial status bar управляет панелью состояния status bar. Эта панель размещается в нижней части главного окна приложения. По умолчанию в этой панели отображается краткую подсказку о режиме работы приложения и о положении клавиш , и . На рисунке 5.5 мы привели примерный вид панели состояния.



    Вы сможете использовать панель состояния и для других целей. Например в этой панели можно отображать текущие дату и время, и даже небольшие пиктограммы.

    Процедура создания однооконного приложения

    Рис. 5.5. Панель состояния

    Переключатель Printing and print preview определяет, сможет ли приложение распечатать подготовленный в нем документ и будет ли доступен режим предварительного просмотра распечатки на экране.

    Переключатель Context-sensitive Help управляет контекстно зависимой подсказкой в вашем приложении. Если этот переключатель включен, тогда AppWizard создаст набор файлов справочных баз данных. Используя эти файлы в качестве шаблона, вы легко сможете добавить собственную справочную информацию к этой базе.

    Переключатель 3D controls определяет внешний вид приложения. Если переключатель 3D controls включен, тогда пользовательский интерфейс приложения, включая главное окно приложения, дочерние окна и диалоговые панели, будет выглядеть объемным.

    Библиотека классов MFC позволяет создавать приложения, поддерживающие технологию WOSA. Эта поддержка позволяет создавать приложения, способные непосредственно работать с почтовой системой, а также взаимодействовать с другими приложениями в локальной или глобальной сети через протокол TCP/IP.

    Переключатель MAPI (Messaging API) управляет поддержкой почтового API. Если вы желаете, чтобы ваше приложение могло передавать и принимать почтовые сообщения (MS Mail, MS Exchenge), включите этот переключатель.

    Переключатель Windows Sockets управляет поддержкой сокетов Windows. Если включить этот переключатель, созданное приложение сможет взаимодействовать с другими приложениями по протоколу TCP/IP.

    Большинство приложений позволяют сохранять документы в файлах на диске. Впоследствии эти файлы можно снова открыть и продолжить с ними работать. Названия нескольких файлов, с которыми ваше приложение работало позже остальных, отображаются в меню File главного окна приложения. Чтобы открыть один из этих файлов надо выбрать его имя из меню File. По умолчанию приложение будет сохранять названия четырех файлов. Вы можете изменить это число, выбрав его в поле How many files would you like on your recent file list?.



    В диалоговой панели MFC AppWizard - Step 4 of 6 располагается кнопка Advanced, позволяющая указать дополнительные характеристики приложения, такие как название главного окна приложения, расширение файлов, в которые приложение будет сохранять свои документы и т. д. Все эти характеристики можно будет настроить непосредственно в исходном тексте приложения, но гораздо удобнее сделать этой сейчас, во время работы MFC AppWizard.

    Нажмите кнопку Advanced. На экране появится диалоговая панель Advanced Options, которая содержит две страницы: Document Template Strings и Window Styles. Рассмотрим их более подробно.

    Страница Document Template Strings представлена нами на рисунке 5.6 и определяет характеристики документов с которыми будет работать приложение. В верхней части этой страницы расположена группа Non-localized strings. В поле File extension вы можете указать расширение, которое будет по умолчанию присваиваться файлам, созданным приложением. Мы указали в этом поле строку lis. В поле File type ID отображается идентификатор, под которым данный тип документов заносятся в регистрационную базу Windows 95.

    Рассмотрим группу Localized strings. Наибольший интерес представляет поле Main frame caption. В нем можно указать заголовок главного окна приложения. По умолчанию это поле содержит имя проекта. В поле Doc type name отображается название типа документов, создаваемых приложением.

    Когда вы создаете новый документ и сохраняете его в файле и когда вы открываете файл, чтобы загрузить содержащийся в нем документ, на экране появляются стандартные диалоговые панели для ввода имени файла. Чтобы в этих панелях по умолчанию отображались только имена файлов с определенным расширением, введите в поле Filter name название фильтра для имен файлов, а в поле File extension само расширение. Обычно имя фильтра формируют на основе названия данного типа документа и расширения имени файлов документа (см. поле File extension). Так, в нашем случае, используется имя фильтра Single Files (*.lis).



    Процедура создания однооконного приложения

    Рис. 5.6. Диалоговая панель Advanced Options, страница Document Template Strings

    Если в приложении определено несколько шаблонов документов, тогда при создании нового документа на экране появляется диалоговая панель File New. Из этой панели можно выбрать шаблон для создания нового документа. Строка из поля File new name (OLE short name) будет показана в этой панели. В случае, если приложение поддерживает технологию OLE как сервер, то строка File new name используется в качестве короткого имени объекта OLE.

    В поле File type name (OLE long name) вы можете ввести имя типа файлов. Это имя будет использоваться в стандартных панелях Open и Save As, в качестве типа файлов документов. Для приложений использующих технологию OLE поле File type name также определяет длинное имя объекта OLE.

    Теперь выберите страницу Window Style диалоговой панели Advanced Options. Для этого достаточно нажать на соответствующую закладку (рис. 5.8).

    Иногда при редактировании документа бывает удобно одновременно просматривать различные участки одного документа. Для этого можно открыть его в двух окнах одновременно. Однако еще удобнее разделить окно на несколько частей (рис. 5.7). Такая возможность реализована, например, в текстовом процессоре Microsoft Word for Windows.

    MFC AppWizard упрощает программисту разработку таких приложений. Чтобы главное окно приложения с однооконным интерфейсом, или MDI окна многооконных приложений можно было разделить на несколько частей достаточно включить переключатель Use split window.

    Процедура создания однооконного приложения

    Рис. 5.7. Разделение окна на несколько частей

    В группе Main frame styles расположены переключатели, определяющие вид главного окна приложения. Ниже приведена таблица, в которой описаны эти переключатели.

    Переключатель

    Описание

    Thick frame

    Главное окно имеет рамку, позволяющую изменить его размер

    Minimize box

    Главное окно содержит кнопку для сворачивания его в пиктограмму

    Maximize box

    Главное окно содержит кнопку для увеличения его размера до максимально возможного

    System menu

    Главное окно приложения будет иметь системное меню

    Minimized

    При запуске приложения его главное окно будет уменьшено до пиктограммы. Потом вы сможете открыть окно

    Maximized

    При запуске приложения его главное окно принимает максимально возможный размер

    <


    Процедура создания однооконного приложения

    Рис. 5.8. Диалоговая панель Advanced Options, страница Window Styles

    Если вы разрабатываете приложение, имеющее многооконный интерфейс, то вам становятся доступны переключатели из группы MDI child frame styles. Они позволяют выбрать внешний вид дочерних окон приложения. Вот краткое описание этих переключателей:

    Переключатель

    Описание

    Thick frame

    Все MDI окна приложения имеют рамку, позволяющую изменить их размер

    Minimize box

    MDI окна содержат кнопку для сворачивания их в пиктограмму

    Maximize box

    MDI окна содержат кнопку для увеличения их размера до максимально возможного

    Minimized

    Открываемые окна MDI будут уменьшены до пиктограммы

    Maximized

    Открываемые окна MDI будут иметь максимально возможный размер

    Конечно, если вы не укажите в диалоговых панелях MFC AppWizard, что приложение должно иметь панель состояния status bar или справочную систему, то их можно будет добавить потом. Но для этого вам потребуется непосредственно исправлять исходный текст приложения. Значительно легче сделать это, включив несколько переключателей в панели MFC AppWizard, чем непосредственно изменять исходный текст.

    Теперь вы можете перейти к следующей диалоговой панели MFC AppWizard. Для этого нажмите кнопку Next >. На экране появится панель, аналогичная панели, представленной на рисунке 1.4. В этой диалоговой панели, вы можете попросить MFC AppWizard снабдить исходный текст приложения комментариями, а также выбрать, как приложение будет использовать библиотеку классов MFC - вызывая библиотеки DLL или включая код классов непосредственно в приложение.

    Теперь перейдите к последнему этапу определения свойств приложения, нажав кнопку Next >. На экране появится диалоговая панель для выбора названий классов приложения. Внешний вид этой панели уже был представлен на рисунке 5.9. Но теперь список классов будет значительно больше. Имена классов образуются от имени проекта. В нашем примере проект называется Single.

    Если вы создаете приложение, имеющее однооконный интерфейс, то в списке классов будут класс приложения с именем CSingleApp, класс главного окна CMainFrame, класс документа CSingleDoc и класс для просмотра документа CSingleView.



    Обратите внимание, что в качестве базового класса для CSingleView можно выбрать различные классы, определенные в библиотеке MFC.

    Процедура создания однооконного приложения

    Рис. 5.9. Выбор базового класса для класса CSingleView

    Вы должны выбрать базовый класс, который лучше всего подходит для вашего приложения. Это может значительно сэкономить время, которое вы потратите на создание приложения.

    Например, если вам надо создать редактор текста, который может воспринимать файлы в формате RTF, то вам достаточно наследовать класс CSingleView от базового класса CRichEditView. И все! Откомпилируйте проект и текстовый редактор готов. Вы можете открывать файлы в текстовом формате и формате RTF, редактировать их и сохранять измененные файлы.

    Если вы создаете многооконное приложение, то проект будет содержать еще один класс - CChildFrame. Этот класс управляет дочерними MDI окнами приложения.

    В зависимости от того, включите ли вы в приложение поддержку технологии OLE, баз данных, стандарта WOSA, в проект могут быть включены и другие классы.

    Теперь, когда вы закончили заполнять диалоговые панели MFC AppWizard, нажмите кнопку Finish. На экране появится диалоговая панель New Project Information. В этой панели приводится описание приложения. Если вас что-то не устраивает, вы можете нажать кнопку Cancel и создать проект заново.

    Чтобы продолжить разработку приложения, нажмите кнопку OK. MFC AppWizard приступит к построению проекта и создаст все файлы проекта. Полученный проект сразу можно оттранслировать и запустить на выполнение. Для этого вы можете воспользоваться меню Build или нажать комбинацию клавиш .


    Просмотр дерева наследования классов


    ClassView предоставляет очень интересную и полезную возможность просмотра дерева наследования классов приложения. Для этого выберите название интересующего вас класса из списка классов и откройте временное меню, нажав правую кнопку мыши. Строки Derived Classes и Base Classes позволяют просмотреть последовательность классов, порожденных от данного класса или последовательность базовых классов.
    На рисунке 2.17 мы воспользовались возможностями ClassView для изучения последовательности базовых классов для основного класса приложения MFHello.
    Просмотр дерева наследования классов
    Рис. 2.17. Порядок наследования, диалоговая панель Base Classes and Members
    Диалоговая панель Base Classes and Members не имеет кнопок OK и Cancel. Она закрывается в том случае, если вы выберите из нее элемент класса или переключитесь на другое окно. Иногда удобно, чтобы диалоговая панель постоянно присутствовала на экране. Вы можете “прикрепить” ее к главному окну Microsoft Visual C++. Нажмите кнопку Просмотр дерева наследования классов, она изменится на Просмотр дерева наследования классов. Теперь в любой момент времени вы сможете открыть панель Base Classes and Members.
    В левой части этой панели отображается список классов в порядке наследования. Базовые классы расположены ниже и правее наследуемых классов. Вы наглядно можете определить, что основным базовым классом для CMFHelloApp является класс CObject. Из этого класса наследуются классы CCmdTarget, CWinThread, CWinApp и только затем класс CMFHelloApp, определенный в приложении.
    В правой части экрана отображаются элементы класса, выбранного из списка классов. Вы можете просмотреть не только элементы классов, определенных в приложении, но также и элементы классов из библиотеки MFC. Все элементы класса разделены на три группы в зависимости от их области видимости - Public, Protected и Private.
    Панель Base Classes and Members позволяет легко перейти к редактированию любого элемента класса. Для этого выберите название этого элемента из списка и сделайте по нему двойной щелчок левой кнопкой мыши.
    Чтобы вам было легче отличить методы от данных класса, перед методами располагается символ f, а перед данными d. Непосредственно перед именем метода или данных расположен символ, позволяющий отличить статические и виртуальные методы. Перед названиями статических методов расположен символ Просмотр дерева наследования классов, а перед виртуальными - Просмотр дерева наследования классов.

    Если список методов и данных класса слишком велик, вы можете отображать в диалоговой панели только их часть. Выберите из поля Functions типы методов, имена которых надо отобразить. Доступны следующие типы методов:

    Тип метода

    Отображать

    All

    Все методы

    Virtual

    Виртуальные методы

    Static

    Статические методы

    Non- Static

    Все методы, кроме статических

    Non- Virtual

    Все методы, кроме виртуальных

    Non- Static Non-Virtual

    Все методы, кроме статических и виртуальных

    None

    Не отображать методы класса

    Аналогично, из списка Data надо выбрать типы элементов данных класса, которые надо отображать на экране. В следующей таблице описаны различные типы данных, представленные в списке Data.

    Тип элементов данных

    Отображать

    All

    Все элементы данных

    Static

    Статические элементы данных

    Non- Static

    Все данные, кроме статических

    None

    Не отображать элементы данных

    В нижней правой части диалоговой панели Base Classes and Members отображаются названия файлов в которых определен (группа Definitions), и в которых используется (группа References) выбранный элемент. Двойной щелчок левой кнопкой мыши позволяет открыть в редакторе соответствующий файл. Курсор при этом сразу устанавливается в то место, где объявляется или используется выбранный элемент класса.


    Просмотр характеристик класса


    Последняя страница 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. Более подробно об этой возможности мы расскажем в одной из следующих книг серии.


    Простейший графический редактор


    В предыдущем разделе мы создали при помощи средств MFC AppWizard работающий шаблон приложения. Теперь мы усовершенствуем его, чтобы пользователь смог рисовать в окне приложения и сохранять свои рисунки в файле на диске.
    За основу нашего нового приложения мы возьмем проект Single и внесем в него все необходимые исправления и добавления. Доработаем приложение Single так, что когда пользователь нажимает левую клавишу мыши в окне отображается окружность, а когда пользователь нажимает правую кнопку - то отображается квадрат.
    В момент нажатия на клавиши мыши создаются соответствующие сообщения, которые передаются классу окна просмотра. Нажатие левой клавиши мыши вызывает сообщение WM_LBUTTONDOWN, а нажатие правой - сообщение WM_RBUTTONDOWN.
    Чтобы класс окна просмотра CSingleView мог отреагировать на это сообщение, вы должны создать метод для его обработки. Лучше всего для этого воспользоваться средствами ClassWizard.
    Откройте страницу Message Maps на панели ClassWizard. Выберите из списков Class name и Object IDs класс CSingleView. В списке Messages появится названия виртуальных методов, которые вы можете переопределить и сообщений, для которых можно создать методы обработки.
    Выберите из списка Messages сообщение WM_LBUTTONDOWN и нажмите кнопку Add Function. ClassWizard добавляет новую строку в таблицу сообщений класса CSingleView, вставляет в класс описание нового метода обработчика сообщения и создает шаблон этого метода.
    Нажмите кнопку Edit Code. В окне редактирования появится шаблон метода, предназначенного для обработки сообщения WM_LBUTTONDOWN.
    void CSingleView::OnLButtonDown(UINT nFlags, CPoint point)
    {
          // TODO: Здесь вы можете разместить код метода
         
          CView::OnLButtonDown(nFlags, point);
    }
    Название этого метода ClassWizard выбирает автоматически на основе сообщения WM_LBUTTONDOWN. Для этого префикс WM_ в названии сообщения заменяется префиксом On и происходит замена некоторых прописных букв строчными.
    Шаблон метода OnLButtonDown содержит вызов метода OnLButtonDown базового класса CView. Вы должны добавить свой код перед вызовом этого метода, сразу после коментария // TODO:.

    void CSingleView::OnLButtonDown(UINT nFlags, CPoint point)

    {

          // TODO: Здесь вы можете разместить код метода

          CClientDC dc(this);

          dc.Ellipse(point.x-10, point.y-10, point.x+10,point.y+10);

         

          CView::OnLButtonDown(nFlags, point);

    }

    Чтобы нарисовать в окне просмотра окружность, сначала необходимо получить контекст отображения. Для этого создается объект dc класса CClientDC. Конструктору передается указатель this, который указывает на объект класса CSingleView.

    Затем вызывается метод Ellipse, который и отображает на экране небольшую окружность с центром, совпадающим с координатами указателя мыши.

    Повторите только что проделанную процедуру для сообщения WM_RBUTTONDOWN. Создайте метод обработчик этого сообщения и добавьте в него команды отображения квадрата.

    void CSingleView::OnRButtonDown(UINT nFlags, CPoint point)

    {

          // TODO: Здесь вы можете разместить код метода

          CClientDC dc(this);

          dc.Rectangle(point.x-10, point.y-10,

                             point.x+10,point.y+10);

          CView::OnRButtonDown(nFlags, point);

    }

    Постройте проект и запустите полученное приложение. Нажимайте правую и левую кнопку мыши. Вы увидите, что на экране появляются окружности и квадраты (рис. 5.13). Поэкспериментируйте с приложением. Вы заметите, что изображение на экране пропадает, если оно перекрывается другими окнами, а также в некоторых других случаях.

    Простейший графический редактор

    Рис. 5.13. Отображение фигур в окне приложения Single

    Это происходит потому, что наше приложение не обрабатывает одно из самых важных сообщений операционной системы Windows - сообщение WM_PAINT. Когда в окно приложения поступает сообщение WM_PAINT, приложение должно обновить информацию, отображаемую в данном окне.

    Мы должны сохранить координаты и размеры нарисованных окружностей и квадратов, чтобы приложение могло воспроизвести их на экране, когда придет сообщение WM_PAINT.

    Так как эти координаты в конечном счете представляют документ с которым работает приложение, то хранить их надо в классе документа приложения - CSingleDoc. Пользователь может нарисовать в документе любое количество фигур. Поэтому для хранения их координат лучшее всего подходит список или массив. Так как мы изучаем объектно-ориентированное программирование, мы воспользуемся для этого специальными классами. В состав библиотеки входят шаблоны для создания таких классов и несколько готовых классов. В нашей программе мы используем шаблон класса CArray.



    Создадим новый класс CFigure, который будет представлять геометрические фигуры - окружности и квадраты. Координаты этих фигур мы будем определять по координатам их центра. Для этого в состав класса включим элемент xyFigCenter класса CPoint. Класс CPoint определяет координаты точки и содержит два элемента x и y, соответствующие координатам точки по оси ординат и абсцисс. Краткое описание класса CPoint представлено в разделе “Класс CPoint - точка на плоскости” главы “Некоторые классы MFC”.

    Второй элемент cType типа char определяет форму геометрической фигуры. Если cType содержит значение 'E' значит данный объект представляет окружность, а если 'R' - квадрат.

    Вы можете создать для класса CFigure отдельный файл, но сейчас мы просто добавим его в самое начало файла SingleDoc.h. Вот определение класса CFigure.

    //////////////////////////////////////////////////////////////

    // Класс определяет геометрическую фигуру

    class CFigure

    {

    public:

          // Координаты центра фигуры

          CPoint  xyFigCenter;

          // Тип фигуры: 'E' - оокружность, 'R' - кволрат

          char      cType;

    };

    Один объект класса CFigure представляет одну геометрическую фигуру. Так как документ нашего приложения может содержать несколько фигур, мы воспользуемся шаблоном CArray, чтобы определить массив объектов класса CFigure. Вы можете получить дополнительную информацию о шаблоне CArray в разделе “Коллекции” главы “Некоторые классы MFC”.

    Определение этого массива, который получил название arrayFig, помещаем в класс документа CSingleDoc, в атрибутах класса.

    //////////////////////////////////////////////////////////////

    // Класс CSingleDoc

    class CSingleDoc : public CDocument

    {

    protected:

          CSingleDoc();

          DECLARE_DYNCREATE(CSingleDoc)

    // Attributes

    public:

          CArray arrayFig;

    Если вы используете шаблоны классов CArray, CMap или CList, вы должны включить в исходный текст приложения файл afxtempl.h. В данном файле содержатся определения этих шаблонов.



    Так как мы работаем с объектами класса CArray в различных файлах, удобнее всего включить его в самом конце файла stdafx.h.

    // Включаемый файл stdafx.h

    // ...

    // Включаемый файл для шаблона CArray

    #include

    Теперь у нас есть структура для хранения геометрических фигур, нарисованных в окне. Мы должны ее заполнить. Так как за взаимодействие с пользователем отвечает класс окна просмотра, мы изменяем определенные нами ранее методы OnLButtonDown и OnRButtonDown таким образом, чтобы одновременно с выводом на экран они сохраняли параметры новой фигуры в массиве arrayFig.

    //////////////////////////////////////////////////////////////

    // Метод OnLButtonDown класса CSingleView

    // Обрабатывает сообщения левой кнопки мыши

    void CSingleView::OnLButtonDown(UINT nFlags, CPoint point)

    {

          // Получаем указатель на документ (объект класса CSingleDoc)

          CSingleDoc* pDoc = GetDocument();

          // Проверяем указатель pDoc

          ASSERT_VALID(pDoc);

          // Отображаем на экране окружность

          CClientDC dc(this);

          dc.Ellipse(point.x-10, point.y-10,

                 point.x+10,point.y+10);

          // Сохраняем характеристики окружности

          CFigure OneFigure;

          OneFigure.xyFigCenter = point;

          OneFigure.cType = 'E';

          // Добавляем к массиву, определяющему документ, новый

          // элемент

          pDoc->arrayFig.Add(OneFigure);

          // Вызываем метод OnLButtonDown базового класса CView

          CView::OnLButtonDown(nFlags, point);

    }

    //////////////////////////////////////////////////////////////

    // Метод OnRButtonDown класса CSingleView

    // Обрабатывает сообщения правой кнопки мыши

    void CSingleView::OnRButtonDown(UINT nFlags, CPoint point)

    {

          // Получаем указатель на документ (объект класса CSingleDoc)

          CSingleDoc* pDoc = GetDocument();

          // Проверяем указатель pDoc

          ASSERT_VALID(pDoc);

          // Отображаем на экране квадрат



          CClientDC dc(this);

          dc.Rectangle(point.x-10, point.y-10,

                             point.x+10,point.y+10);

          // Сохраняем характеристики квадрата

          CFigure OneFigure;

          OneFigure.xyFigCenter = point;

          OneFigure.cType = 'R';

          // Добавляем к массиву, определяющему документ, новый

          // элемент

          pDoc->arrayFig.Add(OneFigure);

          // Вызываем метод OnRButtonDown базового класса CView

          CView::OnRButtonDown(nFlags, point);

    }

    Теперь координаты и форма всех нарисованных фигур запоминаются в классе документа. Следующим шагом надо определить, как отображать эти фигуры на экране. Для этого следует внести изменения в метод OnDraw класса окна просмотра CSingleView.

    //////////////////////////////////////////////////////////////

    // Метод OnDraw класса окна просмотра

    void CSingleView::OnDraw(CDC* pDC)

    {

          CSingleDoc* pDoc = GetDocument();

          ASSERT_VALID(pDoc);

          // TODO:

          int i;

          for (i=0; iarrayFig.GetSize(); i++)

          {

                 if(pDoc->arrayFig[i].cType == 'E')

                       pDC->Ellipse(pDoc->arrayFig[i].xyFigCenter.x-10,

                                                                    pDoc->arrayFig[i].xyFigCenter.y-10,

                                                                    pDoc->arrayFig[i].xyFigCenter.x+10,

                                                                    pDoc->arrayFig[i].xyFigCenter.y+10);

                 else if (pDoc->arrayFig[i].cType == 'R')

                       pDC->Rectangle(pDoc->arrayFig[i].xyFigCenter.x-10,

                                                                    pDoc->arrayFig[i].xyFigCenter.y-10,

                                                                    pDoc->arrayFig[i].xyFigCenter.x+10,

                                                                    pDoc->arrayFig[i].xyFigCenter.y+10);

          }

    }

    Постройте проект и запустите полученное приложение. Вы можете свободно изменять размеры окна приложения, перекрывать его окно окнами других приложений, минимизировать и восстанавливать размеры окна. Изображение документа, которое вы нарисуете, не пропадет.

    Вы даже можете распечатать нарисованный документ на принтере. А ведь вы не написали для этого не единой строки кода. Перед печатью документа его можно проверить в режиме предварительного просмотра (рис. 5.14). Для этого выберите из меню File строку Print Preview

    Простейший графический редактор

    Рис. 5.14. Режим предварительного просмотра документа перед печатью


    Простейший текстовый редактор


    Когда вы создаете приложение с однооконным или многооконным интерфейсом при помощи средств MFC AppWizard, в последней диалоговой панели вы можете выбрать базовый класс для окна просмотра приложения. По умолчанию используется класс CView.
    От класса CView наследуется целая группа классов, которые можно использовать для управление окном просмотра документа. Каждый из этих классов предоставляет дополнительные возможности для отображения документа.
    CEditView     <- |<-   CCtrlView <- | CView <- CWnd

    CRichEditView <- |                  |
    CListView     <- |                  |
    CTreeView     <- |                  |
                                        |
            CFormView <- CScrollView <- |
    Опишем основные характеристики этих классов.

    Класс
    Описание
    CView
    Наиболее общий класс, обеспечивающий отображение документа и взаимодействие с пользователем
    CScrollView
    Класс CScrollView наследован от базового класса CView. В этом классе добавлены полосы просмотра, позволяющие перемещать документ в окне
    CEditView
    Класс наследован от класса CView. Класс CEditView предоставляет возможности простого текстового редактора
    CRichEditView
    Класс наследован от класса CView. Класс предоставляет возможности текстового редактора. В отличие от CEditView, он позволяет работать с текстом в формате RTF
    CFormView
    Класс обеспечивает форматированное отображение документа на основе диалоговой панели
    CListView
    Класс обеспечивает отображение документа с использование спискового органа управления
    CTreeView
    Класс обеспечивает отображение документа с использование древовидного органа управления

    Чтобы создать простейший текстовый редактор создайте новое приложение с однооконным (или многооконным) интерфейсом. В качестве базового класса для класса окна просмотра приложения выберите класс CEditView. Завершите создание шаблона приложения.
    Постройте полученный проект и запустите приложение. Текстовый редактор готов! Вы можете открыть в нем любой текстовый файл, отредактировать его и сохранить, внесенные изменения.

    Более того, ваше приложение может работать с обменным буфером clipboard. Оно может записывать данные в clipboard и вставлять их из clipboard в редактируемый документ.

    Фактически в приложении Editor полностью работают все строки меню Edit. Командные сообщения этого меню обрабатываются классом CEditView, поэтому их не надо реализовывать вручную. Вот краткое описание строк меню Edit.

    Строка меню Edit

    Описание

    Undo

    Отменить последнюю операцию

    Cut

    Удалить выделенный текст и записать его в clipboard

    Copy

    Скопировать выделенный текст в clipboard

    Paste

    Вставить в документ содержимое clipboard


    Простые классы


    MFC содержит классы, соответствующие объектам типа простых геометрических фигур, текстовых строк и объектам, определяющим дату и время. В следующей таблице перечислены названия этих классов и их краткие описания.

    Класс
    Описание
    CPoint
    Объекты класса описывают точку
    CRect
    Объекты класса описывают прямоугольник
    CSize
    Объекты класса определяют размер прямоугольника
    CSrting
    Объекты класса представляют собой текстовые строки переменной длинны
    CTime
    Объекты класса служат для хранения даты и времени. Большое количество методов класса позволяют выполнять над объектами класса различные  преобразования
    CTimeSpan
    Объекты класса определяют период времени



    Проверка целостности объектов класса


    Метод AssertValid выполняет проверку целостности объекта класса. Он проверяет состояние элементов данных класса на недопустимые значения. Если вы работаете с отладочной версией приложения и метод AssertValid обнаружит ошибку во внутреннем представлении объекта класса, выполнение приложения прерывается с выдачей соответствующего сообщения.
    virtual void AssertValid() const;
    Если вы наследуете класс от базового класса CObject и желаете использовать возможности метода AssertValid, вы должны переопределить его. Переопределенный метод AssertValid должен вызывать метод AssertValid базового класса, чтобы проверить целостность соответствующей части объекта. Затем необходимо выполнить проверку элементов порожденного класса. Для этого используйте макрокоманду ASSERT:
    ASSERT(booleanExpression)
    Макрокоманда ASSERT проверяет свой параметр booleanExpression. Если параметр макрокоманды имеет нулевое значение (FALSE), она отображает диагностическое сообщение и прерывает работу приложения. Если параметр booleanExpression не равен нулю (TRUE) работа приложения продолжается и макрокоманда не выполняет никаких действий.
    Макрокоманда ASSERT выполняет проверку только в отладочной версии приложения. В окончательной версии приложения, построенной  без отладочной информации, макрокоманда ASSERT не работает.
    Если проверку параметра макрокоманды необходимо выполнять и в окончательной версии приложения, вы можете использовать вместо макрокоманды ASSERT макрокоманду VERIFY. Но при обнаружении ошибки работа приложения не будет прервана.
    Вот пример переопределения метода AssertValid для класса CFigure, наследованного от базового класса CObject:
    // Класс CFigure наследуется от базового класса CObject
    class    CFigure : public CObject
    {
          // Переопределяем виртуальный метод базового класса
          int   m_area = 0;
          // Остальные элементы класса...
    }
    // Переопределяем виртуальный метод AssertValid класса CObject
    void CFigure::AssertValid() const
    {
          // Сначала проверяем целостность элементов базового класса
          CObject::AssertValid();
          // Проверяем элемент m_area.
          // Он должен быть больше или равен нулю
          ASSERT(m_area >= 0);
    }


    Распределение памяти


    Стандартная библиотека компиляторов содержит специальные функции управления памятью - malloc, free, а также другие разновидности этих функций. Они позволяют получить для использования блок оперативной памяти, и затем отдать его обратно операционной системе.
    В Си++ встроены специальные операторы для управления памятью - оператор new и оператор delete. Эти операторы очень удобны для динамического создания переменных, массивов и объектов классов, поэтому мы остановимся на них более подробно.


    Разграничение доступа к элементам базового класса


    Мы уже рассказывали, что можно управлять доступом к элементам класса, указывая спецификаторы доступа для элементов класса. Элементы класса, объявленные с спецификаторами protected и private доступны только из методов самого класса. Элементы с спецификаторами public доступны не только из методов класса, но и извне.
    При создании порожденного класса встает вопрос о доступе к элементам базового класса. Оказывается, когда вы наследуете класс из базового класса, вы можете управлять разграничением доступа к элементам базового класса. При этом имеет значение то, как объявлены элементы базового класса и какой спецификатор доступа указан для базового класса.
    По следующей таблице вы можете определить как будут доступны элементы базового класса в зависимости от спецификатора доступа базового класса и спецификаторов доступа элементов базового класса.

    Спецификатор доступа базового класса
    Спецификатор доступа элемента базового класса
    public
    protected
    private
    public
    Доступны как public
    Доступны как protected
    Доступны как private
    protected
    Доступны как protected
    Доступны как protected
    Доступны как private
    private
    Недоступны
    Недоступны
    Недоступны



    Разграничение доступа к элементам класса


    Определив класс, вы можете создавать объекты этого класса и манипулировать ими, используя методы. Некоторые данные и методы, объединенные одним классом, можно сделать недоступными вне реализации класса, к другим можно будет обращаться из программы.
    Для управления доступом к элементам класса предусмотрены ключевые слова public, private и protect (спецификаторы доступа). Методы и данные, определенные или описанные после ключевого слова public представляют собой интерфейс класса  - они доступны для использования вне определения класса. Остальные члены класса относятся к его внутренней реализации и обычно недоступны вне класса. Различия между членами класса, описанными после ключевых слов private и protect сказываются только при наследовании от данного класса новых классов. Процедуру наследования мы рассмотрим позже.
    Ключевые слова public, private и protect указываются в определении класса перед элементами класса, доступом к которым они управляют. Ключевые слова, управляющие доступом, могут быть указаны несколько раз в одном классе, порядок их расположения значения не имеет. По умолчанию элементы класса являются private. Рекомендуется всегда явно определять права доступа к членам класса.
    Ниже представлено определение класса Sample:
    class Sample

    {
          int  iX;
          void      Load();
    public:

          void      SetStr();

          void      GetStr();
          char      sDataText[80];

    private:

          char      sNameText[80];
          int  iIndex;
    public:

          void      ConvertStr();

          int  iLevel;
    };
    В классе описаны элементы данных iX, sDataText, sNameText, iIndex, iLevel и методы Load, SetStr, GetStr, ConvertStr.
    Элементы данных и методы SetStr, GetStr, sDataText, ConvertStr, iLevel объявлены public. К ним можно обращаться как из методов класса Sample, так и из программы. Остальные элементы класса объявлены как private. Доступ к ним открыт только для методов самого класса, а также дружественных функций и дружественных методов других классов. Дружественные функции и дружественные классы описаны в следующем разделе.


    Редактирование элементов данных класса


    Временное меню данных класса отличается от временного меню метода. Если выбрать из списка элементов класса данные и нажать правую кнопку мыши, на экране появится меню, представленное на рисунке 2.19.
    Редактирование элементов данных класса
    Рис. 2.19. Временное меню метода


    Редактирование методов класса


    Выберите из списка элементов класса интересующий вас метод и нажмите правую кнопку мыши. На экране появится временное меню, показанное нами на рисунке 2.18. Это меню позволяет перейти к редактированию объявления или определения метода, просмотреть те строки исходного текста приложения, в которых вызывается метод, получить список функций и методов, вызываемых выбранным методом.
    Редактирование методов класса
    Рис. 2.18. Временное меню метода
    Чтобы открыть в редакторе файл, в котором объявляется метод и перейти к его редактированию, выберите из временного меню метода строку Go to Definition. После того как вы добавите в класс новый метод, ClassWizard создаст шаблон метода. Строка Go to Declaration из временного меню метода  позволяет изменить этот шаблон по вашему усмотрению.
    Возможности ClassView можно использовать даже на этапе отладки приложения. Так, из временного меню метода можно установить точку прерывания непосредственно на начало метода. Для этого выберите из меню строку Set Breakpoint.


    Ресурсы приложения


    Добавить в проект новый ресурс очень просто. Вы можете для этого воспользоваться кнопками панели управления Progect или установить указатель мыши на название типа ресурса и нажать правую кнопку мыши. На экране появится временное меню свойств данного ресурса.
    Если у вас уже есть ресурсы, которые разработаны ранее и записаны в отдельных файлах на диске, вы можете подключить их к своему проекту. Для этого надо выбрать из временного меню свойств ресурса строку Import. На экране появится диалоговая панель Import Resource с приглашением ввести имя файла подключаемого вами ресурса. Новый ресурс проекта будет записан в подкаталог RES, расположенный в каталоге проекта.
    Сам по себе новый ресурс, подключенный к приложению, не принесет никакой пользы. Ваше приложение должно обращаться к нему - загружать в память, обрабатывать сообщения, связанные с этим ресурсом
    Среда Visual C++ версии 4.0 не только позволяет просматривать и редактировать ресурсы приложения, она позволяет сразу после создания ресурса вызвать ClassWizard и подключить к ресурсу управляющий программный код. Пока мы не будем использовать ClassWizard, а код, управляющий ресурсами приложения, создадим вручную.

    Большую часть пользовательского интерфейса любого приложения составляют ресурсы - меню, диалоговые панели, пиктограммы, курсоры. Создавая приложение, MFC AppWizard подготавливает для него базовый набор ресурсов. Вы можете редактировать подготовленные для вас ресурсы по своему усмотрению, а также добавлять в проект новые ресурсы.
    Все ресурсы приложения хранятся в отдельном каталоге. Этот каталог называется RES и располагается в главном каталоге проекта. Однако нет необходимости каждый раз вручную открывать файлы с ресурсами. Для этого надо использовать средства, предоставляемые средой Visual C++.
    Просмотреть ресурсы приложения можно в окне Project Workspace на странице ResourceView. Ресурсы приложения представлены в виде дерева и разделяются по типам. Чтобы просмотреть ресурсы определенного типа, надо установить курсор на символ Ресурсы приложения и нажать левую кнопку мыши. Символ Ресурсы приложения изменится на Ресурсы приложения и вам откроется список ресурсов данного типа, включенных в проект. Около имени каждого ресурса размещается небольшая пиктограмма. Чтобы загрузить выбранный ресурс в редактор, сделайте двойной щелчок левой кнопкой мыши по имени ресурса или по его пиктограмме. Вот список ресурсов, доступных для использования в приложении:

    Ресурс
    Описание
    Ресурсы приложения
    Accelerators
    Акселераторы
    Ресурсы приложения
    Bitmaps
    Растровые изображения в формате BMP
    Ресурсы приложения
    Cursors
    Курсоры
    Ресурсы приложения
    Dialogs
    Диалоговые панели
    Ресурсы приложения
    Icons
    Пиктограммы
    Ресурсы приложения
    Menus
    Меню
    Ресурсы приложения
    String tables
    Таблицы текстовых строк
    Ресурсы приложения
    Toolbars
    Панели управления
    Ресурсы приложения
    Version information
    Сведения о версии приложения
    Ресурсы приложения
    Имя определяется программистом
    Ресурс, определяемый самим пользователем

    MFC AppWizard автоматически создает несколько различных ресурсов для приложения Dialog. Вы можете просмотреть эти ресурсы в окне Project Workspace, выбрав страницу ResourceView. Как видите ресурсы приложения включают две диалоговые панели, пиктограмму, таблицу строк и информацию о версии приложения. Рассмотрим эти ресурсы подробнее.



    Приложение с однооконным интерфейсом, созданное средствами MFC AppWizard, имеет гораздо больше ресурсов, чем приложение, использующее в качестве интерфейса обыкновенную диалоговую панель. В нем определены не только диалоговые панели, таблица текстовых строк, пиктограмма и ресурс описания версии приложения, но также меню, панель управления и таблица акселераторов.


    Самый базовый класс MFC (класс CObject)


    Подавляющее большинство классов библиотеки MFC наследовано от базового класса CObject, лежащего в основе всей иерархии классов этой библиотеки. Методы и элементы данных класса CObject представляют наиболее общие свойства наследованных из него классов MFC.
    Класс CObject, а также все классы, наследованные от него, обеспечивают возможность сохранения объектов класса в файлах на диске с их последующим восстановлением.
    Для объектов классов, наследованных от базового класса CObject уже во время работы приложения можно получить разнообразную информацию о классе объекта.
    Ряд методов класса CObject предназначен для получения дампа объектов класса во время отладки приложения. Эта особенность класса может ускорить процесс поиска ошибок в приложении.


    Шаблон документов (классы CDocTemplate, CSingleDocTemplate и CMultiDocTemplate)


    Еще один важный класс, наследуемый от CCmdTarget, называется CDocTemplate. От этого класса наследуются два класса CSingleDocTemplate и CMultiDocTemplate. Все эти классы предназначены для синхронизации и управления основными объектами представляющими приложение - окнами, документами и используемыми ими ресурсами.


    Шаблон меню


    Большой интерес для нас представляет ресурс, описывающий меню приложения. В ресурсах приложения определен только один шаблон меню, имеющий идентификатор IDR_MAINFRAME.
    Когда пользователь выбирает строки меню, операционная система передает командное сообщение главному окну приложения.
    //////////////////////////////////////////////////////////////
    // Меню
    IDR_MAINFRAME MENU PRELOAD DISCARDABLE
    BEGIN
          POPUP "&File"
          BEGIN
                 MENUITEM "&New\tCtrl+N",                   ID_FILE_NEW
                 MENUITEM "&Open...\tCtrl+O",              ID_FILE_OPEN
                 MENUITEM "&Save\tCtrl+S",   ID_FILE_SAVE
                 MENUITEM "Save &As...",       ID_FILE_SAVE_AS
                 MENUITEM SEPARATOR
                 MENUITEM "&Print...\tCtrl+P",ID_FILE_PRINT
                 MENUITEM "Print Pre&view", ID_FILE_PRINT_PREVIEW
                 MENUITEM "P&rint Setup...",  ID_FILE_PRINT_SETUP
                 MENUITEM SEPARATOR
                 MENUITEM "Recent File",        ID_FILE_MRU_FILE1,GRAYED
                 MENUITEM SEPARATOR
                 MENUITEM "E&xit",                ID_APP_EXIT
          END
          POPUP "&Edit"
          BEGIN
                 MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO
                 MENUITEM SEPARATOR
                 MENUITEM "Cu&t\tCtrl+X",    ID_EDIT_CUT
                 MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY
                 MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE
          END
          POPUP "&View"
          BEGIN
                 MENUITEM "&Toolbar",          ID_VIEW_TOOLBAR
                 MENUITEM "&Status Bar",       ID_VIEW_STATUS_BAR
          END
          POPUP "&Help"
          BEGIN
                 MENUITEM "&About Single...",                ID_APP_ABOUT
          END
    END
    Большая часть строк меню IDR_MAINFRAME имеет стандартные идентификаторы, описанные в библиотеке MFC. Некоторые из команд, соответствующих этим идентификаторам полностью обрабатываются MFC. Список стандартных команд с их описанием представлен в разделе “Стандартные команды”.


    Шаблоны в MFC


    В библиотеке классов MFC определен ряд шаблонов для создания таких структур хранения информации как массив, список, словарь. Более подробно об этих шаблонах вы можете прочитать в разделе “Коллекции” главы “Некоторые классы MFC”.


    Шаблоны


    Языки программирования С и Си++ обеспечивают строгую проверку типов данных. Некоторые языки не обеспечивают такой проверки и она полностью ложится на плечи программиста. Например в языке PL1 вы можете сравнивать значение строковой и числовой переменных. Это не будет ошибкой с точки зрения компилятора. Если вы случайно допустите ошибку, то обнаружить ее будет достаточно сложно.
    Однако строгая типизация, присущая некоторым языкам, не всегда удобна. Если вам надо выполнять одинаковые вычисления над переменными различных типов, придется создавать две фактически одинаковые функции, которые оперируют с различными типами данных.
    Аналогичная ситуация возникает, если вам надо создать класс, для работы с различными типами данных. Допустим вам надо создать список из элементов. Удобнее всего это сделать при помощи классов. Но если вам надо, чтобы в качестве элементов списка фигурировали различные типы данных, то вам наверняка придется написать несколько одинаковых классов, различающихся только типом элементов.
    Естественно такая ситуация усложняет работу программиста, увеличивают объем исходных текстов программы, и наконец просто может стать причиной ошибок и несоответствий. Так, если в вашей программе содержится несколько функций или классов, отличающихся только типом данных, которыми они оперируют, то когда вы вносите исправления в одну функцию, надо будет точно также исправлять и все остальные.
     Чтобы облегчить программисту работу, в стандарт языка Си++ было включено понятие шаблона. В Visual C++ шаблоны реализованы начиная с версии 2.0. Ранние версии Visual C++ с ними работать не могли.
    Шаблоны предназначены для создания ряда классов и функций, отличающихся только типом обрабатываемых ими данных. Для определения шаблона предназначено ключевое слово template (шаблон). Общий синтаксис определения шаблона имеет следующий вид:
    template  declaration;
    Аргумент template-argument-list представляет собой список условных имен для определения типов, по которым будут различаться различные реализации классов или функций данного шаблона.


    Синхронизация задач приложения (класс CSyncObject)


    Библиотека MFC позволяет создавать многозадачные приложения. Для синхронизации отдельных задач приложения предусмотрен ряд специальных классов. Все они наследуются от класса CSyncObject, представляющего собой абстрактный класс (рис. 2.8).
    Синхронизация задач приложения (класс CSyncObject)
    Рис. 2.8. Класс CSyncObject
    В некоторых случаях требуется, чтобы участок программного кода мог выполняться только одной задачей. Такой участок называют критической секцией кода. Для создания и управления критическими секциями предназначены объекты класса CCriticalSection.
    Объекты класса CEvent представляют событие. При помощи событий одна задача приложения может передать сообщение другой.
    Объекты класса CMutex позволяют в данный момент времени представить ресурс в пользование только одной задачи. Остальным задачам доступ к ресурсу запрещается.
    Объекты класса CSemaphore представляют собой семафоры. Семафоры позволяют ограничить количество задач, которые имеют доступ к какому-либо ресурсу.


    Следующие версии Microsoft Visual C++


    Весной 1996 года вышла новая версия Microsoft Visual C++ 4.1. В состав этой версии продукта включены дополнительные средства для разработки приложений, поддерживающих глобальную сеть Internet, язык моделирования виртуальной реальности (Virtual Reality Modeling Language - VRML), игровое SDK (Windows 95 Games SDK) и большое количество OCX объектов, которые вы можете включать в свои приложения.
  • Поддержка сети Internet

  • Последние версии Microsoft Internet Information Server, используемого в качестве серверов WWW и FTP, имеют программный интерфейс (Internet Server application programming interface - ISAPI). Используя этот интерфейс, вы можете разрабатывать собственные модули, взаимодействующие с сервером.
    Чтобы облегчить программистам создание таких модулей, Microsoft Visual C++ версии 4.1 содержит “волшебник” ISAPI Extention Wizard, работающий на тех же принципах, что и MFC AppWizard. ISAPI Extention Wizard использует новые классы библиотеки MFC - CHttpServer, CHttpFilter, CHttpServerContext, CHttpFilterContext и CHtmlStream.
  • Язык моделирования виртуальной реальности

  • Язык моделирования виртуальной реальности позволяет помещать на WEB страницах серверов WWW трехмерные изображения. Подключившись к такой странице, пользователь сможет не просто просматривать статические изображения, он сможет перемещаться по трехмерному виртуальному пространству, выбирать различные объекты и т. д.
  • Игровое SDK

  • В настоящее время существует очень мало компьютерных игр, созданных специально для работы в операционной системе Windows. Обычно эти игры достаточно примитивны и не далеко ушли от таких игр как пасьянс и минер, поставляемых вместе с Windows. В то же время игры, которые не нуждаются в средствах Windows, значительно сложнее и имеют несравненно более сложный и реалистичный интерфейс.
    Отсутствие сложных игр для Windows связано в первую очередь с тем, что в этой операционной системе нет средств для быстрого отображения графической информации на экране. Существующие методы не позволяют приложениям Windows отображать информацию с достаточной для компьютерных игр скоростью.
    С появлением операционной системы Windows 95 ситуация начала изменяться к лучшему. В Windows 95 встроен специальный набор функций, обеспечивающий приложениям высокоэффективный доступ к видеоадаптеру. Вышли первые игры для Windows 95. Так фирма idSoftware выпустила специальную версию популярной игры Doom 2, предназначенную для работы исключительно в среде Windows 95.
    Для создания программ, поддерживающих этот набор функций, необходимо использовать специальной средство разработки Games SDK. Ранее оно поставлялось отдельно, а начиная с версии 4.1 компилятора Microsoft Visual C++, вошло в состав этого пакета.
  • Набор OCX объектов

  • По сравнению с Microsoft Visual C++ версии 4.0 в версии 4.1 значительно расширен набор органов управления OLE (OCX). В него Microsoft включила органы управления OCX, разработанные другими фирмами.


    Словари - шаблон CMap


    Словарь, это таблица переменной длины, состоящая из двух колонок. Первая колонка содержит ключевые поля, а вторая - соответствующие им значения. Пользуясь объектами этого класса, вы можете по ключевому полю получить связанное с ним значение. Для поиска по ключу используется алгоритм хеширования, реализация которого вручную заняла бы слишком много времени.
    В MFC включены классы для создания словарей отображающих значения слов, указателей, указателей на объекты типа CObject и объектов CString. Библиотека MFC версии 4.0 содержит шаблоны для организации словарей. Шаблоны позволяют создать класс словарей, основанных на объектах любых типов.
    Ниже представлен прототип шаблона CMap.
    template                     class ARG_VALUE> class CMap : public CObject
    Параметр шаблона KEY определяет тип ключевого поля словаря. Параметр ARG_KEY является типом данных, используемых в качестве аргумента KEY. Параметр VALUE соответствует типу элементов, хранящихся в словаре, а параметр ARG_VALUE используется в качестве аргумента VALUE.


    Сохранение и восстановление документа на диске


    Построенное вами приложение можно использовать для рисования и печати документов, но оно не позволяет сохранять и загружать документ из файла на диске. Вы можете выбрать строку Save As (сохранить под именем) из меню File. На экране появится диалоговая панель Save As. В этой панели вы можете ввести имя файла, в котором надо сохранить документ. Однако несмотря на то, что файл создается, документ в него не записывается - файл остается пустым.
    Вы можете попытаться его открыть, выбрав из меню File строку Open. Однако единственным результатом будет изменение заголовка окна. Чтобы приложение обрело возможность сохранения документов в файле и последующего чтения, надо изменить метод Serialize класса документа CSingleDoc.
    Метод Serialize вызывается всякий раз когда надо сохранить документ в файле на диске или загрузить его из существующего файла. В частности, метод Serialize вызывается, когда пользователь выбирает из меню File строки Save, Save As и Open. Основные принципы работы метода Serialize были рассмотрены нами в разделе “Запись и восстановление объектов”.
    MFC AppWizard подготавливает шаблон метода Serialize для класса CSingleDoc, представляющего документ приложения.
    //////////////////////////////////////////////////////////////
    // Метод Serialize класса CSingleDoc отвечает за сохранение и
    // последующее восстановление документов приложения
    void CSingleDoc::Serialize(CArchive& ar)
    {
          if (ar.IsStoring())
          {
                 // TODO: Здесь выполняется сохранение документа
          }
          else
          {
                 // TODO: Здесь выполняется загрузка документа
          }
    }
    Вы должны определить в методе Serialize, как он должен сохранять и восстанавливать документы приложения. Так как документ, с которым работает наше приложение представлен классом CSingleDoc, то все что должен делать метод Serialize - это сохранять все элементы массива arrayFig.
    //////////////////////////////////////////////////////////////
    // Метод Serialize класса CSingleDoc

    void CSingleDoc::Serialize(CArchive& ar)

    {

          int i;            // временная переменная

          int num;      // количество фигур в документе

          // Сохранение документа

          if(ar.IsStoring())

          {    

                 // Определяем количество элементов массива arrayFig

                num = arrayFig.GetSize();

                 // Записываем полученное число в файл

                 ar << num;

                 // Записываем в файл координаты и тип фигур

                 for(i=0; i
                 {

                       // Сохраняем координаты центра фигуры

                       ar << arrayFig[i].xyFigCenter;

                       // Сохраняем тип фигуры

                       ar << arrayFig[i].cType;

                 }

          }

         

          // Загрузка документа

          else

          {

                 // Считываем количество элементов, составляющих документ

                 ar >> num;

                 // Восстанавливаем документ

                 for(i=0; i
                 {

                       CFigure OneFigure; // описание одной фигуры

                      

                       // Считываем координаты центра фигуры

                       ar >> OneFigure.xyFigCenter;

                       // Считываем тип фигуры

                       ar >> OneFigure.cType;

                       // Добавляем описание очередной фигуры в документ

                       arrayFig.Add(OneFigure);

                 }

          }

    }

    Метод Serialize имеет единственный параметр ar, представляющий ссылку на объект класса CArchive. Этот объект, называемый архивом, представляет файл документа, расположенный на диске. Кроме того, архив несет в себе информацию о том, что делать с документом - записать его в файл или загрузить из файла.

    После вызова, метод Serialize определяет, какую операцию надо выполнить - сохранить документ в файле или загрузить его из файла. Для этого используется метод IsStoring, определенный в классе CArchive. Если метод IsStoring возвращает ненулевое значение для объекта ar, переданного методу Serialize, значит надо сохранить документ в файле.

    Чтобы сохранить все элементы массива, мы определяем количество элементов в нем с помощью метода GetSize. Этот метод определен в шаблоне CArray и возвращает количество элементов массива.

    Мы сохраняем количество элементов массива в файле, представленном архивом ar. Это значение поможет нам при восстановлении документа с файла на диске. Затем в цикле в файл записываются все элементы массива arrayFig.

    Загрузка документа из файла выполняется в том же порядке. Сначала из файла документа, представленного архивом ar считывается значение, определяющее количество фигур в документе. Потом из файла считываются по очереди все элементы документа. При этом они сразу заносятся в массив arrayFig, представляющий документ. Для этого используется метод Add шаблона CArray.

    После того, как вы внесете изменения в метод Serialize, постройте проект. Запустите полученное приложение. Теперь вы сможете записать документ в файл на диске, а затем загрузить его снова.


    Сохранение и восстановление состояния объекта


    В классе CObject определены метод IsSerializable и виртуальный метод Serialize, которые используются для сохранения и восстановления состояния объектов в файлах на диске. Чтобы объекты класса можно было сохранять в файлах на диске с возможностью их последующего восстановления, объявление класса объекта должно содержать макрокоманду DECLARE_SERIAL, а реализация класса макрокоманду IMPLEMENT_SERIAL. Более подробно об сохранении и восстановлении объектов можно прочитать в разделе “Запись и восстановление объектов”.


    Сокеты (классы CAsyncSocket и CSocket)


    Для тех, кто занимается сетевыми коммуникациями, в состав библиотеки MFC включены классы CAsyncSocket и наследованный от него класс CSocket (рис. 2.9). В класс CAsyncSocket включен программный интерфейс Windows Socket.
    Класс CSocket предоставляет программисту более высокий уровень для работы с сокетами. Это значительно облегчает задачу программирования сетевых приложений.
    Программирование на уровне программного интерфейса Windows с использованием сокетов описано в двадцать третьем томе серии “Библиотека системного программиста”, который называется “Глобальные сети компьютеров”.
    Сокеты (классы CAsyncSocket и CSocket)
    Рис. 2.9. Класс CAsyncSocket


    Сообщения от органов управления


    Эта группа включает в себя сообщения WM_COMMAND от дочерних окон (включая окна стандартных классов), передаваемые их родительскому окну. Сообщения от органов управления обрабатываются точно таким же образом, что и оконные сообщения.
    Исключение составляет сообщение WM_COMMAND с кодом извещения BN_CLICKED. Это сообщение передается кнопкой, когда пользователь на нее нажимает. Обработка сообщений с кодом извещения BN_CLICKED от органов управления происходит аналогично обработке командных сообщений.


    Создание меню для приложения MFMenu


    Так как наше приложение будет содержать меню, мы приступим к созданию ресурсов приложения. Для этого создайте новый файл ресурсов. Выберите из меню File строку New, а затем из открывшейся диалоговой панели выберите строку Resource Script и нажмите кнопку OK.
    Теперь надо создать меню. Выберите из меню Insert строку Resource. На экране появится диалоговая панель Insert Resource. Выберите из нее строку Menu и нажмите кнопку OK. Вы сможете в диалоговом режиме разработать меню. Чтобы быстрее перейти к редактированию меню, вы можете нажать кнопку New Menu (Создание меню для приложения MFMenu) из панели управления Project или просто нажать комбинацию кнопок .
    Создайте меню, содержащее единственную строку Test, при выборе которой открывается меню, содержащее три строки - Beep и Exit. Внешний вид меню во время разработки представлен на рисунке 2.22.
    Создание меню для приложения MFMenu
    Рис. 2.22. Разработка меню приложения
    Когда вы создаете новое меню верхнего уровня или определяете строки, входящие в это меню, на экране появляется диалоговое окно Menu Item Properties (рис. 2.23). В нем полностью описывается выбранный элемент меню. В поле Caption задается название меню или строки меню. В названии вы можете вставить символ &. Он будет означать, что символ, следующий за ним, можно будет использовать для быстрого выбора данного элемента меню. Например, если перед названием строки меню Beep поставить символ &, то во время работы приложения символ B будет отображаться с подчеркиванием, и строку Beep можно будет выбрать, нажав комбинацию клавиш .
    Создание меню для приложения MFMenu
    Рис. 2.23. Диалоговая панель Menu Item Properties
    Как вы знаете, каждый элемент меню должен иметь уникальный идентификатор, однозначно его определяющий. Вы можете задать идентификатор в поле ID или выбрать его из списка предопределенных идентификаторов.
    Редактор ресурсов сам предлагает вам название идентификатора, создавая его из названия главного меню и строки меню. Так например строке Beep меню Test по умолчанию будет присвоен идентификатор ID_TEST_BEEP.
    В поле Prompt можно в нескольких словах описать данный пункт меню. Введенная вами строка будет записана в строковый ресурс. Ей будет присвоен тот же самый идентификатор, что и пункту меню. Приложение может использовать строку описания, чтобы предоставить пользователю краткую справку о меню.

    Если вы разрабатываете приложение с помощью средств MFC AppWizard, то при выборе пользователем строк меню их описание будет появляться в панели состояния StatusBar. Что интересно, для этого вам не потребуется написать ни одной строки текста программы. Это будет сделано автоматически благодаря возможностям библиотеки классов MFC и MFC AppWizard.

    Остальные переключатели диалоговой панели Menu Item Properties описаны в следующей таблице.

    Переключатель

    Описание

    Break

    Этот переключатель может принимать три различных значения - None, Column и Bar. Он позволяет задать структуру меню.

    По умолчанию выбрано значение None оно соответствует нормальному виду меню без деления на колонки.

    Если выбрать значение Column, тогда пункт меню будет размещен в новом столбце.

    Значение Bar соответствует Column, за исключением меню верхнего уровня. Если указать Bar для меню верхнего уровня, то новая колонка будет отделена от старой вертикальной линией

    Checked

    Если установить переключатель, то строка меню будет выделена символом Создание меню для приложения MFMenu. Потом, обращаясь к специальным методам, вы сможете удалять или отображать этот символ

    Grayed

    Если включить переключатель Grayed, тогда пункт меню будет отображаться серым цветом и будет недоступен для выбора пользователем. Такая возможность удобна, если вам надо сделать недоступным какие-либо возможности приложения. Впоследствии приложение может сделать пункт меню доступным. Для этого надо вызвать соответствующий метод

    Help

    Если установить переключатель Help, тогда для него будет установлено выравнивание по правому краю. Обычно этот переключатель устанавливают для меню верхнего уровня, которое управляет справочной системной приложения

    Inactive

    Если включен переключатель Grayed, тогда переключатель недоступен. В противном случае вы можете установить переключатель Inactive. В этом случае пункт меню будет неактивен

    Pop-up

    Вы можете создавать многоуровневые меню. Если вы включите переключатель Pop-up, то данный пункт меню будет являться меню верхнего уровня, которое можно открыть. По умолчанию, все пункты главного меню имеют установленный переключатель Pop-up. Так как меню верхнего уровня служит только для объединения других пунктов меню, то оно не имеет идентификатора

    Separator

    Если переключатель установлен, тогда в меню вставляется разделитель. Для разделителя все остальные поля и переключатели диалоговой панели не используются

    <


    Сохраните файл ресурсов в файле с именем MFMenu.rc. Редактор ресурсов создает кроме самого файла ресурсов еще включаемый файл, в котором определяются константы, используемые в файле ресурсов. В нашем случае в нем определяются идентификаторы меню приложения. По умолчанию этот файл сохраняется под именем resource.h. Вы можете изменить его, выбрав из меню View строку Resource Includes. Для нашего приложения мы изменили имя включаемого файла для ресурсов на MFMenuRes.h. Содержимое этого файла представлено листингом 2.8.

    Листинг 2.8. Файл MFMenuRes.h

    //{{NO_DEPENDENCIES}}

    // Включаемый файл, созданный Microsoft Developer Studio

    // Используется в файле ресурсов MFMenu.rc

    //

    #define IDR_MENU                        101

    #define ID_TEST_BEEP                    40001

    #define ID_TEST_EXIT                    40002

    // Следующие значения идентификаторов используются по

    // умолчанию для новых объектов

    #ifdef APSTUDIO_INVOKED

          #ifndef APSTUDIO_READONLY_SYMBOLS

                 #define _APS_NEXT_RESOURCE_VALUE        102

                 #define _APS_NEXT_COMMAND_VALUE         40003

                 #define _APS_NEXT_CONTROL_VALUE         1000

                 #define _APS_NEXT_SYMED_VALUE           101

          #endif

    #endif

    В листинге 2.9 мы привели файл ресурсов MFMenuRes.rc приложения. Этот файл был подготовлен редактором ресурсов Visual C++. Одна из первых строк файла содержит директиву #include которой подключается файл MFMenuRes.h, содержащий описание идентификаторов ресурсов (листинг 2.8).

    Среди прочих служебных строк, необходимых редактору ресурсов и компилятору Visual C++, вы можете обнаружить описание меню приложения IDR_MENU. Для первого приложения, использующего ресурсы мы привели файл ресурсов полностью. Впоследствии мы ограничимся словесным описанием ресурсов и будем приводить только выдержки из файла ресурсов.

    Листинг 2.9. Файл MFMenuRes.rc

    // Файл описания ресурсов приложения, созданный

    // Microsoft Developer Studio

    #include "MFMenuRes.h"



    #define APSTUDIO_READONLY_SYMBOLS

    //////////////////////////////////////////////////////////////

    // Включаем файл afxres.h, содержащий определения стандартных

    // идентификаторов

    #include "afxres.h"

    //////////////////////////////////////////////////////////////

    #undef APSTUDIO_READONLY_SYMBOLS

    //////////////////////////////////////////////////////////////

    // Руссификацированные ресурсы

    #if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_RUS)

          #ifdef _WIN32

                 LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT

                 #pragma code_page(1251)

          #endif //_WIN32

          ////////////////////////////////////////////////////////////              // Меню

          //

          IDR_MENU MENU DISCARDABLE

          BEGIN

                 POPUP "Test"

                 BEGIN

                       MENUITEM "Beep",                        ID_TEST_BEEP

                       MENUITEM SEPARATOR

                       MENUITEM "Exit",                        ID_TEST_EXIT

                 END

          END

          #ifdef APSTUDIO_INVOKED

                

                 ////////////////////////////////////////////////////////

                 // Ресурсы TEXTINCLUDE

                 //

                 1 TEXTINCLUDE DISCARDABLE

                 BEGIN

                       "MFMenuRes.h\0"

                 END

                 2 TEXTINCLUDE DISCARDABLE

                 BEGIN

                       "#include ""afxres.h""\r\n"

                       "\0"

                 END

                 3 TEXTINCLUDE DISCARDABLE

                 BEGIN

                       "\r\n"

                       "\0"

                 END

          #endif         // APSTUDIO_INVOKED

    #endif                // Руссификацированные ресурсы

    //////////////////////////////////////////////////////////////

    #ifndef APSTUDIO_INVOKED

    #endif

    Когда вы создадите ресурсы приложения и включите файл ресурсов в проект обратите внимание на окно Project Workspace. В нем появится еще одна, четвертая страница ResourceView (рис. 2.24). Эта страница показывает все ресурсы, входящие в проект. В приложении MFMenu определен только один ресурс - меню, имеющее идентификатор IDR_MENU.



    Вы можете быстро перейти к редактированию меню, если выберите его идентификатор и щелкните два раза левой кнопкой мыши.

    Создание меню для приложения MFMenu

    Рис. 2.24. Страница ResourceView окна Project Workspace

    Теперь проект готов. Вы можете построить его и запустить полученное приложение MFMenu. Внешний вид приложения представлен на рисунке 2.25. Как видите окно приложения имеет меню Test, состоящее из двух строк - Beep и Exit.

    Если вы выберите строку Beep из меню Test, то услышите на внутреннем динамике компьютера звуковой сигнал. В случае если звуковой сигнал не слышен, проверьте подключен ли внутренний динамик, а если в компьютере установлена звуковая плата, правильно установите громкость сигнала.

    Когда вы завершите работу с приложением, его можно закрыть. Для этого воспользуйтесь системным меню приложения или выберите из меню Test строку Exit.

    Создание меню для приложения MFMenu

    Рис. 2.25. Приложение MFMenu

    Чтобы объекты класса могли обрабатывать сообщения, в определении этого класса необходимо поместить макрокоманду DECLARE_MESSAGE_MAP. По принятым соглашениям эта макрокоманда должна записываться в конце определения класса в секции public.

    //=====================================================

    // Класс CMFMenuWindow - представляет главное окно

    //=====================================================

    class CMFMenuWindow : public CFrameWnd

    {

    public:

          // Объявляем конструктор класса CMFMenuWindow

          CMFMenuWindow();

          // Объявляем методы для обработки команд меню

          afx_msg void MenuCommand();

          afx_msg void ExitApp();

          // Макрокоманда необходима, так как класс

          // CMFMenuWindow обрабатывает сообщения

          DECLARE_MESSAGE_MAP()   

    };

    Однако это еще не все. Необходимо также определить таблицу сообщений. Таблица начинается макрокомандой BEGIN_MESSAGE_MAP и заканчивается макрокомандой END_MESSAGE_MAP. Между этими макрокомандами расположены строки таблицы сообщений, определяющие сообщения, подлежащие обработке данным классом и методы, которые выполняют такую обработку.



    Приложение может содержать несколько классов, обладающих собственными таблицами сообщений. В следующем разделе мы приведем пример такого приложения. Чтобы однозначно определить класс, к которому относится таблица сообщений, имя этого класса записывается в первый параметр макрокоманды BEGIN_MESSAGE_MAP.

    Приложение MFMenu обрабатывает только две команды от меню приложения. Первая команда имеет идентификатор ID_TEST_BEEP и передается, когда пользователь выбирает из меню Test строку Beep. Для обработки этой команды вызывается метод MenuCommand. Вторая команда с идентификатором ID_TEST_EXIT передается приложению, когда пользователь выбирает из меню Test строку Exit. Обработка этого сообщения выполняется методом ExitApp.

    //=====================================================

    // Таблица сообщений класса CMFMenuWindow

    //=====================================================

    BEGIN_MESSAGE_MAP(CMFMenuWindow, CFrameWnd)

          ON_COMMAND(ID_TEST_BEEP, MenuCommand)

          ON_COMMAND(ID_TEST_EXIT, ExitApp)

    END_MESSAGE_MAP()

    Конечно, приложению MFMenu может поступать гораздо больше сообщений и команд, чем указано в таблице сообщений класса CMFMenuWindow. Необработанные сообщения передаются для обработки базовому классу CMFMenuWindow - классу CFrameWnd. Класс, который будет обрабатывать сообщения, не указанные в таблице сообщений, указывается во втором параметре макрокоманды BEGIN_MESSAGE_MAP.


    Создание нового документа


    Документ, который вы можете создать в приложении Single, можно убрать, только полностью закрыв приложение. Функция создания нового документа не работает. Когда вы выбираете из меню File строку New или нажимаете кнопку Создание нового документа, расположенную в панели управления приложения, внешний вид документа не изменяется.
    Оказывается, когда пользователь выбирает из меню File строку New, вызывается виртуальный метод OnNewDocument, определенный в классе CDocument. Если вы не переопределите этот метод, то по умолчанию он вызывает метод DeleteContents, и далее помечает его как чистый (пустой). Вы можете переопределить метод OnNewDocument в своем классе документа, чтобы выполнить его инициализацию. Требуется, чтобы вы вызывали из переопределенного метода OnNewDocument, метод OnNewDocument, определенный в базовом классе CDocument.
    Когда пользователь создает новый документ в приложении, построенном на основе однооконного интерфейса, то на самом деле используется старый документ. Новый объект класса, представляющего документ, не создается. Метод OnNewDocument должен удалить содержимое документа и выполнить повторную инициализацию существующего объекта класса документ.
    Из этого следует, что нельзя выполнять инициализацию документа в конструкторе класса документа, так как конструктор будет вызван только один раз за время работы приложения. Более правильно использовать для этой цели метод OnNewDocument.
    Для приложений, использующих многооконный интерфейс, процедура создания нового документа отличается. Каждый раз, когда пользователь создает новый документ, создается новый объект класса документ. Процесс создания нового документа мы опишем в следующей главе.
    //////////////////////////////////////////////////////////////
    // Метод DeleteContents
    void CSingleDoc::DeleteContents()
    {
          // TODO:
          // Очищаем документ, удаляя все элементы массива arrayFig.
          // Метод RemoveAll определен в классе CArray
          arrayFig.RemoveAll();
          // Вызываем метод DeleteContents базового класса CDocument
          CDocument::DeleteContents();
    }


    Создание нового класса


    Из любой страницы ClassWizard можно добавить в приложение новый класс, созданный на основе базовых классов. В качестве базового класса можно использовать классы, наследованные от класса CCmdTarget или класса CRecordset. Если требуется создать класс, не наследуемый от базовых классов CCmdTarget или CRecordset, использовать средства ClassWizard нельзя. Такие классы надо создавать вручную, непосредственно в текстовом редакторе.
    Объекты порожденные от класса CCmdTarget могут обрабатывать сообщения Windows и команды, поступающие от меню, кнопок, акселераторов. Класс CCmdTarget и другие наследованные от него классы имеют таблицу сообщений (Message map) - набор макрокоманд, позволяющий сопоставить сообщениям Windows и командам методы класса
    Чтобы создать класс, нажмите кнопку Add Class из любой страницы главной диалоговой панели ClassWizard. Откроется временное меню, содержащее три строки: New, From a file, From an OLE TypeLib. Для создания нового класса выберите из этого меню строку New. Если вы уже имеете исходные тексты класса и их просто требуется подключить к проекту, выберите из меню строку From a file. Последняя строка меню From an OLE TypeLib используется для подключения классов из библиотеки OLE.
    Когда вы создаете новый класс, на экране появляется диалоговая панель Create New Class. В поле Name введите имя создаваемого класса. Рекомендуется начинать названия классов с символа “C”. Для создаваемого класса организуются два файла реализации класса, имеющие расширения CPP и H. В них будут помещаться объявления класса, а также определения его методов и данных. Имя файлов реализации отображается в левой части группы File. По умолчанию файлы реализации имеют имена, соответствующие имени класса. Однако их можно изменить, воспользовавшись кнопкой Change из группы File.
    Теперь выберите из списка Base Class имя базового класса. Список Base Class достаточно велик. В нем содержатся не только основополагающие классы типа CCmdTarget, CDialog, CDocument, CFrameWnd, CView, CWinThread, CWnd. Список базовых классов включает классы большинства органов управления, например CAnimateCtrl, CButton, CColorDialog, CComboBox, CDragListBox, CEdit, CEditView, CFileDialog, CFontDialog, CHeaderCtrl, CHotKeyCtrl, CListBox, CListCtrl, CListView, CProgressCtrl, CStatic и многие многие другие. Доступны также базовые классы, предназначенные для работы с базами данных: CDaoRecordSet, CDaoRecordView, CRecordset, CRecordView, классы обеспечивающие технологию OLE: COleDocument, COleLinkingDoc, COleServerDoc.

    Так, например, вы можете создать новый класс CNewClass, наследованный от базового класса окна просмотра CEditView. Определение класса помещается во включаемый файл NewClass.h (листинг 4.7).

    Листинг 4.7. Файл NewClass.h

    // Класс окна просмотра CNewClass

    class CNewClass : public CEditView

    {

    protected:

          CNewClass();      

          DECLARE_DYNCREATE(CNewClass)

    // Attributes

    public:

    // Operations

    public:

    // Overrides

          //{{AFX_VIRTUAL(CNewClass)

    protected:

          virtual void OnDraw(CDC* pDC);

          //}}AFX_VIRTUAL

    // Implementation

    protected:

          virtual ~CNewClass();

    #ifdef _DEBUG

          virtual void AssertValid() const;

          virtual void Dump(CDumpContext& dc) const;

    #endif

    // Методы, предназначенные для обработки сообщений

    protected:

          //{{AFX_MSG(CNewClass)

          //}}AFX_MSG

          DECLARE_MESSAGE_MAP()

    };

    Определение методов класса размещается в другом файле, имеющем расширение CPP (листинг 4.8).

    Листинг 4.8. Файл NewClass.cpp

    #include "stdafx.h"

    #include "Single.h"

    #include "NewClass.h"

    #ifdef _DEBUG

    #define new DEBUG_NEW

    #undef THIS_FILE

    static char THIS_FILE[] = __FILE__;

    #endif

    //////////////////////////////////////////////////////////////

    // Реализация класса CNewClass

    IMPLEMENT_DYNCREATE(CNewClass, CEditView)

    CNewClass::CNewClass()

    {

    }

    CNewClass::~CNewClass()

    {

    }

    BEGIN_MESSAGE_MAP(CNewClass, CEditView)

          //{{AFX_MSG_MAP(CNewClass)

                

          //}}AFX_MSG_MAP

    END_MESSAGE_MAP()

    //////////////////////////////////////////////////////////////

    // Метод OnDraw класса CNewClass

    void CNewClass::OnDraw(CDC* pDC)

    {

          CDocument* pDoc = GetDocument();

          // TODO: здесь можно расположить код, выполняющий вывод в

          // окно

    }

    //////////////////////////////////////////////////////////////

    // Диагностические методы класса CNewClass

    #ifdef _DEBUG

    void CNewClass::AssertValid() const

    {

          CEditView::AssertValid();

    }

    void CNewClass::Dump(CDumpContext& dc) const

    {

          CEditView::Dump(dc);

    }

    #endif //_DEBUG

    Полученная заготовка класса полностью работоспособна. Ее можно дополнить по своему усмотрению новыми методами и данными. Эту работу можно выполнить вручную, но гораздо лучше и проще воспользоваться услугами предоставляемыми ClassWizard. За счет использования ClassWizard процедура создания собственного класса значительно ускоряется и уменьшается вероятность совершить ошибку во время объявления методов.


    Создание приложения средствами MFC AppWizard


    Во второй главе книги мы рассматривали приложение MFDialog, которое не имеет главного окна. Вместо окна это приложение использует обыкновенную диалоговую панель. Сейчас мы расскажем вам как создать приложение, подобное MFDialog, не набрав ни одной строки текста программы. Для этого мы будем использовать средства MFC AppWizard и ClassWizard.
    Выберите из меню File строку New. На экране появится диалоговая панель New, содержащая меню. Выберите из него тип объекта, который надо создать. Для создания нового проекта выберите из этого меню строку Project Workspace. Теперь на экране откроется диалоговая панель New Project Workspace, показанная нами на рисунке 4.1.
    Создание приложения средствами MFC AppWizard
    Рис. 4.1. Диалоговая панель New Project Workspace
    Из списка Type выберите тип приложения, которое вы желаете создать. В следующей таблице перечислены типы приложений, которые вы можете выбрать.

    Тип приложения
    Описание
    MFC AppWizard (exe)
    Приложение, создаваемое с использованием библиотеки классов MFC. С помощью AppWizard вы можете автоматически создать основные классы необходимые для приложения
    MFC AppWizard (dll)
    Библиотека динамической компоновки - DLL, создаваемая с помощью библиотеки классов MFC. AppWizard позволяет автоматически создать все основные файлы, необходимые для DLL
    OLE ControlWizard
    Органы управления OLE, созданные с использованием библиотеки классов MFC. Компилятор автоматически создает базовый набор файлов для проекта этого типа
    Application
    Приложение, созданное на основе библиотеки классов MFC или с использованием только вызовов функций программного интерфейса Windows
    Dynamic-Link Library
    Библиотека динамической компоновки, созданная с использованием только вызовов функций программного интерфейса Windows
    Console Application
    Приложение, разработанное с использованием функций консольного ввода/вывода. Этот тип приложений можно использовать для создания небольших программ, работающих в пакетном режиме
    Static Library
    Библиотека функций
    Makefile
    Предоставляет дополнительные возможности для использования MAKE-файла
    Custom AppWizard
    Позволяет создать собственный “волшебник” Custom AppWizard, который можно будет использовать для разработки шаблонов приложений с заданными вами свойствами
    <
    Список типов приложений, которые может создавать Microsoft Visual C++ версии 4.1, расширен. В него включен “волшебник” ISAPI Extension Wizard, который облегчает создание приложений для Microsoft Internet Information Server.

    В этой книге мы расскажем о создании собственных приложений с использованием средств AppWizard. Поэтому выберите из списка Type строку MFC AppWizard (exe).

    Теперь определите расположение базового каталога, в котором будут размещены проекты. Путь каталога можно ввести непосредственно в поле Location или выбрать, из списка, нажав на кнопку Browse. Затем введите в поле Name имя создаваемого проекта. В базовом каталоге создается одноименный подкаталог и в него записываются все файлы проекта. Имена файлов, составляющих проект, и названия классов приложения также присваиваются AppWizard на основе имени проекта.

    В группе переключателей Platforms надо выбрать, для какой платформы создается приложение. Если вы работаете в среде операционных систем Windows NT или Windows 95, установите переключатель Win32.

    После того как вы заполнили все поля диалоговой панели, нажмите кнопку Create. На экране появится первая диалоговая панель MFC AppWizard. Внешний вид этой панели зависит от того, какой тип приложения вами создается. Если вы создаете выполнимое приложение, то на экране появится диалоговая панель, показанная на рисунке 4.2.

    Создание приложения средствами MFC AppWizard

    Рис. 4.2. Первый шаг MFC AppWizard

    На первом шаге вам предлагается определить, какой тип пользовательского интерфейса должно иметь приложение. Вы можете выбирать между многооконным интерфейсом (Multiple documents), однооконным интерфейсом (Single document) и интерфейсом основанном на диалоговой панели без главного окна (Dialog based).

    После того как вы определите тип пользовательского интерфейса приложения, в заголовке диалоговой панели MFC AppWizard будет указано, сколько еще шагов (диалоговых панелей AppWizard) надо будет заполнить, чтобы определить все свойства приложения. Для приложений, имеющих интерфейс на основе главной диалоговой панели, процесс создания приложения будет состоять из 4 шагов, а для приложений, имеющих однооконный и многооконный интерфейс - 6 шагов.



    Вы также можете выбрать язык, на котором написан интерфейс приложения. К сожалению, в той версии компилятора, которая была у нас, русский язык отсутствовал. Поэтому мы использовали в наших приложениях английский язык.

    Заполнив первую диалоговую панель MFC AppWizard, нажмите кнопку Next >. На экране появится следующая диалоговая панель MFC AppWizard. В зависимости от того, какой тип интерфейса пользователя вы выбрали для приложения, вид этой диалоговой панели может несколько отличаться.

    Если вы выбрали интерфейс приложения, основанный на диалоговой панели, тогда диалоговая панель на втором шаге будет иметь вид, показанный на рисунке 4.3. В этой панели можно указать, будет ли у создаваемого приложения информационная диалоговая панель, справочная подсистема, трехмерные органы управления, возможности использования технологии OLE и коммуникации с помощью протокола TCP/IP. Вы также сможете определить заголовок главной диалоговой панели приложения.

    Создание приложения средствами MFC AppWizard

    Рис. 4.3. Второй шаг MFC AppWizard

    Если включить переключатель About box, то приложение будет иметь небольшую информационную панель About. В ней обычно содержится краткая информация о приложении - его название, номер версии, авторские права, небольшая пиктограмма. Чтобы вызвать эту панель, пользователь должен будет выбрать из системного меню главной диалоговой панели приложения строку About App…

    Операционная система Windows имеет хорошо развитую справочную систему. Обычно каждое приложение имеет собственный справочный файл данных, содержащий разнообразную информацию о приложении. MFC AppWizard позволяет легко создать заготовку такого файла и подключить ее к приложению. Для этого следует включить переключатель Context sensitive Help. Теперь главная диалоговая панель приложения будет иметь кнопку Help, с помощью которой можно запустить справочную систему приложения.

    Современный дизайн интерфейса приложений предполагает, что все органы управления, например кнопки и переключатели, должны выглядеть объемными. Чтобы получить эффект трехмерных органов управления, включите переключатель 3D controls.



    Средства автоматизированного создания приложений легко позволяют создавать приложения, использующие OLE технологию. Для приложений, интерфейс пользователя которых основан на диалоговой панели, вы можете использовать технологию OLE automation. Эта технология позволяет приложению работать с объектами, созданными в других приложениях.

    Чтобы облегчить программистам создание приложений Windows, разработаны органы управления OLE. Если вы будете их использовать, включите переключатель OLE controls.

    Библиотека классов MFC версии 4.0 позволяет создавать приложения, взаимодействующие друг с другом через сетевой протокол TCP/IP. Чтобы включить поддержку этого протокола, включите переключатель Windows Sockets.

    По умолчанию название главной диалоговой панели приложения совпадает с именем проекта. Вы можете изменить это название в поле Please enter a title for your dialog.

    После того, как вы заполнили диалоговую панель, нажмите кнопку Next >. На экране появится следующая диалоговая панель, предназначенная для определения основных свойств приложения. Мы представили ее на рисунке 4.4.

    Создание приложения средствами MFC AppWizard

    Рис. 4.4. Третий шаг MFC AppWizard

    В этой диалоговой панели вы можете попросить MFC AppWizard немного приподнять завесу тайны над волшебством автоматического создания приложения. Если вы переместите переключатель Would you like to generate source file comments в положение Yes, please, то исходный текст приложения будет снабжен комментариями.

    Приложение может использовать библиотеку классов MFC двумя способами - вызывая библиотеки DLL или включая код классов непосредственно в приложение.

    В первом случае приложение будет иметь меньший размер, но вместе с ним вы должны будете распространять dll-библиотеки MFC. Описание dll-библиотек MFC вы можете найти в разделе “Первое приложение MFC” главы “Введение в MFC”.

    Во втором случае выполнимый файл приложения будет иметь больший размер, но он будет полностью содержать весь код, необходимый для его работы.

    Способ подключения библиотеки MFC определяется положением переключателя How would you like to use the MFC library. Если он находится в положении As a shared DLL, то используется dll-библиотека MFC, а если в положении As a statically linked library, то код классов MFC включается непосредственно в выполнимый файл приложения.



    Теперь вы можете перейти к последнему этапу определения свойств приложения. Нажмите кнопку Next >. На экране появится диалоговая панель для выбора названий классов приложения. Внешний вид этой панели представлен на рисунке 4.5.

    Создание приложения средствами MFC AppWizard

    Рис. 4.5. Четвертый шаг MFC AppWizard

    В списке AppWizard creates the following classes for you перечислены названия всех классов, которые создает MFC AppWizard для вашего приложения. Названия этих классов являются производными от названия проекта. Ниже этого списка расположены четыре поля Class name, Base class, Header file, Implementation file. Когда вы выбираете из списка AppWizard creates the following classes for you название класса приложения в этих полях отображаются следующая информация:

    Имя поля

    Описание

    Class name

    Имя класса приложения, выбранное вами из списка. Вы можете изменить его по вашему усмотрению или оставить как есть

    Base class

    Имя базового класса MFC, из которого наследуется класс выбранный из списка Class name. Для ряда классов базовый класс можно изменить. Более подробно об этом мы расскажем позже

    Header file, Implementation file

    Эти два поля определяют названия включаемого файла, в котором описан класс, и файла, содержащего исходный код методов класса. Вы можете изменить их по своему усмотрению

    Теперь все готово к созданию исходных текстов приложения. Для этого достаточно нажать кнопку Finish. На экране появится панель, содержащая информацию о свойствах приложения: тип интерфейса пользователя, названия классов приложения, а также другие особенности, определенные вами с помощью диалоговых панелей MFC AppWizard. Если все правильно, нажмите кнопку OK. MFC AppWizard сразу приступит к построению проекта, полностью создаст все файлы проекта и загрузит их в среду Microsoft Visual C++.

    MFC AppWizard создаст проект, который сразу можно оттранслировать и получить приложение, полностью готовое к запуску. Запустите полученное приложение. На экране появится главная диалоговая панель приложения (рис. 4.6).

    Создание приложения средствами MFC AppWizard

    Рис. 4.6. Приложение Dialog

    Полученное приложение имеет только две кнопки OK и Cancel. Нажав на любую из них, вы можете завершить приложение. Взяв за основу полученный проект, измените его в соответствии с вашими потребностями. Вы можете добавить в диалоговую панель новые органы управления, подключить к ним программный код, создать другие ресурсы, и т. д. Все эти задачи легко решаются в среде Microsoft Visual C++ версии 2.0 и 4.0.


    Списки - шаблон CList


    В состав MFC входят классы, предназначенные для организации двунаправленных списков указателей, строк, состоящих из объектов CString, указателей на объекты класса CObject. В MFC версии 4.0 добавлен шаблон класса списка CList. С помощью этого шаблона можно создавать списки, состоящие из любых объектов.
    Списковый класс имеет методы для вставки и удаления элементов в начало, конец и середину списка. Вы можете получить предыдущий и следующий элемент из списка.
    Ниже представлен прототип шаблона CList.
    template
          class CList : public CObject


    Средства ClassView 


    Конечно, вы можете продолжать набирать исходные тексты приложения вручную непосредственно в текстовом редакторе. Но во многих случаях среда VIsual C++ может оказать вам значительную помощь. Одним из средств, способных оказать вам такую помощь уже сейчас, является ClassView.
    Используя ClassView можно быстро добавлять к классам новые элементы, просматривать структуру наследования классов и выполнять другие полезные операции. После того как вы разберетесь с основными принципами построения приложений Windows с использованием классов MFC, мы продолжим изучение вспомогательных средств VIsual C++. В следующих главах книги мы рассмотрим средства автоматизированного проектирования приложений MFC AppWizard и средство для разработки классов ClassWizard.
    А сейчас приступим к описанию возможностей ClassView. Выберите из списка ClassView название класса, который вы желаете просмотреть или изменить, и нажмите правую клавишу мыши. На экране появится временное меню, представленное на рисунке 2.14.
    Средства ClassView 
    Рис. 2.14. Временное меню класса
    Строки этого меню позволяют выполнять над классами все основные действия. Строка Go to Definition позволяет просмотреть в окне редактирования исходный текст класса. Вы можете редактировать класс непосредственно, но для добавления в класс новых методов и данных удобнее пользоваться строками Add Function и Add Variable.


    Средства ClassWizard


    Разработка приложения не заканчивается, когда MFC AppWizard создаст для вас исходные файлы проекта. Теперь вы должны добавить к приложению собственный программный код, выполняющий основные функции приложения. Среда Microsoft Visual C++ версии 4.0 позволяет максимально облегчить дальнейшую разработку приложения. Для этого предназначены такие средства как ClassView и ClassWizard. Они позволяют с минимальными усилиями добавлять в классы новые методы и данные, быстро высвечивать в окне редактирования интересующие вас объявления и определения классов, методов, данных.
    Мы уже изучили возможности ClassView, предназначенные для работы с классами в разделе “Средства ClassView” главы “Введение в MFC”. По сравнению с ClassView, “волшебник” ClassWizard предоставляет более полные услуги. Он позволяет не только добавлять к классу новые методы и данные. Вы можете использовать ClassWizard, чтобы добавить к классу новый метод, служащий для обработки сообщений, переменную, предназначенную для обмена информацией с полями диалоговой панели.
    Запустить ClassWizard очень просто, для этого можно нажать кнопку Средства ClassWizard из стандартной панели управления (окно Standard) или выбрать из меню View строку ClassWizard. На экране появится главная диалоговая панель ClassWizard. Мы привели внешний вид этой панели на рисунке 4.10.
    Средства ClassWizard 
    Рис. 4.10. Главная панель ClassWizard
    Главная панель ClassWizard содержит пять страниц - Message Maps, Member Variables, OLE Automation, OLE Events и Class Info. Страница Message Maps позволяет просмотреть сообщения, вырабатываемые объектами, и создать методы для их обработки. Вторая страница Member Variables позволяет управлять данными, записанными в классе. Следующие две страницы OLE Automation и OLE Events отвечают за поддержку вашим приложением технологии OLE. Эти страницы будут рассмотрены позже. Последняя страница Class Info позволяет получить различную информацию о классе.
    Когда вы просматриваете исходный текст приложения, в верхней части окна редактора может отображаться панель WizardBar (рис. 4.11). Органы управления этой панели позволяют получить быстрый доступ к некоторым возможностям ClassWizard. WizardBar позволяет управлять методами, обрабатывающими сообщения от органов управления.
    Средства ClassWizard
    Рис. 4.11. Панель WizardBar


    Ссылки


    В языке Си++ вы можете определить ссылку на объект - переменную или объект класса. Ссылка содержит адрес объекта, но вы можете использовать ее, как будто она представляет сам объект. Для объявления ссылки используется оператор &.
    В следующей программе мы определили переменную iVar типа int и ссылку iReferenceVar на нее. Затем мы отображаем и изменяем значение переменной iVar используя ее имя и ссылку.
    // Включаемый файл для потокового ввода/вывода
    #include
    void main(void)
    {
          // Определяем переменную iVar
          int        iVar = 10;
          // Определяем ссылку iReferenceVar на переменную iVar
          int&     iReferenceVar = iVar;
          // Отображаем значение переменной и ссылки
          cout << "iVar = " << iVar << "; iReferenceVar = " <<
                       iReferenceVar << '\n';
          // Изменяем значение переменной iVar пользуясь ссылкой
          iReferenceVar = 20;
          // Отображаем значение переменной и ссылки
          cout << "iVar = " << iVar << "; iReferenceVar = " <<
                       iReferenceVar << '\n';
    }
    Вы можете использовать ссылки для передачи параметров функциям. При этом фактически вы передаете функции указатель на объект, представленный ссылкой. Внутри функции вы можете работать с ссылкой как с самим объектом, а не как с указателем.
    Функция может не только принимать ссылки в качестве своих параметров, она также может возвращать ссылку. Такую функцию можно привести в левой части оператора присваивания.


    Стандартные командные сообщения


    Подавляющее большинство приложений, созданных на основе MFC, использует ряд стандартных командных сообщений, как правило соответствующих элементам меню или кнопкам панели управления. К ним относятся командные сообщения для завершения работы приложения, создания нового документа, открытия документа, записанного на диске, сохранения документа на диске, вызова справочной системы, управления текстовым редактором и т. д. За каждым таким командным сообщением зарезервирован отдельный идентификатор.
    MFC обеспечивает различный уровень обработки стандартных командных сообщений, начиная от простого резервирования идентификатора и кончая полной его обработкой.
    Элемент меню или кнопка панели управления приложения имеет тот же идентификатор, что и командное сообщение. Просмотрите список идентификаторов меню приложения Single. В нем вы найдете многие стандартные команды, описанные ниже.
    Ниже коротко описаны наиболее важные стандартные командные сообщения.


    Статические методы


    Вы можете объявить некоторые методы класса статическими методами. Для этого вы должны воспользоваться ключевым словом static. Статические методы не принимают параметр this. На использование статических методов накладывается ряд ограничений.
  • Статические методы могут непосредственно обращаться только к статическим членам класса.

  • Статический метод не может быть объявлен как виртуальный метод.

  • Вы не можете определить нестатический метод с тем же именем и тем же набором параметров, что и статический метод класса.

  • Статические методы имеют одну интересную особенность - вы можете вызывать их даже без создания объектов класса. Чтобы вызвать из программы статический метод, вы должны указать его полное имя, включая имя класса.
    Ниже представлен класс Circle, в котором определена статический метод GetPi. Он используется для получения значения статического элемента класса fPi.
    class Circle
    {
    public:
          static void GetPi()
         { return fPi; }
    private:
          static float fPi;   
    };
    float Circle::fPi = 3.1415;
    Вы можете вызвать метод GetPi следующим образом:
    float fNumber;
    fNumber = Circle::GetPi();
    Обратите внимание, что объект класса Circle не создается.


    Структуры


    Понятие структуры в языке Си++ значительно расширено. Структура в Си++ обладает всеми возможностями классов. В структуры Си++ можно включать не только элементы данных, но и методы. Вы можете наследовать от структур новые структуры, точно также как вы наследуете новые классы от базовых классов.
    Различие между структурами и обычными классами заключается только в управлении доступом к их элементам. Так, если элементы класса по умолчанию объявлены как private, то все элементы структуры по умолчанию объявлены как public.
    Ниже мы привели пример объявления структуры StructData и класса ClassData, которые содержат одинаковые элементы с одинаковыми правами доступа к ним. Фактически, структура StructData и класс ClassData совершенно равнозначны.
    //====================================================
    // Класс ClassData
    class ClassData
    {
          int  iPrivateValue;
    public:
          int  iPublicValue;
    };
    //====================================================
    // Структура StructData
    struct StructData
    {
          int  iPublicValue;
    private:
          int  iPrivateValue;
    };
    Еще одно различие между структурами и классами проявляется в разграничении доступа к элементам базового класса (см. раздел “Разграничение доступа к элементам базового класса”). Если вы наследуете новый класс от базового класса и не указываете спецификатор доступа, по умолчанию используется спецификатор private. Когда же вы наследуете от базового класса структуру, по умолчанию используется спецификатор public.


    Таблица акселераторов


    Чтобы ускорить доступ к строкам меню приложения, MFC AppWizard добавляет в файл ресурсов таблицу акселераторов. Когда пользователь нажимает комбинацию клавиш, представленную в таблице акселераторов, приложению поступает командное сообщение с соответствующим идентификатором.
    //////////////////////////////////////////////////////////////
    // Таблица акселераторов
    IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE
    BEGIN
          "N",                  ID_FILE_NEW,                       VIRTKEY,CONTROL
          "O",                  ID_FILE_OPEN,                     VIRTKEY,CONTROL
          "S",                   ID_FILE_SAVE,                      VIRTKEY,CONTROL
          "P",                   ID_FILE_PRINT,    VIRTKEY,CONTROL
          "Z",                   ID_EDIT_UNDO,                    VIRTKEY,CONTROL
          "X",                  ID_EDIT_CUT,                       VIRTKEY,CONTROL
          "C",                   ID_EDIT_COPY,                    VIRTKEY,CONTROL
          "V",                   ID_EDIT_PASTE,   VIRTKEY,CONTROL
          VK_BACK,       ID_EDIT_UNDO,                    VIRTKEY,ALT
          VK_DELETE,   ID_EDIT_CUT,                       VIRTKEY,SHIFT
          VK_INSERT,    ID_EDIT_COPY,                    VIRTKEY,CONTROL
          VK_INSERT,    ID_EDIT_PASTE,   VIRTKEY,SHIFT
          VK_F6,       ID_NEXT_PANE,                         VIRTKEY
          VK_F6,             ID_PREV_PANE,                    VIRTKEY,SHIFT
    END


    Таблица сообщений класса CDialogApp


    Таблица сообщений класса CDialogApp состоит из макрокоманд BEGIN_MESSAGE_MAP и END_MESSAGE_MAP. Между ними расположены макрокоманды, определяющие сообщения, обрабатываемые данным классом. В таблице определено только одно командное сообщение, имеющее идентификатор ID_HELP. Для его обработки вызывается метод OnHelp базового класса CWinApp.
    Необработанные сообщения передаются базовому классу CWinApp, так как он указан во втором параметре макрокоманды BEGIN_MESSAGE_MAP.
    //////////////////////////////////////////////////////////////
    // Таблица сообщений класса CDialogApp
    BEGIN_MESSAGE_MAP(CDialogApp, CWinApp)
          //{{AFX_MSG_MAP(CDialogApp)
           // ClassWizard размещает в данном блоке макрокоманды для
           // обработки сообщений. Не изменяйте содержимое этого блока
          //}}AFX_MSG
          ON_COMMAND(ID_HELP, CWinApp::OnHelp)
    END_MESSAGE_MAP()
    Обратите внимание, что внутри таблицы сообщений расположены две макрокоманды AFX_MSG, помещенные за знаками комментария. Сразу после создания приложения между ними нет ни одной макрокоманды. Когда вы будете создавать обработчики сообщений при помощи ClassWizard, он будет располагать новые обработчики между этими комментариями. Не рекомендуется вручную вносить изменения в код, расположенный в блоке AFX_MSG. Используйте для этого средства ClassWizard.
    Если вам требуется добавить новый обработчик в таблицу сообщений, без использования ClassWizard, расположите его после блока AFX_MSG и до макрокоманды END_MESSAGE_MAP.
    Приложение Dialog содержит еще одну таблицу сообщений, принадлежащую классу диалоговой панели приложения. Мы рассмотрим эту таблицу позже.
    Непосредственно после таблицы сообщений главного класса приложения расположено определение конструктора CDialogApp. Этот конструктор вызывается в момент создания объекта приложения. Конструктор, созданный MFC AppWizard, пустой и не выполняет никаких дополнительных действий. Для инициализации приложения используются методы InitInstance и InitApplication.


    Таблица сообщений класса CDialogDlg


    Файл DialogDlg.cpp содержит таблицу сообщений класса CDialogDlg. Таблица включает три макрокоманды. Как видите, они расположены в блоке AFX_MSG_MAP, поэтому для управления ими используется ClassWizard.
    BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)
          //{{AFX_MSG_MAP(CDialogDlg)
          ON_WM_SYSCOMMAND()
          ON_WM_PAINT()
          ON_WM_QUERYDRAGICON()
          //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    При помощи ClassWizard вы можете обнаружить, что макрокоманды выполняют обработку сообщений WM_SYSCOMMAND, WM_PAINT, WM_QUERYDRAGICON, вызывая для этого методы OnSysCommand, OnPaint и OnQueryDragIcon.


    Таблица сообщений класса CMainFrame


    Класс CMainFrame может получать и обрабатывать сообщения, поэтому в определении класса указана макрокоманда DECLARE_MESSAGE_MAP, а в файле реализации класса MainFrm.cpp, расположена таблица сообщений.
    // Таблица сообщений класса CMainFrame
    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
          //{{AFX_MSG_MAP(CMainFrame)
          ON_WM_CREATE()
          //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    Изначально в таблице сообщений расположена единственная макрокоманда ON_WM_CREATE. Эта макрокоманда устанавливает для обработки сообщения WM_CREATE метод OnCreate. Как вы знаете, сообщение WM_CREATE передается функции окна сразу после его создания, но до того, как окно появится на экране.


    Таблица сообщений класса CSingleApp


    Обратите внимание, что в последней строке определения класса CSingleApp расположена макрокоманда DECLARE_MESSAGE_MAP. Загадочная макрокоманда DECLARE_MESSAGE_MAP определена в файле afxwin.h следующим образом:
    #define DECLARE_MESSAGE_MAP() \
    private: \
          static const AFX_MSGMAP_ENTRY _messageEntries[]; \
    protected: \
          static AFX_DATA const AFX_MSGMAP messageMap; \
          virtual const AFX_MSGMAP* GetMessageMap() const; \
    Таким образом, DECLARE_MESSAGE_MAP не является расширением языка Си++, а просто добавляет к вашему классу несколько новых элементов.
    Так как в классе CSingleApp расположена макрокоманда DECLARE_MESSAGE_MAP, то он может обрабатывать сообщения и имеет таблицу сообщений. Таблица сообщений класса CSingleApp расположена в файле реализации Single.cpp.
    //////////////////////////////////////////////////////////////
    // Таблица сообщений класса CSingleApp
    BEGIN_MESSAGE_MAP(CSingleApp, CWinApp)
          //{{AFX_MSG_MAP(CSingleApp)
          ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
          //}}AFX_MSG_MAP
          // Стандартные команды для работы с документами
          ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
          ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
          // Стандартная команда выбора принтера
          ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
    END_MESSAGE_MAP()
    Кроме команды для обработки командного сообщения ID_APP_ABOUT, расположенного в блоке AFX_MSG_MAP, таблица сообщений содержит еще три макрокоманды, предназначенные для обработки командных сообщений с идентификаторами ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_PRINT_SETUP.
    Командные сообщения ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_PRINT_SETUP поступают, когда пользователь выбирает из меню приложения строки с соответствующими идентификаторами. Для обработки этих командных сообщений вызываются методы класса CWinApp.


    Таблица сообщений класса CSingleDoc


    Макрокоманда IMPLEMENT_DYNCREATE размещается в файле реализации класса. Для класса CSingleDoc этот файл называется SingleDoc.cpp. Обычно MFC AppWizard размещает макрокоманду IMPLEMENT_DYNCREATE непосредственно перед таблицей сообщений класса (если конечно данные класс обрабатывает сообщения).
    // Макрокоманда необходима для динамического создания объектов
    // CSingleDoc
    IMPLEMENT_DYNCREATE(CSingleDoc, CDocument)
    // Таблица сообщений класса CSingleDoc
    BEGIN_MESSAGE_MAP(CSingleDoc, CDocument)
          //{{AFX_MSG_MAP(CSingleDoc)
          //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    Сазу после создания проекта таблица сообщений класса CSingleDoc не содержит обработчиков сообщений. Когда вы продолжите разработку приложения, вы будете добавлять обработчики различных сообщений к классу CSingleDoc и другим классам приложения. Для добавления новых обработчикоов сообщений, а также для внесения других изменений в классы, следует использовать ClassWizard.


    Таблица сообщений класса CSingleView


    Таблица сообщений класса CSingleView располагается в файле SingleView.cpp. Непосредственно перед ней находится макрокоманда IMPLEMENT_DYNCREATE.
    // Объекты класса CSingleView могут создаваться динамически
    IMPLEMENT_DYNCREATE(CSingleView, CView)
    // Таблица сообщений класса CSingleView
    BEGIN_MESSAGE_MAP(CSingleView, CView)
          //{{AFX_MSG_MAP(CSingleView)
          //}}AFX_MSG_MAP
          // Стандартные команды предназначенные для печати документа
          ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
          ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
          ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
    END_MESSAGE_MAP()


    Таблица сообщений


    В библиотеке классов MFC для обработки сообщений используется специальный механизм, который получил название Message Map - таблица сообщений.
    Таблица сообщений состоит из набора специальных макрокоманд, ограниченных макрокомандами BEGIN_MESSAGE_MAP и END_MESSAGE_MAP. Между ними расположены макрокоманды, отвечающие за обработку отдельных сообщений.
    Макрокоманда BEGIN_MESSAGE_MAP представляет собой заголовок таблицы сообщений. Она имеет два параметра. Первый параметр содержит имя класса таблицы сообщений. Второй параметр указывает его базовый класс.
    Если в таблице сообщений класса отсутствует обработчик для сообщения, оно передается для обработки базовому классу, указанному вторым параметром макрокоманды BEGIN_MESSAGE_MAP. Если таблица сообщений базового класса также не содержит обработчик этого сообщения, оно передается следующему базовому классу и т. д.
    В том случае если ни один из базовых классов не может обработать сообщение, выполняется обработка по умолчанию, зависящая от типа сообщения.
  • Стандартные сообщения Windows обрабатываются функцией “default window procedure”

  • Командные сообщения передаются по цепочке следующему объекту, который может обработать командное сообщение. Более подробно мы расскажем об этой цепочке в главах “Однооконный интерфейс” и “Многооконный интерфейс”

  • В библиотеке MFC определены несколько макрокоманд, отвечающих за обработку сообщений. Их названия представлены в следующей таблице.

    Макрокоманда
    Устанавливает методы для обработки сообщений
    ON_WM_
    Стандартных сообщений операционной системы Windows
    ON_REGISTERED_MESSAGE
    Зарегистрированные сообщения операционной системы Windows
    ON_MESSAGE
    Сообщений, определенных пользователем
    ON_COMMAND,

    ON_COMMAND_RANGE
    Командных сообщений
    ON_UPDATE_COMMAND_UI,

    ON_UPDATE_COMMAND_UI_RANGE
    Сообщений, предназначенных для обновления пользовательского интерфейса
    ON_,

    ON_CONTROL_RANGE
    Сообщений от органов управления

    Перечисленные в таблице макрокоманды имеют различное количество параметров в зависимости от типа обрабатываемых ими сообщений.


    Таблица текстовых строк


    В таблице текстовых строк проекта Dialog определена только одна текстовая строка &About Dialog..., имеющая идентификатор IDS_ABOUTBOX. Эта строка содержит текст нового элемента, который будет добавлен к системному меню главной диалоговой панели приложения. Если пользователь выберет эту строку меню, приложение выведет на экран небольшую диалоговую панель, с краткой информацией о приложении.
    //////////////////////////////////////////////////////////////
    // Таблица строк
    STRINGTABLE DISCARDABLE
    BEGIN
          IDS_ABOUTBOX             "&About Dialog..."
    END

    Для получения более полной информации вы можете изучить метод GetDocString, который позволяет определить отдельные фрагменты строки, описывающий документ. Описание метода GetDocString смотрите в справочной системе Visual C++.
    Второй блок таблицы текстовых строк содержит две строки с идентификаторами AFX_IDS_APP_TITLE и AFX_IDS_IDLEMESSAGE. Строка, имеющая идентификатор AFX_IDS_IDLEMESSAGE, отображается в панели состояния, когда приложение находится в состоянии ожидания.
    Когда пользователь создает объект главного класса приложения, он может указать имя приложения. Если это имя не указано, как в нашем приложении, тогда в качестве имени приложения используется строка, имеющая идентификатор AFX_IDS_APP_TITLE.
    STRINGTABLE PRELOAD DISCARDABLE
    BEGIN
          AFX_IDS_APP_TITLE                    "Single"
          AFX_IDS_IDLEMESSAGE                "Ready"
    END
    В следующем блоке текстовых строк определены несколько текстовых строк, имеющих стандартные идентификаторы. Эти строки используются для отображения различной информации в панели состояния.
    STRINGTABLE DISCARDABLE
    BEGIN
          ID_INDICATOR_EXT     "EXT"
          ID_INDICATOR_CAPS    "CAP"
          ID_INDICATOR_NUM    "NUM"
          ID_INDICATOR_SCRL    "SCRL"
          ID_INDICATOR_OVR      "OVR"
          ID_INDICATOR_REC      "REC"
    END
    И наконец, последний, самый большой блок текстовых строк содержит краткие описания каждой строки меню приложения. Идентификаторы этих строк соответствуют идентификаторам строк меню, которые они описывают.
    Строки, описывающие меню, состоят из двух частей, разделенных символом перевода строки \n. Первая часть строки отображаются в панели состояния, когда пользователь выбирает строки меню. Вторая часть строки содержит краткую подсказку, которая отображается, если поместить указатель мыши на кнопки и подождать несколько секунд. Если вы не нуждаетесь в короткой подсказке для кнопок управляющей панели, то вторую часть строки можно не приводить.


    STRINGTABLE DISCARDABLE
    BEGIN
              ID_FILE_NEW                                                        "Create a new document\nNew"
              ID_FILE_OPEN                                                      "Open an existing document\nOpen"
              ID_FILE_CLOSE   "Close the active document\nClose"
              ID_FILE_SAVE                                                       "Save the active document\nSave"
              ID_FILE_SAVE_AS                      " Save the active document with a new
                                                                                                    name\nSave As"
              ID_FILE_PAGE_SETUP             "Change the printing options\nPage Setup"
              ID_FILE_PRINT_SETUP            "Change the printer and printing
                                                                                                    options\nPrint Setup"
              ID_FILE_PRINT     "Print the active document\nPrint"
              ID_FILE_PRINT_PREVIEW "Display full pages\nPrint Preview"
              ID_APP_ABOUT   "                       Display program information, version
                                                                                                    number and copyright\nAbout"
              ID_APP_EXIT                                                         "Quit the application; prompts to save
                                                                                                    documents\nExit"
              ID_FILE_MRU_FILE1                 "Open this document"
              ID_FILE_MRU_FILE2                 "Open this document"
              ID_FILE_MRU_FILE3                 "Open this document"
              ID_FILE_MRU_FILE4                 "Open this document"
              ID_FILE_MRU_FILE5                 "Open this document"
              ID_FILE_MRU_FILE6                 "Open this document"


              ID_FILE_MRU_FILE7                 "Open this document"
              ID_FILE_MRU_FILE8                 "Open this document"
              ID_FILE_MRU_FILE9                 "Open this document"
              ID_FILE_MRU_FILE10               "Open this document"
              ID_FILE_MRU_FILE11               "Open this document"
              ID_FILE_MRU_FILE12               "Open this document"
              ID_FILE_MRU_FILE13               "Open this document"
              ID_FILE_MRU_FILE14               "Open this document"
              ID_FILE_MRU_FILE15               "Open this document"
              ID_FILE_MRU_FILE16               "Open this document"
              ID_NEXT_PANE   " Switch to the next window pane\nNext Pane"
              ID_PREV_PANE   "Switch back to the previous window
                                                                                                    pane\nPrevious Pane"
              ID_WINDOW_SPLIT                   "Split the active window into panes\nSplit"
              ID_EDIT_CLEAR  "Erase the selection\nErase"
              ID_EDIT_CLEAR_ALL               "Erase everything\nErase All"
              ID_EDIT_COPY                                                     "Copy the selection and put it on the
                                                                                                    Clipboard\nCopy"
              ID_EDIT_CUT                                                         "Cut the selection and put it on the
                                                                                                    Clipboard\nCut"
              ID_EDIT_FIND                                                       "Find the specified text\nFind"
              ID_EDIT_PASTE   "Insert Clipboard contents\nPaste"
              ID_EDIT_REPEAT                        "Repeat the last action\nRepeat"
              ID_EDIT_REPLACE                     "Replace specific text with different
                                                                                                    text\nReplace"
              ID_EDIT_SELECT_ALL             "Select the entire document\nSelect All"
              ID_EDIT_UNDO                                                    "Undo the last action\nUndo"
              ID_EDIT_REDO                                                     "Redo the previously undone action\nRedo"
              ID_VIEW_TOOLBAR                   "Show or hide the toolbar\nToggle ToolBar"
              ID_VIEW_STATUS_BAR            "Show or hide the status bar\nToggle
                                                                                                    StatusBar"
    END

    Тип исключения


    Если вызывается исключение, для которого отсутствует обработчик и не определен универсальный обработчик исключений всех типов, тогда вызывается функция terminate из стандартной библиотеки. Она вызывает функцию abort, завершающую работу программы.
    Вы можете определить собственную функцию, которая будет вызываться перед аварийным завершением программы. Для этого вы должны вызвать функцию set_terminate, указав ей в качестве параметра имя вашей функции. Если вы воспользуетесь функцией set_terminate несколько раз, то будет вызываться только функция, указанная в последнем вызове set_terminate.
    #include
    #include
    #include
    void FastExit(void);
    int main()
    {
          // Устанавливаем функцию term_func
          set_terminate(FastExit);
          try
          {
                 // ...
                 // Вызываем исключение типа int
                 throw (int) 323;
                 // ...
          }
         
          // Определяем обработчик типа char. Обработчик исключений
          // типа int и универсальный обработчик не определены
          catch(char)
          {
                 cout << "Exception " << endl;
          }
          return 0;
    }
    // Определение функции FastExit
    void FastExit()
    {
          cout << "Exception handler not found" << endl;
          exit(-1);
    }
    Среда Visual C++ версии 4.0 позволяет запретить или разрешить обработку исключений языка Си++. Для управления исключениями выберите из меню Build строку Settings. На экране появится диалоговая панель Project Settings, в которой определяются все режимы работы. Выберите страницу C/C++. Затем из списка Category выберите строку C++ Language. Чтобы включить обработку исключительных ситуаций установите переключатель Enable exception handling.


    Универсальный обработчик исключений


    В одном блоке try можно вызывать исключения разных типов. В этом случае после блока try должны следовать обработчики для исключений каждого типа. Вы можете определить обработчик, обслуживающий исключения всех типов. Для этого вместо типа в операторе catch надо указать три точки:
    catch(...)
    {
          ...
    }
    Исключения в языке Си++ могут быть различного типа, в том числе они могут быть объектами классов. Вы можете определить несколько обработчиков исключений различного типа. В этом случае исключение будет обрабатывать обработчик соответствующего типа.


    Версия


    Во всех приложениях, созданных с использованием MFC AppWizard, определен специальный ресурс, содержащий различные сведения о версии приложения (4.8). Приложение Dialog также содержит такой ресурс, который имеет идентификатор VS_VERSION_INFO.
    Версия
    Рис. 4.8. Информация о версии приложения
    Вы можете внести изменения в этот ресурс, однако имеет смысл делать это только на конечной стадии разработки приложения. Поэтому мы не станем сейчас подробно останавливаться на описании этого ресурса.

    Как и каждое приложение, созданное средствами MFC AppWizard, приложение Single включает ресурс, описывающий версию приложения.  В этом ресурсе содержится информация о приложении и ее версии, данные о фирме-разработчике, авторские права.
    Приложения, как правило, имеют только один ресурс, содержащий данные о версии, и который имеет имя VS_VERSION_INFO. Приложение может получить данные из ресурса, описывающего версию приложения. Для этого можно воспользоваться функциями GetFileVersionInfo и VerQueryValue.
    Сейчас мы не станем подробно останавливаться на этом ресурсе. Большинство приложений не нуждается в определении данного ресурса, тем более на начальном этапе разработки. Поэтому мы продолжим изучения исходного текста самого приложения.


    Виртуальные методы


    Методы базового класса могут быть переопределены в порожденных классах. Если вы создадите объект порожденного класса и вызовете для него переопределенный метод, то будет вызван именно метод порожденного класса, а не соответствующий метод базового класса. Однако, если вы вызовете переопределенный метод для объекта порожденного класса, используя указатель или ссылку на объект базового класса, будет вызван именно метод базового класса. Иными словами метод вызывается в соответствии с классом указателя на объект, а не с классом самого объекта.
    В Си++ вы можете указать, что некоторые методы базового класса, которые будут переопределены в порожденных классах, являются виртуальными. Для этого достаточно указать перед описанием метода ключевое слово virtual. Статический метод не может быть виртуальным. Методы, объявленные в базовом классе виртуальными считаются виртуальными и в порожденных классах.
    Если вы переопределите в порожденном классе виртуальный метод, и создадите объект этого класса, то переопределенный метод будет использоваться вне зависимости от того, как он был вызван. При вызове переопределенного метода играет роль только класс объекта для которого вызывается метод.
    Все сказанное нами не означает, что никак нельзя вызвать виртуальный метод базового класса, если он был переопределен. Виртуальный метод базового класса можно вызвать, если указать его полное имя, включая имя базового класса.
    Виртуальный метод базового класса можно оставить без изменения и не переопределять в порожденном классе. В этом случае, он будет работать как обычный не виртуальный метод.
    Следующая программа демонстрирует разницу между виртуальными и невиртуальными методами класса. В базовом классе Figure определены два метода PrintName и PrintDimention, причем метод PrintName определен как виртуальный. От класса Figure наследуется класс Rectangle, в котором методы PrintName и PrintDimention переопределяются.
    В программе создается объект класса Rectangle, а затем несколько раз вызываются методы PrintName и PrintDimention. В зависимости от того, как вызывается метод, будет работать метод, определенный в классе Figure или Rectangle.

    #include

    // Базовый класс Figure

    class      Figure

    {

    public:

          // Виртуальный метод

          virtual void PrintName(void)

                 {cout << Figure PrintName << ‘\n’};

          // Невиртуальный метод

          void      PrintDimention(void)

                 {cout << Figure PrintDimention << ‘\n’};

    };

    // Порожденный класс Rectangle

    class      Rectangle : public Figure

    {

          // Переопределяем виртуальный метод базового класса

          virtual void PrintName(void)

                 {cout << Rectangle PrintName << ‘\n’};

          // Переопределяем невиртуальный метод базового класса

          void      PrintDimention(void);

                 {cout << Rectangle PrintDimention << ‘\n’};

    };

    // Главная функция

    void main(void)

    {

          // Определяем объект порожденного класса

          Rectangle    rectObject;

          // Определяем указатель на объект порожденного класса

          // и инициализируем его

          *Rectangle  ptrRectObject = &rectObject;

          // Определяем указатель на объект базового класса Figure

          // и записываем в него адрес объекта порожденного класса.

          *Figure        ptrFigObject = &rectObject;

          // Вызываем методы класса Rectangle, используя имя объекта

          rectObject.PrintName;

          rectObject.PrintDimention;

          cout << ‘\n’;

          // Вызываем методы класса базового класса Figure

          rectObject.Figure::PrintName;

          rectObject.Figure::PrintDimention;

          cout << ‘\n’;

          // Вызываем методы класса Rectangle, используя указатель на

          // объекты класса Rectangle

          ptrRectObject->PrintName;

          ptrRectObject->PrintDimention;

          cout << ‘\n’;

          // Вызываем методы класса Rectangle, используя указатель на

          // объекты класса Figure

          ptrFigObject->PrintName;

          ptrFigObject->PrintDimention;

    }

    Если вы запустите приведенную выше программу, она выведет на экран следующую информацию:

    Rectangle PrintName

    Rectangle PrintDimention

    Figure PrintName

    Figure PrintDimention

    Rectangle PrintName

    Rectangle PrintDimention

    Figure PrintName

    Figure PrintDimention


    Виртуальный метод GetRuntimeClass


    Виртуальный метод GetRuntimeClass возвращает указатель на структуру CRuntimeClass, описывающую класс объекта, для которого метод был вызван:
    virtual CRuntimeClass* GetRuntimeClass() const;
    Для каждого класса, наследованного от CObject поддерживается своя структура CRuntimeClass. Если вы желаете использовать метод GetRuntimeClass в своем классе, наследованном от CObject, вы должны поместить в реализации класса макрокоманду IMPLEMENT_DYNAMIC или IMPLEMENT_SERIAL.
    Структура CRuntimeClass содержит различную информацию о классе. Ниже перечислены несколько основные полей этой структуры.

    Поле структуры CRuntimeClass
    Описание
    const char* m_pszClassName
    Указатель на строку, закрытую двоичным нулем, в которой расположено имя класса
    int m_nObjectSize
    Размер объектов класса
    WORD m_wSchema
    Номер схемы (schema number) класса. Используется при автоматическом сохранении и восстановлении объектов класса в файле. Если объекты класса не могут быть сохранены и восстановлены (в объявлении класса отсутствует макрокоманда IMPLEMENT_SERIAL), m_wSchema содержит значение -1
    void (*m_pfnConstruct)

    (void* p)
    Указатель на конструктор класса, используемый по умолчанию. Этот конструктор не имеет параметров
    CRuntimeClass* m_pBaseClass
    Указатель на структуру CRuntimeClass, содержащую аналогичную информацию о базовом классе

    Кроме перечисленных элементов структуры, она содержит метод CreateObject. Этот метод позволяет динамически создать объект соответствующего класса уже во время работы приложения. Если объект класса не создан, метод возвращает значение NULL.
    CObject* CreateObject();


    Виртуальный метод Serialize


    Виртуальный метод Serialize вызывается, когда надо сохранить или восстановить объект класса из файла на диске. Вы должны переопределить этот метод в своем классе, чтобы сохранить или восстановить его элементы. Переопределенный метод Serialize должен вызывать метод Serialize базового класса:
    virtual void Serialize(CArchive& ar);

          throw(CMemoryException);

          throw(CArchiveException);

          throw(CFileException);
    Ниже прототипа метода Serialize мы указали исключения, которые могут быть им вызваны. Более подробно об исключениях вы можете прочитать в разделе “Исключения - класс CException” данной главы.


    Включение в класс новых элементов данных


    ClassWizard позволяет включать в класс не только новые методы, но также и элементы данных, связанные с полями диалоговых панелей, форм просмотра и форм для просмотра записей баз данных и полей наборов записей. ClassWizard, использует специальные процедуры, чтобы привязать созданные им элементы данных класса к полям диалоговых панелей. Эти процедуры носят название обмен данными диалоговой панели и проверка данных диалоговой панели (Dialog Data Exchange and Dialog Data Validation - DDX/DDV). Чтобы привязать поля из наборов записей к переменным, используются процедуры обмена данными с полями записей - (Record Field Exchange - RFX).
    Процедуры DDX/DDV и RFX значительно упрощают программисту работу с диалоговыми панелями. Они позволяют связать поля диалоговых панелей и переменные. Когда пользователь редактирует поля диалоговых панелей, процедуры DDV проверяют введенные значение и блокируют ввод запрещенных значений. Затем процедуры DDX автоматически копируют содержимое полей диалоговых панелей в привязанные к ним элементы данных класса. И наоборот, когда приложение изменяет элементы данных класса, привязанные к полям диалоговой панели, процедуры DDX могут сразу отобразить новые значения полей на экране компьютера.
    Базовые сведения о процедурах обмена данных и проверке введенных значений мы привели в разделе “Диалоговая панель” главы “Введение в MFC”. А теперь мы рассмотрим средства, предоставляемые ClassWizard.
    Откройте ClassWizard и выберите из списка Class name имя класса, к которому надо добавить новый элемент данных. Теперь из списка Control IDs выберите идентификатор органа управления диалоговой панели, к которому надо привязать элемент данных класса. Если из списка Class name выбрать имя класса, не соответствующего диалоговой панели, форме просмотра, форме просмотра записей или набору записей, то список Control IDs остается пустым.
    В столбце Type отображается тип элемента данных, а в столбце Member имя этого элемента данных. Если к органу управления не подключен элемент данных класса, то соответствующие позиции в столбцах Type и Member остаются незаполненными.


    Включение в класс новых методов


    Очень удобно использовать ClassWizard для включения в состав класса новых методов. Вы можете добавлять к классу методы, служащие для обработки сообщений Windows и команд от объектов, а также методы, переопределяющие виртуальные методы базовых классов.
    Выберите из списка Class name имя класса, к которому надо добавить новый метод. Теперь из списка Object IDs выберите идентификатор объекта, для которого надо создать метод. Если вам надо переопределить виртуальный метод базового класса, выберите из этого списка имя самого класса.
    Обратите внимание на список Messages. Если вы выбрали в списке Object IDs идентификатор объекта, то в списке Messages отображаются сообщения, которые этот объект может вырабатывать. Например, если в качестве объекта фигурирует строка меню, то в списке сообщений отображаются два сообщения - COMMAND и UPDATE_COMMAND_UI. Сообщение COMMAND передается, когда данная строка выбирается из меню, а сообщение UPDATE_COMMAND_UI - когда открывается меню, содержащее эту строку.
    Выберите из списка Messages то сообщение, для которого надо создать обрабатывающий его метод и нажмите кнопку Add Function. Откроется диалоговая панель Add Member Function. В ней надо определить название нового метода. По умолчанию вам будет предложено название, построенное на основе имени объекта и имени самого сообщения. Нажмите кнопку OK. ClassWizard добавит описание нового метода в класс, а также сформирует шаблон для этого метода, учитывающий все особенности объекта и сообщения.
    Название сообщения, для которого создан метод, или имя переопределенного метода из списка Messages выделяется жирным шрифтом.  Название нового метода появится в списке методов класса - Member Function, расположенном в нижней части диалоговой панели ClassWizard. Перед названием методов размещаются символы Включение в класс новых методов или Включение в класс новых методов. Символ Включение в класс новых методов располагается перед виртуальными методами, а символ Включение в класс новых методов перед методами, обрабатывающими сообщения Windows.
    Чтобы перейти к редактированию метода класса, установите на его имя указатель мыши и сделайте двойной щелчок левой кнопкой мыши или нажмите кнопку Edit Code. Откроется окно редактора и курсор встанет непосредственно в начало определения метода.
    ClassWizard не только позволяет добавить в класс новые методы, но и удалить их. ClassWizard самостоятельно удалит объявление метода из класса. Чтобы удалить метод, выберите его название из списка Member Function и нажмите кнопку Delete Function.


    Встраивание


    В некоторых случаях более удобно и эффективно выполнять подстановку тела функции вместо ее вызова. Непосредственная подстановка тела функции позволит сэкономить время процессора на вызове функции. В языке Си этого можно достичь при помощи директивы препроцессора #define. Однако неправильное использование директивы может стать причиной ошибок.
    Си++ предусматривает специальный механизм для встраивания функций. Чтобы указать компилятору, что данную функцию необходимо встраивать, перед ее объявлением или определением надо указать ключевое слово inline:
    inline unsigned int Invert(unsigned int number) {

          return (~number);

    }


    Введение в MFC


    На сегодня существует более десятка версий библиотеки MFC. Практически каждая новая версия среды Microsoft Visual C++ (MSVC) поставляется с обновленной версией библиотеки MFC, в которой исправлены обнаруженные ошибки и добавлены новые классы.
    Все версии библиотеки MFC можно разделить на две группы. К первой относятся 16-разрядные версии MFC, предназначенные для операционных систем Windows 3.1 и 3.11. Вторая группа включает версии MFC, предназначенные для 32-разрядных операционных систем Windows NT и Windows 95. В следующей таблице перечислены все основные версии Microsoft Visual C++ и соответствующие им версии MFC.

    Среда разработки
    Версия MFC
    Разрядность
    Microsoft C/C++ версии 7.0
    1.0
    16
    MSVC 1.0
    2.0
    16
    MSVC 1.1
    2.1
    32
    MSVC 1.5
    2.5
    16
    MSVC 2.0
    2.51
    16
    MSVC 2.1
    2.52
    16
    MSVC 2.2
    2.52b
    16
    MSVC 4.0
    2.5c
    16
    MSVC 2.0
    3.0
    32
    MSVC 2.1
    3.1
    32
    MSVC 2.2
    3.2
    32
    MSVC 4.0
    4.0
    32
    MSVC 4.1
    4.1
    32

    Вы легко можете определить версию библиотеки MFC, установленной на вашем компьютере. Для этого достаточно просмотреть включаемый файл afxver_.h, расположенный в каталоге include библиотеки MFC. В одной из первых строк этого файла определена константа _MFC_VER, содержащая версию MFC:
    // Microsoft Foundation Classes версии 4.00
    #define _MFC_VER 0x0400
    Мы будем рассматривать библиотеку MFC версий 3.0, 4.0 и 4.1, однако приведенная информация верна и для других версий MFC. В тех случаях, когда эти версии имеют существенные отличия мы будем на это специально указывать.


    Библиотеки системного программиста” мы ориентировались


    В предыдущих томах серии “ Библиотеки системного программиста” мы ориентировались в первую очередь на язык программирования Си. Даже если некоторые программы были написаны на Си++, то богатые возможности этого языка практически не использовались.
    Сегодня уровень сложности программного обеспечения настолько высок, что разработка коммерческих приложений Windows с использованием средств одного только языка Си значительно затрудняется. Программист должен будет затратить массу времени на решение стандартных задач по созданию многооконного интерфейса. Реализация технологии связывания и встраивания объектов - OLE потребует от программиста еще более тяжелой работы.
    Чтобы облегчить работу программиста практически все современные компиляторы с языка Си++ содержат специальные библиотеки классов. Такие библиотеки включают в себя практически весь программный интерфейс Windows и позволяют пользоваться при программировании средствами более высокого уровня, чем обычные вызовы функций. За счет этого значительно упрощается разработка приложений, имеющих сложный интерфейс пользователя, облегчается поддержка технологии OLE и взаимодействие с базами данных.
    Современные интегрированные средства разработки приложений Windows позволяют автоматизировать процесс создания приложения. Для этого используются генераторы приложений. Вы отвечаете на вопросы генератора приложений и определяете свойства приложения - поддерживает ли оно многооконный режим, технологию OLE, трехмерные органы управления, справочную систему. Генератор приложений создаст приложение, отвечающее вашим требованиям и предоставит вам его исходные тексты. Пользуясь ими как шаблоном, вы сможете быстро разрабатывать свои приложения.
    Подобные средства автоматизированного создания приложений включены в компилятор Microsoft Visual C++ и называются MFC AppWizard - волшебник. Действительно, то что делает MFC AppWizard сродни волшебству. Заполнив несколько диалоговых панелей, можно указать характеристики приложения и получить его исходные тексты с обширными комментариями. MFC AppWizard позволяет создавать однооконные и многооконные приложения, а также приложения, не имеющих главного окна - вместо него используется диалоговая панель. Вы можете включить поддержку технологии OLE, баз данных, справочной системы.


    Возможности MFC AppWizard позволяют всего за несколько минут создать собственный многооконный редактор текста с возможностью сервера и клиента OLE. При этом вы не напишите ни единой строчки текста, а исходный текст приложения, созданный MFC AppWizard, можно сразу оттранслировать и получить выполнимый модуль приложения, полностью готовый к использованию.
    Конечно волшебство MFC AppWizard не всесильно. Прикладную часть приложения вам придется разрабатывать самостоятельно. Исходный текст приложения, созданный MFC AppWizard станет только основой, к которой надо подключить остальное. Но не стоит разочаровываться - работающий шаблон приложения это уже половина всей работы. Исходные тексты приложений автоматически полученных от MFC AppWizard могут составлять сотни строк текста. Набор его вручную был бы очень утомителен.
    Надо отметить, что MFC AppWizard создает исходные тексты приложений только с использованием библиотеки классов MFC. Поэтому только изучив MFC вы сможете пользоваться средствами автоматизированной разработки и создавать свои приложения в кратчайшие сроки.

    Ввод/вывод


    Как вы знаете, операторы << и >> выполняют сдвиг числового значения влево и вправо на опеределенное число бит. В программах, приведенных в нашей книге, эти операторы также используются для ввода информации с клавиатуры и вывода на экран.
    Если с левой стороны от оператора << расположен символ cout, то этот оператор осуществляет вывод на экран информации, указанной справа от оператора. Форма, в которой выполняется вывод на экран, зависит от типа выводимого значения. Используя оператор <<, вы можете отображать на экране текстовые строки, а также значения переменных различных типов. В качестве левого параметра оператора << можно использовать не только cout, но также результат работы предыдущего оператора <<. Это позволяет строить цепочки из операторов <<. Чтобы перейти к отображению следующей строки, вы можете передать cout значение \n.
    Так, например, следующий фрагмент кода отображает на экране значения переменных iInt, cChar и szString с соответствующими комментариями:
    cout << “Значение переменной iInt = ”;
    cout << iInt;
    cout << “\n”;
    cout << “Значение переменной cChar = ” << cChar << “\n”;
    cout << “Строка szString = ” << szString << “\n”;
    Оператор >> и символ inp предназначены для ввода данных. Они позволяют пользователю ввести с клавиатуры значение какой-либо переменной. Ниже мы привели пример, в котором для ввода целочисленного значения используется inp и оператор >>:
    int  iNum;
    cout << “Введите целочисленное значение:”;
    cin >> iNum;
    Чтобы воспользоваться возможностями потокового ввода/вывода, необходимо включить в программу файл iostream.h.
    Забегая вперед, скажем, что символы inp и outp, которые иногда называют потоками, представляют собой объекты специального класса, предназначенного для ввода и вывода информации. Операторы << и >> переопределены в этом классе и выполняют новые функции. О переопределении операторов вы можете прочитать в разделе “Перегрузка операторов”.


    Забегая вперед


    Вы, конечно, можете определить таблицу сообщений класса вручную, однако наиболее удобно воспользоваться для этой цели средствами ClassWizard. ClassWizard не только позволит в удобной форме выбрать сообщения, которые должен обрабатывать ваш класс. Он включит в состав класса соответствующие методы-обработчики. Вам останется только вставить в них необходимый код. К сожалению использовать все возможности ClassWizard можно только в том случае, если приложение создано с использованием средств автоматизированного программирования MFC AppWizard.
    Однако ClassWizard не всесилен. Так он не позволяет определить один метод для обработки нескольких сообщений. Как вы уже знаете, для этих целей предназначены макрокоманды ON_COMMAND_RANGE и ON_CONTROL_RANGE. Если вы решите воспользоваться этими макрокомандами, вам придется редактировать таблицу сообщений непосредственно, без использования ClassWizard.
    Более подробно об использовании ClassWizard для создания обработчиков сообщений вы можете прочитать в разделе “Средства ClassWizard” главы “Приложение с главной диалоговой панелью”. А сейчас мы рассмотрим механизм обработки сообщений, используемый MFC, на примере нескольких приложений.


    Задание параметров функции по умолчанию


    Еще одна интересная возможность, которая появляется у вас после перехода от Си к Си++, позволяет при определении функций задавать некоторые ее параметры по умолчанию. Вызывая такую функцию, можно не указывать параметры, заданные по умолчанию.
    Если большинство вызовов функции выполняется с одинаковыми параметрами, это позволяет сократить текст программы, а главное, уменьшить возможность совершения ошибок во время набора параметров функции.
    Параметры по умолчанию можно задать во время объявления функции или во время ее определения. По умолчанию задают только последние параметры функций:
    int Summa(int first, int second, int third=0, int fourth=0) {

          return(first + second + third + fourth);

    }
    Функцию Summa можно использовать для сложения четырех, трех или двух чисел. Если складываются два числа, то третий и четвертый параметр можно опустить:
    void main() {

          int value1 = 10, value2 = 20, value3 = 30, value4 = 40;

          int result;

          // Вызываем функцию с четырьмя параметрами

          result = Summa(value1, value2, value3, value4);

          print(“Сумма четырех чисел равна %d”, result);

          // Вызываем функцию с тремя параметрами

          result = Summa(value1, value2, value3);

          print(“Сумма трех чисел равна %d”, result);

          // Вызываем функцию с двумя параметрами,

          // последний параметр задается по умолчанию

          result = Summa(value1, value2);

          print(“Сумма первых двух чисел равна %d”, result);

    }


    одной книги такого объема явно


    Конечно, одной книги такого объема явно недостаточно, чтобы полно изучить все средства Visual C++ и библиотеки MFC. Поэтому мы предполагаем продолжить изучение MFC в следующих книгах серии “Библиотека системного программиста”.
    В следующей книге из серии “Библиотека системного программиста” мы более подробно рассмотрим классы, составляющие основу приложений MFC, в том числе мы расскажем об элементах классов CWinApp, CView, CDocument и СDialog, для описания которых нам не хватило места в этой книге. Вы узнаете больше о классах, представляющих органы управления диалоговых панелей - CAnimateCtrl, CBitmapButton, CComboBox, CEdit, CListBox, CListCrtl, CRichEditCtrl, CTreeCtrl и некоторых других.
    Отдельная глава книги будет посвящена программированию приложений, имеющих многооконный интерфейс. Мы представим пример приложения, которое может одновременно работать с документами различных типов.
    Мы также не оставим без внимания возможности библиотеки MFC по работе с базами данных. Нами будут рассмотрены классы, позволяющие приложению обращаться с базами данных через ODBС и DAO. В качестве примера мы приведем несколько приложений, работающих с базами данных.

    Закрытие файлов


    После того, как вы поработали с файлом, его надо закрыть. Класс CFile имеет для этого специальный метод Close:
    virtual void Close();

          throw(CFileException);
    Метод закрывает файл. Если вы создали объект класса CFile и открыли файл, а затем объект удаляется, то связанный с ним файл закрывается автоматически с помощью деструктора.


    Запись и восстановление объектов


    Одна из задач, решаемых программистом при разработке приложений, которые могут создавать и редактировать документы различных типов, например текстовые или графические, заключается в том, чтобы предоставить пользователю возможность записать внутреннее представление документа в файл и восстановить его.
    Чтобы облегчить программисту решение этой задачи, библиотека классов MFC определяет механизм записи и восстановления объектов (serialization). Поддержка механизма записи и восстановления объектов осуществляется средствами класса CObject.
    Классы, наследованные от CObject также могут обеспечивать работу механизма записи и восстановления объектов. Для этого при объявлении класса надо указать макрокоманду DECLARE_SERIAL, а при определении - макрокоманду IMPLEMENT_SERIAL.
    Макрокоманду DECLARE_SERIAL надо поместить перед описанием вашего класса во включаемом файле. Непосредственно после имени макрокоманды надо указать имя класса - class_name:
    DECLARE_SERIAL(class_name)
    Макрокоманду IMPLEMENT_SERIAL надо указать перед определением класса в файле исходного текста приложения, имеющего расширение CPP. Прототип макрокоманды IMPLEMENT_SERIAL представлен ниже:
    IMPLEMENT_SERIAL(class_name, base_class_name, wSchema)
    Параметр class_name определяет имя вашего класса, base_class_name - имя базового класса из которого непосредственно наследуется ваш класс. Последний параметр wSchema - это число типа UINT, определяющее версию программы. Если вы разработаете новую версию своего приложения и измените набор данных, которые необходимо записать в файл, измените параметр wSchema.
    В классе должны быть определены специальные методы для записи и восстановления состояния объектов этого класса. Обычно эти методы сохраняют и восстанавливают элементы данных из класса. Таким образом, объекты класса сами отвечают за то, как они сохраняют и восстанавливают свое состояние.
    Методы, сохраняющие и восстанавливающие состояние класса, взаимодействуют с объектом класса CArchive, который осуществляет непосредственную запись и чтение информации из файла на диске.

    Класс CObject содержит виртуальный метод Serialize, отвечающий за запись и чтение объектов классов, наследованных от класса CObject:

    virtual void Serialize(CArchive& ar);

          throw(CMemoryException);

          throw(CArchiveException);

          throw(CFileException);

    В качестве параметра ar, методу передается указатель на объект класса CArchive, используемый для записи и восстановления его состояния из файла. Чтобы узнать, какую операцию должен выполнить метод Serialize, воспользуйтесь методами CArchive::IsLoading или CArchive::IsStoring.

    Новая реализация метода Serialize должна первым делом вызвать метод Serialize базового класса. Это гарантирует, что при сохранении и восстановлении объекта будут обработаны все элементы всех базовых классов.

    Метод Serialize вызывается объектами класса CArchive когда приложение читает или записывает этот объект, вызывая методы CArchive::ReadObject или CArchive::WriteObject. Сразу отметим, что с методами CArchive::ReadObject и CArchive::WriteObject непосредственно связаны операторы записи << и чтения >>.

    Перед тем как создать объект класса CArchive, необходимо создать объект класса CFile. Связывая с объектом CFile файл на диске, имейте в виду, что если вы желаете записать объект  файл, то файл надо открыть на запись, а если надо считать файл с диска и загрузить из него данные в объект, открыть файл надо для чтения.

    Конструктор класса CArchive имеет следующий вид:

    CArchive(CFile* pFile, UINT nMode, int nBufSize = 512,

          void* lpBuf = NULL);

          throw(CMemoryException, CArchiveException, CFileException);

    Параметр pFile должен содержать указатель на объект класса CFile, из которого будут считываются или записываться данные. Перед вызовом конструктора файл, связанные с объектом pFile должен быть уже открыт.

    Параметр nMode определяет, будут данные записываться в файл или считываться из него. Параметр nMode может принимать одно из трех значений CArchive::load, CArchive::store или CArchive::bNoFlushOnDelete, описанных в следующей таблице.



    Константа

    Описание 

    CArchive::load

    Данные считываются из файла на диске. Впоследствии они будут записаны в восстанавливаемый объект. В этом случае файл, определенный параметром pFile, должен быть открыт для чтения.

    CArchive::store

    Данные будут записываются в файл на диске. Файл, определенный параметром pFile должен быть открыт для записи.

    CArchive:: bNoFlushOnDelete

    Когда вызывается деструктор класса CArchive, он автоматически вызывает метод Flush для файла pFile. Если вы укажите этот флаг, то метод Flush вызван не будет. Чтобы предотвратить потерю данных, вы должны будете перед вызовом деструктора закрыть данный файл.

    Для операций чтения и записи в файл класс CArchive выполняет буферизацию. Размер этого буфера определяется необязательным параметром nBuf Size в байтах. Если вы не укажите размер буфера, используется буфер размером 512 байт.

    Конструктор класса CArchive сам получает у операционной системы блок оперативной памяти для буфера. Однако с помощью необязательного параметра lpBuf вы можете предоставить собственный буфер. В этом случае параметр nBufSize должен указывать размер этого буфера.

    Созданный вами объект класса CArchive можно будет использовать только для записи или только для чтения. Если вам надо сначала считать данные, а потом записать, следует создать для этого два отдельных объекта класса CArchive.

    Не используйте методы класса CFile для доступа к файлу во время его использования совместно с объектами класса CArchive. Запись, чтение или перемещение указателя файла может вызвать нарушения во внутренней структуре файла архива.

    Когда созданный объект класса CArchive передается функции Serialize, вы можете определить, предназначен он для записи или для чтения, вызвав метод CArchive::IsLoading или CArchive::IsStoring.

    Метод IsStoring возвращает ненулевое значение, если данный объект предназначен для записи в файл и нуль в противном случае:

    BOOL IsStoring() const;

    Метод IsLoading является полной противоположностью метода IsStoring. Он возвращает ненулевое значение, если данный объект предназначен для чтения из файла и нуль в противном случае. Вы можете использовать любой метод IsLoading или IsStoring.

    Основное предназначение объекта класса CArchive заключается в том, что объекты вашего класса могут посредством него записать свое состояние в файл, а затем при необходимости восстановить его. Для этого в классе CArchive определены операторы записи в файл << и чтения из файла >>. Вы также можете использовать методы WriteString, Write, ReadString и Read. Опишем эти операторы и методы более подробно.


    Запись в архивный файл


    Когда приложение желает сохранить состояние объекта данного класса в файле, оно вызывает для него метод Serialize. В качестве параметра этому методу передается указатель на объект класса CArchive, связанные с файлом, открытым на запись.
    В этом случае реализация метода Serialize должна сохранить в файле все элементы данных, которые потом потребуется восстановить. Для этого необходимо воспользоваться оператором << или методами WriteString и Write, определенными в классе CArchive.
    Оператор << можно использовать для записи в архивный файл переменных простых типов, например long, int, char и объектов других классов, которые наследованы от класса CObject.
    В одной строке программы можно использовать оператор << несколько раз. Такая запись сокращает исходный код программы и делает его более легким для понимания, так как все операторы << будут сгруппированы вместе.
    Для записи в архивный файл массивов удобнее использовать метод Write класса CArchive:
    void Write(const void* lpBuf, UINT nMax);

          throw(CFileException);
    Он позволяет записать в файл определенное количество байт из указанного буфера памяти.
    Параметр lpBuf является указателем на сохраняемую область данных, а параметр nMax определяет количество байт из этой области, которое надо записать в файл.
    Если требуется сохранить строку символов, закрытую нулем, то гораздо удобнее вместо метода Write использовать метод WriteString. Метод WriteString записывает в архивный файл строку lpsz:
    void WriteString(LPCTSTR lpsz);

          throw(CFileException);


    

        Биржевая торговля: Механические торговые системы - Создание - Программирование