Программирование на языке MFC

Мой второй блог в серии программирования

Что мы можем здесь увидеть? То, что в описании этого класса присутствует поле rnJTemplateList класса CPtrList, только подтвер­ждает нашу догадку о том, что de facto объекты класса CDocMan­ager являются списками указателей на какие-то шаблоны. Для того чтобы убедиться, что наша догадка верна, достаточно взглянуть на исходный код методов GetFirstDocTemplatePosition() и GetNext-DocTemplate(), который находится в файле docmgr.cpp:

POSITION CDocManager::GetFirstDocTemplatePosition() const {

return m_templateList.GetHeadPosition() ;

}

CDocTemplate* CDocManager::GetNextDocTemplate(

POSITIONS pos) const

{

return (CDocTemplate*)m_templateList.GetNext(pos) ;

Теперь возникает вопрос о том, что за шаблоны включаются в список. Позвольте, уважаемый читатель, высказать предположе­ние о том, что этими шаблонами являются шаблоны ДОКУМЕН­ТОВ, с которыми работает данное приложение. Я не буду сейчас останавливаться на том, что такое шаблон документа. Это станет ясно из дальнейшего изложения.

Сейчас нам необходимо научиться создавать объект этого класса. Естественно, что для создания объекта мы восполь­зуемся конструктором. Раз конструктор класса CDocManager параметров не имеет, то создание объекта этого класса за­труднений не вызовет. Но каким-то образом нам необходимо передать нашему приложению информацию о том, с документа­ми каких типов (шаблонов) он будет иметь дело! Вспомним, что у класса CWinApp для добавления шаблона в список есть метод AddDocTemplate(), который, фактически является вызовом одно­именного метода класса CDocManager. Наверное, именно этим методом и следует воспользоваться при добавлении шаблона документа!



Похожие статьи: , ,

Наверное, из описания класса CWinApp мы не сумеем извлечь еще какую-то информацию о классе CDocManager. Настало время взглянуть на описание этого класса. Думаю, нет необходимости описывать всю внутреннюю реализацию этого класса. Достаточно будет, если мы рассмотрим назначение наиболее часто используе­мых полей и методов.

Класс CDocManager в файле afxwin.h описан следующим образом:

class CDocManager : public CObject {

DECLARE_DYNAMIC(CDocManager) public:

// Constructor CDocManager();

//Document functions

virtual void AddDocTemplate(CDocTemplate* pTemplate); virtual POSITION GetFirstDocTemplatePosition() const; virtual CDocTemplate* GetNextDocTemplate(

POSITIONS pos) const; virtual void RegisterShellFileTypes(BOOL bCompat); void UnregisterShellFileTypes();

virtual CDocument* OpenDocumentFile(LPCTSTR IpszFileName);

// open named file

virtual BOOL SaveAllModified(); // save before exit virtual void CloseAllDocuments(BOOL bEndSession);

// close documents before exiting virtual int GetOpenDocumentCount();

// helper for standard commdlg dialogs virtual BOOL DoPromptFileName(CString& fileName

UINT nIDSTitle, DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate);

//Commands

// Advanced: process async DDE request

virtual BOOL OnDDECommand(LPTSTR IpszCommand); virtual void OnFileNew(); virtual void OnFileOpen();

// Implementation protected:

CPtrList m_templateList;

int GetDocumentCount(); // helper to count number

// of total documents

public:

static CPtrList* pStaticList;

// for static CDocTemplate objects static BOOL bStaticInit;

// TRUE during static initialization static CDocManager* pStaticDocManager;

// for static CDocTemplate objects

public:

virtual -CDocManager(); #ifdef _DEBUG

virtual void AssertValid() const;

virtual void Dump(CDumpContext& dc) const; #endif };



Похожие статьи: , , , , ,

Значит, либо я сделал что-то не так, либо в программе не опре­делил какие-то данные. Второй вариант, конечно, более вероя­тен. Для того чтобы убедиться в этом, давайте, уважаемый чи­татель, посмотрим на то место, в котором была обнаружена ошибка. Как оказалось, на этом месте находится исходный текст метода OnFileOpenQ класса CWinApp. Ниже приведен текст это­го метода:

void CWinApp::OnFileOpen() {

ASSERT(m_pDocManager != NULL); m_pDocManager->OnFileOpen();

}

Помните, я говорил, что имеющий уши да услышит? Во-первых, судя по наименованию переменных, используемых в приведенном выше тексте метода, MFC по умолчанию считает, что наше прило­жение построено в соответствии с идеологией «Документ/представ­ление»! Другими словами, именно эта идеология считается при­оритетной при работе с MFC! Значит, именно ЭТОЙ идеологии в MFC отводится очень важная роль. Возможно, подумал я, что понимание одного из ключевых моментов MFC приблизит меня к по­ниманию идеологии всей библиотеки в целом! Но оставим «лири­ческое отступление» и продолжим нашу работу.

Во-вторых, здесь MFC сама подсказывает нам причину ошибки в программе! Из приведенного выше текста метода мы можем сделать три важных заключения. Заключение первое состоит в том, что у класса CWinApp есть член m_pDocManager, кото­рый, судя по его названию, является указателем на какой-то ме­неджер документов. Заключение второе – вероятнее всего, имен­но этот менеджер документов и отвечает за управление доку­ментами в приложении. И наконец, заключение третье. Очевид­но, что единственной причиной, которая может привести к выда­че приведенного выше сообщения об ошибке, является равен­ство нулю указателя на менеджер документов. Следовательно, причина этой ошибки состоит в том, что мы просто-напросто не создали менеджер документов!

А что следует из этих заключений? Из этих заключений следует один очень важный вывод.

Тсли наше приложение не обрабатывает команду ID_FILE_OPEN и обработку производит метод CWinApp::OnFileOpen, то перед вызовом этого метода дол­жен быть создан объект класса CDocManager и указатель на этот объект должен быть присвоен полю CWinApp::m_pDocManager.

К сожалению, искать информацию о менеджере документов в MSDN совершенно бесполезно. И в описании членов класса CWi­nApp мы также не найдем и упоминания об m_pDocManager. При­дется опять смотреть исходные коды MFC…



Похожие статьи: ,

Я начал реализовывать этот план и какое-то время работал в этом направлении. Однако достаточно быстро я одумался и за­дал себе один вопрос: а почему, собственно говоря, мне приходит­ся делать всю черновую работу самому? Исходя из того, что я чи­тал про MFC ранее, все должно быть намного проще. Если я иду в правильном направлении, то почему мне приходится делать все самому вручную? Где же хваленые возможности MFC? Все же, на­верное, я чего-то не понимаю. И здесь мне пришла в голову мысль, результатом которой и явилось правильное решение. А не попро­бовать ли мне возложить обработку открытия файла на MFC? Ведь не зря же у класса CWinApp есть метод OnFileOpenQ, не так ли? Карту сообщений своего объекта приложения я немного изменил. Теперь обработчик команды ID__FILE__OPEN стал выглядеть сле­дующим образом:

ON_COMMAND( ID_FILE_OPEN, CWinApp::OnFileOpen )

Тем самым обработку открытия файла я возложил на MFC. Мне осталось только понять, к чему это приведет…

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



Похожие статьи: ,

Характерной особенностью этой программы является то, что она практически ничего не делает. Я написал этот вариант только для того, чтобы посмотреть, как MFC будет реагировать на открытие файла и т.д. Но, с другой стороны, обратите внимание на то, что обработку открытия файла я, тем не менее, возложил на основ­ное окно приложения. Этот факт привел к тому, что было потеряно достаточно много времени. Тем не менее, это вынудило меня хо­рошенько пораскинуть мозгами, и в конце концов эти размышле­ния привели меня к правильному решению.

Я старался рассуждать логически. Я хочу отобразить содержи­мое файла. При этом хочу сделать это в соответствии с требова­ниями архитектуры «документ/представление». Но у меня нет яс­ности по многим вопросам. Когда, в какой момент файл на диске должен превратиться в документ? Наверное, в момент открытия файла. Другими словами, при обработке команды на открытие файла, полученной от меню, я должен открыть файл и превратить его в документ. Но какой файл надо открыть? Естественно, тот файл, который я укажу. Где укажу? Конечно же, в стандартном диа­логовом окне для выбора открываемого файла! Значит, мне необ­ходимо первым делом подготовить данные для открытия стандарт­ного диалога. После того как я выберу файл, мне необходимо бу­дет его открыть, разобрать по косточкам и каким-то образом со­держимое файла превратить в документ… Н-да, работы непоча­тый край…



А теперь текст непосредственно программы:

#include "stdafx.h"

// Объявляем класс приложения, его поля и методы.

class CDocViewlApp : public CWinApp

{

public:

CDocViewlApp () ; :* protected:

afx_msg void OnFileOpen(); virtual BOOL Initlnstance (); DEC LARE_ME S S AG E_MA P()

};

CDocViewlApp::CDocViewlApp()

{

}

void CDocViewlApp::OnFileOpen()

{

}

class CMainFrame : public CMDIFrameWnd {

DECLARE_DYNAMIC( CMainFrame ); public:

CMainFrame () ; «’

};

IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd)

