Удалить фокус с кнопки «СОХРАНИТЬ» в win32 с именем OPENFILENAME?

#c #winapi

Вопрос:

Проблема:

Если пользователь удерживает кнопку «enter» на клавиатуре и открывает диалоговое окно «Сохранить как» с именем файла, оно автоматически сохранит файл — диалоговое окно только мигает.

Желаемый результат:

Пользователь удерживает кнопку клавиатуры «enter», открывает диалоговое окно «Сохранить как имя файла», ничего не происходит. Ему нужно нажать на кнопку «Сохранить» или еще раз нажать кнопку «Ввод» на клавиатуре, чтобы сохранить файл.

Мой текущий код:

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

// Initialize OPENFILENAME
ZeroMemory(amp;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*.*Text*.TXT");
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

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

Фактический диалог

Возможное решение:

  • Когда файл ofn.lpstrFile пуст, ничего не делайте; Не удается сохранить файл, если нет имени файла
  • Когда файл ofn.lpstrFile предложил имя файла, затем отключите фокус на кнопке «Сохранить» или каким-то образом проигнорируйте удержание кнопки ввода.

Я пытался это сделать, но потерпел неудачу, я новичок в CPP 🙁

Спасибо за помощь

Комментарии:

1. Первого пункта кажется достаточным: просто не устанавливайте lpstrFile , текстовое поле «Имя файла» не будет предварительно заполнено опцией по умолчанию, кнопка «Сохранить» не будет включена, и, таким образом, пользователь не сможет вслепую <Ввести> свой путь через диалоговое окно. Чтобы сделать что-либо еще, вам нужно будет подключить диалоговое окно и удалить стиль кнопки по умолчанию из кнопки Сохранить. Это возможно, но сложнее, чем захочет взять на себя новичок.

2. Возможно, вместо этого вы могли бы использовать IFileDialog (GetSaveFileName является устаревшим API и фактически указывает на IFileDialog внутри) с IFileDialog::setOptions(FOS_OKBUTTONNEEDSINTERACTION) (непроверено). docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/…

3. @SimonMourier в этом документе специально сказано следующее FOS_OKBUTTONNEEDSINTERACTION : » Примечание: Отключение кнопки» ОК «не препятствует отправке диалогового окна с помощью клавиши ввода».

4. @RemyLebeau — Я знаю, но мне не на 100% ясно, что это значит, вот почему это нуждается в некотором тестировании

5. «Если пользователь удерживает кнопку «ввод»…» — что произойдет, если пользователь удержит кнопку питания? Можете ли вы защитить его? Кстати, ofn.nMaxFile должно быть 260 , нет sizeof szFile .

Ответ №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, amp;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(amp;ofn) == TRUE) ...
 

Комментарии:

1. Похоже, что они хотят удалить BS_DEFPUSHBUTTON стиль из кнопки «Сохранить» в процедуре подключения в ответ на создание диалогового окна. Это остановит автоматическое закрытие диалогового окна с помощью клавиши <Enter>. Вы не хотите возиться с фокусом; это все равно не решит проблему, так как для вызова кнопки «Сохранить» не требуется фокус для <Enter>.

2. @CodyGray, это зависит от того, на чем вы фокусируетесь. Если вы нажмете другую кнопку, [Ввод] не сохранится. Тем не менее, удаление сохранения по умолчанию для x * время повтора клавиатуры, возможно, неплохая идея.

3. «Если вы сфокусируете другую кнопку…» Да, конечно, но на какой другой кнопке вы собираетесь сфокусироваться? Единственный вариант, который у вас действительно есть, — это «Отмена», и нажатие <Enter>, когда оно активно, также будет иметь нежелательные последствия. Если сценарий, описанный в вопросе, когда пользователь удерживает клавишу <Enter>, повторяется с фокусировкой на кнопке «Отмена», диалоговое окно будет только мигать, закрываясь, как только оно будет открыто. Чем-то похоже на старую шутку пользователя, который, столкнувшись с диалогом «Вы хотите выйти?», выбирает «Отмена» просто для того, чтобы он исчез.

4. @Cody Есть также панель инструментов, хотя я не помню, есть ли на панелях инструментов кнопка по умолчанию. Вы также можете добавить свою собственную кнопку 0x0 пикселей «ничего не делать».

5. Да, @Maciej, этот тип подключения отключает тематизацию. Вот почему вам нужно использовать IFileSaveDialog вместо этого, который предоставляет все те же функции и, таким образом, будет заменой, даже не затрагивая пользовательский интерфейс. (В качестве альтернативы вы могли бы установить крюк CBT или что-то в этом роде, но… иик, это еще уродливее.)