5. Однооконный интерфейс

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

В этой главе мы остановимся на приложениях с однооконным интерфейсом. Приложения с многооконным интерфейсом будут рассмотрены в следующей главе, которая называется “Многооконный интерфейс”.

Сейчас мы рассмотрим модель “документ - окно просмотра”, лежащую в основе как однооконных, так и многооконных приложений, созданных с использованием библиотеки классов MFC.

Модель “документ - окно просмотра”

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

Пользователь может работать с документом, просматривая или редактируя его. Пользователь взаимодействует с документом через окно просмотра - View. Один документ одновременно может иметь несколько окон для его просмотра.

Измененный документ можно сохранить в файле на диске и продолжить с ним работу в следующий раз. Процесс сохранения документа и его загрузки в приложение называется записью и восстановлением объектов - serialize.

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

Процедура создания однооконного приложения

Сначала вы должны создать новый проект и выбрать тип проекта 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. Эта панель размещается в нижней части главного окна приложения. По умолчанию в этой панели отображается краткую подсказку о режиме работы приложения и о положении клавиш <CapsLock>, <NumLock> и <ScrollLock>. На рисунке 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 XE "CRichEditView". И все! Откомпилируйте проект и текстовый редактор готов. Вы можете открывать файлы в текстовом формате и формате RTF, редактировать их и сохранять измененные файлы.

Если вы создаете многооконное приложение, то проект будет содержать еще один класс - CChildFrame XE "CChildFrame". Этот класс управляет дочерними MDI окнами приложения.

В зависимости от того, включите ли вы в приложение поддержку технологии OLE, баз данных, стандарта WOSA, в проект могут быть включены и другие классы.

Теперь, когда вы закончили заполнять диалоговые панели MFC AppWizard, нажмите кнопку Finish. На экране появится диалоговая панель New Project Information. В этой панели приводится описание приложения. Если вас что-то не устраивает, вы можете нажать кнопку Cancel и создать проект заново.

Чтобы продолжить разработку приложения, нажмите кнопку OK. MFC AppWizard приступит к построению проекта и создаст все файлы проекта. Полученный проект сразу можно оттранслировать и запустить на выполнение. Для этого вы можете воспользоваться меню Build или нажать комбинацию клавиш <Ctrl + F5>.

Приложение 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, на экране открывается стандартная диалоговая панель для выбора файла. Вы можете выбрать из этой панели любой файл и открыть его. Однако от этого изменится только заголовок окна приложения - в нем появится название открытого файла. Содержимое файла будет недоступно. Чтобы вы смогли просматривать и изменять содержимое открытого файла, необходимо добавить специальный код. Мы займемся этим в разделе “Простейший графический редактор” данной главы.

Рис. 5.10. Приложение Single

Ресурсы приложения

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

Шаблон меню

Большой интерес для нас представляет ресурс, описывающий меню приложения. В ресурсах приложения определен только один шаблон меню, имеющий идентификатор 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. Список стандартных команд с их описанием представлен в разделе “Стандартные команды”.

Панель управления 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"

Пиктограмма

В файле ресурсов приложения 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

Таблица текстовых строк

Одним из наиболее объемных ресурсов приложения является таблица текстовых строк. В ней определены название главного окна приложения, строки, отображаемые в панели состояния и т. д.

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


//////////////////////////////////////////////////////////////
// Таблица текстовых строк
STRINGTABLE PRELOAD DISCARDABLE 
BEGIN
	IDR_MAINFRAME "Single\n\nSingle\n\n\nSingle.Document\n
					    Single Document"
END

Строка описания типа документа (IDR_MAINFRAME) состоит из семи фрагментов, разделенных символами перевода строки \n. Эти фрагменты строки отвечают за различные характеристики документа.

Формирование этой строки выполняется MFC AppWizard на основании информации, которую вы указали на страница Document Template Strings диалоговой панели Advanced Options (рис. 5.6).

