【问题标题】:Remove focus from SAVE button in OPENFILENAME win32?从 OPENFILENAME win32 中的“保存”按钮中删除焦点?
【发布时间】:2021-12-25 11:58:15
【问题描述】:

问题:

如果用户按住“输入”键盘按钮并打开 OPENFILENAME 另存为对话框,它将自动保存文件 - 对话框只会闪烁。

期望的结果:

用户按住“输入”键盘按钮,打开 OPENFILENAME 另存为对话框,什么都没有发生。他需要单击“保存”按钮或再次单击“输入”键盘按钮来保存文件。

我当前的代码:

OPENFILENAME ofn;
TCHAR szFile[260] = { 't','e','s','t'}; // example filename

// Initialize OPENFILENAME
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;    
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
//Files like: (ALL - *.*), (Text - .TXT)
ofn.lpstrFilter = _T("All\0*.*\0Text\0*.TXT\0");
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

if (GetSaveFileName(&ofn) == TRUE)
{
    // file saved
}

可能的解决方案:

  • 当 ofn.lpstrFile 为空时,什么也不做;在那里无法保存文件 没有文件名
  • 当 ofn.lpstrFile 建议文件名时,关闭“保存”按钮的焦点或以某种方式忽略按钮输入。

我试图这样做但失败了,我是 CPP 的初学者 :(

感谢您的帮助

【问题讨论】:

  • 第一个项目符号似乎足够了:只是不要设置lpstrFile,“文件名”文本框不会预先填充默认选项,“保存”按钮不会启用,因此用户将无法通过对话框盲目地。要执行其他任何操作,您需要挂钩对话框,并从保存按钮中删除默认按钮样式。这是可能的,但比初学者想要的要复杂。
  • 也许您可以使用 IFileDialog 代替(GetSaveFileName 是遗留 API,实际上在内部指向 IFileDialog),使用 IFileDialog::SetOptions(FOS_OKBUTTONNEEDSINTERACTION)(未经测试)。 docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/…
  • @SimonMourier 该文档专门为FOS_OKBUTTONNEEDSINTERACTION 说明了这一点:“注意:禁用“确定”按钮不会阻止按 Enter 键提交对话框。
  • @RemyLebeau - 我知道,但我不是 100% 清楚这意味着什么,这就是为什么它需要一些测试
  • "如果用户按住 "enter"..." - 如果用户按住电源按钮会发生什么?你能保护它吗?顺便说一句,ofn.nMaxFile 必须是 260 而不是 sizeof szFile

标签: c++ winapi


【解决方案1】:

防止数据丢失的简单解决方案是添加OFN_OVERWRITEPROMPT 标志。如果建议的名称尚未作为文件存在,这并不能防止问题的发生。

要与对话框进行实际交互,您需要OFN_ENABLEHOOK 和一个钩子函数。当您收到WM_NOTIFY 时,如果没有足够的时间,您可以处理CDN_FILEOK 以阻止建议的名称,或者可以更改CDN_INITDONE 中的焦点。

无论哪种方式,您都必须注意您正在更改通用对话框的工作方式,这可能会激怒一些用户。

这是一种方法。使对话框恢复正常的实际延迟是您必须自己决定的。

const int btnid = 1337;

void CALLBACK resetsavedlgdefpush(HWND hWnd, UINT Msg, UINT_PTR idEvent, DWORD Time)
{
    KillTimer(hWnd, idEvent);
    HWND hDlg = GetParent(hWnd);
    UINT id = LOWORD(SendMessage(hDlg, DM_GETDEFID, 0, 0));
    if (id == btnid)
    {
        SendMessage(hDlg, DM_SETDEFID, IDOK, 0);
    }
}

UINT_PTR CALLBACK mysavehook(HWND hWndInner, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    if (Msg == WM_NOTIFY)
    {
        OFNOTIFY*pOFN = (OFNOTIFY*) lParam;
        if (pOFN->hdr.code == CDN_INITDONE)
        {
            HWND hDlg = GetParent(hWndInner);
            CreateWindowEx(0, TEXT("BUTTON"), 0, BS_DEFPUSHBUTTON|BS_TEXT|WS_CHILD|WS_VISIBLE, 0, 0, 0, 0, hWndInner, (HMENU) btnid, 0, 0);
            SendMessage(hDlg, DM_SETDEFID, btnid, 0);
            PostMessage(hDlg, DM_SETDEFID, btnid, 0);
            int keydelay = 0;
            SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &keydelay, 0);
            SetTimer(hWndInner, 0, (250 * ++keydelay) * 5, resetsavedlgdefpush);
        }
    }
    return 0;
}

...
ofn.Flags = OFN_PATHMUSTEXIST|OFN_EXPLORER|OFN_OVERWRITEPROMPT|OFN_ENABLESIZING|OFN_ENABLEHOOK;
ofn.lpfnHook = mysavehook;
MessageBox(ofn.hwndOwner, TEXT("Hold enter to test..."), 0, 0);
if (GetSaveFileName(&ofn) == TRUE) ...

【讨论】:

  • 似乎他们想要的是从挂钩过程中的“保存”按钮中删除 BS_DEFPUSHBUTTON 样式以响应对话框的创建。这将阻止 键自动关闭对话框。你不想摆弄焦点;无论如何,这不会解决问题,因为“保存”按钮不需要焦点让 调用它。
  • @CodyGray 确实取决于您强制聚焦的位置。如果您聚焦另一个按钮,[Enter] 将不会保存。话虽如此,删除 x * keyboard-repeat-time 的默认值保存可能不是一个坏主意。
  • “如果您关注另一个按钮...” 是的,当然,但是您要关注其他哪个按钮?您真正拥有的唯一选项是“取消”,当它处于活动状态时按 也会产生不良后果。如果问题中描述的场景(用户按住 键)重复自身并将焦点放在“取消”按钮上,则对话框只会闪烁,一旦打开就会关闭。有点像用户的老笑话,当面对“你想退出吗?”对话框,选择“取消”只是为了让它消失。
  • @Cody 还有一个工具栏,虽然我不记得工具栏是否吃掉了默认按钮。您还可以添加自己的 0x0 像素“什么都不做”按钮。
  • 是的,@Maciej,这种类型的挂钩会禁用主题。这就是为什么您需要改用IFileSaveDialog 的原因,它提供了所有相同的功能,因此可以直接替代,甚至不会影响用户界面。 (或者,你可以安装一个 CBT 挂钩或其他东西,但是……哎呀,那更难看。)
猜你喜欢
  • 2012-12-29
  • 2011-02-19
  • 2012-11-05
  • 1970-01-01
  • 1970-01-01
  • 2021-06-04
  • 1970-01-01
  • 1970-01-01
  • 2014-02-20
相关资源
最近更新 更多