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. |