Tips2 複数の拡張子を使用する方法

前のTips 次のTips


 MFCで作成される通常のアプリケーションはドキュメント-ビュー構造を持っています。 ドキュメントはひとつの拡張子を持っていて、ファイルオープンダイアログではその拡張子の ファイルのみが表示されるので、ユーザの好みに応じてファイル 種別を選択することができません。これは、独自のファイルフォーマットに加え一般的な ファイルフォーマット(たとえばテキストファイル)を取り扱いたいといった極めて一 般的な要求があるときに困ってしまいます。
 本Tipsでは CDocManager をサブクラス化することで、上記の機能をインプリメントする 方法について説明します。

■ MFCにおけるファイルオープン・セーブダイアログ

 MFCではファイルオープン・セーブの時にコモンダイアログを用いてファイル名 の取得を行いますが、この部分の処理は、"mfc\src\Docmgr.cpp": BOOL CDocManager:: DoPromptFileName(CString& fileName, UINT nIDSTitle, DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate) で行われます。この中で、通常はアプリケーションが保持する ドキュメントテンプレートから各ドキュメントクラスを参照し、ドキュメントクラスが もつフィルターストリングをコモンダイアログのフィルターストリングに追加していきます。 その後すべてのファイル(*.*)のためのフィルタストリングを追加し、コモンダイアログ をオープンしています。
 この DoPromptFileName は仮想関数なので、これをオーバーライドすることで フィルタストリングを追加することができます。

■ インプリメント手順

 インプリメント手順は以下のようになります。

  1. CWinApp の派生クラスで AddDocTemplate をオーバーライドし、ドキュメント マネージャを自分で定義するクラス(CMyDocManager)に変更する。
  2. ドキュメントマネージャをサブクラス化して DoPromptFileName をオーバーライドし、 フィルタストリングに適当なものを加える。