CMainFrame::CMainFrame()

{

}

BOOL CDocViewlApp::Initlnstance() {

#ifdef _AFXDLL

Enable3dControls() ; #else

Enable3dControlsStatic(); #endif

CMainFrame* pMainFrame = new CMainFrame; pMainFrame->LoadFrame( IDR_RESOURCE ); m_pMainWnd = pMainFrame; pMainFrame->ShowWindow(m_nCmdShow ); pMainFrame->UpdateWindow();

return TRUE;

}

BEGIN_MESSAGE_MAP( CDocViewlApp, CWinApp )

ON_COMMAND( ID_FILE_OPEN, OnFileOpen ) END_MESSAGE_MAP()

CDocViewlApp theApp;



Похожие статьи: ,

С чего я начал? Написал небольшую программу, при помощи которой хотел всего-навсего выяснить характер взаимодействия объектов раз­ных классов при работе в соответствии с идеологией архитектурой «документ/представление». Эта программа не должна делать ниче­го, кроме отображения пустого документа в (естественно!) пустом окне отображения. Впоследствии я планировал использовать текст этой программы как заготовку для других программ.

Ниже приведен текст файла ресурсов моей программы. Ком­ментарии, созданные средой Visual С++, я предварительно удалил.

#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS

#include "afxres.h"

#undef APSTUDIO_READONLY_SYMBOLS

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)

#ifdef _WIN32

LANGUAGE LAN G__RU SSI AN, SUBLANG_DE FAULT #pragma code_page(1251) #endif //_WIN32

#ifdef APSTUDIO_INVOKED

1 TEXTINCLUDE DISCARDABLE BEGIN

"resource. h\0"

END

2 TEXTINCLUDE DISCARDABLE . BEGIN

"#include xw‘afxres.h""\r\n" "\0"

END

3 TEXTINCLUDE DISCARDABLE BEGIN

"\r\n" "\0"

END

#endif // APSTUDIO_INVOKED

IDR_RESOURCE ICON DISCARDABLE "ResWCommon.ico"

IDR_RESOURCE MENU DISCARDABLE BEGIN

ID_FILE_OPEN ID APP EXIT

POPUP "&File"

BEGIN

MENUIТЕМ "&Open\tCtrl+0", MENUIТЕМ SEPARATOR MENUIТЕМ "E&xit\tCtrl+x",

END

POPUP "SWindow", GRAYED BEGIN

MENUITEM "Tile &horizontally\tCtrl+H", ID_WINDOW_TILEHORZ, GRAYED

MENUITEM "Tile &vertically\tCtrl+V", ID_WINDOW_TILE_VERT, GRAYED

MENUIТЕМ "&Cascade\tCtrl+C", ID_WINDOW_CASCADE

END

POPUP "Help" BEGIN

MENUIТЕМ "&About", ID_APP_ABOUT

END

END

STRINGTABLE DISCARDABLE BEGIN

ID_FILE_OPEN "Open an existing document"

END

"Display program information, version number and copyright" "Quit the application"

STRINGTABLE DISCARDABLE BEGIN

ID_APP_ABOUT

