Глава 20. Мастера AppWizard и ClassWizard

В предьщущих четырех главах вы узнали, как создаются приложения для 32-разрядных версий Windows95, 98 и NT с использованием методов как процедурного, так и объектно-ориентированного программирования с применением библиотеки MFC. Указанные главы были посвящены, главным образом, работе с программными шаблонами, благодаря которым программист может задействовать однотипные, часто повторяющиеся блоки в разных приложениях, не набирая их каждый раз по-новому. Но речь шла лишь об одном типе шаблонов — статических шаблонах, переносимых из одного приложения в другое вручную.

Однако часто этого бывает недостаточно. Конечно, если в новую программу необходимо внедрить лишь средства ввода/вывода, скажем создания, открытия и сохранения файлов, можно воспользоваться статическими шаблонами. Они же помогут вам и в тех случаях, когда в приложение нужно добавить стандартное меню с обычными командами — Cut, Copy, Paste и прочими. Но давайте расширим наши требования к программе. Предположим, нам необходимо создать MDI-приложение (MultipleDocumentInterface— многодокументный интерфейс), поддерживающее работу с несколькими документами, или добавить в программу поддержку технологии OLE. В этом случае одного статического шаблона будет недостаточно.

Специалисты Microsoft решили создать универсальный генератор динамических шаблонов, так называемый мастер AppWizard. Он тесно связан с библиотекой MFC и генерирует объектно-ориентированный код. Чтобы запустить мастер, необходимо в меню File активизировать команду New, а затем в раскрывшемся диалоговом окне выбрать из списка опцию MFCAppWizard. Мастер позволит вам самостоятельно определить функциональные возможности, которыми должна обладать программа. При этом у вас останется возможность модифицировать программу по своему усмотрению.

Близким "родственником" мастера AppWizardявляется другой мастер, ClassWizard. С его помощью можно добавить в приложение новый класс или отредактировать существующий. Часто мастер ClassWizardиспользуют для редактирования программного кода, созданного перед этим с применением мастера AppWizard. Мастер классов запускается командой ClassWizard из меню View.

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

Программа Graph

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

Мастер приложений

В окне компилятора MicrosoftVisualC++ в меню Fileвыберите команду New. Перед вами откроется диалоговое окно (рис. 20.1), с помощью которого можно начать новый проект, выбрав в списке элемент MFCAppWizard ,(exe). Присвойте новому проекту имя Graph, задайте каталог проекта и щелкните на кнопке ОК.


Рис. 20.1. Выбор типа проекта

Первое, что нужно будет сделать дальше, — это указать мастеру, должно ли приложение поддерживать работу с одним документом, с несколькими документами или же это будет диалоговое окно (рис. 20.2).


Рис.20.2. Шаг 1: выбор опции Single Document

SDI-приложение, поддерживающее работу с одним документом, создать проще всего, поскольку в этом случае не нужно учитывать взаимодействия между разными документами, одновременно открытыми одним приложением. Убедитесь, что установлена опция Document/View architecture support?. При этом можно принять язык ресурсов, предлагаемый по умолчанию. Чтобы перейти к следующему окну мастера, показанному на рис. 20.3, щелкните на кнопке Next.


Рис. 20.3. Шаг 2: выбор опции None для отключения поддержки баз данных

Установки во втором окне мастера делаются только в том случае, если в приложении будет осуществляться работа с базами данных. В нашем примере следует оставить опцию None. Щелкните на кнопке Next, и вы перейдете к третьему окну (рис. 20.4).


Рис. 20.4. Шаг 3: составные документы не поддерживаются

Теперь можно выбрать тип OLE-приложения (контейнер или сервер) а также задать другие опции, относящиеся к технологии OLE или ActiveX. Оставьте активной опцию None и щелкните на кнопке Next, чтобы перейти к следующему окну (рис. 20.5).


Рис. 20.5. Шаг 4: выбор дополнительных функциональных возможностей

Четвертое окно мастера позволяет добавлять в программу различные средства, определяющие интерфейс приложения (например, панель инструментов или строку состояния). Задавать какие-либо дополнительные возможности мы пока не будем, поэтому щелкните на кнопке Next, чтобы перейти к пятому окну мастера (рис. 20.6).


Рис. 20.6. Шаг 5: выбор стиля окна приложения, добавление комментариев и выбор способа компоновки библиотеки MFC

Установите опции так, как показано на рис. 20.6, а затем щелкните на кнопке Next, и вы перейдете к шестому окну мастера (рис. 20.7).


Рис. 20.7. Шаг 6: перечень генерируемых классов

В шестом окне мастера AppWizard отображается список классов, которые будут сгенерированы для нашего приложения, а именно CGraphApp, CMainFrame, CGraphDoc и CGraphView.

Для класса CGraphView, реализующего функции области просмотра, в списке Baseclass можно выбрать базовый класс (CEditView, CFormView, CHtmlView, CListView, CRichEditView, CScrollView, CTreeView или CView), от которого он будет порождаться. Класс CView, потомок CWnd, является родительским для всех перечисленных классов. Область просмотра служит буфером между документом и пользователем и представляет собой дочернее окно главного окна приложения. Она содержит графический образ документа, выводимого на экран или принтер, а также поддерживает возможность редактирования документа с помощью клавиатуры и мыши. Класс CFormView описывает область просмотра с полосами прокрутки, основанную на шаблоне диалогового окна и включающую элементы управления. Класс CEditView реализует функции текстового редактора и будет использован в следующем примере этой главы.