Фрагмент Поле панели Advanced Options Описание
Первый Main frame caption Заголовок главного окна приложения. Используется для приложений с однооконным интерфейсом
Второй Doc type name
(только для приложений с многооконным интерфейсом)
для приложений с однооконным интерфейсом этот фрагмент изначально пуст
Имя файла, присваиваемое новому документу по умолчанию. Если этот фрагмент отсутствует, используется имя Untitled
Третий File new name (OLE short name) Название типа документа. Если в приложении определено несколько типов документов (обычно используется для приложений с многооконным интерфейсом), то при создании нового документа отображается диалоговая панель, из которой вы должны выбрать название типа создаваемого документа
Четвертый Filter name Название фильтра для имен файлов документов данного типа. Это название отображается в стандартных диалоговых панелях Open и Save As, в списке типов документов
Пятый File extension Расширение файлов документов данного типа, используемое по умолчанию
Шестой File type ID Идентификатор, под которым данный тип документов заносятся в регистрационную базу Windows 95. Вы можете просмотреть регистрационную базу Windows 95 при помощи приложения REGEDIT.
Седьмой File type name (OLE long name). Тип файлов, используемых для хранения документов данного типа. Также используется в качестве длинного имени объекта OLE, если приложение использует OLE технологию

Для получения более полной информации вы можете изучить метод 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&qupt;
	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

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

В ресурсах приложения определена только одна диалоговая панель с идентификатором 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

Таблица акселераторов

Чтобы ускорить доступ к строкам меню приложения, 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

Версия

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

Приложения, как правило, имеют только один ресурс, содержащий данные о версии, и который имеет имя VS_VERSION_INFO XE "VS_VERSION_INFO". Приложение может получить данные из ресурса, описывающего версию приложения. Для этого можно воспользоваться функциями GetFileVersionInfo и VerQueryValue.

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

Общие замечания о ресурсах приложения

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

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

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

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

Класс приложения Базовый класс Описание
CSingleApp CWinApp XE "CWinApp" Главный класс приложения
CMainFrame CFrameWnd Класс главного окна приложения
CSingleDoc CDocument XE "CDocument" Класс документа приложения
CSingleView CView XE "CView" Класс окна просмотра документа

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

Класс 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()
};
Таблица сообщений класса CSingleApp

Обратите внимание, что в последней строке определения класса CSingleApp расположена макрокоманда DECLARE_MESSAGE_MAP XE "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 XE "ID_FILE_NEW", ID_FILE_OPEN XE "ID_FILE_OPEN", ID_FILE_PRINT_SETUP XE "ID_FILE_PRINT_SETUP".

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

Главный объект приложения

В приложении создается всего один объект класса CSingleApp. Этот объект определяется как статический, поэтому его конструктор получает управление сразу после запуска приложения.

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

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


//////////////////////////////////////////////////////////////
// Конструктор класса CSingleApp 
CSingleApp::CSingleApp()
{
	// TODO: Здесь вы можете разместить свой код
}
Метод InitInstance

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

MFC AppWizard переопределяет метод InitInstance автоматически для приложений с любым пользовательским интерфейсом. Однако реализация этого метода может различаться. Сравните переопределенный метод InitInstance для приложений с главной диалоговой панелью (приложение Dialog) и оконным интерфейсом (приложение Single).


//////////////////////////////////////////////////////////////
// Метод CSingleApp 
BOOL CSingleApp::InitInstance()
{
#ifdef _AFXDLL
	Enable3dControls();
#else
	Enable3dControlsStatic();
#endif

	// Загружаем файл конфигурации
	LoadStdProfileSettings(); 

	// Создаем шаблон документа
	CSingleDocTemplate* pDocTemplate;
	pDocTemplate = new CSingleDocTemplate(
		IDR_MAINFRAME,
		RUNTIME_CLASS(CSingleDoc),
		RUNTIME_CLASS(CMainFrame), 
		RUNTIME_CLASS(CSingleView));

	// Регистрируем шаблон документа
	AddDocTemplate(pDocTemplate);

	// Выполняем стандартную обработку командной строки
	// приложения
	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);

	// Обрабатываем командную строку приложения
	if (!ProcessShellCommand(cmdInfo))
		return FALSE;

	return TRUE;
}

После вызова метода Enable3dControls, описанного ранее, вызывается метод LoadStdProfileSettings. Этот метод загружает файл конфигурации приложения, имеющий расширение INI. В INI-файле записаны имена нескольких файлов, с которыми работало приложение. Эти имена файлов будут добавлены как отдельные строки в меню File приложения. Кроме того, в INI-файле может храниться и другая информация.

