0

============================== UPDATE ==============================

I've tried using what Barrnet Chou suggested. I've created SelectDialog.h and SelectDialog.cpp files with the implementation mentioned in the comment, I've included SelectDialog.h in the main .cpp file and I've tried using the code from my main .cpp file as follows:

CSelectDialog ofd(TRUE, _T("*.*"), NULL, OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT,
                                            _T("All files and folders(*.*)|*.*||"));
    CString path, nFile, info;

    if (ofd.DoModal() != IDOK)
        return;

    for (int i = 0; i < ofd.m_SelectedItemList.GetCount(); i++) {
        nFile = ofd.GetFileName();
        path = ofd.GetFolderPath();
        info = ofd.m_SelectedItemList[i].GetString();
    }

    MessageBox(path);
    MessageBox(nFile);
    MessageBox(info);

I don't know what did I do wrong, but I'm still having the same problem, I can only choose files and not folders. I've also tried adding the flag FOS_PICKFOLDERS as IInspectable mentioned when I call the constructor of ofd varibale, but it didn't help either. any ideas about what I've missed?

============================== ==============================

I'm writing a dialog based MFC application on Visual Studio 2017 in C++. I want to enable the user to choose a file or folder into which he will write later. After the user will choose the desired file or folder I want to save the path into pFile variable for later use.

I've seen some questions on this subject, but all the answers to those posts provide only one functionality - either to choose a file or a folder.

what I've tried:

Method #1

OPENFILENAME file;

    TCHAR szFileName[MAX_PATH] = _T("");

    SecureZeroMemory(&file, sizeof(file));

    file.lStructSize = sizeof(OPENFILENAME);
    file.hwndOwner = NULL;
    file.lpstrFilter = NULL;
    file.lpstrFile = szFileName;
    file.nMaxFile = MAX_PATH;
    file.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
    file.lpstrDefExt = "txt";

    GetSaveFileName(&file);
    
    pFile = file.lpstrFile;

Problem: Allows to choose only files as seen in the picture below:

enter image description here

Method #2

    CFileDialog dlgFile(TRUE);
    CString fileName;
    const int c_cMaxFiles = 100;
    const int c_cbBuffSize = (c_cMaxFiles * (MAX_PATH + 1)) + 1;
    dlgFile.GetOFN().lpstrFile = fileName.GetBuffer(c_cbBuffSize);
    dlgFile.GetOFN().nMaxFile = c_cbBuffSize;

    CString file_name, dir_path;
    if (dlgFile.DoModal() == IDOK) {
        file_name = dlgFile.GetFileName();
        dir_path = dlgFile.GetFolderPath();
        pFile = dir_path + "\\" + file_name;
    }
    fileName.ReleaseBuffer();

Problem: Allows user to select files only as seen at the picture attached to Method #1

Method #3

    LPCSTR m_strFolderPath = ("C:\\"); // Just for sample    
    LPCSTR m_strDisplayName;

    CFolderDialog dlg(_T("Dialog Title"), m_strFolderPath, this);

    if (dlg.DoModal() == IDOK)
    {
        pFile = dlg.GetFolderPath();
    }

Problem: Allows the user to choose folders only as seen in the picture below:

enter image description here

Method #4

BROWSEINFO   bi;
    ZeroMemory(&bi, sizeof(bi));
    TCHAR   szDisplayName[MAX_PATH];
    szDisplayName[0] = (TCHAR)("");

    bi.hwndOwner = NULL;
    bi.pidlRoot = NULL;
    bi.pszDisplayName = szDisplayName;
    bi.lpszTitle = _T("Please select a folder for storing received files :");
    bi.ulFlags = BIF_RETURNONLYFSDIRS;
    bi.lParam = NULL;
    bi.iImage = 0;

    LPITEMIDLIST   pidl = SHBrowseForFolder(&bi);
    TCHAR   szPathName[MAX_PATH];
    if (NULL != pidl)
    {
        BOOL bRet = SHGetPathFromIDList(pidl, szPathName);
        if (FALSE == bRet)
            return;
        AfxMessageBox(szPathName);
    }

Problem: Allows user to select folders only as seen at the picture attached to Method #3

Method #5

I've tried using IFileDialog for this purpose, but I'm not sure how to use it to choose a file or a folder. I think that's the solution I need, and I'll appreciate it if anyone could refer me to some examples for how to use it? I've seen some such as this from Microsoft Docs documentation and this one from git and also this and that one from stack overflow questions but I wasn't able to apply them. I've tried constructing something similar by using the previously mentioned codes, but I only got this far:

IFileDialog *pfd;
    IFileSaveDialog *pfsd;
    HRESULT hr = pfd->QueryInterface(&pfsd);
    LPWSTR *pszName;
    if (SUCCEEDED(hr)) {
        hr = pfd->GetFileName(pszName);
        if (SUCCEEDED(hr)) {
            MessageBox((LPCSTR)pszName);
        }
    }

I'd really appreciate If anyone could explain me how to use IFileDialog to enable the user to choose files and folders, or if there's any other solution for this problem, I'd like to try it.

Thank you.

user14092875
  • 145
  • 1
  • 1
  • 12
  • this is discussed in [link](https://stackoverflow.com/questions/1304784/cfiledialog-browse-folders/12090721) – Tom Tom Sep 14 '20 at 12:15
  • @TomTom Thank you for your reply. I've seen this post, but all the answers there allow me to choose either files or folders, I want to enable choosing both. Do you have any idea how can I do this? – user14092875 Sep 14 '20 at 13:38
  • Based on this discussion and coding I see they uses two different methods or class to accomplish this. They also shows that even MS does this way. – Tom Tom Sep 15 '20 at 09:14
  • `FOS_PICKFOLDERS` toggles between item- and folder-selection modes. Those modes are mutually exclusive for `IFileDialog` just as well. Allowing the user to select a file or a folder introduces a very confusing interface. For example, it wouldn't be obvious what double-clicking a folder should do (navigate into the directory or select the folder?). – IInspectable Sep 15 '20 at 14:27

1 Answers1

0

If you want to select files and folders at the same time, I suggest that you could refer to the following example: CSelectDialog.

Instructions:

CSelectDialog ofd(TRUE, _T("*.*"), NULL, OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT, _T("All files and folders(*.*)|*.*||") );
 
if( ofd.DoModal() != IDOK )
    return;
 
for( int i=0; i<ofd.m_SelectedItemList.GetCount(); i++ ){
    //... 
}

Detailed implementation: 1.

#pragma once
 
#include <dlgs.h> // for (MULTI)FILEOPENORD
 
// CSelectDialog
class CSelectDialog : public CFileDialog
{
    DECLARE_DYNAMIC(CSelectDialog)
 
public:
    CSelectDialog(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs
        LPCTSTR lpszDefExt = NULL,
        LPCTSTR lpszFileName = NULL,
        DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT |
                        OFN_EXPLORER & (~OFN_SHOWHELP),
        LPCTSTR lpszFilter = NULL,
        CWnd* pParentWnd = NULL);
    virtual ~CSelectDialog();
    
protected:
    virtual void OnInitDone();
    virtual void OnFolderChange();
    virtual BOOL OnFileNameOK();
    static LRESULT CALLBACK WindowProcNew(HWND hwnd,UINT message, WPARAM wParam, LPARAM lParam);
    DECLARE_MESSAGE_MAP()
 
public:
    static CString m_strCurrendDirectory;
    static CStringArray m_SelectedItemList; /*this list includes files and folders
                                            are selected by user. */
    static WNDPROC m_wndProc;
};
#include "stdafx.h"
#include "SelectDialog.h"
 
#pragma warning( push )
#pragma warning( disable : 4311 4312 )
// CSelectDialog
CString CSelectDialog::m_strCurrendDirectory;
CStringArray CSelectDialog::m_SelectedItemList;
WNDPROC CSelectDialog::m_wndProc = NULL;
 
IMPLEMENT_DYNAMIC(CSelectDialog, CFileDialog)
 
CSelectDialog::CSelectDialog(BOOL bOpenFileDialog,
                             LPCTSTR lpszDefExt,
                             LPCTSTR lpszFileName,
                             DWORD dwFlags,
                             LPCTSTR lpszFilter,
                             CWnd* pParentWnd)
                             :CFileDialog(
                             bOpenFileDialog,
                             lpszDefExt,
                             lpszFileName,
                             dwFlags | OFN_EXPLORER | OFN_HIDEREADONLY & (~OFN_SHOWHELP),
                             lpszFilter,
                             pParentWnd)
{
    dwFlags |= (OFN_EXPLORER | OFN_HIDEREADONLY & (~OFN_SHOWHELP));
};
 
CSelectDialog::~CSelectDialog()
{
};
 
BEGIN_MESSAGE_MAP(CSelectDialog, CFileDialog)
END_MESSAGE_MAP()
 
// CSelectDialog message handlers
BOOL CSelectDialog::OnFileNameOK()
{
    if (CFileDialog* pDlg = (CFileDialog*)CWnd::FromHandle(GetParent()->m_hWnd))
    {
        CWnd* pWnd = pDlg->GetDlgItem(lst2);    //getting list
        if (pWnd == NULL)
            return FALSE;
 
        m_SelectedItemList.RemoveAll();         // emptying list
        
        CListCtrl* wndLst1 = (CListCtrl*)(pWnd->GetDlgItem(1));
 
        int nSelected = wndLst1->GetSelectedCount();
        if (!nSelected)     // nothing selected -- don't retrieve list
            return FALSE;
        CString strItemText, strDirectory = m_strCurrendDirectory;
        if (strDirectory.Right(1) != _T("\\"))
            strDirectory += _T("\\");
 
        CString fileslist = _T("");
        pDlg->SendMessage(CDM_GETSPEC, (WPARAM)MAX_PATH,
            (LPARAM)fileslist.GetBuffer(MAX_PATH));
        fileslist.ReleaseBuffer();
 
        strItemText = strDirectory + fileslist;
        if(nSelected == 1 && fileslist != _T(""))
        {
            m_SelectedItemList.Add(strItemText);
            return CFileDialog::OnFileNameOK();
        }
    }
    ::MessageBeep( MB_ICONQUESTION );
    return 1; //don't let the dialog to close
};
 
void CSelectDialog::OnFolderChange()
{
    m_strCurrendDirectory = GetFolderPath();
    CFileDialog::OnFolderChange();
};
 
void CSelectDialog::OnInitDone()
{
    m_strCurrendDirectory = GetFolderPath();
    CWnd* pFD = GetParent();
 
    HideControl(edt1);
    HideControl(cmb1);
    HideControl(stc2);
 
    //HideControl(cmb13);
    //HideControl(stc3);
 
    CRect rectCancel; pFD->GetDlgItem(IDCANCEL)->GetWindowRect(&rectCancel);
    pFD->ScreenToClient(&rectCancel);
 
    CRect rectOK; pFD->GetDlgItem(IDOK)->GetWindowRect(&rectOK);
    pFD->ScreenToClient(&rectOK);
    pFD->GetDlgItem(IDOK)->SetWindowPos(0,rectCancel.left - rectOK.Width() - 5, rectCancel.top, 0,0, SWP_NOZORDER | SWP_NOSIZE);
 
    CRect rectList2; pFD->GetDlgItem(lst1)->GetWindowRect(&rectList2);
    pFD->ScreenToClient(&rectList2);
    pFD->GetDlgItem(lst1)->SetWindowPos(0,0,0,rectList2.Width(), abs(rectList2.top - (rectCancel.top - 5)), SWP_NOMOVE | SWP_NOZORDER);
 
    CRect rectStatic;pFD->GetDlgItem(stc3)->GetWindowRect(&rectStatic);
    pFD->ScreenToClient(&rectStatic);
    pFD->GetDlgItem(stc3)->SetWindowPos(0,rectCancel.left - 375,rectCancel.top + 5, rectStatic.Width(), rectStatic.Height(), SWP_NOZORDER);
 
    CRect rectEdit1;pFD->GetDlgItem(cmb13)->GetWindowRect(&rectEdit1);
    pFD->ScreenToClient(&rectEdit1);
    pFD->GetDlgItem(cmb13)->SetWindowPos(0,rectCancel.left - 320,rectCancel.top, rectEdit1.Width() - 15, rectEdit1.Height(), SWP_NOZORDER);
 
    SetControlText(stc3, _T("Item name:"));
    SetControlText(IDOK, _T("Select"));
 
    m_wndProc = (WNDPROC)::SetWindowLong(pFD->m_hWnd, GWL_WNDPROC, (long)WindowProcNew);
    pFD->CenterWindow();
};
 
LRESULT CALLBACK CSelectDialog::WindowProcNew(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if (message ==  WM_COMMAND)
    {
        if (HIWORD(wParam) == BN_CLICKED)
        {
            if (LOWORD(wParam) == IDOK)
            {
                if (CFileDialog* pDlg = (CFileDialog*)CWnd::FromHandle(hwnd))
                {
                    m_SelectedItemList.RemoveAll();         // emptying list
                    CWnd* pWnd = pDlg->GetDlgItem(lst2);    //getting list
                    if (pWnd == NULL)
                        return FALSE;
 
                    CListCtrl* wndLst1 = (CListCtrl*)(pWnd->GetDlgItem(1));
 
                    int nSelected = wndLst1->GetSelectedCount();
                    if (!nSelected)     // nothing selected -- don't retrieve list
                        return FALSE;
                    CString strItemText, strDirectory = m_strCurrendDirectory;
                    if (strDirectory.Right(1) != _T("\\"))
                        strDirectory += _T("\\");
 
                    int nItem = wndLst1->GetNextItem(-1,LVNI_SELECTED);
                    CString fileslist = _T("");
                    pDlg->SendMessage(CDM_GETSPEC, (WPARAM)MAX_PATH,
                        (LPARAM)fileslist.GetBuffer(MAX_PATH));
                    fileslist.ReleaseBuffer();
                    //   Add directory names to list
                    while((nSelected--) > 0)
                    {
                        strItemText = wndLst1->GetItemText(nItem,0);
                        strItemText = strDirectory + strItemText;
                        DWORD attr = GetFileAttributes(strItemText);
                        if((attr != 0xFFFFFFFF) && (attr & FILE_ATTRIBUTE_DIRECTORY))
                            m_SelectedItemList.Add(strItemText);                            
                        nItem = wndLst1->GetNextItem(nItem, LVNI_SELECTED);
                    }
                    //   Add FILE names to list
                    strItemText = _T("");
                    nSelected = wndLst1->GetSelectedCount();
                    if(nSelected > m_SelectedItemList.GetCount())
                    {
                        int MoreThanOnFile = fileslist.Find(_T("\""));
                        if(MoreThanOnFile != -1)
                        {
                            for(int i=0; i<fileslist.GetLength(); i++)
                                if(fileslist[i] != '\"')
                                {
                                    strItemText.AppendFormat(_T("%c"),fileslist[i]);
                                    if(fileslist[i-1] == '\"' && fileslist[i] == ' ')
                                        strItemText.Delete(strItemText.GetLength()-1);
                                }
                                else if(!strItemText.IsEmpty())
                                {
                                    m_SelectedItemList.Add((strDirectory+strItemText));
                                    strItemText.Empty();
                                }
                        }
                        else
                            m_SelectedItemList.Add(strDirectory+fileslist);
                    }
                    pDlg->EndDialog(IDOK);
                    return NULL;
                } // if IDOK
            }
        } // if BN_CLICKED
    }// if WM_COMMAND
    return CallWindowProc(m_wndProc, hwnd, message, wParam, lParam);
}


#pragma warning( pop )
Barrnet Chou
  • 1,738
  • 1
  • 4
  • 7
  • thank you very much for your detailed answer. I've tried using the code you've suggested, I've added (1) into an `SelectDialog.h` file and (2) into `SelectDialog.cpp` file, and used your instructions. I've tried to print inside the for loop in your instructions the file path and name, but when I tried running it, I wasn't able to select folders, only files. I've probably misunderstood something, do you have any idea what could've go wrong? thank you once again. – user14092875 Sep 15 '20 at 18:35
  • If you could upload your code, it will help solve the problem. Of course, you could also refer to the [source code](https://github.com/SafeJKA/RohosDiskBrowser/tree/fb955dc542e020e041ff069854ab6fb0f6c54c30). – Barrnet Chou Sep 16 '20 at 06:35
  • thank you for your reply. I've edited my post, and I've added the code I used for creating the CSelectDialog and printing the file path. perhaps you have any idea about what I've missed? – user14092875 Sep 16 '20 at 07:49
  • You could try to modify it. It may be It may be because some mfc messages are blocked. CSelectDialog::CSelectDialog(BOOL bOpenFileDialog, LPCTSTR lpszDefExt, LPCTSTR lpszFileName, DWORD dwFlags, LPCTSTR lpszFilter, CWnd* pParentWnd) :CFileDialog( bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags | OFN_EXPLORER | OFN_HIDEREADONLY & (~OFN_SHOWHELP), lpszFilter, pParentWnd, 0, FALSE) { dwFlags |= (OFN_EXPLORER | OFN_HIDEREADONLY & (~OFN_SHOWHELP)); }; – Barrnet Chou Oct 09 '20 at 01:29