После щелчка на кнопке Finish будет выведено окно с итоговым отчетом, подготовленным мастером AppWizard(рис. 20.8).


Рис. 20.8. Отчет, выдаваемый мастером AppWizard после установки всех опций

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

Мастер классов

С помощью мастера ClassWizard для разрабатываемого приложения можно сгенерировать дополнительные программные блоки. В частности, вы можете создать подпрограммы обработки сообщений wm_paint, WM_MOUSEMOVE и т.д. Как уже упоминалось, для запуска мастера классов в меню View нужно выбрать команду ClassWizard.... Добавив в программу функцию OnPaint(), мы получим возможность обрабатывать сообщения wm_paint, связанные с перерисовкой рабочей области окна. Для этого в раскрывшемся окне, в списках Classname и ObjectIDs, необходимо выбрать класс CGraphView(рис. 20.9).


Рис. 20.9. Окно мастера классов, в котором выбран класс CGraphView

В списке Messages теперь отображается перечень сообщений, связанных с этим классом. Выполните двойной щелчок на сообщении wm_paint, и в списке Memberfunctions появится функция OnPaint() (рис. 20.10). После щелчка на кнопке ОК данная функция будет добавлена в файл GRAPHVIEW.CPP.

Следующий этап — это компиляция и тестирование нового приложения.


Рис. 20.10 В проект добавлена функция OnPaint()

Построение приложения

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


Рис. 20.11. Команда Rebuild All выполняет построение приложения

Во время построения приложения все данные об этом процессе будут выводиться на экран на вкладке Build окна Output(рис. 20.12).


Рис. 20.12. Вывод информации о ходе компиляции

Компилятор сообщает о том, что четыре исходных файла — GRAPH.CPP, MAINFRM.CPP, GRAPHDOC.CPP и GRAPHVIEW.CPP- были успешно скомпилированы и скомпонованы. По правде говоря, это лишь вершина айсберга. Если вы просмотрите папку проекта, то найдете там еще около 30 различных файлов, сообщить о которых компилятор не посчитал нужным.

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

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


Рис. 20.13. Окно программы

Aнализ программного кода

Мастер AppWizard автоматически создал четыре файла: Graph.cpp MainFrm.cpp GraphDoc.cpp и GraphView.cpp, каждый из них связан с соответствующим файлом заголовков. Файлы заголовков содержат описания классов, используемых в исходных файлах.

Файл Graph.cpp


Это основной файл и его код показан ниже:

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

// Единственный объект класса CGraphApp
CGraphApp theApp;

//////////////////////////////////////////
// ициализация класса CGraphApp

CGraphApp::Initlnstance()

//Стандартная инициализация.

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

//Измените раздел реестра, где будут храниться 
//параметры программы.
tRegistryKey(_T("LocalAppWizard-GeneratedApplications"));

adStdProfileSettings(); // Загрузка параметров из INI-файла,

//в том числе списка последних открытых файлов

//Регистрация шаблона документов приложения

SingleDocTemplate* pDocTemplate; 'OcTemplate = new CSingleDocTemplate (

//////////////////////////////////
// CGraphApp

BEGIN_MESSAGE_MAP(CGraphApp, CWinApp) //{{AFX_MSG_MAP(CGraphApp) ON_COMMAND(ID_APP_ABOUT, OnAppAbout)

// ПРИМЕЧАНИЕ:  мастер классов будет добавлять и удалять здесь

//         макросы схемы сообщений.

// НЕ РЕДАКТИРУЙТЕ то,что здесь находится.
//}}AFX_MSG_MAP

// Стандартные операции с документами
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
END_MESSAGE_MAP()

//{{AFX_DATA_INIT(CAboutDlg) //}}AFX DATA INIT

}

void CAboutDlg::DoDataExchange(CDataExchange* pDX) f

CDialog::DoDataExchange(pDX);

//({AFX_DATA_MAP(CAboutDlg)

//}}AFX_DATA_MAP i

BEGIN_MESSAGE_MAP (CAboutDlg, CDialog)

// { (AFX_MSG_MAP (CAboutDlg)

//}}AFX_MSG_MAP END_MESSAGE_MAP ( )

// Функция, управляющая выводом окна About

void CGraphApp : : OnAppAbout ( )

{

CAboutDlg aboutDlg;

aboutDlg . DoModal ( ) ;

}

//////////////////////////////////
//Другие функции класса CGraphApp

Первая схема сообщений, содержащаяся в этом файле, принадлежит классу CGraphApp. В ней сообщения с идентификаторами id_app_about, id_file_new и ID_FILE_OPEN связываются С обработчиками OnAppAbout() , CWinApp: :OnFileNew()и CWinApp: :OnFileOpen(). В файле содержатся реализации конструктора класса CGraphApp, а также его методов initinstance() и OnAppAbout().

Диалоговое окно About принадлежит классу CAboutDlg, являющемуся потомком класса CDialog. У него имеется схема сообщений, конструктор и метод DoDataExchange ().

Файл MAINFRM.CPP