ID_APP_EXIT

END

STRINGTABLE DISCARDABLE BEGIN

IDR_RESOURCE "PE-file viewer and disassembler"

END



08.02.2010

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

void CArchive::Abort() {

ASSERT(m_bDirectBuffer ||

m_lpBufStart == NULL || AfxIsValidAddress(m_lpBufStart,

m_lpBufMax – m_lpBufStart, IsStoring()));

ASSERT(m_bDirectBuffer ||

m_lpBufCur == NULL || AfxIsValidAddress(m_lpBufCur,

m_lpBufMax – m_lpBufCur, IsStoring()));

// disconnect from the file mjpFile = NULL;

if (!m_bUserBuf) {

ASSERT(!m_bDirectBuffer); delete[] m_lpBufStart; m_lpBufStart = NULL; m_lpBufCur = NULL;

}

delete m_pSchemaMap; m_pSchemaMap = NULL;

// m_pStoreMap and m_pLoadArray are unioned, // so we only need to delete one

ASSERT((CObject*)m_pStoreMap == (CObject*)m_pLoadArray); delete (CObject*)m_pLoadArray; m_pLoadArray = NULL;Посмотрим, что происходит с архивом в случае прекращения работы посредством вызова метода AbortQ. Первым делом метод «отсоединяет» архив от файла, присваивая полю m__pFile значе­ние NULL. Затем в том случае, если у архива есть ассоциированный с ним буфер, производится удаление буфера. Если в буфере остались данные, которые не были записаны в файл, то эти данные будут потеряны. Указатели на начало буфера и на текущую позицию буфера делаются равными NULL. Затем удаляется указатель на хэш-таблицу, содержащую номера схем классов, а за ней и хэш-таблица (или массив) сохраненных объектов.



Похожие статьи: ,

Для нас главным в этом методе является то, что происходит вызов метода PreCreateWindow() для нашего объекта окна пред­ставления:

Пример


BOOL CEditView::PreCreateWindow(CREATESTRUCT& cs) {

m_dwDefaultStyle = dwStyleDefault; return CCtrlView::PreCreateWindow(cs);

}

Вызов же метода PreCreateWindow() родительского объекта все ставит на свои места:

BOOL CCtrlView::PreCreateWindow(CREATESTRUCT& cs) {

ASSERT(cs.IpszClass == NULL); cs.IpszClass = m_strClass;

// initialize common controls

VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG)) ; AfxDeferRegisterClass(AFX_WNDCOMMCTLSNEW_REG);

// map default CView style to default style // WS_BORDER is insignificant

if ((cs.style I WS_BORDER) == AFX_WS_DEFAULT_VIEW)

cs.style = m_dwDefaultStyle & (cs.style | ~WS_BORDER);

return CView::PreCreateWindow(cs);

}



Похожие статьи:

03.02.2010

Нетрудно заметить, что метод Create(), в свою очередь, вызы­вает метод CreateEx():

BOOL CWnd::CreateEx(DWORD dwExStyle,

LPCTSTR IpszClassName,

LPCTSTR IpszWindowName,

DWORD dwStyle,

int x,

int y,

int nWidth,

int nHeight,

HWND hWndParent,

HMENU nIDorHMenu,

LPVOID lpParam)

{

/I allow modification of several common create parameters CREATESTRUCT cs; cs.dwExStyle = dwExStyle; cs.IpszClass = IpszClassName; cs.lpszName = IpszWindowName; cs.style = dwStyle; cs.x = x; cs.y = y; cs.cx = nWidth; cs.cy = nHeight; cs.hwndParent = hWndParent; cs.hMenu = nIDorHMenu;

cs.hlnstance = AfxGetlnstanceHandle(); cs.lpCreateParams = lpParam;

if (!PreCreateWindow(cs)) {

PostNcDestroy(); return FALSE;

}

AfxHookWindowCreate(this);

HWND hWnd = ::CreateWindowEx(cs.dwExStyle,

cs.IpszClass, cs.IpszName, cs.style, cs .x, cs.y, cs.cx, cs.cy,

cs.hwndParent,
cs.hMenu,
‘*
cs.hlnstance,

cs.lpCreateParams);

#ifdef _DEBUG

if (hWnd == NULL) {

TRACE1("Warning: Window creation failed:

GetLastError returns 0x%8.8X\n",

GetLastError ());

#endif

if (!AfxUnhookWindowCreate()) PostNcDestroy();

// cleanup if CreateWindowEx fails too soon

if (hWnd == NULL)

return FALSE;
ASSERT (hWnd == m_hWnd) ;
fl

// should have been set in send msg hook return TRUE;

}



Похожие статьи:

Как легко заметить, конструктору CCtrlView::CCtrlView() в каче­стве параметра передается имя класса, которое тут же записыва­ется в поле m_strClass. Отметим этот факт. Затем в методе CFrameWnd::CreateView() вызывается метод Create() нашего объ­екта представления:

BOOL CWnd::Create(LPCTSTR IpszClassName, LPCTSTR IpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext)

{

// can’t use for desktop or pop-up windows // (use CreateEx instead) ASSERT(pParentWnd != NULL); ASSERT((dwStyle & WS_POPUP) == 0); return CreateEx (0,

IpszClassName,

IpszWindowName,

dwStyle I WS_CHILD,

rect.left, rect.top,

rect.right – rect.left,

rect.bottom – rect.top,

pParentWnd->GetSafeHwnd(),

(HMENU)nID,

(LPVOID)pContext);

}



Мне бы хотелось, чтобы вы вспомнили, что происходит при по­лучении окном класса CMDIChildWnd сообщения WM_CREATE. Во время работы этого метода вызываются еще много других мето­дов, в том числе CFrameWnd::CreateView(). Именно при работе этого метода и происходит вызов метода CreateObject() для объекта клас­са окна представления. Естественно, метод CreateObject() вызы­вает конструктор объекта окна представления. И, разумеется, вы­зываются строго по порядку конструкторы классов, от которых унас­ледован наш объект. Если мы в качестве окна приложения исполь­зуем объект класса CEditView, то первым вызывается конструктор CEditView::CEditView(). Исходный код этого конструктора находит­ся в файде viewedit.cpp:

// pass a NULL style because dwStyleDefault stays for // backward compatibility

CEditView::CEditView() : CCtrlView(_T("EDIT"), NULL)

m_nTabStops = 8*4; // default 8 character positions m_hPrinterFont = NULL; m_hMirrorFont = NULL; m_pShadowBuffer = NULL; m_nShadowSize = 4);

}

Перед тем, как отработает этот конструктор, управление будет передано конструктору класса CCtrlView.

Исходный текст конструктора класса CCtrlView можно найти в файле viewcore.cpp:

CCtrlView::CCtrlView(LPCTSTR IpszClass, DWORD dwStyle) {

m_strClass = IpszClass;

m_dwDefaultStyle = dwStyle;

}



Похожие статьи:

Я начал писать программу и остановился, словно услышал вопрос, задаваемый читателем: «Как же так, если, к примеру, мне хочется отображать данные в виде дерева или, скажем, в виде текста, мне так и придется прорисовывать дерево или использовать TextOut()?» Что ж, вопрос вполне закономерный. Мне тоже очень не хотелось думать, что разработчики MFC остановились на полпути и не до­вели дело до конца. Помните, читатель, мое федположение о том, что класс CView является абстрактным и предоставляет только базовые возможности для объектов класса окна представления? Отправной точкой дальнейших изысканий послужило предположе­ние, что более специализированные классы окон представлений наследуются от CView. Заглянув в исходные коды MFC, я слегка обомлел. Оказывается, от CView наследуются классы CCtrlView, CScrollView. В свою очередь от CCtrlView унаследованы классы CListView, CTreeView, CEditView, CRichEditView. От CScrollView унас­ледованы CFormView, CPreviewView. От CFormView унаследованы классы CDaoRecordView, CRecordView, CHtmlView, COIeDBRecord-View. Графически это можно представить так:

Пример

(CVievT)

-[CCtrlView ]

( CListView ]

[CTreeView]

-(CEditVievT)

CRichEditView]

-(CScrollView ~)

[CFormView ]

-(CDAORecordView)

(CRecordView ]

-(CHtmlView ]

[CQIeDBRecordView ]

—[ CPreviewView]

Даже судя по названиям, уже можно сделать выводы о предна­значении этих классов. Наверное, класс окна отображения в моей программе мне нужно было наследовать не от CView, а от более специализированного класса, скажем, CEditView. Что ж, попробу­ем так и сделать. Но теперь нам нужно отследить,каким образом работают объекты этого класса. Давайте, читатель, подумаем вме­сте вами. На каком этапе начинает работать объект класса, унас­ледованного от CView? Наверное, при создание объекта окна пред­ставления. Давайте попробуем проследить, что произойдет в мо­мент создания окна представления.



А теперь давайте займемся подсчетами. Вся моя программа со всеми описаниями классов, комментариями и пропусками состоит из 128 строк. Теперь вспомним, что нам пришлось бы сделать в том случае, если бы мы писали программу без использования архитек­туры "документ/представление", используя только функции API. Во-первых, нам понадобилось бы так называемое «стандартное за­клинание», в котором необходимо заполнить структуру типа WND-CLASS, зарегистрировать класс окна и запустить цикл обработки сообщений. Во-вторых, понадобилась бы обработка, как минимум, сообщения WM_CREAfE, во время которой необходимо создать окно класса «MDICLIENT». В-третьих, регистрация класса окна представления и написание оконной процедуры окна представле­ния. .. Можно только представить себе, сколько строк содержалось бы в исходном коде такого приложения!

Я достаточно долго программировал с использованием только API, поэтому не буду утверждать, что MFC хуже или лучше «чисто­го» API. Тем не менее, возможности, которые предоставляет архи­тектура «документ/представление», не могут не впечатлять.



Какое сообщение получает окно в том случае, когда окно должно перерисовать себя? Естественно, сообщение WM__PAINT. Какой метод должен отвечать за обработку сообщения WM_PAINT у объекта класса CView? Конечно же, CView::OnPaint(). Следовательно, нам неплохо было бы заглянуть в исходный текст этого метода, который, кстати, находится в файле viewcore.cpp:

void CView::OnPaint() {

// standard paint routine
CPaintDC dc(this);
OnPrepareDC(&dc);
OnDraw(&dc);
}_________

[CView::OnPaint ) Видно, что вызов конструктора класса

Ucview.OnPrepareDC) СРаПЮС опРеделяетчт0 «”вод будет
^ – > осуществляться на окно класса CView.

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

метода CView::OnPrepareDC():

void CView::OnPrepareDC(CDC* pDC, CPrintlnfo* plnfo) {

ASSERT_VALID(pDC);

UNUSED(pDC); // unused in release builds

// Default to one page printing if doc length not known if (plnfo != NULL)

plnfo->m_bContinuePrinting =

(pInfo->GetMaxPage() != Oxffff || (plnfo->m_nCurPage == 1) ) ;

}

void CView::OnDraw(CDC*)

{

}

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

Пример

class CDocView : public CView {

DECLARE_DYNCREATE ( CDocView ) public:

CDocView();

virtual ~CDocView(); void OnDraw( CDC* pDC );

};

Ниже я привожу текст метода OnDraw():

void CDocView::OnDraw( CDC* pDC ) {

pDC->TextOut( 10, 10, GetDocument()->GetPathName () );

}

Программа откомпилировалась без ошибок!



Обратите внимание, читатель, что указатель на структуру типа CCreateContext (в нее чуть раньше мы записали наши указатели на документ,шаблон документа, а также указатели на фрейм и на окно представления документа) записывается сначала в поле lpCreateParams структуры cs типа CREATESTRUCT, а потом в поле IParam структу­ры mcs типа MDICREATESTRUCT Указатель на структуру mcs по­сылается окну типа MDICLIENT посредством сообщения WM_MDICREATE. Что же происходит дальше? После получения этого сообщения создается дочернее окно MDI, которое будет иг­рать роль окна фрейма. Естественно, при своем создании оно по­лучает сообщение WM_CREATE, на что реагирует вызовом мето­да CMDIChildWnd::OnCreate():

int CMDIChildWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) {

// call base class with IParam context (not MDI one) MDICREATESTRUCT* lpmcs;

lpmcs = (MDICREATESTRUCT*)lpCreateStruct->lpCreateParams; CCreateContext* pContext = (CCreateContext*)lpmcs->lParam;

return OnCreateHelper(lpCreateStruct, pContext);

Из переданной окну структуры типа CREATESTRUCT выбирает­ся указатель на CCreateContext, после чего указатели на структуры типов CREATESTRUCT и CCreateStruct передаются методу CFrameWnd::OnCreateHelper:

int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs.

CCreateContext* pContext)

if (CWnd::OnCreate(lpcs) == -1) return -1;

// create special children first

if (!OnCreateClie*nt (lpcs, pContext) ) {

TRACEO("Failed to create client pane/view for frame.\n") ; return -1;

}

// post message for initial message string

PostMessage(WM_SETMESSAGESTRING, AFX__IDS_IDLEMESSAGE);

// make sure the child windows have been properly sized RecalcLayout();

return 0; // create ok

}

BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT,

CCreateContext* pContext)

{

// default create client will create a view if asked for it if (pContext != NULL && pContext->m_pNewViewClass != NULL) {

if (CreateView (pContext, AFX_IDW_PANE_FIRST) == NULL) return FALSE;

}

return TRUE;

}

CWnd* CFrameWnd::CreateView(CCreateContext* pContext,

UINT nID)

{

ASSERT(m_hWnd != NULL); ASSERT(::IsWindow(m_hWnd)); ASSERT(pContext != NULL);

ASSERT(pContext->m_pNewViewClass != NULL);

// Note: can be a CWnd with PostNcDestroy self cleanup CWnd* pView =

(CWnd*)pContext->m_pNewViewClass->CreateObject() ; if (pView == NULL) {

TRACE1("Warning: Dynamic create of view type

%hs failed.\n",

pContext->m_pNewViewClass->m_lpszClassName) ; return NULL;

ASSERT_KINDOF(CWnd, pView);

// views are always created with a border! if (!pView->Create(NULL, NULL,

AFX_WS_DEFAULT_VIEW, CRect(0,0,0, 0), this, nID,

pContext))

{

TRACEO("Warning: could not create view for frame.\n");
return NULL;
// can’t continue without a view

}

if (afxData.bWin4 && (pView->GetExStyle () &

WS_EX_CLIENTEDGE))

{

// remove the 3d style from the frame, since the view is // providing it.

// make sure to recalc the non-client area

ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED);

}

return pView;

}

Смотрите внимательно, читатель! Сначала создается объект (представление) того класса, который мы указали при создании шаблона документа. После этого создается непосредственно окно представления. При этом видно, родительским окном объ­является окно фрейма! Так что же получается? MFC решило за программиста одну из достаточно часто встречавшихся задач -расположение дочернего окна поверх клиентской области роди­тельского окна. Теперь программисту нет необходимости само­му создавать дочернее окно и отслеживать изменения размеров родительского окна – эти задачи решает за него MFC. Единст­венное, что в этом случае требуется от программиста, так это написать приложение в соответствии с требованиями архитек­туры «документ/представление»!

Осталось понять одну «мелочь» – как осуществляется отобра­жение данных документа в окне представления.

Я опять старался рассуждать логически.



Похожие статьи:

Читатель, взгляните, пожалуйста, на исходный код метода CMDIChildWnd::LoadFrame(). В нем вы увидите следующий опе­ратор:

if (!Create(GetlconWndClass(dwDefaultStyle, nIDResource), strTitle, dwDefaultStyle, rectDefault,

(CMDIFrameWnd*) pParentWnd, pContext)) …

BOOL CMDIChildWnd::Create(LPCTSTR IpszClassName,

LPCTSTR IpszWindowName, DWORD dwStyle, const RECT& rect, CMDIFrameWnd* pParentWnd, CCreateContext* pContext)

{

if (pParentWnd == NULL) {

CWnd* pMainWnd = AfxGetThread()->m_pMainWnd; ASSERT(pMainWnd != NULL); ASSERT_KINDOF(CMDIFrameWnd, pMainWnd); pParentWnd = (CMDIFrameWnd*)pMainWnd;

ASSERT(::IsWindow(pParentWnd->m_hWndMDIClient));

// insure correct window positioning pParentWnd->RecalcLayout();

// first copy into a CREATESTRUCT for PreCreate CREATESTRUCT cs; cs.dwExStyle = OL; cs.IpszClass = IpszClassName; cs.lpszName = IpszWindowName; cs.style = dwStyle; cs.x = rect.left; cs.y = rect.top; cs.cx = rect.right – rect.left; cs.cy = rect.bottom – rect.top; cs.hwndParent = pParentWnd->m_hWnd; cs.hMenu = NULL;

cs.hlnstance = AfxGetlnstanceHandle(); cs.lpCreateParams = (LPVOID)pContext;

if (!PreCreateWindow(cs)) {

PostNcDestroy(); return FALSE;

}

// extended style must be zero for MDI Children // (except under Win4)

ASSERT(afxData.bWin4 || cs.dwExStyle == 0);

ASSERT(cs.hwndParent == pParentWnd->m_hWnd); // must not change

// now copy into a MDICREATESTRUCT for real create MDICREATESTRUCT mcs; mcs.szClass = cs :*lpszClass; mcs.szTitle = cs.lpszName; mcs.hOwner = cs.hlnstance;

mcs.x = cs.x; mcs.у = cs.y; mcs.cx = cs.cx; mcs.cy = cs.cy;

mcs.style = cs.style & -(WS_MAXIMIZE | WS_VISIBLE); mcs.lParam = (LONG)cs.lpCreateParams;

// create the window through the MDICLIENT window AfxHookWindowCreate(this); HWND hWnd =

(HWND)::SendMessage(pParentWnd->m_hWndMDIClient, WM_MDICREATE, 0,

(LPARAM)&mcs); if (!AfxUnhookWindowCreate()) PostNcDestroy(); // cleanup if MDICREATE fails too soon

if (hWnd ~ NULL) return FALSE;

// special handling of visibility (always created invisible) if (cs.style & WS_VISIBLE) {

// place the window on top in z-order before showing it ::BringWindowToTop(hWnd);

// show it as specified

if (cs.style & WS_MINIMIZE)

ShowWindow(SW_SHOWMINIMIZED); else if (cs.style & WS_MAXIMIZE)

ShowWindow(SW_SHOWMAXIMIZED) ; else

ShowWindow(SW_SHOWNORMAL);

// make sure it is active (visibility == activation) pParentWnd->MDIActivate(this);

// refresh MDI Window menu

::SendMessage(pParentWnd->m_hWndMDIClient, WM_MDIREFRESHMENU, 0, 0) ; }

ASSERT(hWnd == m_hWnd); return TRUE;

}

