12

When I try to instantiate a CFileDialog object it shows both the folders and files. How do you create a CFileDialog that browses for folders alone?

Ajay
  • 18,086
  • 12
  • 59
  • 105
Owen
  • 4,063
  • 17
  • 58
  • 78

7 Answers7

19

It is very simple, really.

Use CFolderPickerDialog which is derived from the class CFileDialog!

Ajay
  • 18,086
  • 12
  • 59
  • 105
user5498719
  • 191
  • 1
  • 2
  • 1
    This class exists starting from Visual Studio 2010. we've just switched from VS2008 to VS2013, and IFileOpenDialog::SetOptions(FOS_PICKFOLDERS) surprisingly stopped working. IMHO it's against backward compatibility. – blackbada_cpp Dec 10 '15 at 14:38
11

You can't do it with CFileDialog.

Either you will use SHBrowseForFolder Function or a wrapper for it,
like CFolderDialog - Selecting Folders.

Nick Dandoulakis
  • 42,588
  • 16
  • 104
  • 136
  • 4
    `SHBrowseForFolder` is heavily outdated. From the remarks: _"For Windows Vista or later, it is recommended that you use IFileDialog with the FOS_PICKFOLDERS option rather than the SHBrowseForFolder function. This uses the Open Files dialog in pick folders mode and is the preferred implementation."_ – zett42 May 24 '18 at 12:27
  • You actually can, if you are willing to reimplement `CFileDialog` - see [here](https://stackoverflow.com/a/66049570/10571377). – Juv Feb 04 '21 at 16:25
  • @Juv, the question is about displaying in the dialog only the folders. – Nick Dandoulakis Feb 04 '21 at 20:11
7

Like someone mentioned, use CFolderPickerDialog which works great. I would like to give you example how to use it especially when using the multi select flag:

CFolderPickerDialog folderPickerDialog(initialFolder, OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_ENABLESIZING, this,
        sizeof(OPENFILENAME));

    CString folderPath;

    if (folderPickerDialog.DoModal() == IDOK)
    {

        POSITION pos = folderPickerDialog.GetStartPosition();

        while (pos)
        {
            folderPath = folderPickerDialog.GetNextPathName(pos);

        }
    }
Gautam Jain
  • 6,789
  • 10
  • 48
  • 67
6

Starting from Vista it's recommended to use IFileDialog with the FOS_PICKFOLDERS option (see msdn):

CFileDialog od(TRUE/*bOpenFileDialog*/, NULL, NULL,
      OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT , NULL, NULL, 0,
      TRUE/*bVistaStyle*/);
   IFileOpenDialog * openDlgPtr = od.GetIFileOpenDialog();
   if ( openDlgPtr != NULL )
   {
      openDlgPtr->SetOptions(FOS_PICKFOLDERS);
      openDlgPtr->Release();
   }

   od.DoModal();
blackbada_cpp
  • 422
  • 4
  • 11
  • 2
    Starting Visual Studio 2010 this will not work. Use CFolderPickerDialog instead (https://msdn.microsoft.com/ru-ru/library/dd795962%28v=vs.120%29.aspx) – blackbada_cpp Dec 10 '15 at 14:36
3

starting from windows vista,you can use the Common Item Dialog .

void CQiliRegrvDlg::OnBnClickedSelectDir()
{
HRESULT hr = S_OK;

// Create a new common open file dialog.
IFileOpenDialog *pfd = NULL;
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER,
    IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
    // Set the dialog as a folder picker.
    DWORD dwOptions;
    hr = pfd->GetOptions(&dwOptions);
    if (SUCCEEDED(hr))
    {
        hr = pfd->SetOptions(dwOptions | FOS_PICKFOLDERS);
    }

    // Set the title of the dialog.
    if (SUCCEEDED(hr))
    {
        hr = pfd->SetTitle(L"Folder");
    }
    // Show the open file dialog.
    if (SUCCEEDED(hr))
    {
        hr = pfd->Show(m_hWnd);
        if (SUCCEEDED(hr))
        {
            // Get the selection from the user.
            IShellItem *psiResult = NULL;
            hr = pfd->GetResult(&psiResult);
            if (SUCCEEDED(hr))
            {
                PWSTR pszPath = NULL;
                hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
                if (SUCCEEDED(hr))
                {
                    m_appDir = pszPath;
                    SetDlgItemText(IDC_STATIC, m_appDir);
                    CoTaskMemFree(pszPath);
                }
                psiResult->Release();
            }
        }
    }

    pfd->Release();
  }
  }
Li Kui
  • 640
  • 9
  • 21
1

Seems to me the answer you are asking for is inside the code of

CMFCPropertyGridFileProperty::OnClickButton(CPoint /*point*/)

of the

<Your Visual Studio installation folder>\VC\atlmfc\src\mfc\afxpropertygridctrl.cpp

file.

If you do not have access to the code, I will post the essential part of it:

CString strPath = m_varValue.bstrVal;
BOOL bUpdate = FALSE;

if (m_bIsFolder)
{
    if (afxShellManager == NULL)
    {
        CWinAppEx* pApp = DYNAMIC_DOWNCAST(CWinAppEx, AfxGetApp());
        if (pApp != NULL)
        {
            pApp->InitShellManager();
        }
    }

    if (afxShellManager == NULL)
    {
        ASSERT(FALSE);
    }
    else
    {
        bUpdate = afxShellManager->BrowseForFolder(strPath, m_pWndList, strPath);
    }
}
else
{
    CFileDialog dlg(m_bOpenFileDialog, m_strDefExt, strPath, m_dwFileOpenFlags, m_strFilter, m_pWndList);
    if (dlg.DoModal() == IDOK)
    {
        bUpdate = TRUE;
        strPath = dlg.GetPathName();
    }
}

As you see, Microsoft itself does not use the Cfiledialog class when wants to open a dialog for picking folders.

For using code like that, your application class MUST be derived from CWinAppEx, not CWinApp

sergiol
  • 4,122
  • 4
  • 47
  • 81
0

Actually there is a way to do this - I found it in codeguru: "Selected files and folders in CFileDialog"

If you are willing to make your own implementation of CFileDialog such as: class CMyFileDialog : public CFileDialog

You can add the following code and it should work (It is slightly different from the codeguru example):

// This code overrides the OnNotify message of the CFileDialog
// and catches the CDN_SELCHANGE, this way you can also do 
// something with the selected folders.
BOOL CMyFileDialog::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    NMHDR* pNotificationParam = (NMHDR*)lParam;

    // Check that we got to the selection change notification.
    int code = pNotificationParam->code;
    if (code == CDN_SELCHANGE)
    {
        CStringArray theSelection;

        GetListControllSelectedItems(theSelection);

        // Do as you want with theSelection.
    }

    return CFileDialog::OnNotify(wParam, lParam, pResult);
}