Файл MAINFRM.CPPсодержит реализацию класса CMainFrame, который порождается от класса CFrameWnd и управляет SDI-окном приложения.

// MainFrm.cpp: реализация класса CMainFrame//

#include "stdafx.h"
#include "Graph.h"

#include "MainFrm.h"

#ifdef _DEBUG

#def ine new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[]= _ FILE

#endif

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

1 1 CMainFrame

IMPLEMENT_DYNCREATE (CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP (CMainFrame, CFrameWnd)
// { (AFX_MSG_MAP (CMainFrame)

// ПРИМЕЧАНИЕ: мастер классов будет добавлять и удалять здесь
//           макросы схемы сообщений.
// НЕ РЕДАКТИРУЙТЕ то,что здесь находится.
//}}AFX_MSG_MAP
END_MESSAGE_MAP ( )
//////////////////////////////////

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

CMainFrame:: CMainFrame() {

// TODO: здесь добавьте код конструктора,

}

CMainFrame::-CMainFrame()

{

}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT Ses) {

if(ICFrameWnd::PreCreateWindow(cs))

returnFALSE;

// TODO: здесь можно модифицировать класс окна,
//      изменяя поля структуры cs.

return TRUE; }

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

// Диагностика класса CMainFrame

#ifdef _DEBUG

void CMainFrame: :AssertValid()const

{

CFrameWnd: :AssertValid() ;

void CMainFrame: : Dump (CDumpContext &dc) const {

CFrameWnd: : Dump (dc);

}

#endif //_DEBUG

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

// Обработчики сообщений класса CMainFrame

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

Файл GRAPHDOC.CPP

Файл GRAPHDOC.CPP содержит реализацию класса CGraphDoc, который управляет работой с документом, а также обеспечивает загрузку и сохранение данных документа.

// GraphDoc.cpp: реализация класса CGraphDoc//

#include "stdafx.h"

#include "Graph. h"

#include "GraphDoc. h"

tifdef _DEBOG #define new DEBUGJSIEW

#undef THIS_FILE

static char THIS_FILE [ ] = _ FILE _ ;

#endif

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

// CGraphDoc

IMPLEMENT_DYNCREATE (CGraphDoc, CDocument)

BEGIN_MESSAGE_MAP (CGraphDoc, CDocument) //( (AFX_MSGJXIAP (CGraphDoc)

// ПРИМЕЧАНИЕ: мастер классов будет добавлять и удалять здесь
//           макросы схемы сообщений.
// НЕ РЕДАКТИРУЙТЕ то,что здесь находится.
//}}AFX_MSG_MAP
END_MESSAGE_MAP ( )

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

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

CGraphDoc:: CGraphDoc() {

// TODO: здесь добавьте код конструктора

}

CGraphDoc::-CGraphDoc ()

{

}

BOOL CGraphDoc::OnNewDocument()

if (ICDocument::OnNewDocument{)) return FALSE;

: // TODO: здесь добавьте код повторной инициализации
//      (специфика SDI-приложений).

return TRUE; )

//////////////////////////////////
// Сериализация класса CGraphDoc

void CGraphDoc: : Serialize (CArchive Sar) {

if (ar . IsStoring () ) {

// TODO: здесь добавьте код сохранения }

else{

// TODO: здесь добавьте код загрузки

} }

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

// Диагностика класса CGraphDoc

fifdef _DEBUG

void CGraphDoc::AssertValid() const

{

CDocument::AssertValid(); }

void CGraphDoc: : Dump (CDumpContext Sdc) const f

CDocument :: Dump (dc); ) #endif //_DEBUG

//////////////////////////////////
// Другие функции класса CGraphDoc

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

Файл GRAPHVBEW.CPP

Файл GRAPHVIEW.CPP содержит реализацию класса CGraphview, который порождается от класса CView и отвечает за отображение документа.

// GraphView.cpp: реализация класса CGraphView//

#include "stdafx.h"
#include "Graph. h"

#include "GraphDoc.h"
#include "GraphView.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS__FILE

static  char  THIS_FILE[]   = _ FILE _ ;

#endif

//////////////////////////////////
// CGraphview

IMPLEMENT_DYNCREATE (CGraphview, CView)

BEGIN_MESSAGE_MAP (CGraphview, CView)

// ( {AFX_MSG_MAP (CGraphview)

ON_WM_PAINT()

//}}AFX_MSG_MAP END_MESSAGE_MAP ( )

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

CGraphview: : CGraphview() {

// TODO: здесь добавьте код конструктора

}

CGraphView::~CGraphView()

{

}

BOOL CGraphView::PreCreateWindow(CREATESTRUCT Ses)

{

// TODO: здесь можно модифицировать класс окна,
//      изменяя поля структуры cs.

return CView::PreCreateWindow(cs); }

//////////////////////////////////
// Отображениедокумента

void CGraphview::OnDraw(CDC* pDC)

{

CGraphDoc*  pDoc  =  GetDocument(); ASSERT_VALID(pDoc);

//  TODO:   здесь  добавьте код отображения }

//////////////////////////////////
// Диагностика класса CGraphView

#ifdef _DEBUG

void CGraphView::AssertValid() const

{

CView::AssertValid(); }

void CGraphview: : Dump (CDumpContext Sdc) const {

CView: : Dump (dc); }

CGraphDoc* CGraphview: : GetDocument () // отладочнаяверсия(

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

return (CGraphDoc*)m_pDocument; } #endif //_DEBUG

