【问题标题】:In Win32 app the folder selection dialog closes if the folder is not selected and the user clicks OK在 Win32 应用程序中,如果未选择文件夹并且用户单击确定,则文件夹选择对话框将关闭
【发布时间】:2019-11-24 15:05:40
【问题描述】:

在我的 C++ Win 32 应用程序中,我正在创建一个对话框以使用 IFileOpenDialog 选择一个文件夹。请看下面的代码:

HRESULT DialogService::CreateDialogToPickFolder(HWND hWnd)
{
     IFileOpenDialog* pPickFolderDialog = NULL;
     IShellItem* pPickedFolder = NULL;
     HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pPickFolderDialog));

     if (SUCCEEDED(hr))
     {
         DWORD dialogOptions;
         hr = pPickFolderDialog->GetOptions(&dialogOptions);
         if (SUCCEEDED(hr))
         {
             hr = pPickFolderDialog->SetOptions(dialogOptions | FOS_PICKFOLDERS);
             if (SUCCEEDED(hr))
             {
                 hr = pPickFolderDialog->Show(hWnd);
                 if (SUCCEEDED(hr))
                 {
                     hr = pPickFolderDialog->GetResult(&pPickedFolder);
                     if (SUCCEEDED(hr))
                     {
                         PWSTR pszFolderPath = NULL;
                         hr = pPickedFolder->GetDisplayName(SIGDN_FILESYSPATH, &pszFolderPath);
                         if (SUCCEEDED(hr))
                         {
                             // Some code...
                         }
                     }
                     pPickedFolder->Release();
                 }
             }
         }
         pPickFolderDialog->Release();
    }
    return hr;
}

我无法处理以下问题。如果我没有选择文件夹(“文件夹” 字段,在对话框中,为空)并单击“选择文件夹”按钮(即确定),代码行

hr = pPickedFolder->GetDisplayName(SIGDN_FILESYSPATH, &pszFolderPath);

写 先前选择到 pszFolderPath 变量中的路径并关闭对话框。但我想要 以确保在这种情况下对话框保留在屏幕上,并且 pszFolderPath 的值保持为 0x00000000。因为只有当用户真正选择了文件夹时,对话框才应该关闭,并且所选文件夹的名称显示在对话框的“文件夹”字段中。 (当然,如果要关闭对话框,用户单击“选择文件夹”按钮。这是不言而喻的。) 在未选择文件夹时单击“确定”按钮时,如何防止对话框关闭?请帮帮我。

【问题讨论】:

  • 许多用户没有在选定的子文件夹中单击“确定”。相反,他们倾向于进入文件夹,然后单击“确定” - 您将获得当前显示的文件夹的路径(正如他们所期望的那样)。
  • 附带说明,无论pPickFolderDialog->GetResult() 成功还是失败,都会调用pPickedFolder->Release()。它需要移动到它上面的大括号内

标签: c++ winapi dialog


【解决方案1】:

OnFileOk 事件在对话框即将返回结果之前调用。它使对话框保持打开状态。如果您认为文件夹路径无效,请返回 S_FALSE 以防止对话框关闭。

    // Create an event handling object, and hook it up to the dialog.
    IFileDialogEvents *pfde = NULL;
    hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
    if (SUCCEEDED(hr))
    {
        // Hook up the event handler.
        DWORD dwCookie;
        hr = pPickFolderDialog->Advise(pfde, &dwCookie);
        if (SUCCEEDED(hr))
        {            
            // ...
            hr = pPickFolderDialog->GetOptions(&dialogOptions);
            // ...

            // Unhook the event handler.
            pfd->Unadvise(dwCookie);
        }
        pfde->Release();
    }


// IFileDialogEvents methods
IFACEMETHODIMP OnFileOk(IFileDialog * pfd) 
{ 
    // If the folder field is empty, return S_FALSE, the dialog should remain open.
    // Check folder is selected or not here.
    //return S_FALSE;

    return S_OK; 
};

可能需要OnFileOk方法中的解析操作来确认“文件夹:”字段是否为空。

参考“Common Item Dialog”。

更新: 添加代码行以检查“文件夹:”中的路径是否为空。下面是一个使用UIAutomation的例子,大家可以参考。

#include <UIAutomation.h>

IUIAutomation *pClientUIA;
IUIAutomationElement *pRootElement;