Метод LoadStdProfileSettings определен в классе CWinApp XE "CWinApp" следующим образом:


void LoadStdProfileSettings(UINT nMaxMRU = _AFX_MRU_COUNT); 

Необязательный параметр nMaxMRU определяет, сколько имен файлов документов будет запоминаться. Если указать в качестве параметра nMaxMRU нулевое значение, список файлов запоминаться не будет. По умолчанию параметру nMaxMRU присваивается значение _AFX_MRU_COUNT. Константа _AFX_MRU_COUNT XE "_AFX_MRU_COUNT" определена в файле afxwin.h.


#define _AFX_MRU_COUNT   4

Напомним вам, что количество запоминаемых имен файлов можно указать в диалоговой панели MFC AppWizard - Step 4 of 6 (рис. 5.3), во время разработки приложения средствами MFC AppWizard.

Затем начинается создание шаблона документов. Сначала создается указатель pDocTemplate на соответствующий класс. Для однооконных приложений это класс CSingleDocTemplate, а для многооконных - CMultiDocTemplate. Создается новый объект класса и в переменную pDocTemplate записывается указатель на него. Для создания шаблона документа используется оператор new.

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


CSingleDocTemplate(UINT nIDResource, 
	CRuntimeClass* pDocClass, 
	CRuntimeClass* pFrameClass, 
	CRuntimeClass* pViewClass );

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

Остальные три параметра pDocClass, pFrameClass, pViewClass содержат указатели на объекты класса CRuntimeClass, полученные с помощью макрокоманд RUNTIME_CLASS XE "RUNTIME_CLASS" из классов документа CSingleDoc XE "CSingleDoc", окна CMainFrame XE "CMainFrame" и окна просмотра CSingleView XE "CSingleView". Таким образом, шаблон документа объединяет всю информацию, относящуюся к данному типу документов.

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

void AddDocTemplate(CDocTemplate* pTemplate);

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

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

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

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

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

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

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

Метод 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.

Класс 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 XE "CSingleDoc", можно было создавать динамически, необходимо выполнить следующее:

MFC AppWizard автоматически выполняет все эти требования для класса документа приложения CSingleDoc, класса окна приложения CMainFrame и класса окна просмотра CSingleView.

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

Макрокоманда IMPLEMENT_DYNCREATE XE "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.

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

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


// Конструктор класса CSingleDoc
CSingleDoc::CSingleDoc()
{
	// TODO:
}

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


// Деструктор класса CSingleDoc
CSingleDoc::~CSingleDoc()
{
}
Методы OnNewDocument и Serialize

В классе CSingleDoc переопределены два виртуальных метода OnNewDocument и Serialize. Виртуальный метод OnNewDocument определен в классе CDocument XE "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”.

Методы AssertValid и Dump

Класс CSingleDoc содержит переопределения еще двух виртуальных методов - AssertValid и Dump, входящих в базовый класс CObject. Описание методов AssertValid и Dump вы можете найти в разделе “Класс CObject - основной класс MFC” главы “Некоторые классы MFC”.

Обратите внимание, что описание этих методов и их определение расположено в блоке #ifdef _DEBUG XE "_DEBUG". Поэтому эти методы используются только для отладочной версии приложения. Когда выполняется окончательное построение приложения, эти методы не переопределяются.


//////////////////////////////////////////////////////////////
// Диагностические методы класса CSingleDoc 
#ifdef _DEBUG
void CSingleDoc::AssertValid() const
{
	CDocument::AssertValid();
}

void CSingleDoc::Dump(CDumpContext& dc) const
{
	CDocument::Dump(dc);
}
#endif //_DEBUG

Класс 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.

Таблица сообщений класса 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()
Конструктор и деструктор класса CSingleView

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


// Конструктор класса CSingleView 
CSingleView::CSingleView()
{
	// TODO:
}

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


// Деструктор класса CSingleView 
CSingleView::~CSingleView()
{
}
Метод 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 отображает соответствующее сообщение и прерывает работу приложения.

Метод 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);
}
Метод 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, вы получите данные из документа и отобразите их на экране.

Методы 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: здесь можно выполнить действия после печати
	// документа
}
Методы AssertValid и Dump

