CWinApp 雑記 |
Last Updated: Jan.5,2007 |
使用しているコンパイラは、Microsoft Visual C++ 6.0 です。これ以外のバージョンでは記述と食い違うことがあるかも知れません。 |
MFC アプリケーションでは、ひとつのドキュメントに対してひとつの拡張子しか扱えず、ファイルオープンダイアログでフィルタを選択することが出来ません。例えば、以下のようなリソースを定義して、
IDS_APP_FILTER "テキスト ファイル (*.txt)|*.txt|HTML ファイル (*.html;*.htm)|*.html;*.htm|すべてのファイル (*.*)|*.*||"
というフィルタを使用できるようにしたいと思います。なお、以下の説明では、
としています。
■ 既存のファイルを開く場合
CWinApp::OnFileOpen のソースでは単に CDocManager::OnFileOpen を呼び出し、CDocManager::OnFileOpen では、CDocManager::DoPromptFileName と CWinApp::OpenDocumentFile を呼び出しています。この CDocManager::DoPromptFileName がファイルオープンダイアログを表示しているようです。
そこでとりあえず、CMyApp::OnFileOpen を以下のようにしておきます。CMyApp::DoMyPromptFileName については後ほど説明します。
void CMyApp::OnFileOpen() { CString newName; if (!DoMyPromptFileName(newName, AFX_IDS_OPENFILE, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, NULL)) return; OpenDocumentFile(newName); }
■ ファイルを保存する場合
ここで、CDocManager::DoPromptFileName が呼び出されている MFC のソースの個所を検索してみますと、上記以外では、CWinApp::DoPromptFileName だけです。しかも、CDocManager::DoPromptFileName を呼び出しているだけの言わば「仲介関数」です。また、CWinApp::DoPromptFileName が呼び出されている個所も CDocument::DoSave からだけなので修正するには好都合です。
既存のファイルを開く | ファイルを保存 |
---|---|
↓ CWinApp::OnFileOpen |
↓ CDocument::DoSave |
↓ CDocManager::OnFileOpen |
↓ CWinApp::DoPromptFileName |
↓ CDocManager::DoPromptFileName |
保存を司っているのは、CDocument::DoSave なので、この(仮想)関数をオーバーライドします。コードは CDocument::DoSave を丸写しにして、CWinApp::DoPromptFileName の部分を CMyApp::DoMyPromptFileName に置き換えます。
BOOL CMyDoc::DoSave(LPCTSTR lpszPathName, BOOL bReplace) { CString newName = lpszPathName; if (newName.IsEmpty()) { CDocTemplate* pTemplate = GetDocTemplate(); ASSERT(pTemplate != NULL); newName = m_strPathName; if (bReplace && newName.IsEmpty()) { newName = m_strTitle; int iBad = newName.FindOneOf(_T(" #%;/\\")); if (iBad != -1) newName.ReleaseBuffer(iBad); CString strExt; if (pTemplate->GetDocString(strExt, CDocTemplate::filterExt) && !strExt.IsEmpty()) { ASSERT(strExt[0] == '.'); newName += strExt; } } // ** 以下を修正 ** if (!((CMyApp*)AfxGetApp())->DoMyPromptFileName(newName, bReplace ? AFX_IDS_SAVEFILE : AFX_IDS_SAVEFILECOPY, OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, FALSE, pTemplate)) return FALSE; // ** ここまで ** } CWaitCursor wait; if (!OnSaveDocument(newName)) { if (lpszPathName == NULL) { TRY { CFile::Remove(newName); } CATCH_ALL(e) { TRACE0("Warning: failed to delete file after failed SaveAs.\n"); do { e->Delete(); } while (0); // ** エラーが出るので修正 ** } END_CATCH_ALL } return FALSE; } if (bReplace) SetPathName(newName); return TRUE; }
■ DoMyPromptFileName の実装
いよいよ、CMyApp::DoMyPromptFileName を実装します。CDocManager::DoPromptFileName は、ドキュメントテンプレート文字列からフィルタを作成して、ファイルオープンダイアログを表示するだけの単純なもので、同じ動作を実装してやればうまくいきそうです。もちろん、フィルタは独自のものを使いますが。ちなみに引数の最後の pTemplate は実はもう不要になっています。
BOOL CMyApp::DoMyPromptFileName( CString& fileName, UINT nIDSTitle, DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate) { CFileDialog dlgFile(bOpenFileDialog, _T("txt")); CString title; VERIFY(title.LoadString(nIDSTitle)); dlgFile.m_ofn.Flags |= lFlags; CString strFilter; VERIFY(strFilter.LoadString(IDS_APP_FILTER)); // ** 独自のもの ** int nCount = strFilter.Replace(_T('|'), _T('\0')); dlgFile.m_ofn.nMaxCustFilter = nCount / 2; dlgFile.m_ofn.lpstrFilter = strFilter; dlgFile.m_ofn.lpstrTitle = title; dlgFile.m_ofn.lpstrFile = fileName.GetBuffer(_MAX_PATH); int nResult = dlgFile.DoModal(); fileName.ReleaseBuffer(); return nResult == IDOK; }
こっちの方がソースもすっきりしていて気分がいいですね。
Back | Copyright (c) 2007 AOK. All Rights Reserved. |