DOCUMENT:Q200421 31-JUL-2001 [visualc] TITLE :HOWTO: Enhance File Open Dialog with Multiple Extension Filters PRODUCT :Microsoft C Compiler PROD/VER:winnt:5.0,6.0 OPER/SYS: KEYWORDS:kbCmnDlg kbCmnDlgFileO kbDlg kbMFC KbUIDesign kbVC500 kbVC600 kbGrpDSMFCATL kbDialog ====================================================================== ------------------------------------------------------------------------------- The information in this article applies to: - The Microsoft Foundation Classes (MFC), included with: - Microsoft Visual C++, 32-bit Enterprise Edition, version 5.0 - Microsoft Visual C++, 32-bit Professional Edition, version 5.0 - Microsoft Visual C++, 32-bit Enterprise Edition, version 6.0 - Microsoft Visual C++, 32-bit Professional Edition, version 6.0 - Microsoft Visual C++, 32-bit Learning Edition, version 6.0 ------------------------------------------------------------------------------- SUMMARY ======= When you use the Open File common dialog class, CFileDialog, you can specify filters containing multiple extensions. In such a case, when the user enters the file name without an extension, the Open File common dialog class looks for a file with the specified file name, or a file name obtained by concatenating the file name and the .one default extension (specified in the lpstrDefExt field of the OPENFILENAME structure). This article shows you how to enhance this behavior. After the user specifies a file name and clicks OK, the enhanced dialog class looks for a file that matches the specified file name. If none is found, the new dialog class tries to build file names by successively concatenating the user specified file name with the extensions from the current filter. If a file exists that matches the new file name, this file name is accepted. MORE INFORMATION ================ The class behaves as described in the SUMMARY section if the following three conditions are satisfied: 1. The open file dialog box is used to open a file, not save one. 2. The flag OFN_ALLOWMULTISELECT is not specified in the m_ofn.Flags member variable. 3. The m_ofn.lpstrDefExt variable specifies a NULL default extension. To achieve the goals specified in the SUMMARY section of this article, a CFileDialog-derived class has to be declared. The core of the solution is in providing an override for the OnFileNameOK() function. Step-by-Step Procedure ---------------------- 1. Using ClassWizard, add a new class derived from CFileDialog, and name it CMultiExtFilterFileDialog. 2. Using ClassWizard, add a new BOOL variable, m_bFileMustExist, to the new class. 3. Add the following line to your implementation file: #define WM_SETUP_FLAGS WM_APP+0x500 Your dialog uses this as a private internal message. 4. Add overrides for the DoModal() and OnFileNameOK() functions. 5. In the dialog implementation file, define the DoModal() override as follows: int CMultiExtFilterFileDialog::DoModal() { // If opening a file, and multi select is not enabled, // and no default extension is specified: if (m_bOpenFileDialog && !(m_ofn.Flags & OFN_ALLOWMULTISELECT) && !m_ofn.lpstrDefExt) { m_bFileMustExist = m_ofn.Flags & OFN_FILEMUSTEXIST; m_ofn.Flags &= ~OFN_FILEMUSTEXIST; } return CFileDialog::DoModal(); } 6. In the dialog implementation file, define the OnFileNameOK() override as follows: BOOL CMultiExtFilterFileDialog::OnFileNameOK() { // If saving, or if multi select is enabled, or a default extension // is available, preserve original functionality: if (!m_bOpenFileDialog || (m_ofn.Flags & OFN_ALLOWMULTISELECT) || (m_ofn.lpstrDefExt)) return CFileDialog::OnFileNameOK(); CString strFName = GetPathName(); WIN32_FIND_DATA fd; // Use if the file exists as specified in the file name edit box: if (::FindFirstFile(strFName, &fd) != INVALID_HANDLE_VALUE) { // Accept file name: return 0; } // Use if a file extension was specified: if (strFName.ReverseFind('.') > strFName.ReverseFind('\\')) { // Reject file name: return 1; } CString strMultiExt, strExt; // Get current filter extensions: AfxExtractSubString(strMultiExt, m_ofn.lpstrFilter, 2*m_ofn.nFilterIndex - 1, (TCHAR)'\0'); // For every extension in the filter: for (int i = 0; AfxExtractSubString(strExt, strMultiExt, i, (TCHAR)';'); i++) { // No '*' is allowed in filter extensions: if (_tcsrchr((LPCTSTR)strExt + 1, (TCHAR)'*')) continue; if (INVALID_HANDLE_VALUE != ::FindFirstFile(strFName+((LPCTSTR)strExt+1), &fd)) { unsigned int flen = _tcslen(m_ofn.lpstrFile); // If the buffer is not big enough, ignore this extension: if (flen + strExt.GetLength() > m_ofn.nMaxFile) continue; _tcscpy(m_ofn.lpstrFile + flen, (LPCTSTR)strExt+1); // Accept the file name: return 0; } } // To get FileMustExist validation, reset the flag: if (m_bFileMustExist) { // Set the file exist check back to the original: m_ofn.Flags |= OFN_FILEMUSTEXIST; // Get default processing (for instance, the file must exist // message box): PostMessage(WM_KEYDOWN, (WPARAM)(VK_RETURN), (LPARAM)(1)); // Reset the file exist check: PostMessage(WM_SETUP_FLAGS); return 1; } else return 0; } 7. Finally, provide a handler for the user message specified in step 3: a. In your header file, in the declaration of multiExtFilterFileDialog, add the following lines: class CMultiExtFilterFileDialog: public CFileDialog { // ... afx_msg LRESULT OnSetupFlags(WPARAM, LPARAM); // <-- Add this DECLARE_MESSAGE_MAP() }; b. In your implementation file, add the following code: BEGIN_MESSAGE_MAP(CMultiExtFilterFileDialog, CFileDialog) //{{AFX_MSG_MAP(CMultiExtFilterFileDialog) //}}AFX_MSG_MAP ON_MESSAGE(WM_SETUP_FLAGS, OnSetupFlags) END_MESSAGE_MAP() LRESULT CMultiExtFilterFileDialog::OnSetupFlags(WPARAM, LPARAM) { if (m_bFileMustExist) m_ofn.Flags &= ~OFN_FILEMUSTEXIST; return 0; } Once these steps are completed, you can use the dialog as follows: TCHAR szFilters[] = _T("Text Files (*.txt;*.log;*.bak)\0*.txt;*.log;*.bak\0") _T("C++ Files (*.cc;*.cpp;*.h;*.hpp)\0*.cc;*.cpp;*.h;*.hpp\0"); CMultiExtFilterFileDialog ofdlg(TRUE); ofdlg.m_ofn.lpstrFilter = szFilters; if (ofdlg.DoModal() == IDOK) { AfxGetApp()->m_pDocManager->OpenDocumentFile(ofdlg.m_ofn.lpstrFile); } REFERENCES ========== The following article in the Microsoft Knowledge Base provides an excellent sample of how to customize common file dialogs: OfnKing Demonstrates CFileDialog Customization Additional query words: ====================================================================== Keywords : kbCmnDlg kbCmnDlgFileO kbDlg kbMFC KbUIDesign kbVC500 kbVC600 kbGrpDSMFCATL kbDialog Technology : kbAudDeveloper kbMFC Version : winnt:5.0,6.0 Issue type : kbhowto ============================================================================= THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY. Copyright Microsoft Corporation 2001.