//////////////////////////////////
// Обработчикисообщенийкласса CGraphview

void CGraphview: :OnPaint () {

CPaintDCdc(this);// контекст устройства для рисования

// TODO: здесь добавьте собственный код обработки сообщения

//Не вызывайте метод CView: :OnPaint ()

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

В функции OnDraw( ) запрашивается указатель pDoc на данные, содержащиеся в документе. Для функций AssertValid() и Dump()вызываются их аналоги из базового класса.

Вывод данных в окно

На первом этапе разработки приложения Graph с помощью мастеров Арр Wizardи ClassWizard был создан интерфейс приложения, ориентированный на работу с одним документом. С помощью мастера классов в класс CGraphview был добавлен обработчик сообщений WM_PAINT. Код для функции OnPaint()  мы возьмем из программы GDI.CPP, рассматривавшейся в предыдущей главе.

// Обработчики сообщений класса CGraphView
voidCGraphView::OnPaint()
{

static DWORD dwColor[9]={
RGB(0,0, 0),       // черный
RGB(255,0, 0),      // красный
RGB(0,255,0),     // зеленый
RGB(0,0, 255),      // синий
RGB(255, 255, 0),    // желтый
RGB(255,0, 255),    // пурпурный
RGB(0,255, 255),    // голубой
RGB (127,12V,127),  // серый
RGB (255,255, 255)};//.белый
int xcoord; POINT polylpts[4], polygpts[5];

CBrush newbrush; CBrush* oldbrush;
CPen newpen; CPen* oldpen;

CPaintDCdc(this);// контекст устройства для рисования

// рисование эллипса и заливка его красным цветом

newpen.CreatePen(PS_SOLID, 1, dwColor[1]);

oldpen = dc.SelectObject(Sriewpen);

newbrush.CreateSolidBrush(dwColor[1]) ;

oldbrush = dc.SelectObject(Snewbrush);

dc.Ellipse(275,300, 200, 250);

dc.TextOut(220,265,"ellipse",7);

dc.SelectObject(oldbrush) ;

newbrush.DeleteObject() ;

dc.SelectObject(oldpen) ;

newpen.DeleteObject();

// рисование круга и заливка его синим цветом

newpen.CreatePen(PS_SOLID, 1, dwColor[3]);

oldpen = dc.SelectObject(snewpen) ;

newbrush.CreateSolidBrush(dwColor[3]) ;

oldbrush = dc.SelectObject(Snewbrush);

dc.Ellipse(375,75, 525, 225);

dc.TextOut(435,190,"circle",6) ;

dc.SelectObject(oldbrush) ;

newbrush.DeleteObject();

dc.SelectObject(oldpen);

newpen. DeleteObj ect ();  

// рисование нескольких зеленых точек
for(xcoord = 400; xcoord < 450; xcoord += 5) dc.SetPixel(xcoord, 350, OL);

dc.TextOut(460,345, "<-pixels",9);

// рисование толстой черной диагональной линии
newpen.CreatePen(PS_SOLID, 6, dwColor[0]);
oldpen = dc.SelectObject (Snewpen);
dc.MoveTo(20, 20); dc.LineTo(100, 100);

dc.TextOut(60,20,"<-diagonal line",16);
dc.SelectObject(oldpen); newpen.DeleteObject();

// рисование синей дуги

newpen.CreatePen(PS_DASH, 1, dwColor[3]);

oldpen = dc.SelectObject(Snewpen);

dc.Arc(25,125, 175, 225, 175, 225, 100, 125);

dc.TextOut(50,150,"small arc ->",12);

dc.SelectObject(oldpen);

newpen.DeleteObject();

// рисование зеленого сегмента с толстым контуром

newpen.CreatePen(PS_SOLID, 8, dwColor[2]);

oldpen = dc.SelectObject(Snewpen);

dc.Chord(125, 125, 275, 225, 275, 225, 200, 125);

dc.TextOut(280,150, "<-chord", 8);

dc.SelectObject(oldpen);

newpen.DeleteObject();

// рисование черного сектора и заливка его зеленым цветом

newpen.CreatePen(PS_SOLID, I, dwColor[0]);

oldpen = dc.SelectObject(Snewpen);

newbrush.CreateSolidBrush(dwColor[2]);

oldbrush = dc.SelectObject(Snewbrush);

dc.Pie(200,0, 300, 100, 200, 50, 250, 100);

dc.TextOut (260,80, "<-pie wedge", 12);

dc.SelectObject(oldbrush);

newbrush.DeleteObject();

dc.SelectObject(oldpen);

newpen.DeleteObject();

// рисование черного прямоугольника и заливка его серым цветом

newbrush.CreateSolidBrush(dwColor[7]);

oldbrush = dc.SelectObject(Snewbrush);

dc.Rectangle(25,300, 150, 375);

dc.TextOut(50,325,"rectangle", 9);

dc.SelectObject(oldbrush);

newbrush.DeleteObject();

// рисование черного закругленного прямоугольника
// и заливка его синим цветом

newbrush.CreateHatchBrush(HS_CROSS, dwColor[3]);
oldbrush = dc.SelectObject(Snewbrush);
dc.RoundRect(350, 250, 400, 290,20, 20);
dc.TextOut(410,270, "<-rounded rectangle", 20);

dc.SelectObject(oldbrush);
newbrush.DeleteObject() ;

// рисование толстой ломаной линии пурпурного цвета

newpen.CreatePen(PS_SOLID, 3, dwColor[5]);

oldpen=dc.SelectObject(Snewpen);

polylpts[0].x = 10;

polylpts[0].у = 30;

polylpts[1].x = 10;

polylptstl]-У = 100;

polylpts[2].x = 50;

polylpts[2].y = 100;

polylpts[3].x = 10;

polylpts[3].y = 30; 

dc.Polyline(polylpts, 4);

dc.TextOut(10,110, "polyline",8) ;

dc.SelectObject(oldpen) ;

newpen.DeleteObject() ;

// рисование голубого многоугольника

//изаливка его диагональной желтой штриховкой

newpen.CreatePen(PS_SOLID, 4, dwColor[6]);

oldpen = dc.SelectObject(Snewpen);

newbrush.CreateHatchBrush(HS_FDIAGONAL, dwColor[4]);

oldbrush = dc.SelectObject(Snewbrush);  }

