【问题标题】:Possible to specify the start position of an IFileDialog?可以指定 IFileDialog 的开始位置吗?
【发布时间】:2012-11-20 15:12:52
【问题描述】:

我在 Microsoft 文档中找不到任何内容,所以我想知道是否有人知道,是否可以指定 IFileDialog 的起始位置?具体来说,我希望第一次打开对话框时,它会在父窗口的中心打开。

除了以某种方式连接到底层WM_* 消息之外,我没有看到直接的方法来做到这一点。

是否可以使用SetWindowPos 之类的东西?

【问题讨论】:

  • 迟到的评论,但你真的要实现IFileDialog 还是需要OpenFileDialogGetOpenFileName 之类的东西
  • IFileOpenDialog 是 Vista 及更高版本的首选解决方案。它取代了GetOpenFileName()。但是,GetOpenFileName() 支持挂钩,它允许访问对话框的 HWND,因此可以手动定位。 IFileOpenDialog 没有公开相同的功能。
  • 标准技巧是在显示对话框之前对自己使用 PostMessage()。对话框初始化完成后您将恢复它,现在您可以找到窗口“#32770”并将其移动到您想要的任何位置。

标签: windows winapi filedialog common-dialog


【解决方案1】:

shell 的 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();
}

【讨论】:

  • 我是 Windows 编程新手,因此无法判断任一答案的优劣。支持者,如果您认为一个答案更好,请离开 cmets。谢谢!否则,赏金结束后,我将奖励较早的答案。
  • 这不会导致每次用户更改选择时对话框都会更改其位置吗?
【解决方案2】:

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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-11
    • 2011-12-18
    • 1970-01-01
    • 1970-01-01
    • 2011-12-26
    • 2013-09-17
    • 1970-01-01
    • 2010-09-27
    相关资源
    最近更新 更多