一、当在MFC程序中,一个view上,根据菜单,展现不同的view 的时候。比如Outlook类似程序,根据左边的Pane,右边显示不同的view 。
如果右边要显示的是一个EditView,如何实现呢?
疑问:
1、加入新建一个类,继承之CEditView,那么需要document配合吗?
参考:http://www.skinplusplus.com/bbs/archiver/tid-114896.html
http://www.vckbase.com/study/article/vc_chap/chap5_3.htm
1、CView继承类,和其他窗口类的区别,很重要的就是对CDocument类和CFrameWnd类的操作,
2、还应注意,默认的从CView继承的类,其构造函数和析构函数是protected的,需要修改成public。
新建一个应用程序,应用程序的View继承之CEditView类;
一、假若要显示一个txt文本文件,可以使用CString存储文本文件(CString最大可以存储2G大小的内容)
并在OnUpdate()中显示文本内容
void CEditViewView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
CString content;
CStdioFile file;
CString fileName=_T("D:\\析构顺序.txt");
if (file.Open(fileName,CFile::modeRead|CFile::typeBinary))
{
TCHAR buf[512];
memset(buf,0,sizeof(buf));
int len=512-2;
LPTSTR pText=file.ReadString(buf,len); // A null character ('\0') is appended.
while (NULL!=pText)
{
content+=pText;
pText=file.ReadString(buf,len);
}
file.Close();
}
this->SetWindowText(content);
}
请注意工程的编码方式,否则某些txt文件打开会是乱码。
二、文件修改后如何保存?
1、假若什么都没做,并在view中更改了文件内容,然后点击保存(菜单或者工具栏),会触发下列流程:
(1)、保存菜单对应 ID_FILE_SAVE 消息,该消息在
ON_COMMAND(ID_FILE_SAVE, &CDocument::OnFileSave) (见doccore.cpp文件)
(2)、触发OnFileSave函数
void CDocument::OnFileSave()
{
DoFileSave();
}
(3)、触发DoFileSave函数
BOOL CDocument::DoFileSave()
{
DWORD dwAttrib = GetFileAttributes(m_strPathName);
if (dwAttrib & FILE_ATTRIBUTE_READONLY)
{
// we do not have read-write access or the file does not (now) exist
if (!DoSave(NULL))
{
TRACE(traceAppMsg, 0, "Warning: File save with new name failed.\n");
return FALSE;
}
}
else
{
if (!DoSave(m_strPathName))
{
TRACE(traceAppMsg, 0, "Warning: File save failed.\n");
return FALSE;
}
}
return TRUE;
}
(4)、触发DoSave函数
BOOL CDocument::DoSave(LPCTSTR lpszPathName, BOOL bReplace)
// Save the document data to a file
// lpszPathName = path name where to save document file
// if lpszPathName is NULL then the user will be prompted (SaveAs)
// note: lpszPathName can be different than 'm_strPathName'
// if 'bReplace' is TRUE will change file name if successful (SaveAs)
// if 'bReplace' is FALSE will not change path name (SaveCopyAs)
{
CString newName = lpszPathName;
if (newName.IsEmpty())
{
CDocTemplate* pTemplate = GetDocTemplate();
ASSERT(pTemplate != NULL);
newName = m_strPathName;
if (bReplace && newName.IsEmpty())
{
newName = m_strTitle;
// check for dubious filename
int iBad = newName.FindOneOf(_T(":/\\"));
if (iBad != -1)
newName.ReleaseBuffer(iBad);
// append the default suffix if there is one
CString strExt;
if (pTemplate->GetDocString(strExt, CDocTemplate::filterExt) &&
!strExt.IsEmpty())
{
ASSERT(strExt[0] == '.');
int iStart = 0;
newName += strExt.Tokenize(_T(";"), iStart);
}
}
if (!AfxGetApp()->DoPromptFileName(newName,
bReplace ? AFX_IDS_SAVEFILE : AFX_IDS_SAVEFILECOPY,
OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, FALSE, pTemplate))
return FALSE; // don't even attempt to save
}
CWaitCursor wait;
if (!OnSaveDocument(newName))
{
if (lpszPathName == NULL)
{
// be sure to delete the file
TRY
{
CFile::Remove(newName);
}
CATCH_ALL(e)
{
TRACE(traceAppMsg, 0, "Warning: failed to delete file after failed SaveAs.\n");
DELETE_EXCEPTION(e);
}
END_CATCH_ALL
}
return FALSE;
}
// reset the title and change the document name
if (bReplace)
SetPathName(newName);
return TRUE; // success
}
(5)、AfxGetApp()->DoPromptFileName(newName,
bReplace ? AFX_IDS_SAVEFILE : AFX_IDS_SAVEFILECOPY,
OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, FALSE, pTemplate)
弹出保存窗口
(6)、
BOOL CWinApp::DoPromptFileName(CString& fileName, UINT nIDSTitle, DWORD lFlags,
BOOL bOpenFileDialog, CDocTemplate* pTemplate)
// if pTemplate==NULL => all document templates
{
ENSURE(m_pDocManager != NULL);
return m_pDocManager->DoPromptFileName(fileName, nIDSTitle, lFlags,
bOpenFileDialog, pTemplate);
}
(7)、如果选择保存,将会触发
OnSaveDocument(newName)操作,该操作最终导致序列化。
BOOL CDocument::OnSaveDocument(LPCTSTR lpszPathName)
{
ENSURE(lpszPathName);
CFileException fe;
CFile* pFile = NULL;
pFile = GetFile(lpszPathName, CFile::modeCreate |
CFile::modeReadWrite | CFile::shareExclusive, &fe);
if (pFile == NULL)
{
ReportSaveLoadException(lpszPathName, &fe,
TRUE, AFX_IDP_INVALID_FILENAME);
return FALSE;
}
CArchive saveArchive(pFile, CArchive::store | CArchive::bNoFlushOnDelete);
saveArchive.m_pDocument = this;
saveArchive.m_bForceFlat = FALSE;
TRY
{
CWaitCursor wait;
Serialize(saveArchive); // save me
saveArchive.Close();
ReleaseFile(pFile, FALSE);
}
CATCH_ALL(e)
{
ReleaseFile(pFile, TRUE);
TRY
{
ReportSaveLoadException(lpszPathName, e,
TRUE, AFX_IDP_FAILED_TO_SAVE_DOC);
}
END_TRY
DELETE_EXCEPTION(e);
return FALSE;
}
END_CATCH_ALL
SetModifiedFlag(FALSE); // back to unmodified
return TRUE; // success
}
(8)、真正执行保存操作
void CEditViewDoc::Serialize(CArchive& ar)
{
// CEditView contains an edit control which handles all serialization
reinterpret_cast<CEditView*>(m_viewList.GetHead())->SerializeRaw(ar);
}
2、假若什么都没做,并在view中更改了文件内容,然后关闭应用程序,会触发如下流程
(1)、点击关闭,会触发VM_Close消息,并在CFrameWnd中捕获
void CFrameWnd::OnClose()
{
if (m_lpfnCloseProc != NULL)
(*m_lpfnCloseProc)(this);
// Note: only queries the active document
CDocument* pDocument = GetActiveDocument();
if (pDocument != NULL && !pDocument->CanCloseFrame(this))
{
// document can't close right now -- don't close it
return;
}
(2)、当前文档会判断是否需要可以关闭主框架
pDocument->CanCloseFrame(this)
BOOL CDocument::CanCloseFrame(CFrameWnd* pFrameArg)
// permission to close all views using this frame
// (at least one of our views must be in this frame)
{
ASSERT_VALID(pFrameArg);
UNUSED(pFrameArg); // unused in release builds
POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{
CView* pView = GetNextView(pos);
ASSERT_VALID(pView);
CFrameWnd* pFrame = pView->GetParentFrame();
// assume frameless views are ok to close
if (pFrame != NULL)
{
// assumes 1 document per frame
ASSERT_VALID(pFrame);
if (pFrame->m_nWindow > 0)
return TRUE; // more than one frame refering to us
}
}
// otherwise only one frame that we know about
return SaveModified();
}
(3)、SaveModified()判断文档是否有修改
BOOL CDocument::SaveModified()
{
if (!IsModified())
return TRUE; // ok to continue
// get name/title of document
CString name;
if (m_strPathName.IsEmpty())
{
// get name based on caption
name = m_strTitle;
if (name.IsEmpty())
ENSURE(name.LoadString(AFX_IDS_UNTITLED));
}
else
{
// get name based on file title of path name
name = m_strPathName;
AfxGetFileTitle(m_strPathName, name.GetBuffer(_MAX_PATH), _MAX_PATH);
name.ReleaseBuffer();
}
CString prompt;
AfxFormatString1(prompt, AFX_IDP_ASK_TO_SAVE, name);
switch (AfxMessageBox(prompt, MB_YESNOCANCEL, AFX_IDP_ASK_TO_SAVE))
{
case IDCANCEL:
return FALSE; // don't continue
case IDYES:
// If so, either Save or Update, as appropriate
if (!DoFileSave())
return FALSE; // don't continue
break;
case IDNO:
// If not saving changes, revert the document
break;
default:
ASSERT(FALSE);
break;
}
return TRUE; // keep going
}
请注意,switch (AfxMessageBox(prompt, MB_YESNOCANCEL, AFX_IDP_ASK_TO_SAVE))弹出对话框,询问是否保存。
case IDYES:
// If so, either Save or Update, as appropriate
if (!DoFileSave())
执行DoFileSave()操作,转到1中的流程。
请思考:
3、如果我想在保存的时候,不需要弹出询问的对话框,直接保存文件,该如何做呢?
4、为什么有的程序在退出的时候,点击关闭按钮,不会提示保存文档呢?