fCMDIChJldWnd::OnCreate)



Похожие статьи: ,

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

class CDocView : public CView {

DECLARE_DYNCREATE ( CDocView ) public:

CDocView();

virtual -CDocView();

>;

IMPLEMENT_DYNCREATE( CDocView, CView )

CDocView::CDocView()

{

}

CDocView::-CDocView()

{

}

Кроме этого, я изменил метод lnitlnstance() класса CDocViewl:

BOOL CDocViewlApp::Initlnstance() {

#ifdef _AFXDLL

Enable3dControls(); #else

Enable3dControlsStatic(); #endif

LoadStdProfileSettings(); CDocTemplate* pDocTemplate;

pDocTemplate = new CMultiDocTemplate( IDR_DOCUMENT,

RUNTIME__CLASS ( CDoc ) , RUNTIME_CLASS( CMDIChildWnd ), RUNTIME_CLASS( CDocView ) ) ; AddDocTemplate( pDocTemplate ); CMainFrame* pMainFrame = new CMainFrame; pMainFrame->LoadFrame( IDR_RESOURCE ); m_pMainWnd = pMainFrame; pMainFrame->ShowWindow(m_nCmdShow ); pMainFrame->UpdateWindow();

return TRUE;

}

Когда я постарался откомпилировать эту программу, я получил сообщение о том, что у класса, наследуемого от CView, не опреде­лен метод OnDraw(). Ну, конечно же! Откуда же наш произвольный класс окна представления будет знать, каким образом ему необхо­димо перерисовываться? Давайте опять постараемся рассуждать логически. Какое сообщение получает окно, когда ему нужно пе­рерисовать себя? Правильно, WM_PAINT. Но для обработки со­общения WM_PAINT объекты MFC используют метод OnPaintQ.

Не является исключением и объекты класса CView и унаследо­ванных от него. Исходный код этого метода находится в файле viewcore.cpp:

void CView::OnPaint() {

// standard paint routine CPaintDC dc(this); OnPrepareDC(&dc); OnDraw(&dc);

}

Очевидно, что перерисовка осуществляется методом OnDraw(). Взглянем на исходный код этого метода, который также находится в файле viewcore.cpp:

void CView::OnDraw(CDC*)

{

}

To, что этот метод не делает ничего, подтверждает нашу догад­ку о том, что нам нужно переписать именно этот метод. Что ж опи­сание нашего класса CDocView придется немного изменить. Те­перь оно будет выглядеть так:

class CDocView : public CView {

DEСLARE_DYNCREATE ( CDocView ) public:

CDocView();

virtual ~CDocView(); void OnDraw( CDC* pDC );

};

Естественно, нам придется переопределить и добавить в про­грамму метод OnDravtr(). Пока этот метод нас не очень интересует, поэтому оставим его пустым:

void CDocView::OnDraw( CDC* pDC )

{

}

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

Любознательный читатель уже, наверное, приготовил мне во­прос: да, конечно, все что рассказывается, это верно, но каким же образом создается окно представления и как оно относится к окну фрейма и документу?



Похожие статьи: , ,

Но вернемся от нашей программы к методу CDocument.On-OpenDocument(). Естественно, что после того, как мы считали со­держимое архива в поля нашего документа, архив и файл нашей программе больше не нужны и мы их можем спокойно закрыть, что и происходит. Для этого используются методы CArchive::Close() и CDocument::ReleaseFile().

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



Документ, естественно, рождается из файла, поэтому первым делом метод, вызывая GetFile(), открывает файл, имя которого, как я уже говорил, он получил в качестве аргумента. Затем начинается самое интересное. На основе файла создается объект класса САг-chive, т. е. АРХИВ. В архитектуре «документ/представление» счи­тается, что программист должен иметь возможность легко сохра­нять созданные в памяти структуры данных в дисковом файле, после чего он должен иметь возможность вновь считать их из фай­ла. Архив позволяет программисту читать и записывать в файл не просто некоторые объемы информации, а ОБЪЕКТЫ всевозмож­ных типов! Именно поэтому его использование в большинстве слу­чаев представляется оправданным. И дальше вызывается метод Serialize(), который работает уже не с файлом, а с архивом! По умолчанию этот метод не делает ничего. Естественно, откуда про­грамме знать, что и как программисту захотелось сохранить в ар­хиве? А для программиста здесь раздолье! Можно проверить фор­мат открытого файла, определить, при необходимости, его струк­туру, выполнить все мыслимые и немыслимые действия. Для того чтобы продемонстрировать работу метода Serialize(), давайте по­пробуем открыть файл в нашей программе и считать его в буфер. Внесем небольшие изменения в наш класс документа – добавим два поля, в которые запишем, во-первых, размер файла, а второй будет являться указателем на буфер, в который мы будем читать файл. После внесенных изменений наша программа выглядит сле­дующим образом:

#include "stdafx.h" #include <afxcview.h> #include "resource.h"

// Объявляем класс приложения, его поля и методы.

class CDoc : public CDocument {

DECLARE_DYNCREATE( CDoc ) int nFileLength; void* pMyFile; public: CDocO ;

void Serialize ( CArchive &ar ); virtual -CDoc();

}/

• IMPLEMENT_DYNCREATE( CDoc, CDocument )

CDoc::CDoc()

{

}

CDoc::-CDoc()

{

}

void CDoc::Serialize( CArchive &ar ) {

if( ar.IsStoring() )

{

}

else {

// Определяем длину файла.

nFileLength = ar.GetFile()->GetLength();’

TRACE( _T( "File length = %d.\n"), nFileLength’ );

// Выделяем буфер в памяти, в который будем считывать файл. pMyFile = new chart nFileLength ];

// Считываем файл в буфер.

ar.Read( pMyFile, nFileLength ); }

}

class CDocViewlApp : public CWinApp {

public:

CDocViewlApp(); protected:

afx_msg void OnFileOpen(); virtual BOOL Inifrlnstance(); DEC LARE_ME S S AGE_MA P()

};

CDocViewlApp::CDocViewlApp()

{

}

void CDocViewlApp::OnFileOpen()

{

}

class CMainFrame : public CMDIFrameWnd {

DECLARE_DYNAMIC( CMainFrame ); public:

CMainFrame();

>;

IMPLEMENT_DYNAMIC (CMainFrame, CMDIFrameWnd)

CMainFrame::CMainFrame()

{

}

BOOL CDocViewlApp::Initlnstance() {

#ifdef _AFXDLL

Enable3dControls(); #else

Enable3dControlsStatic(); #endif

LoadStdProfileSettings(); CDocTemplate* pDocTemplate;

pDocTemplate = new CMultiDocTemplate( IDR_DOCUMENT,

RUNTIME_CLASS( CDoc ) , RUNTIME_CLASS( CMDIChildWnd ), NULL );

AddDocTemplate( pDocTemplate ); CMainFrame* pMainFrame = new CMainFrame; pMainFrame->LoadFrame ( IDR__RESOURCE ) ; m_pMainWnd = pMainFrame; pMainFrame->ShowWindow(m_nCmdShow ); pMainFrame->UpdateWindow();

return TRUE;

}

BEGIN_MESSAGE_MAP( CDocViewlApp, CWinApp )

ON_COMMAND( ID_FILE_OPEN, CWinApp::OnFileOpen ) END_MESSAGE_MAP()

CDocViewlApp theApp;

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



Похожие статьи: , , ,

Я надеюсь, что читатель помнит о том, что пока при создании шаблона нашего документа указатель на информацию времени исполнения класса окна представления равен нулю. Следова­тельно, пока мы можем на некоторое время оставить вопрос о создании окна представления и вернуться в метод CMDIChildWnd::LoadFrame().

Из текста метода CMDIChildWnd::LoadFrame() кроме сказанно­го выше становится понятным и назначение начальной подстроки строкового ресурса – эта подстрока определяет заголовок созда­ваемого окна фрейма.

Теперь, когда мы знаем все о том, как происходит создание нового документа и создание нового фрейма, нам придется вернуться к методу ь CMultiDocTemplate::OpenDocumentFile() и продолжить его рассмотрение на имя откры­ваемого файла. Текст этого метода, приведенный ниже, можно най­ти в файле doccore.cpp:

BOOL CDocument::OnOpenDocument(LPCTSTR IpszPathName) {

if (IsModifiedO )

TRACEO("Warning: OnOpenDocument replaces an unsaved

document.\n") ;

CFileException fe;

CFile* pFile = GetFile(IpszPathName,

CFile::modeRead|CFile::shareDenyWrite,

&fe) ; if (pFile == NULL) {

ReportSaveLoadException(IpszPathName,

return FALSE;

DeleteContents ();

&fe, FALSE,

AFX_IDP FAILED TO_OPEN_DOC);

SetModifiedFlag(); // dirty during de-serialize

CArchive loadArchive(pFile,

CArchive::load | CArchive:rbNoFlushOnDelete); loadArchive.m_pDocument = this; loadArchive.m_bForceFlat = FALSE; TRY {

CWaitCursor wait;

if (pFile->GetLength() != 0)

Serialize(loadArchive); // load me

loadArchive.Close(); ReleaseFile(pFile, FALSE);

}

CATCH_ALL(e) {

ReleaseFile(pFile, TRUE);

DeleteContents(); // remove failed contents

TRY {

ReportSaveLoadException(IpszPathName, e, FALSE,

AFX_IDP_FAILED_TO_OPEN_DOC);

}

END_TRY

DELETE_EXCEPTION(e); return FALSE;

}

END_CATCH_ALL

SetModifiedFlag(FALSE); // start off with unmodified

return TRUE;

}

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



Похожие статьи: ,

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

Сначала определяется класс окна. В том случае, если класс окна до сих пор не определен (и ТОЛЬКО в этом случае), то полю Ipsz­Class присваивается значение _afxWndFrameOrView. Что находит­ся в этой переменной, станет ясным после того, как мы взглянем на участок кода, находящийся в файле afximpl.h:

// special AFX window class name mangling

#ifndef _UNICODE #define _UNICODE_SUFFIX #else

#define _UNICODE_SUFFIX _TPu") #endif

#ifndef _DEBUG #define DEBUG SUFFIX

#else

#define _DEBUG_SUFFIX _T(M") #endif

#ifdef _AFXDLL #define _STATIC_SUFFIX #else

#define _STATIC_SUFFIX _T(^s") #endif

#define AFX_WNDCLASS(s) \ _TPAfx") _T(s) _T("42") _DEBUG_SUFFIX

#define AFX_WND #define AFX_WNDCONTROLBAR #define AFX_WNDMDIFRAME #define AFX_WNDFRAMEORVIEW #define AFX WNDOLECONTROL

STATIC SUFFIX UNICODE SUFFIX

AFX_WNDCLASS("Wnd") AFX_WNDCLASSrControlBar") AFX_WNDCLASS("MDIFrame") AFX_WNDCLASS("FrameOrView") AFX_WNDCLASSrOleControl")

С другой стороны, в файле wincore.cpp мы увидим:

const TCHAR _afxWnd[] = AFX_WND;

const TCHAR _afxWndControlBar[] = AFX_WNDCONTROLBAR; const TCHAR _afxWndMDIFrame[] = AFX_WNDMDIFRAME; const TCHAR _afxWndFrameOrView[] = AFX_WN D FRAME ORVIEW; const TCHAR _afxWnd01eControl[] = AFX_WNDOLECONTROL;

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

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



Похожие статьи:

Уважаемый читатель, обратите, пожалуйста, внимание, на тот факт, что сразу после создания структуры cs типа CREATESTRUCT все поля этой структуры обнуляются, в том числе обнуляется и поле IpszClass. В таком случае возникает законный вопрос – какого же класса окно будет создано? Ответ на этот вопрос можно найти в ме­тоде CFrameWnd::PreCreateWindow():

Пример

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)

{

if (cs.IpszClass == NULL) {

VERIFY (Af xDef erRegisterClass (AFX_WNDFRAMEORVIEW_REG) ) ; cs.IpszClass = _afxWndFrameOrView; // COLOR_WINDOW background }

if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4) cs.style |= FWS_PREFIXTITLE;

if (afxData.bWin4)

cs.dwExStyle |= WS_EX_CLIENTEDGE;

return TRUE;

}



Похожие статьи:

Для только что созданного объекта осуществляется вызов метода CMDIChildWnd::LoadFrame(). При этом в качестве аргументов ме­тоду передаются пресловутый идентификатор ресурсов, набор сти­лей окна (не объекта, а окна!), которое будет создано, указатель на родительское окно и, наконец, указатель на структуру типа Ccreate-Context, которую мы рассмотрели чуть раньше. Здесь стоит заме­тить, что по умолчанию стилями окна являются WSJDVERLAPPED-WINDOW и FWS_ADDTOTITLE. Исходный текст этого метода на­ходится в файле winmdi.cpp:

Пример

BOOL CMDIChildWnd::LoadFrame(UINT nIDResource,

DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)

{

// only do this once

ASSERT_VALID_IDR(nIDResource) ;

ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource);
ASSERT(m_hMenuShared == NULL);
// only do once
m_nIDHelp = nIDResource;

// ID for help context (+HID_BASE_RESOURCE)

// parent must be MDI Frame (or NULL for default)

ASSERT(pParentWnd == NULL ||

pParentWnd->IsKindOf(RUNTIME_CLASS(CMDIFrameWnd))); // will be a child of MDIClient

ASSERT(!(dwDefaultStyle & WS_POPUP));

dwDefaultStyle |= WS_CHILD;

// if available – get MDI child menus from doc template
ASSERT(m_hMenuShared == NULL);
// only do once

CMultiDocTemplate* pTemplate; if (pContext != NULL &&

(pTemplate = (CMultiDocTemplate*)pContext->

m_pNewDocTemplate) != NULL)

{

ASSERT_KINDOF(CMultiDocTemplate, pTemplate); // get shared menu from doc template

m_hMenuShared = pTemplate->m_hMenuShared; m_hAccelTable = pTemplate->m_hAccelTable;

}

else {

TRACEO(“Warning: no shared menu/acceltable for MDI Child

window.\n”);

// if this happens, programmer must load these manually }

CString strFullString, strTitle;

if (strFullString.LoadString(nIDResource))

AfxExtractSubString(strTitle, strFullString, 0); // first sub-string

ASSERT(m_hWnd == NULL);

if (!Create(GetlconWndClass(dwDefaultStyle, nIDResource), strTitle, dwDefaultStyle, rectDefault,

(CMDIFrameWnd*)pParentWnd, pContext))

{

return FALSE; // will self destruct on failure normally

// it worked ! return TRUE;

}

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

Этот метод непосредственно создает окно фрей­ма. Но до того момента, пока окно фрейма будет создано, проис­ходит еще несколько интересных вещей. Например, перед вызо­вом метода Create() вызывается метод CFrameWnd::GetlconWnd-Class(). В качестве аргументов этому методу передаются стиль окна и опять-таки идентификатор ресурсов. Текст этой функции нахо­дится в файле winfrm.cpp:

LPCTSTR CFrameWnd::GetlconWndClass(DWORD dwDefaultStyle,

UINT nIDResource)

{

ASSERT_VALID_IDR(nIDResource); HINSTANCE hlnst =

AfxFindResourceHandle(MAKEINTRESOURCE(nIDResource),

RT_GROUP_ICON); HICON hlcon = ::LoadIcon(hlnst,

MAKEINTRESOURCE(nIDResource));

if (hlcon != NULL) {

CREATESTRUCT cs;

memset(&cs, 0, sizeof(CREATESTRUCT));

cs.style = dwDefaultStyle;

PreCreateWindow(cs); // will fill IpszClassName with default WNDCLASS name // ignore instance handle from PreCreateWindow.

WNDCLASS wndcls;

if (cs.IpszClass != NULL &&

GetClassInfo(AfxGetlnstanceHandle(), cs.IpszClass,

&wndcls) && wndcls.hlcon != hlcon)

{

// register a very similar WNDCLASS

return AfxRegisterWndClass(wndcls.style,

wndcls.hCursor, wndcls.hbrBackground, hlcon);

}

}

return NULL; // just use the default

}

Так… Оказывается, в состав ресурсов, передаваемых шаблону документа, может входить и иконка, которая будет использоваться при отображении окна фрейма! Функция пытается загрузить икон­ку из ресурсов, и, если иконка не загрузилась, то ничего не проис­ходит и метод Create() использует данные, принятые по умолча­нию, загрузилась, Если же иконка загрузилась нормально, то MFC, естественно, регистрирует класс окна, которому принадлежит икон­ка. После этого вызывается метод PreCreateWindow(), который мы можем использовать для того, чтобы произвести какие-то дейст­вия ДО создания окна.



Похожие статьи: ,

Пока мы видим, что сразу после проверки того, что указатель на документ и идентификатор ресурсов ненулевые, создается струк­тура типа CCreateContext. Описание этой структуры находится в файле afxext.h:

struct CCreateContext // Creation information structure // All fields are optional and may be NULL

{

// for creating.,new views CRuntimeClass* m_pNewViewClass;

// runtime class of view to create or NULL CDocument* m_pCurrentDoc;

// for creating MDI children (CMDIChildWnd::LoadFrame) CDocTemplate* m_pNewDocTemplate;

// for sharing view/frame state from the // original view/frame

CView* m_pLastView; CFrameWnd* m_pCurrentFrame;

// Implementation CCreateContext();

};

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



Похожие статьи: ,