BOOL IsPathEmpty(HWND hwnd)
{
    HRESULT hr;

    hr = CoCreateInstance(CLSID_CUIAutomation, NULL, CLSCTX_INPROC_SERVER, IID_IUIAutomation, reinterpret_cast<void**>(&pClientUIA));
    if (S_OK != hr)
    {
        printf("CoCreateInstance error: %d\n", GetLastError());
        return FALSE;
    }

    hr = pClientUIA->ElementFromHandle(hwnd, &pRootElement);
    if (S_OK != hr)
    {
        printf("ElementFromHandle error: %d\n", GetLastError());
        return FALSE;
    }
    BSTR name;
    hr = pRootElement->get_CurrentClassName(&name);
    if (S_OK != hr)
    {
        printf("get_CurrentClassName error: %d\n", GetLastError());
        return FALSE;
    }
    wprintf(L"Class Name: %s\n", name);


    IUIAutomationCondition *pCondition;
    VARIANT varProp;
    varProp.vt = VT_I4;
    varProp.uintVal = UIA_EditControlTypeId;
    hr = pClientUIA->CreatePropertyCondition(UIA_ControlTypePropertyId, varProp, &pCondition);
    if (S_OK != hr)
    {
        printf("CreatePropertyCondition error: %d\n", GetLastError());
        return FALSE;
    }

    IUIAutomationElementArray *pElementFound;
    hr = pRootElement->FindAll(TreeScope_Descendants, pCondition, &pElementFound);
    if (S_OK != hr)
    {
        printf("CreatePropertyCondition error: %d\n", GetLastError());
        return FALSE;
    }

    int eleCount;
    pElementFound->get_Length(&eleCount);
    for (int i = 0; i < eleCount; i++)
    {
        IUIAutomationElement *pElement;
        hr = pElementFound->GetElement(i, &pElement);
        if (S_OK != hr)
        {
            printf("CreatePropertyCondition error: %d\n", GetLastError());
            return FALSE;
        }
        hr = pElement->get_CurrentName(&name);
        if (S_OK != hr)
        {
            printf("CreatePropertyCondition error: %d\n", GetLastError());
            return FALSE;
        }
        wprintf(L"Control Name: %s\n", name);
        OutputDebugString(name);

        if (0 == wcscmp(name, L"Folder:"))
        {
            VARIANT varPropText;
            hr = pElement->GetCurrentPropertyValue(UIA_ValueValuePropertyId, &varPropText);
            if (S_OK != hr)
            {
                printf("CreatePropertyCondition error: %d\n", GetLastError());
                return FALSE;
            }

            if (0 == wcscmp(varPropText.bstrVal, L""))
            {
                return TRUE;
            }
        }
    }

    return FALSE;
}

OnFileOk方法都可以这样IsPathEmpty方法:

    // IFileDialogEvents methods
    IFACEMETHODIMP OnFileOk(IFileDialog * pfd)
    { 
        // If the folder field is empty, return S_FALSE, the dialog should remain open.
        // Check folder is selected or not here.
        //return S_FALSE;

        IOleWindow *pWindow;
        HRESULT hr = pfd->QueryInterface(IID_PPV_ARGS(&pWindow));

        if (SUCCEEDED(hr))
        {
            HWND hwndDialog;
            hr = pWindow->GetWindow(&hwndDialog);

            if (SUCCEEDED(hr))
            {
                if (IsPathEmpty(hwndDialog))
                    return S_FALSE;
            }
            pWindow->Release();
        }

        return S_OK; 
    };

【讨论】:

  • 丽塔,我愿意:IFACEMETHODIMP DialogEventHandler::OnFileOk(IFileDialog* pfd) { IShellItem *psiResult; HRESULT hr = pfd->GetResult(&psiResult); if (SUCCEEDED(hr)) { SFGAOF 属性; hr = psiResult->GetAttributes(SFGAO_FOLDER, &attributes); if (SUCCEEDED(hr)) { if (attributes & SFGAO_FOLDER) { hr = S_OK; } 其他 { hr = S_FALSE; } } psiResult->Release(); } 返回小时; }。无论文件夹是否被选中,hr 总是 S_OK。所以它对我没有帮助。
  • @Nupogodi 通过 UI 自动化可以检查路径是否为空。我已经更新了我的答案,你可以试试看是否有帮助。
  • 丽塔,非常感谢。一切正常。你真的帮了我。对于延迟回复,我深表歉意。
  • @Nupogodi 不客气。乐意效劳。你可以accept the answer帮助更多人搜索这个问题。
猜你喜欢
  • 2011-04-29
  • 2015-11-29
  • 1970-01-01
  • 2020-11-24
  • 1970-01-01
  • 1970-01-01
  • 2017-10-21
  • 2017-11-30
  • 1970-01-01
相关资源
最近更新 更多