polygpts[0].x = 40;

polygpts[0].y = 200;

polygptstl].x = 100;

polygpts[l].y = 270;

polygpts[2^.x = 80;

polygpts[2].y = 290;

polygpts[3].x = 20;

polygpts[3].y = 220;

polygpts[4].x = 40;

polygpts[4].y = 200;

dc.Polygon(polygpts, 5);

dc.TextOut(80,230, "<-polygon", 10);

dc.SelectObject(oldbrush) ;

newbrush.DeleteObject() ;  

dc.SelectObject(oldpen);

newpen.DeleteObject() ;

// He вызывайте метод CView::OnPaint() }

После внесения изменений скомпилируйте и запустите новую версию приложения. Мастер AppWizard сгенерировал базовый код программы и строку меню с подменю File, Edit и Help. При выборе в меню Help команды About на экран открывается диалоговое окно About. Остальные команды меню пока не работают. Почему? Потому что базовая программа не содержит функций, отвечающих за обработку сообщений, которые поступают при выборе команд меню.

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

Текстовый редактор

Теперь займемся разработкой приложения, которое позволит нам вводить и редактировать текст. Выберите в окне компилятора MicrosoftVisualC++ команду New из меню File, а в открывшемся диалоговом окне New— опцию MFCAppWizard (exe), чтобы запустить мастер AppWizard. Имя нового проекта — Editor(рис. 20.16).


Рис. 20.16. Выбор типа проекта

Мастер приложений автоматически создаст папку для нового приложения с тем именем, которое вы задали. На экране откроется первое, уже знакомое вам окно мастера. Теперь установите опцию Multiple documents, отвечающую за создание приложения, которое будет поддерживать работу с несколькими документами (рис. 20.17).


Рис. 20.17. Шаг 1: Выбор многодокументного интерфейса

Как следует из рис. 20.18 и 20.19, в создаваемом приложении не предусмотрено использование баз данных и составных документов.

На следующем этапе (шаг 4) установите опции, позволяющие добавить в окно приложения панель инструментов, строку состояния, трехмерные элементы управления и включить возможность вывода документов на печать (рис. 20.20).

Далее устанавливаются опции, подавляющие включение комментариев в код программы и задающие статический режим компоновки библиотеки MFC(рис. 20.21).

Теперь (шаг 6) мастер должен вывести список из пяти классов, которые будут им созданы, а именно: CEditorApp, CMainFrame, CChildFrame, CEditorDocи CEditorView(рис. 20.22).


Рис.20.18. Шаг 2: базы данных не используются


Рис. 20.19. Шаг 3: работа с составными документами не поддерживается, но используются элементы ActiveX


Рис. 20.20. Шаг 4: в окне приложения теперь можно использовать панель инструментов, строку состояния, трехмерные элементы управления, а также выводить документы на печать


Рис.20.21. Шаг 5: отменено добавление комментариев в код программы и задан статический режим компоновки библиотеки MFC


Рис. 20.22. Шаг 6: для работы приложения Editor необходимо сгенерировать пять классов


Рис. 20.23. Отчет об установках, сделанных в окнах мастера приложений

Если выбрать класс CEditorView, в списке Baseclass будет предложен перечень классов, которые можно использовать в качестве базового. в их числе: CEditview, CFormView, CHtmlView, CListView, CRichEditView, CScrollView, CTreeView и CView. В нашем примере класс CEditorView порождается от CEditview и используется в качестве родительского для пользовательских классов областей просмотра. Базовый класс CEditview инкапсулирует основные функции текстового редактора.
Далее необходимо щелкнуть на кнопке Finish, в результате чего будет выведен отчет о работе, выполняемой мастером AppWizard(рис. 20.23).

Щелкните на кнопке ОК, чтобы запустить процесс создания программного кода. На рис. 20.24 показан список файлов проекта, созданных мастером автоматически. Все эти файлы сохраняются в папке, которая была выбрана для проекта в окне New.

Класс CEditview реализует базовые функции текстового редактора. Созданный шаблон программы позволит вывести документ на печать, осуществить поиск и замену текста, вырезать, скопировать, вставить и удалить текстовые блоки, а также выполнить отмену предыдущей команды. Этоткласспоумолчаниюобраба­тываетсообщениясидентификаторами ID_FILE_PRINT, id_edit_cut, ID_EDIT_COPY, ID_EDIT_PASTE, ID_EDIT_CLEAR, ID_EDIT_UNDO, ID_EDIT_SE-LECT_ALL, ID_EDIT_FIND, ID_EDIT_REPLACE U ID_EDIT__REPEAT.


Рис. 20.24. На вкладке FileView выведен перечень файлов, которые были автоматически сгенерированы мастером AppWizard

Построение приложения

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

Давайте проанализируем код программы, сгенерированный мастером AppWizard, и рассмотрим некоторые новые блоки.

Анализ программного кода

Мастер приложений создал для проекта Editorпять исходных файлов: EDITOR.CPP, MAINFRM.CPP, EDITORDOC.CPP, CHILDFRM.CPP и EDITOR-VIEW.CPP. Каждому из них соответствует свой файл заголовков: EDITOR.H, MAINFRM.H, EDITORDOC.H, CHILDFRM.H и EDITORVIEW.H. Файлы заголовков содержат описания классов, используемых в исходных файлах.

Файл EDITOR.CPP

Файл EDITOR.CPPявляется основным файлом приложения. Он содержит реализацию класса CEditorApp.

// Editor. cpp: определяет работу приложения.

#include "stdafx.h"

#include "Editor. h"

#include "MainFrm.h"
#include "ChildFrm.h"
#include "EditorDoc.h"
#include "EditorView.h"

#ifdef _DEBUG

#define new DEBUG_NEW

lundef THIS_FILE

static char THIS_FILE[] = _ FILE _ ;

#endif

//////////////////////////////////
// CEditorApp

BEGIN_MESSAGE_MAP(CEditorApp, CWinApp)

// { (AFX_MSG_MAP (CEditorApp)

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 ( )

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

CEditorApp: : CEditorApp ()

}

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

