2.2--当用户每次创建一个新文档时,如何管理 多文档模板 而不用弹出对话框?.......包含2.6的内容

      一旦用户开始使用多于一个的文档模板对象,MFC就会弹出一个New对话框,让用户选择需要的文档模板类型。当在应用程序的InitInstance()函数中调用AddDocTemplate()来注册多个文档模板对象时,MFC无法知道应该使用哪一个文档模板对象来完成用户的“File->New”请求。因此,MFC弹出一个对话框,该对话框列出了 各种注册过的文档模板对象 以允许用户指出应该使用哪一个来调用CDocTemplate::OpenDocumentFile(),来创建一个新文档,以下MFC的源码,有助于理解这种逻辑:

 

当应用程序启动时,在InitInstance()中由标准AppWizard生成的代码ProcessShellCommand()将直接调用CWinApp::OnFileNew(),调用顺如下:

CWinApp::OnFileNew()->CDocManager::OnFileNew() Void CWinApp::OnFileNew() { if(m_pDocManager!=NULL) { m_pDocManager->OnFileNew();//m_pDocManager为CDocManager类型 } } CDocManager::OnFileNew() { //1、如果没有注册文档模板,提示错误 if( m_templateList.IsEmpty() ) { TRACE0("ERROR:no document templates registered with CWinApp./n"); AfxMessageBox("AFX_IDP_FAILED_TO_CREATE_DOC"); return ; } //2、默认选择第一个文档模板对象 CDocTemplate* pTemplate=(CDocTemplate*)m_templateList.GetHead(); //3、如何有多个注册过的文档模板对象,则弹出“New”对话框 if( m_templateList.GetCount() > 1) { //显示对话框提示用户从多个文档模板中选择 CNewTypeDlg dlg(&m_templateList); int nID=dlg.DoModal(); if(nID==IDOK) pTemplate=dlg.m_pSelectedTemplate;//4、存储所选择的模板对象的指针 else return; } //5、使用所选择的 文档模板对象 来创建新文档、框架窗口和视图 pTemplate->OpenDocumentFile(NULL); } 

应用程序启动时,如果不想弹出对话框(直接用用户希望的 文档模板 创建新的空文档) 供用户选择文档模板,遵循以下步骤:

 

第一步:在应用程序的InitInstance()函数中创建各种文档模板对象,并把指向每个对象的指针存储在 应用程序类 的成员变量中。

 

第二步:使用ClassWizard在 应用程序类中 增加“File->New”菜单命令的句柄,在句柄内,从存储的 文档模板对象的指针 中选择你希望的文档模板对象指针,传递一个NULL参数来请求创建一个新(空)文档,如下:

void CTestApp::OnFileNew()
{
       m_ptDefaultTemplate->OpenDocumentFile(NULL);
}

上面解决方案第二步时,标准的MFC消息映射机制 能够保证 在用户选择File->New菜单命令时调用自己的句柄( CMyApp::OnFileNew() ),而不是默认的CWinApp::OnFileNew()函数。

上面代码段中的CNewTypeDlg类在其表中只显示那些文档模板资源ID的fileNewName字串不为空的文档模板对象。如果只有一个文档模板对象满足这一条件,那么就自动返回该对象,作为所“选择”的模板对象,而不显示对话框。为了更好的理解这种行为,看一下CNewTypeDlg()的伪代码,程序清单如下:

BOOL CNewTypeDlg::OnInitDialog() { //1、------得到对话框上的列表框的指针 CListBox* pListBox = (CListBox*)GetDlgItem(AFX_IDC_LISTBOX); ASSERT(pListBox != NULL); // 使用文档模板填充列表 pListBox->ResetContent(); //2、------遍历应用程序的文档模板表 POSITION pos = m_pList->GetHeadPosition(); //通过名称在表中增加所有的CDocTemplates while (pos != NULL) { //3、------对每一个文档模板对象 CDocTemplate* pTemplate = (CDocTemplate*)m_pList->GetNext(pos); ASSERT_KINDOF(CDocTemplate, pTemplate); //4、在把字符串添加到列表框中之前,检查“fileNewName”字串是否为空 CString strTypeName; if (pTemplate->GetDocString(strTypeName, CDocTemplate::fileNewName) && !strTypeName.IsEmpty()) { // 增加到列表框 int nIndex = pListBox->AddString(strTypeName); if (nIndex == -1) { EndDialog(-1); return FALSE; } pListBox->SetItemDataPtr(nIndex, pTemplate); } } int nTemplates = pListBox->GetCount(); if (nTemplates == 0) { //5、如果为空,则list->error! TRACE0("Error: no document templates to select from!/n"); EndDialog(-1); // abort } else if (nTemplates == 1) { // 6、如果只有一个,则自动选择这个文档模板对象 并赋值给item,不显示对话框 m_pSelectedTemplate = (CDocTemplate*)pListBox->GetItemDataPtr(0); ASSERT_VALID(m_pSelectedTemplate); ASSERT_KINDOF(CDocTemplate, m_pSelectedTemplate); EndDialog(IDOK); // 完成 } else { // 把所选择的模板对象设置为第一个 pListBox->SetCurSel(0); } //至此,将出现New对话框 return CDialog::OnInitDialog(); } 

 

      每次当启动一个标准AppWizard生成的应用程序时,MFC就会自动的创建一个新(空)文档,同时还有关联的视图和框架窗口。你可能会很想知道,这个空文档是在代码的什么位置被创建的。同时,如果在应用程序启动时不初始创建任何文档?

如果不想再在应用程序启动时打开一个新(空)文档应遵循以下步骤:

在InitInstance()函数中修改代码如下: BOOL CTestApp::InitInstance() { // ...... // 解析 标准shell命令、DDE、打开文档 的命令行 CCommandLineInfo cmdInfo; //取消默认的OnFileNew调用 cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing; ParseCommandLine(cmdInfo); // 分派命令行中确定的命令 if (!ProcessShellCommand(cmdInfo)) return FALSE; //...... } 

解释:

从MFC4.0版本开始,在应用程序启动时,程序在何处初始化新文档就不是非常明显了。你可能理解最好要调用CWinApp::OnFileNew(),但是这种调用来自于何处呢?实际上,导致CWinApp::OnFileNew()调用的整个过程都隐藏在这3行代码中:

CCommandLineInfo cmdInfo;

ParseCommandLine(cmdInfo);//遍历命令行,搜寻以字符'/'或'-'开头的选项,根据找到的命令行参数,设置cmdInfo对象的成员变量

if(!processShellCommand(cmdInfo))//该函数根据cmdInfo中的m_nShellCommand变量 进行不同的函数调用

       return false;

      1、以上代码在堆栈中创建了一个名为cmdInfo的CCommandLineInfo对象,MFC将用该对象分析各种转换开关,并把对象的信息存储在该cmdInfo对象的public成员变量中,其中的各种转换开关可能出现在应用程序的命令行中。

      CCommandLineInfo类的定义如下:

      class CCommandLineInfo

      {

           //......

           enum {FileNew , FileOpen , FilePrint , FilePrintTo , FileDDE , AppUnregistr ,

                 FileNothing=-1 }m_nShellCommand;

          //......

      };

 

      2、cmdInfo对象创建时调用该类的(CCommandLineInfo)构造函数,该构造函数初始化m_nShellCommand成员,赋值CCommandLineInfo::FileNew;

 

      3、CWinApp::ParseCommandLine(cmdInfo)函数遍历命令行,搜寻以字符‘/’或 ‘-’开头的选项。对每一个找到的命令行参数

      ParseCommandLine()就对CmdInfo对象应用CCommandLineInfo::ParaseParam()方法,该方法根据各种命令行参数设置CmdInfo对象的成员变量,这些参数由PraseCommandLine()传送。只有在命令行中找到实际参数时,才改变m_nShellCommand成员,否则m_nShellCommand保留它上一次的值(即在构造函数中设置的值CCommandLineInfo::FileNew)。

 

      4、CWinApp::ParseCommandLine(cmdInfo)函数返回后CmdInfo对象作为一个参数调用ProcessShellCommand(cmdInfo)函数,该函数执行基于cmdInfo.m_nShellCommand值的相应动作。最常发生的动作如下表所示:

cmdInfo.m_nShellCommand的数值                               ProcessShellCommand()进行的函数调用

CCommandLineInfo::FileNew                                      CWinApp::OnFileNew();//这个函数不是虚函数

CCommandLineInfo::FileOpen                                     OpenDocumentFile(cmdInfo.m_strFileName);

CCommandLineInfo::FilePrint                                      m_pMainWnd->SendMessage(WM_COMMAND,

CCommandLineInfo::FilePrintTo                                        ID_FILE_PRINT_DIRECT);

CCommandLineInfo::FileNothing                                 None

因此,可以看出cmdInfo对象的成员变量m_nShellCommand在第2步中,被初始化为了FileNothing,最终导致了ProcessShellCommand()内部调用什么也不做,正好达到了我们的目的。

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(框架,null,存储,文档,mfc,templates)