Во время отладки приложения в состав класса 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

Класс CMainFrame XE "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)
Таблица сообщений класса 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 передается функции окна сразу после его создания, но до того, как окно появится на экране.

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

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


// Конструктор класса CMainFrame
CMainFrame::CMainFrame()
{
	// TODO: 
}

// Деструктор класса CMainFrame
CMainFrame::~CMainFrame()
{
}
Метод 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 XE "CFrameWnd", а затем создает и отображает внутри главного окна панель управления toolbar и панель состояния status bar.

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

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

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

Наиболее интересная задача, которую выполняет метод OnCreate, заключается в отображении панелей управления и состояния. Для панели управления и панели состояния в библиотеке классов MFC предусмотрены два класса CStatusBar XE "CStatusBar" и CToolBar XE "CToolBar".

Классы CStatusBar и CToolBar имеют длинную цепочку наследования.


CStatusBar <- | <- CControlBar <- CWnd <- CCmdTarget <- CObject
CToolBar <- |

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

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

Класс CToolBar XE "CToolBar" представляет панель управления XE "панель управления" toolbar. Обычно панель управления содержит несколько кнопок и разделителей между ними. Для панели управления определен специальный ресурс типа Toolbar.

Чтобы создать панель управления toolbar, следует выполнить следующие действия.

Чтобы облегчить вам работу, 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.

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

Для управления панелью состояния используется класс 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);
Метод PreCreateWindow

MFC AppWizard переопределяет для класса CMainFrame виртуальный метод PreCreateWindow, но не вносит в структуру cs никаких изменений и вызывает метод PreCreateWindow базового класса CFrameWnd.


BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Здесь вы можете внести изменения в структуру cs

	return CFrameWnd::PreCreateWindow(cs);
}
Методы AssertValid и Dump

Когда вы выполняете построение отладочной версии приложения, в состав класса 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. В следующей главе мы расскажем как вы можете доработать полученное приложение, чтобы создать простейший графический редактор, с возможностью записи подготовленных в нем документов в файл.

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

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

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

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

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

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

Порядок обработки сообщений

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

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

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

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

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

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

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

Документ

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

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

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

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

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

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

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

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

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

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

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

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

Изменение порядка обработки сообщений

Порядок обработки командных сообщений определяется виртуальным методом OnCmdMsg. Этот метод первоначально определен в классе CCmdTarget и переопределен в классах CFrameWnd, CView, CDocument и некоторых других классах MFC.

В ряде случаев может понадобиться изменить порядок, в котором командные сообщения передаются объектам приложения или ввести новый объект в цепочке стандартных объектов. В этом случае вы должны переопределить виртуальный метод OnCmdMsg соответствующим образом. В качестве примера использования метода OnCmdMsg вы можете взять класс CView. Если вы установили Visual C++ с исходными текстами библиотеки MFC, вы можете найти определение этого метода в файле Viewcore.cpp.

Стандартные командные сообщения

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

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

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

Ниже коротко описаны наиболее важные стандартные командные сообщения.

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

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

Идентификатор командного сообщения Описание
ID_FILE_NEW XE "ID_FILE_NEW" Создать новый документ. Класс CWinApp содержит стандартный обработчик этого сообщения - метод OnFileNew. Если вы желаете его использовать, необходимо поместить в таблицу сообщений главного класса приложения соответствующую макрокоманду (см. приложение Single)
ID_FILE_OPEN XE "ID_FILE_OPEN" Открыть документ, записанный на диске. Класс CWinApp содержит стандартный обработчик этого сообщения - метод OnFileOpen. Если вы желаете его использовать, необходимо поместить в таблицу сообщений главного класса приложения соответствующую макрокоманду (см. приложение Single)
ID_FILE_CLOSE XE "ID_FILE_CLOSE" Закрыть текущий документ. Класс CDocument содержит метод OnFileClose, предназначенный для обработки этого командного сообщения. Метод OnFileClose вызывает метод SaveModified, если документ приложения был изменен, а затем вызывает метод OnCloseDocument
ID_FILE_SAVE XE "ID_FILE_SAVE" Сохранить текущий документ. За обработку этого командного сообщения отвечает метод OnSaveDocument класса CDocument
ID_FILE_SAVE_AS XE "ID_FILE_SAVE_AS" Сохранить текущий документ под новым именем. За обработку этого командного сообщения отвечает метод OnSaveDocument класса CDocument
ID_FILE_SAVE_COPY_AS XE "ID_FILE_SAVE_COPY_AS" Сохранить копию текущего документа под новым именем
ID_FILE_PAGE_SETUP XE "ID_FILE_PAGE_SETUP" Вызывает диалоговую панель выбора формата документа
ID_FILE_PRINT_SETUP XE "ID_FILE_PRINT_SETUP" Вызвать диалоговую панель для настройки принтера
ID_FILE_PRINT XE "ID_FILE_PRINT" Выполнить печать текущего документа
ID_FILE_PRINT_PREVIEW XE "ID_FILE_PRINT_PREVIEW" Перейти в режим предварительного просмотра документа перед печатью
ID_FILE_MRU_FILE1 XE "ID_FILE_MRU_FILE1"...
FILE16
Открыть один из наиболее часто используемых файлов приложения

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

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

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

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