//Единственный объект класса CEditorApp

CEditorApp theApp;

//////////////////////////////////
// Инициализациякласса CEditorApp

BOOL CEditorApp: : Initlnstance ()

COLORREF clrCtlBk, clrCtlText;

// Цвет фона диалоговых окон задается синим,
// а цвет текста — белым
SetDialogBkColor(clrCtlBk = RGB (О,О, 255),

clrCtlText = RGB(255, 255, 255));

AfxEnableControlContainer();

// Стандартная инициализация

tifdef _AFXDLL

EnableSdControls();
// эта функция вызывается при
// динамической компоновке MFC

#else

EnableSdControlsStatic(); // эта функция вызывается при

// статической компоновке MFC

#endif

// Измените раздел реестра, где будут храниться

// параметры программы.

SetRegistryKey(_T ("LocalAppWizard-Generated Applications"));

LoadStdProfileSettings();
// загрузка параметров из INI-файла
// Регистрация шаблонов документов

CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate(

IDR_EDITORTYPE,

RUNTIME_CLASS(CEditorDoc),

RUNTIME_CLASS(CChildFrame), // пользовательское

// дочернее MDI-окно

RONTIME_CLASS(CEditorView)); AddDocTemplate(pDocTemplate);

// Создание основного MDI-окна
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))

return FALSE; m_pMainWnd = pMainFrame;

// Анализ командной строки на предмет поиска системных команд,
// DDE-команд или команд открытия файлов.
CCommandLinelnfo cmdlnfo;
ParseCommandLine(cmdlnfo);

// Обработка команд, указанных в командной строке
if(!ProcessShellCommand(cmdlnfo))

return FALSE;

pMainFrame->ShowWindow(m_nCmdShow); pMainFrame->UpdateWindow();

return TRUE;

}

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

// Класс CAboutDlg, управляющий окном About

class CAboutDlg : public CDialog

