C++中的窗口打开文件对话框

4

我有C ++ 代码显示对话框选择文件。 我希望用户只能选择指定类型的文件。 我的对话框可以显示指定类型的文件,但是用户可以在“文件名”中输入其他类型的文件,例如我的图片。

enter image description here

那么,我该如何让用户只输入lpstrFilter中指定的文件名和搜索类型文件呢?或者我可以禁用文件名框吗?

这是我的代码:

const wchar_t* ChooserFile(const char* typeFile)
{
    try
    {
        ZeroMemory( &sfn , sizeof( sfn));
        sfn.lStructSize = sizeof ( sfn );
        sfn.hwndOwner = NULL ;
        wchar_t w_syFile[MAX_PATH];
        //mbstowcs(w_syFile, syFile, strlen(syFile)+1);//Plus null
        size_t convertedChars = 0;
        mbstowcs_s(&convertedChars, w_syFile, MAX_PATH, syFile, _TRUNCATE);
        sfn.lpstrFile = w_syFile ;
        sfn.lpstrFile[0] = _T('\0');
        sfn.nMaxFile = sizeof( syFile );

        //TypeFile
        sfn.lpstrFilter = TEXT("Microsoft Office Word Documents (*.xlsx)\0*.XLSX\0");

        sfn.nFilterIndex =1;
        sfn.lpstrFileTitle = NULL ;
        sfn.nMaxFileTitle = 0 ;
        sfn.lpstrInitialDir=NULL;

        //sfn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT|OFN_EXPLORER | OFN_ENABLEHOOK ;
        sfn.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST|OFN_NOVALIDATE|OFN_HIDEREADONLY  ;
        if (GetOpenFileName( &sfn ) != TRUE)
        {
            wstrPathFile = TEXT("");
            return wstrPathFile.c_str();
        }

        DWORD  retval=0;
        //BOOL   success; 
        TCHAR  buffer[BUFSIZE]=TEXT(""); 
        TCHAR  buf[BUFSIZE]=TEXT(""); 
        TCHAR** lppPart={NULL};

        wchar_t wstrPath[BUFSIZE];
        retval = GetFullPathNameW(sfn.lpstrFile,sfn.nMaxFile,wstrPath,lppPart);
        if (retval==0)
        {
            wstrPathFile = TEXT("");
            return wstrPathFile.c_str();
        }
        std::wstring s(wstrPath);
        wstrPathFile = s;
        wcout<<wstrPathFile<<endl;
        return wstrPathFile.c_str();
    }
    catch (...)
    {
        PrintToFile("ChooserFile","Error");
        wstrPathFile = TEXT("");
        return wstrPathFile.c_str();
    }
}

2
使用FILEOKSTRING回调消息来拒绝你不喜欢的文件名。 - nwp
1
如果你的目标是Vista或更高版本,你应该使用通用项目对话框。然后你可以使用IFileDialogEvents::OnFileOk回调方法。如果你喜欢文件名,返回S_OK,如果不喜欢则返回S_FALSE - theB
谢谢 @nwp,但我不知道如何使用FILEOKSTRING。你能给我一个例子吗? - TuanVietNam
谢谢theB,但我想在Win XP中使用它!有什么想法吗? - TuanVietNam
2个回答

4
我希望用户只能选择指定类型的文件。但是,你无法通过编辑控件来阻止用户选择任何他们喜欢的文件。因此,你应该让他们这样做,而是验证文件名是否符合你的要求。
你有几个选项可供选择:
1. 让对话框返回,如果文件名不符合你的要求,则向用户显示错误对话框,让他们知道出了什么问题。 2. 在`OPENFILENAME`结构体的`lpfnHook`成员中提供一个钩子过程。当用户尝试接受文件时,它将发送一个CDN_FILEOK通知消息。在响应该消息时执行验证。如果文件名不符合要求,请显示相应的消息,并返回非零值以强制对话框保持打开状态。

为了让用户更不容易选择非法文件名,还有一个选项:调整文件名编辑控件的自动完成功能(参见使用自动完成)。 - IInspectable
在OPENFILENAME结构体的lpfnHook成员中提供一个钩子过程。当用户尝试接受文件时,它将收到一个CDN_FILEOK通知消息。请在回应该消息时执行验证。如果文件名不符合要求,请显示相应的消息并返回非零值以强制对话框保持打开状态。 =>谢谢David,你能给我一个示例吗? - TuanVietNam
我相信你可以自己完成这个任务。 - David Heffernan

0

你的代码注释掉了 OFN_EXPLOREROFN_ENABLEHOOK 标志,所以你肯定已经知道 资源管理器风格的钩子 的存在。正如其他人告诉你的那样,你可以使用该钩子来捕获 CDN_FILEOK 通知以接受/拒绝所选文件名。例如:

UINT_PTR CALLBACK MyOFNHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
    if (uiMsg == WM_NOTIFY)
    {
        LPOFNOTIFY ofn = (LPOFNOTIFY) lParam;    
        if (ofn->hdr.code == CDN_FILEOK)
        {
            LPOPENFILENAMEW lpOFN = (LPOPENFILENAMEW) ofn->lpOFN;

            LPWSTR lpExt = PathFindExtensionW(lpOFN->lpstrFile);
            if (lstrcmpiW(lpExt, L".XLSX") != 0)
            {
                SetWindowLongPtr(hdlg, DWL_MSGRESULT, 1);
                return 1;
            }
        }
    }

    return 0;
}

std::wstring ChooserFile(const char* typeFile)
{
    OPENFILEAMEW sfn = {0};
    wchar_t w_syFile[MAX_PATH+1] = {0};
    size_t convertedChars = 0;

    sfn.lStructSize = sizeof(sfn);
    sfn.hwndOwner = NULL;
    mbstowcs_s(&convertedChars, w_syFile, MAX_PATH, syFile, _TRUNCATE);
    sfn.lpstrFile = w_syFile;
    sfn.nMaxFile = MAX_PATH;

    //TypeFile
    sfn.lpstrFilter = L"Microsoft Office Word Documents (*.xlsx)\0*.XLSX\0";

    sfn.nFilterIndex = 1;
    sfn.lpstrFileTitle = NULL;
    sfn.nMaxFileTitle = 0;
    sfn.lpstrInitialDir = NULL;

    sfn.lpfnHook = &MyOFNHookProc;

    sfn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOVALIDATE | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLEHOOK;

    if (!GetOpenFileNameW(&sfn))
        return L"";

    WCHAR szPath[MAX_PATH+1] = {0};

    DWORD retval = GetFullPathNameW(sfn.lpstrFile, MAX_PATH, szPath, NULL);
    if ((retval == 0) || (retval > MAX_PATH))
        return L"";

    std::wstring wstrPath(szPath, retval);
    std::wcout << wstrPath << endl;
    return wstrPath;
}

非常感谢你,Remy。已经生效了。你能告诉我如何将此对话框设置为始终最前吗?我尝试过搜索,但没有找到任何信息! - TuanVietNam
你可以在钩子内部访问对话框的HWND(GetParent(hdlg)),因此您可以调用SetWindowPos()将其设置为HWND_TOPMOST。但是为什么要这样做呢?将文件对话框置于所有运行窗口之上不是标准的UI实践。如果您只想确保对话框不会落后于特定窗口的zorder,则应该设置sfn.hwndOwner字段。 - Remy Lebeau
不要将其置于最顶层。设置对话框的所有者。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接