Идентификатор командного сообщения Описание
ID_EDIT_CLEAR XE "ID_EDIT_CLEAR" Удалить выделенный объект
ID_EDIT_CLEAR_ALL XE "ID_EDIT_CLEAR_ALL" Удалить содержимое документа
ID_EDIT_COPY XE "ID_EDIT_COPY" Скопировать выделенный объект в универсальный буфер обмена clipboard
ID_EDIT_CUT XE "ID_EDIT_CUT" Удалить выделенный объект и записать его в clipboard
ID_EDIT_FIND XE "ID_EDIT_FIND" Отобразить на экране диалоговую панель для поиска заданного объекта в документе
ID_EDIT_PASTE XE "ID_EDIT_PASTE" Вставить в документ содержимое Clipboard
ID_EDIT_REPEAT XE "ID_EDIT_REPEAT" Повторить последнюю операцию
ID_EDIT_REPLACE XE "ID_EDIT_REPLACE" Отобразить диалоговую панель для поиска и замены текста
ID_EDIT_SELECT_ALL XE "ID_EDIT_SELECT_ALL" Выбрать (выделить) весь документ
ID_EDIT_UNDO XE "ID_EDIT_UNDO" Отменить последнюю операцию
ID_EDIT_REDO XE "ID_EDIT_REDO" Выполнить последнюю отмененную операцию

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

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

Идентификатор командного сообщения Описание
ID_WINDOW_NEW XE "ID_WINDOW_NEW" Открыть новое окно с текущим документом
ID_WINDOW_ARRANGE XE "ID_WINDOW_ARRANGE" Выровнять пиктограммы в нижней части окна MDI
ID_WINDOW_CASCADE XE "ID_WINDOW_CASCADE" Выполнить каскадное размещение окон
ID_WINDOW_TILE_HORZ XE "ID_WINDOW_TILE_HORZ" Расположить окна рядом по горизонтали
ID_WINDOW_TILE_VERT XE "ID_WINDOW_TILE_VERT" Расположить окна рядом по вертикали
ID_WINDOW_SPLIT XE "ID_WINDOW_SPLIT" Разделить окно на две части

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

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

Идентификатор командного сообщения Описание
ID_APP_EXIT XE "ID_APP_EXIT" Завершить приложение.
Данное командное сообщение обрабатывается методом OnAppExit класса CWinApp. Метод OnAppExit передает сообщение WM_CLOSE главному окну приложения
ID_APP_ABOUT XE "ID_APP_ABOUT" Отобразить на экране краткую справку о программе - диалоговую панель About.
Ни один из классов MFC не выполняет обработки этого сообщения по умолчанию, но MFC AppWizard автоматически создает необходимый для этого программный код

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

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

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

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

Идентификатор командного сообщения Описание
ID_HELP_INDEX XE "ID_HELP_INDEX" Отобразить список статей из справочной базы данных, записанной в HLP-файле
ID_HELP_USING XE "ID_HELP_USING" Отобразить подсказку об использовании справочной системы
ID_CONTEXT_HELP XE "ID_CONTEXT_HELP" Перейди в режим контекстной подсказки. Передается также при нажатии комбинации клавиш <Shift+F1>
ID_HELP XE "ID_HELP" Получить справочную информацию по данному контексту
ID_DEFAULT_HELP XE "ID_DEFAULT_HELP" Получить справочную информацию определенную по умолчанию для данного контекста

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

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