{

public:

CAboutDlg () ;

// Данные диалогового окна//
{(AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; //}}AFX_DATA

// Виртуальные функции, сгенерированные мастером ClassWizard

// { {AFX_VIRTUAL (CAboutDlg)

protected:

virtual void DoDataExchange(CDataExchange* pDX);

//}}AFX__VIRTUAL

// Реализацияprotected:

//{(AFX_MSG(CAboutDlg)

// Обработчики сообщений отсутствуют

//) }AFX__MSG

DECLARE MESSAGE MAP()

};

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

//{{AFX_DATA_INIT (CAboutDlg)

//})AFX_DATA_INIT }

void CAboutDlg::DoDataExchange(CDataExchange* pDX)

{

CDialog::DoDataExchange(pDX);

//{(AFX_DATA_MAP(CAboutDlg)

/ / } } AFX_DATA_MAP (

BEGIN_MESSAGE_MAP (CAboutDlg, CDialog)

// ( {AFX_MSG_MAP (CAboutDlg)

// Обработчики сообщений отсутствуют

//}}AFX_MSG_MAPEND_MESSAGE_MAP( )

// Функция, управляющая выводом окна About

void CEditorApp : : OnAppAbout ( )

{

CAboutDlg aboutDlg;

aboutDlg . DoModal ( ) ;

}

//////////////////////////////////
//   Другие  функции  класса  CEditorApp

Первая схема сообщений принадлежит классу CEditorApp. В ней сообщения с идентификаторами id_app_about, id_file_new, id_file_openи id_file_ PRINT_SETUPсвязываются соответственно с обработчиками OnAppAbout( ) , CWin-Арр: :OnFileNew() , CWinApp: :OnFileOpen() И CWinApp: :OnFilePrintSetup() . В этом файле реализуются конструктор класса CEditorApp, а также его методы Initlnstance() И OnAppAbout().

Единственное изменение, внесенное нами в сгенерированный текст программы, — это переустановка цвета фона и цвета текста всех диалоговых окон, создаваемых программой.

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

// Регистрация шаблонов документов
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate (

IDR_EDITORTYPE,

RUNTIME_CLASS (CEditorDoc) ,

RUNTIME_CLASS (CChildFrame) , // пользовательское

// дочернее MDI-окно

RUNTIME_CLASS (CEditorView) ) ; AddDocTemplate (pDocTemplate} ;

Диалоговое окно About принадлежит классу CAboutDlg, являющемуся, как и в предыдущем примере, потомком класса CDialog. У него имеется схема сообщений, конструктор И метод DoDataExchange( ) .

Файл MAINFRM.CPP

Файл MAINFRM.CPPсодержит реализацию класса CMainFrame, который поро­ждается от класса CFrameWndи управляет всеми дочерними MDI-окнами.

// MainFrm.cpp: реализация класса CMainFrame

//

#include "stdafx.h" #include "Editor, h"

#include "MainFrra.h"

lifdef _DEBOG

#define new DEBDG_NEW

#undef THIS_FILE

static char THIS_FILE[] = _ FILE _ ;

#endif

//////////////////////////////////
//    CMainFrame

IMPLEMENT_DYNAMIC (CMainFrame, CMDIFrameWnd)

BEGIN_MESSAGE_MAP (CMainFrame, CMDIFrameWnd)

// { (AFX_MSG_MAP (CMainFrame)

ON_WM_CREATE ( )

//}}AFX_MSG_MAP END_MESSAGE_MAP ( )

static UINT indicators!] =

{

ID_SEPARATOR,      // поля строки состояния
ID_IKDICATOR_CAPS , ID_INDICATOR_NUM , ID_INDICATOR_SCRL ,

};

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

CMainFrame::CMainFrame()

{

}

CMainFrame :: --CMainFrame ()

{

1

int CMainFrame: :OnCreate (LPCREATESTRUCT IpCreateStruct) {

if (CMDIFrameWnd: :OnCreate (IpCreateStruct) == -1) return -1;

if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD |

WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRSJFLYBY | CBRS_SIZE_DYNAMIC) | | ! m_wndToolBar . LoadToolBar { IDR_MAINFRAME) ) <

TRACED ("Failed to create toolbarXn") ;

return-1;     //не удалось создать панель инструментов

}

if (!m_wndStatusBar.Create(this) ||

!m_wndStatusBar.Setlndicators(indicators,

sizeof(indicators)/sizeof(UINT))) {

TRACED("Failed to create status bar\n"); return -1;     //не удалось создать строку состояния

m_wndToolBar . EnableDocking (CBRS_ALIGN_ANY) ; EnableDocking(CBRS_ALIGN_ANY) ; DockControlBar (Sm wndToolBar) ;

return 0; }

BOOL CMainFrame: : PreCreateWindow (CREATESTRUCT Ses) (

if(! CMDIFrameWnd: : PreCreateWindow (cs)) return FALSE;

return TRUE;

}

//////////////////////////////////
// Диагностика класса CMainFrame

#ifdef __DEBUG

void CMainFrame::AssertValid() const

{

CMDIFrameWnd::AssertValid();

}

void CMainFrame::Dump(CDumpContext &dc) const

CMDIFrameWnd::Dump(dc); }

#endif //_DEBUG

//////////////////////////////////
// Обработчики сообщений класса CMainFrame

Заметьте, что в схему сообщений добавлен обработчик сообщений wm_create. Обратите также внимание на этот небольшой фрагмент:

static UINT indicators[] =

ID_SEPARATOR,      // поля строки состояния

ID_INDICATOR_CAPS,

ID__INDICATOR_NUM,

ID_INDICATOR_SCRL,

Как вы помните, в одном из окон мастера AppWizard была задана возможность добавления в окно приложения строки состояния. В массиве indicators перечислены идентификаторы различных полей строки состояния, которые служат индикаторами нажатия клавиш [CapsLock], [NumLock] и [ScrollLock].

Файл EDITORDOC.CPP

Файл EDITORDOC.CPPсодержит реализацию класса CEditorDoc, который управляет работой с конкретным документом, а также обеспечивает загрузку и сохранение данных документа.

// EditorDoc.cpp: реализация класса CEditorDoc

#include "stdafx.h"

#include "Editor.h"

#include "EditorDoc.h"

#ifdef _DEBUG Idefine new DEBDG_NEW

#undef THIS_FILE

static char THIS_FILE[]= _ FILE _ ;

#endif

//////////////////////////////////
//CEditorDoc

IMPLEMENT_DYNCREATE (CEditorDoc, CDocument)