以下、詳細に述べます(ここでは CWinApp の派生クラスを CMyApp としている)。

  1. CWinApp の派生クラスで AddDocTemplate をオーバーライドし、ドキュメントマネージャ を自分で定義するクラス(CMyDocManager)に変更する。
  2. CWinApp オブジェクトは CDocManager クラスのメンバ変数を持つ。
    class CWinApp : public CWinThread
    {
    	DECLARE_DYNAMIC(CWinApp)
    public:
    		CDocManager* m_pDocManager;
    	:
    };
    

    m_pDocManager の初期化は CMyApp::InitInstance() からコールされる CMyApp::AddDocTemplate() で行われる。したがって、クラスの定義に AddDocTemplate を加え、ポインタの初期化部分で クラス名を派生クラスに置き換える。

    class CMyApp : public CWinApp
    {
    public:
    	:
    	virtual void AddDocTemplate(CDocTemplate* pTemplate);	// 追加
    	:
    };
    
    void CMyApp::AddDocTemplate(CDocTemplate* pTemplate)	// 追加
    {
    	if (m_pDocManager == NULL)
    		m_pDocManager = new CMyDocManager;	
    			// クラス名を CDocManager から変更
    	m_pDocManager->AddDocTemplate(pTemplate);
    }
    
  3. ドキュメントマネージャをサブクラス化して DoPromptFileName をオーバーライドオーバーライドし、 フィルタストリングに適当なものを加える。
  4. CDocManager の派生クラスを定義する。

    //
    //		DoPromptFileName をオーバーライドするためのクラス
    //
    class CMyDocManager : public CDocManager {
    	DECLARE_DYNAMIC(CMyDocManager)
    
    public:
    	// helper for standard commdlg dialogs
    	virtual BOOL DoPromptFileName(CString& fileName, UINT nIDSTitle,
    			DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate);
    };
    

    インプリメントを行う。
    コードのほとんどはMFCのソースそのままである。ここで追加した部分はボールドのところのみ。
    ここでは、無条件に "Text Files (*.txt)\0*.txt\0" を追加している。 (ほんとはストリングリソースにすべき)

    //---------------------------------------------------------------------------
    //		Implementation of CMyDocManager class
    //---------------------------------------------------------------------------
    
    IMPLEMENT_DYNAMIC( CMyDocManager, CDocManager )
    
    static void AppendFilterSuffix(CString& filter, OPENFILENAME& ofn,
    	CDocTemplate* pTemplate, CString* pstrDefaultExt)
    {
    	ASSERT_VALID(pTemplate);
    	ASSERT_KINDOF(CDocTemplate, pTemplate);
    
    	CString strFilterExt, strFilterName;
    	if (pTemplate->GetDocString(strFilterExt, CDocTemplate::filterExt) &&
    	 !strFilterExt.IsEmpty() &&
    	 pTemplate->GetDocString(strFilterName, CDocTemplate::filterName) &&
    	 !strFilterName.IsEmpty())
    	{
    		// a file based document template - add to filter list
    #ifndef _MAC
    		ASSERT(strFilterExt[0] == '.');
    #endif
    		if (pstrDefaultExt != NULL)
    		{
    			// set the default extension
    #ifndef _MAC
    			*pstrDefaultExt = ((LPCTSTR)strFilterExt) + 1;  // skip the '.'
    #else
    			*pstrDefaultExt = strFilterExt;
    #endif
    			ofn.lpstrDefExt = (LPTSTR)(LPCTSTR)(*pstrDefaultExt);
    			ofn.nFilterIndex = ofn.nMaxCustFilter + 1;  // 1 based number
    		}
    
    		// add to filter
    		filter += strFilterName;
    		ASSERT(!filter.IsEmpty());  // must have a file type name
    		filter += (TCHAR)'\0';  // next string please
    #ifndef _MAC
    		filter += (TCHAR)'*';
    #endif
    		filter += strFilterExt;
    		filter += (TCHAR)'\0';  // next string please
    		ofn.nMaxCustFilter++;
    	}
    }
    
    BOOL CMyDocManager::DoPromptFileName(CString& fileName, UINT nIDSTitle, DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate)
    {
    	CFileDialog dlgFile(bOpenFileDialog);
    
    	CString title;
    	VERIFY(title.LoadString(nIDSTitle));
    
    	dlgFile.m_ofn.Flags |= lFlags;
    
    	CString strFilter;
    	CString strDefault;
    	if (pTemplate != NULL)
    	{
    		ASSERT_VALID(pTemplate);
    		AppendFilterSuffix(strFilter, dlgFile.m_ofn, pTemplate, &strDefault);
    	}
    	else
    	{
    		// do for all doc template
    		POSITION pos = m_templateList.GetHeadPosition();
    		BOOL bFirst = TRUE;
    		while (pos != NULL)
    		{
    			CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetNext(pos);
    			AppendFilterSuffix(strFilter, dlgFile.m_ofn, pTemplate,
    				bFirst ? &strDefault : NULL);
    			bFirst = FALSE;
    		}
    	}
    
    	// append the "*.txt" text files filter		//  追加した部分
    	strFilter += _T("Text Files (*.txt)");
    	strFilter += (TCHAR)'\0';   // next string please
    	strFilter += _T("*.txt");
    	strFilter += (TCHAR)'\0';   // last string
    	dlgFile.m_ofn.nMaxCustFilter++;
    
    	// append the "*.*" all files filter
    	CString allFilter;
    	VERIFY(allFilter.LoadString(AFX_IDS_ALLFILTER));
    	strFilter += allFilter;
    	strFilter += (TCHAR)'\0';   // next string please
    #ifndef _MAC
    	strFilter += _T("*.*");
    #else
    	strFilter += _T("****");
    #endif
    	strFilter += (TCHAR)'\0';   // last string
    	dlgFile.m_ofn.nMaxCustFilter++;
    
    	dlgFile.m_ofn.lpstrFilter = strFilter;
    #ifndef _MAC
    	dlgFile.m_ofn.lpstrTitle = title;
    #else
    	dlgFile.m_ofn.lpstrPrompt = title;
    #endif
    	dlgFile.m_ofn.lpstrFile = fileName.GetBuffer(_MAX_PATH);
    
    	BOOL bResult = dlgFile.DoModal() == IDOK ? TRUE : FALSE;
    	fileName.ReleaseBuffer();
    	return bResult;
    }
    

前のTips 次のTips 津田伸秀 のホームページに戻る。

Last Updated on 22-Aug-1996, Copyright (c) 1996 by Nobuhide Tsuda, All Right Reserved.
このホームページに関するご質問、ご要望、バグレポート等は  Nobuhide_Tsuda@jsn.justnet.or.jp までメールをいただければ幸いです。