Можно ли указать начальную позицию IFileDialog?

Я ничего не могу найти в документации Microsoft, поэтому мне просто интересно, знает ли кто-нибудь, можно ли указать начальную позицию для IFileDialog? В частности, я хотел бы, чтобы диалоговое окно открывалось в первый раз, чтобы оно открывалось в центре родительского окна.

Я не вижу простого способа сделать это, кроме как каким-то образом подключиться к основным сообщениям WM_*.

Можно ли использовать что-то вроде SetWindowPos ?


person erlloyd    schedule 20.11.2012    source источник
comment
Поздний комментарий, но вы действительно собираетесь реализовать IFileDialog или вам нужно что-то вроде OpenFileDialog или GetOpenFileName   -  person ST3    schedule 09.03.2014
comment
IFileOpenDialog является предпочтительным решением для Vista и более поздних версий. Он заменяет GetOpenFileName(). Однако GetOpenFileName() поддерживает перехват, который позволяет получить доступ к HWND диалога и, таким образом, может быть позиционирован вручную. IFileOpenDialog не предоставляет ту же функциональность.   -  person Remy Lebeau    schedule 21.01.2015
comment
Стандартный трюк состоит в том, чтобы использовать PostMessage() непосредственно перед отображением диалогового окна. Вы получите его обратно после завершения инициализации диалога, теперь вы можете вернуть окно #32770 и переместить его куда угодно.   -  person Hans Passant    schedule 28.09.2018


Ответы (2)


Реализация IFileDialog оболочки поддерживает IOleWindow. интерфейс (обратите внимание, что он не задокументирован...). Но диалог должен быть открыт, прежде чем вы сможете получить его дескриптор окна.

Таким образом, хитрость заключается в том, чтобы подписаться на события диалога, используя IFileDialogEvents, получите дескриптор окна и переместите его, как в примере ниже. Я выбираю OnSelectionChange, потому что это хорошее место. Конечно, это нужно адаптировать (вы не хотите перемещать окно каждый раз при изменении выбора...).

class Events : public IFileDialogEvents
{
  // poor man's IUnknown implementation :-)
  STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) { *ppvObject = NULL; return E_NOINTERFACE; }
  STDMETHODIMP_(ULONG) AddRef() { return 1; };
  STDMETHODIMP_(ULONG) Release() { return 1; }
  STDMETHODIMP OnFileOk(IFileDialog *pfd) { return S_OK; }
  STDMETHODIMP OnFolderChanging(IFileDialog *pfd, IShellItem *psiFolder) { return S_OK; }
  STDMETHODIMP OnShareViolation(IFileDialog *pfd, IShellItem *psi, FDE_SHAREVIOLATION_RESPONSE *pResponse) { return S_OK; }
  STDMETHODIMP OnTypeChange(IFileDialog *pfd) { return S_OK; }
  STDMETHODIMP OnOverwrite(IFileDialog *pfd, IShellItem *psi, FDE_OVERWRITE_RESPONSE *pResponse) { return S_OK; }
  STDMETHODIMP OnFolderChange(IFileDialog *pfd) { return S_OK; }

  STDMETHODIMP OnSelectionChange(IFileDialog *pfd) {
    IOleWindow *window;
    if (SUCCEEDED(pfd->QueryInterface(&window)))
    {
      HWND hwnd;
      if (SUCCEEDED(window->GetWindow(&hwnd)))
      {
        MoveWindow(hwnd, 0, 0, 600, 600, FALSE);
      }
      window->Release();
    }
    return S_OK;
  }
};

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
  CoInitialize(NULL);
  IFileOpenDialog *dlg;
  if (SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_PPV_ARGS(&dlg))))
  {
    DWORD cookie;
    Events *evts = new Events();
    dlg->Advise(evts, &cookie);
    dlg->Show(NULL);
    dlg->Unadvise(cookie);
    dlg->Release();
    delete evts;
  }
  CoUninitialize();
}
person Simon Mourier    schedule 29.09.2018
comment
Я новичок в программировании для Windows, поэтому я не могу судить о превосходстве любого ответа. Голосующие, пожалуйста, оставьте комментарии, если вы считаете, что один ответ лучше. Спасибо! В противном случае я присужу предыдущий ответ, когда награда закончится. - person azalea; 02.10.2018
comment
Разве это не приводит к тому, что диалоговое окно меняет свое местоположение каждый раз, когда пользователь меняет выбор? - person ghord; 11.03.2019