Идентификатор командного сообщения Описание
ID_VIEW_TOOLBAR XE "ID_VIEW_TOOLBAR" Отобразить или скрыть панель управления toolbar
ID_VIEW_STATUS_BAR XE "ID_VIEW_STATUS_BAR" Отобразить или скрыть панель состояния status bar

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

Простейший графический редактор

В предыдущем разделе мы создали при помощи средств 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<CFigure, CFigure&> arrayFig;

Если вы используете шаблоны классов CArray, CMap или CList, вы должны включить в исходный текст приложения файл afxtempl.h. В данном файле содержатся определения этих шаблонов.

Так как мы работаем с объектами класса CArray в различных файлах, удобнее всего включить его в самом конце файла stdafx.h.


// Включаемый файл stdafx.h
//...
// Включаемый файл для шаблона CArray
#include <afxtempl.h>

Теперь у нас есть структура для хранения геометрических фигур, нарисованных в окне. Мы должны ее заполнить. Так как за взаимодействие с пользователем отвечает класс окна просмотра, мы изменяем определенные нами ранее методы 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; i<pDoc->arrayFig.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. Режим предварительного просмотра документа перед печатью

Создание нового документа

Документ, который вы можете создать в приложении 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();
}

Сохранение и восстановление документа на диске

Построенное вами приложение можно использовать для рисования и печати документов, но оно не позволяет сохранять и загружать документ из файла на диске. Вы можете выбрать строку 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<num; i++)
		{
			// Сохраняем координаты центра фигуры
			ar << arrayFig[i].xyFigCenter;
			// Сохраняем тип фигуры
			ar << arrayFig[i].cType;
		}
	}
	
	// Загрузка документа
	else
	{
		// Считываем количество элементов, составляющих документ
		ar >> num; 

		// Восстанавливаем документ
		for(i=0; i<num; 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, постройте проект. Запустите полученное приложение. Теперь вы сможете записать документ в файл на диске, а затем загрузить его снова.

Для забывчивых пользователей

В ряде случаев пользователь может забыть сохранить внесенные им изменения документа в файле. Попробуйте отредактировать ранее сохраненный документ приложения 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);
}

Простейший текстовый редактор

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

От класса CView XE "CView" наследуется целая группа классов, которые можно использовать для управление окном просмотра документа. Каждый из этих классов предоставляет дополнительные возможности для отображения документа.


CEditView      <- |<- CCtrlView <- | CView <- CWnd
CRichEditView  <- |                |
CListView      <- |                |
CTreeView      <- |                |
                                   |
       CFormView <- CScrollView <- |

Опишем основные характеристики этих классов.

Класс Описание
CView XE "CView" Наиболее общий класс, обеспечивающий отображение документа и взаимодействие с пользователем
CScrollView XE "CScrollView" Класс CScrollView наследован от базового класса CView. В этом классе добавлены полосы просмотра, позволяющие перемещать документ в окне
CEditView XE "CEditView" Класс наследован от класса CView. Класс CEditView предоставляет возможности простого текстового редактора
CRichEditView XE "CRichEditView" Класс наследован от класса CView. Класс предоставляет возможности текстового редактора. В отличие от CEditView, он позволяет работать с текстом в формате RTF
CFormView XE "CFormView" Класс обеспечивает форматированное отображение документа на основе диалоговой панели
CListView XE "CListView" Класс обеспечивает отображение документа с использование спискового органа управления
CTreeView XE "CTreeView" Класс обеспечивает отображение документа с использование древовидного органа управления

Чтобы создать простейший текстовый редактор создайте новое приложение с однооконным (или многооконным) интерфейсом. В качестве базового класса для класса окна просмотра приложения выберите класс CEditView. Завершите создание шаблона приложения.

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

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

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

Строка меню Edit Описание
Undo Отменить последнюю операцию
Cut Удалить выделенный текст и записать его в clipboard
Copy Скопировать выделенный текст в clipboard
Paste Вставить в документ содержимое clipboard