// The following Code is accessing the selection in the CFileDialog
// and filling a string array with the selected names
BOOL CMyFileDialog::GetListControllSelectedItems(CStringArray& selectedItemNames)
{
    BOOL rc = FALSE;
    // Get the list control of the file dialog.
    CWnd* pParentWnd = GetParent();
    CWnd* pListControlWnd = pParentWnd->GetDlgItem(lst2);
    if (pListControlWnd) {

        // Get the selection from the list control.
        CListCtrl* pListCtrl = (CListCtrl*)(pListControlWnd->GetDlgItem(1));

        UINT selectionCount = pListCtrl->GetSelectedCount();

        // When there are items selected.
        if (selectionCount) {
            rc = TRUE;
            selectedItemNames.RemoveAll();
            POSITION itemPos = pListCtrl->GetFirstSelectedItemPosition();
            while (itemPos != NULL)
            {
                int itemNum = pListCtrl->GetNextSelectedItem(itemPos);
                CString currentItemName = pListCtrl->GetItemText(itemNum, 0);
                selectedItemNames.Add(currentItemName);
            }
        }
    }
    
    return rc;
}

Note: In CFileDialog::OnFileNameChange of the Microsoft MFC documentation they actually do hint toward this solution, but without elaborating too much.

I had a problem in my very old, legacy code, where I have a customized file dialog that actually needs to save a folder!!!

After twenty two years of hardship and pain, my code is now complete...

Juv
  • 744
  • 7
  • 12