IFileDialog не дает возможности управлять расположением диалогового окна, но это может быть достигнуто путем подкласса диалогового окна.

Сообщение WM_WINDOWPOSCHANGING:

Отправляется в окно, размер, положение или положение которого в Z-порядке должны измениться в результате вызова функции SetWindowPos или другой функции управления окнами.

Окно получает это сообщение через свою функцию WindowProc.

Поэтому, если мы перехватим это сообщение, мы можем изменить его параметры, чтобы они указывали на нужное нам место, прежде чем окно будет перемещено туда. Мы можем сделать это путем подкласса диалога, который заменяет оконную процедуру диалога нашей собственной оконной процедурой. Наша пользовательская оконная процедура может выглядеть так:

LRESULT MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_WINDOWPOSCHANGING)
    {
        WINDOWPOS* wp = (WINDOWPOS*)lParam;
        wp->x = 100;
        wp->y = 100;
    }
    return CallWindowProc(owp, hwnd, msg, wParam, lParam);
}

Диалог имеет подкласс SetWindowLong функция. Однако, чтобы использовать эту функцию, нам нужно знать HWND диалогового окна. В свою очередь, мы можем получить дескриптор диалогового окна, настроив временный хук с SetWindowsHookEx, который будет соответствовать, например, заголовку диалогового окна и удалить себя при создании подкласса окна, соответствующего заданному заголовку:

LRESULT CALLBACK GetMsgProc(
    _In_ int    code,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
    )
{
    if (code == HC_ACTION)
    {
        MSG* msg = (MSG*)lParam;
        WCHAR title[120];
        GetWindowText(msg->hwnd, title, sizeof(title));
        if (lstrcmpW(title, L"MyTest01") == 0)
        {
            if (sizeof(long long) == 8)
                owp = (WNDPROC)SetWindowLongPtr(msg->hwnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);
            else
                owp = (WNDPROC)SetWindowLong(msg->hwnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);
            LRESULT r = CallNextHookEx(hhk, code, wParam, lParam);
            UnhookWindowsHookEx(hhk);
            return r;
        }
    }
    return CallNextHookEx(hhk, code, wParam, lParam);
}

Весь пример:

#include <windows.h>
#include <shobjidl.h> 

HHOOK hhk = NULL;
WNDPROC owp = NULL;

LRESULT MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_WINDOWPOSCHANGING)
    {
        WINDOWPOS* wp = (WINDOWPOS*)lParam;
        wp->x = 100;
        wp->y = 100;
        (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)owp);
    }
    return CallWindowProc(owp, hwnd, msg, wParam, lParam);
}

LRESULT CALLBACK GetMsgProc(
    _In_ int    code,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
    )
{
    if (code == HC_ACTION)
    {
        MSG* msg = (MSG*)lParam;
        WCHAR title[120];
        GetWindowText(msg->hwnd, title, sizeof(title));
        if (lstrcmpW(title, L"MyTestIFileOpenDialog01") == 0)
        {
            if (sizeof(long long) == 8)
                owp = (WNDPROC)SetWindowLongPtr(msg->hwnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);
            else
                owp = (WNDPROC)SetWindowLong(msg->hwnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);
            LRESULT r = CallNextHookEx(hhk, code, wParam, lParam);
            UnhookWindowsHookEx(hhk);
            return r;
        }
    }
    return CallNextHookEx(hhk, code, wParam, lParam);
}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED |
        COINIT_DISABLE_OLE1DDE);
    if (SUCCEEDED(hr))
    {
        IFileOpenDialog *pFileOpen;

        // Create the FileOpenDialog object.
        hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
            IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

        if (SUCCEEDED(hr))
        {
            hhk = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, NULL, GetCurrentThreadId());
            pFileOpen->SetTitle(L"MyTestIFileOpenDialog01");
            // Show the Open dialog box.
            hr = pFileOpen->Show(NULL);

            // Get the file name from the dialog box.
            if (SUCCEEDED(hr))
            {
                IShellItem *pItem;
                hr = pFileOpen->GetResult(&pItem);
                if (SUCCEEDED(hr))
                {
                    PWSTR pszFilePath;
                    hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);

                    // Display the file name to the user.
                    if (SUCCEEDED(hr))
                    {
                        MessageBox(NULL, pszFilePath, L"File Path", MB_OK);
                        CoTaskMemFree(pszFilePath);
                    }
                    pItem->Release();
                }
            }
            pFileOpen->Release();
        }
        CoUninitialize();
    }
    return 0;
}
person mnistic    schedule 28.09.2018