BEGIN_MESSAGE_MAP (CEditorDoc, CDocument)

// { (AFX_MSG_MAP (CEditorDoc)

//)}AFX_MSG_MAP END_MESSAGE_MAP ( )

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

CEditorDoc: : CEditorDoc ()

{

}

CEditorDoc::-CEditorDoc()

{

}

BOOL CEditorDoc: : OnNewDocument () {

if ( ! CDocument : : OnNewDocument ( ) ) return FALSE;

return TRUE;

}

//////////////////////////////////
// Сериализация класса CEditorDoc

void CEditorDoc::Serialize(CArchive &ar) (

((CEditView*)m_viewList.GetHead())->SerializeRaw(ar);

}

//////////////////////////////////
// Диагностика класса CEditorDoc

#ifdef  DEBUG

void CEditorDoc: :AssertValid()const {

CDocument : :AssertValid() ; )

void CEditorDoc: : Dump (CDumpContext &dc) const {

CDocument : : Dump (dc); } #endif //_DEBOG

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

// Другие функции класса CEditorDoc

Строка функции Serialize(), выделенная полужирным шрифтом, поддержи­вает работу команд-меню FUe, обеспечивающих создание, открытие и сохранение файлов.

Файл EDITORV1EW.CPP

Файл EDITORVIEW.CPPсодержит реализацию класса CEditorView, который порождается от класса CEditViewи управляет отображением документа.

// EditorView.cpp: реализация класса CEditorView//

# include "stdafx.h"

#include . "Editor .h"  

#include "EditorDoc.h" #include "EditorView.h"

#ifdef _DEBUG

#define new DEBOG_NEW

#undef THIS_FILE

static char THIS_FILE[] = _ FILE _ ;

#endif

//////////////////////////////////
// CEditorView

IMPLEMENT_DYNCREATE (CEditorView, CView)

BEGIN_MESSAGE_MAP (CEditorView, CView) //{{AFX_MSG_MAP (CEditorView) //}}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 ( )

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

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


CEditorView::CEditorView()

{ }

CEditorView::-CEditorView()

{

}

BOOL CEditorView: :PreCreateWindow(CREATESTRUCT Ses) {

BOOL bPreCreated = CEditView: :PreCreateWindow (cs);

cs.style &= ~ (ES_AUTOHSCROLL I WS_HSCROLL) ; // разрешен перенос слов

return bPreCreated;

)

//////////////////////////////////
// Отображение документа

void CEditorView::OnDraw(CDC* pDC)

{

CEditorDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc) ; }

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

// Печать документа

BOOL CEditorView: :OnPreparePrinting (CPrintlnf o* plnfo) {

// стандартные действия по подготовке к печати

return CEditView: :OnPreparePrinting (plnfo);

}

void CEditorView: :OnBeginPrinting (CDC* pDC, CPrintlnfo* plnfo) {

CEditView: :OnBeginPrinting (pDC, plnfo); }

void CEditorView: :OnEndPrinting (CDC* pDC, CPrintlnfo* plnfo) {

CEditView: :OnEndPrinting (pDC, plnfo);

}

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

// Диагностика класса CEditorView

#ifdef _DEBUG 
void CEditorView: :A-ssertValid() const
{

CView::AssertValid() ; }

void CEditorView: : Dump (CDumpContext &dc) const

{

CView::Dump(dc); }

CEditorDoc* CEditorView::GetDocument() // отладочнаяверсия{

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

return (CEditorDoc*)m_pDocument; }
#endif //_DEBUG

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

//Обработчики сообщений класса CEditorView

void CEditorView::OnRButtonDown(DINT nFlags, CPoint point)

{

char szTimeStr[20];

CTime tm = CTime::GetCurrentTime();

sprintf{szTimeStr, "It'snow %02d:%02d:%02d", tn>. GetHour () , tm. GetMinute () , tm.GetSecondO ) ;

MessageBox(szTimeStr, "Isit time to quit yet?", MB_OK);

CEditView::OnRButtonDown(nFlags, point); }

Анализируя схему сообщений, вы заметите, что в ней содержится макрос on_wm__rbuttondown, который был добавлен мастером ClassWizard, и обработчики

Сообщений ID_FILE_PRINT,  ID_FILE_PRINT_DIRECT,   ID_FILE_PREVIEW, используемые совместно с классом CEditorView. Конструктор и деструктор класса остались пустыми. Печать документов реализуется с помощью функций onPreparePrinting(), OnBeginPrinting() и OnEndPrinting().

В конце листинга помещен текст функции OnRButtonDown(}, осуществляющей обработку сообщений wm_rbuttondown. Процесс добавления этой функции с помощью мастера ClassWizard проиллюстрирован на рис. 20.25.

Выполните двойной щелчок на элементе OnRButtonDown в списке Memberfunctions, чтобы перейти непосредственно к разделу файла EDITORVIEW.CPP, отвечающему за обработку сообщений. На рис. 20.26 показано место, куда был вставлен новый программный блок.

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


Рис. 20.25. В приложение добавляется обработчик сообщений, связанных с нажатием правой кнопки мыши


Рис. 20.26. Добавление функции OnRButtonDown



Рис. 20.27. Диалоговое окно, отображающееся при нажатии правой кнопки мыши.