2020-11-30 01:04 阅读 0

Multiple file selection in nana::filebox

Am I merely overlooking this feature/option? I often utilize a file dialog's ability to open multiple (but not necessarily all) files from a given folder (e.g. using the Shift/Ctrl keys in MSWin).

Feature request:

nana::filebox with option to select single file or multiple files (selected files or entire folder) when is_open_mode == true. I believe the implementation in MSW is: IFileOpenDialog.

Perhaps a member function (::files()?) could return a vector of file paths?

Open multiple files in VS


  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

7条回答 默认 最新

  • weixin_39851977 weixin_39851977 2020-11-30 01:04

    Talking about nana's implementation for Windows, nana::filebox::show() does not use COM based IFileOpenDialog yet. nana::folderbox::show() uses it though.

    nana::filebox::show() still uses outdated GetOpenFileName.


    点赞 评论 复制链接分享
  • weixin_39851977 weixin_39851977 2020-11-30 01:04

    OPENFILENAMEW struct is used with outdated GetOpenFileName and OFN_ALLOWMULTISELECT must be set to the structure's Flags member to allow multiple selection of files.

    In the case of COM based IFileOpenDialog, FOS_ALLOWMULTISELECT should be set with IFileDialog::SetOptions method to allow multiple file selection.

    点赞 评论 复制链接分享
  • weixin_39799565 weixin_39799565 2020-11-30 01:04

    Yeah, I ended up with this for testing. It uses ATL's smart-pointers & seems to work fine, but the MinGW & OSX snippets that Nana would need are beyond me.


    #include <exception>
    #include <vector>
    #include <string>
    namespace ms
        struct UtilizeCOM
                // From MSDN: "it is a good idea to set the COINIT_DISABLE_OLE1DDE flag in the dwCoInit
                //  parameter. Setting this flag avoids some overhead associated with Object Linking and
                //  Embedding (OLE) 1.0, an obsolete technology"
                if (FAILED(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)))
                    throw std::runtime_error("Couldn't initialize Windows' (OLE-less) COM");
            ~UtilizeCOM() { ::CoUninitialize(); }
            UtilizeCOM(const UtilizeCOM&)           = delete;
            UtilizeCOM& operator=(const UtilizeCOM&)    = delete;
        //  e.g.: FileFilter filter{ L"JPEG Files", L"*.jpeg; *.jpg" };
        struct FileFilter {
            // Friendly (short) descriptor of the filter
            std::wstring descriptor;
            // Semi-colon delimited extensions (including wildcards [see example above])
            std::wstring extensions;
        std::wstring PopupOpenFile(
            const std::vector<FileFilter>& file_filters = {});
        std::vector<std::wstring> PopupOpenFiles(
            const std::vector<FileFilter>& file_filters = {});
        std::wstring PopupSaveFile(
            const std::vector<FileFilter>& file_filters = {},
            const std::wstring& default_file_extension = {});


    #include "ms.h"
    #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
    #define NOMINMAX        // Exclude Windows' horrid preemptor of std::min/max
    #include <Windows.h>        // lots of stuff
    #include <atlbase.h>        // CComPtr, CComHeapPtr
    #include <vector>
    using std::vector;
    #include <string>
    using std::wstring;
    #include <exception>
    using std::exception;
    vector<wstring> local_PopupOpenFiles(bool allowMulti_, const vector<ms::FileFilter>& filters_)
        // Create the dialog object
        CComPtr<IFileOpenDialog> dlg;
        if (FAILED(dlg.CoCreateInstance(__uuidof(FileOpenDialog))))
            throw exception("Couldn't create file open dialog");
        // Set some options
        DWORD options;
        if (FAILED(dlg->GetOptions(&options)))
            throw exception("Couldn't retrieve default options");
        if (allowMulti_)
            options |= FOS_ALLOWMULTISELECT;
        if (FAILED(dlg->SetOptions(options)))
            throw exception("Couldn't set additional options");
        // Set file type options to display
        if (!filters_.empty())
            vector<COMDLG_FILTERSPEC> filters;
            for (const auto& filter : filters_)
                filters.push_back({ filter.descriptor.c_str(), filter.extensions.c_str() });
            if (FAILED(dlg->SetFileTypes(static_cast<unsigned int>(filters.size()), filters.data())))
                throw exception("Couldn't set file filter types");
        // Display the dialog & wait for user to signal completion
        switch (dlg->Show(nullptr))
            case S_OK: break;
            case HRESULT_FROM_WIN32(ERROR_CANCELLED): return {};
            default: throw exception("Couldn't display modal dialog");
        // Populate wstring vector with user's selection(s)
        CComPtr<IShellItemArray> filePaths;
        if (FAILED(dlg->GetResults(&filePaths)))
            throw exception("Couldn't fetch dialog results");
        DWORD count;
        if (FAILED(filePaths->GetCount(&count)))
            throw exception("Couldn't count results");
        vector<wstring> filenames;
        for (DWORD i = 0; i < count; ++i)
            CComPtr<IShellItem> filePath;
            if (FAILED(filePaths->GetItemAt(i, &filePath)))
                throw exception("Couldn't get file selection");
            CComHeapPtr<wchar_t> wsFilePath;
            if (FAILED(filePath->GetDisplayName(SIGDN_FILESYSPATH, &wsFilePath)))
                throw exception("Couldn't get name of selection");
            filenames.push_back({ wsFilePath.m_pData });
        return filenames;
    wstring ms::PopupOpenFile(const vector<ms::FileFilter>& filters_ /*= {}*/)
        auto result{ local_PopupOpenFiles(false, filters_) };
        // Handle if user pressed cancel
        if (result.empty()) return {};
        // Check for unexpected behavior
        if (result.size() > 1)
            throw exception("Encountered unexpected issue upon single file selection");
        return result.front();
    vector<wstring> ms::PopupOpenFiles(const vector<ms::FileFilter>& filters_ /*= {}*/)
        return result{ local_PopupOpenFiles(true, filters_) };
    wstring ms::PopupChooseSaveFile(const vector<ms::FileFilter>& filters_, const wstring& defaultExt_)
    { … }
    点赞 评论 复制链接分享
  • weixin_39851977 weixin_39851977 2020-11-30 01:04

    Yeah, I ended up with this for testing. It uses ATL's smart-pointers & seems to work fine, but the MinGW & OSX snippets that Nana would need are beyond me.

    Thank you for the code. It looks very handy. I use Windows Runtime C++ Template Library (WRL) instead of ATL lately. https://github.com/Microsoft/DirectXTK/wiki/ComPtr

    Nana has own filebox implementation so expanding it would be ideal to support multi-platforms. https://github.com/cnjinhao/nana/blob/dbd8a4a6910aa3b81b77989a2d1866fdb26e4f6f/source/gui/filebox.cpp#L45 But doing so is not trivial...

    点赞 评论 复制链接分享
  • weixin_39799565 weixin_39799565 2020-11-30 01:04

    No sweat; and, thanks, I'll give WRL a look. I do realize Nana has its own filebox implementation. I was merely offering my work-around as a potential jumping-off point for adding multiselect functionality to filebox (and, perhaps, getting rid of GetOpenFileName()). As mentioned previously, I would have no clue about MinGW or OSX. Take care.

    点赞 评论 复制链接分享
  • weixin_39851977 weixin_39851977 2020-11-30 01:04

    I created PR #349 to address this issue. In the PR, I added below 2 methods for the feature.

            const ::std::vector<::std::string>& files() const;
            void allow_multi_select(bool allow);

    nana::filebox::files method returns full file paths of selected files. nana::filebox::allow_multi_select method activates/deactivates the feature.

    I hope the interface change will suffice.

    点赞 评论 复制链接分享
  • weixin_39949954 weixin_39949954 2020-11-30 01:04

    The multiple file selection is supported now, based on 's #349

    点赞 评论 复制链接分享