IFileDialog:添加自定义位置 - select 该位置
IFileDialog: Add a Custom Place - and select that place
我正在使用 IFileDialog::AddPlace
添加例如"c:\my\custom\location"
作为自定义位置 select 文件从左侧的导航面板,并将其设置为 (default/forced) 初始文件夹。
但是,当对话框打开时,根驱动器(示例中的 C:)被 selected 而不是自定义位置。
(我使用 SHCreateItemFromParsingName
从路径创建 IShellItem
,并在 AddPlace
和 SetFolder
中使用相同的 Shellitem)
我不确定您是否可以直接从 IFileDialog 界面 select 虚拟文件夹。但是你可以从里面订阅file dialog events and have access to the explorer's left tree view
树视图实现了INameSpaceTreeControl接口
int main()
{
CoInitialize(NULL);
{
LPCWSTR customPath = L"c:\temp"; // use a path that exists...
CComPtr<IFileDialog> dlg;
Check(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&dlg)));
// subscribe to events
MyFileDialogEvents* fde = new MyFileDialogEvents();
DWORD cookie;
Check(dlg->Advise(fde, &cookie));
CComPtr<IShellItem> location;
Check(SHCreateItemFromParsingName(customPath, nullptr, IID_PPV_ARGS(&location)));
Check(dlg->AddPlace(location, FDAP_TOP));
Check(dlg->SetFolder(location));
Check(dlg->Show(0));
Check(dlg->Unadvise(cookie));
delete fde;
}
CoUninitialize();
return 0;
}
class MyFileDialogEvents : public IFileDialogEvents
{
// called when selection has changed
HRESULT STDMETHODCALLTYPE OnSelectionChange(IFileDialog* pfd)
{
// get comdlg service provider
CComPtr<IServiceProvider> sp;
Check(pfd->QueryInterface(&sp));
// get explorer browser
// note this call would fail if we call it from IFileDialog* directly instead of from an event
CComPtr<IUnknown> unk;
Check(sp->QueryService(SID_STopLevelBrowser, &unk));
// get its service provider
CComPtr<IServiceProvider> sp2;
Check(unk->QueryInterface(&sp2));
// get the tree control
CComPtr<INameSpaceTreeControl> ctl;
Check(sp2->QueryService(IID_INameSpaceTreeControl, &ctl));
// get all roots, "Application Links" is a root
CComPtr<IShellItemArray> roots;
Check(ctl->GetRootItems(&roots));
DWORD count;
Check(roots->GetCount(&count));
// search for "Application Links" folder
for (DWORD i = 0; i < count; i++)
{
CComPtr<IShellItem> root;
Check(roots->GetItemAt(i, &root));
// get the normalized name, not the display (localized) name
CComHeapPtr<wchar_t> name;
Check(root->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &name));
// CLSID_AppSuggestedLocations?
if (!lstrcmpi(name, L"::{C57A6066-66A3-4D91-9EB9-41532179F0A5}"))
{
// found, expand it
ctl->SetItemState(root, NSTCIS_EXPANDED, NSTCIS_EXPANDED);
// get the first child
// TODO: loop over all suggested location (places) and use the one we're after instead of blindly taking the first one...
CComPtr<IShellItem> child;
ctl->GetNextItem(root, NSTCGNI_CHILD, &child);
if (child.p) // this will probably not succeed the first time we're called
{
// select the item
CComHeapPtr<wchar_t> childName;
ctl->SetItemState(child, NSTCIS_SELECTED, NSTCIS_SELECTED);
}
else
{
// select something so we can get back here
HRCHECK (pfd->SetFolder(location));
}
break;
}
}
return S_OK;
}
// poor-man's COM implementation for demo purposes...
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject)
{
*ppvObject = NULL;
if (riid == IID_IFileDialogEvents)
{
*ppvObject = (IFileDialogEvents*)this;
return S_OK;
}
if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown*)this;
return S_OK;
}
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef() { return 1; }
ULONG STDMETHODCALLTYPE Release() { return 1; }
HRESULT STDMETHODCALLTYPE OnFileOk(IFileDialog* pfd) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnFolderChanging(IFileDialog* pfd, IShellItem* psiFolder) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnFolderChange(IFileDialog* pfd) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnShareViolation(IFileDialog* pfd, IShellItem* psi, FDE_SHAREVIOLATION_RESPONSE* pResponse) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnTypeChange(IFileDialog* pfd) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnOverwrite(IFileDialog* pfd, IShellItem* psi, FDE_OVERWRITE_RESPONSE* pResponse) { return S_OK; }
};
注意:为了简单起见,我使用 Visual Studio 的 ATL 智能指针 类。
我正在使用 IFileDialog::AddPlace
添加例如"c:\my\custom\location"
作为自定义位置 select 文件从左侧的导航面板,并将其设置为 (default/forced) 初始文件夹。
但是,当对话框打开时,根驱动器(示例中的 C:)被 selected 而不是自定义位置。
(我使用 SHCreateItemFromParsingName
从路径创建 IShellItem
,并在 AddPlace
和 SetFolder
中使用相同的 Shellitem)
我不确定您是否可以直接从 IFileDialog 界面 select 虚拟文件夹。但是你可以从里面订阅file dialog events and have access to the explorer's left tree view
树视图实现了INameSpaceTreeControl接口
int main()
{
CoInitialize(NULL);
{
LPCWSTR customPath = L"c:\temp"; // use a path that exists...
CComPtr<IFileDialog> dlg;
Check(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&dlg)));
// subscribe to events
MyFileDialogEvents* fde = new MyFileDialogEvents();
DWORD cookie;
Check(dlg->Advise(fde, &cookie));
CComPtr<IShellItem> location;
Check(SHCreateItemFromParsingName(customPath, nullptr, IID_PPV_ARGS(&location)));
Check(dlg->AddPlace(location, FDAP_TOP));
Check(dlg->SetFolder(location));
Check(dlg->Show(0));
Check(dlg->Unadvise(cookie));
delete fde;
}
CoUninitialize();
return 0;
}
class MyFileDialogEvents : public IFileDialogEvents
{
// called when selection has changed
HRESULT STDMETHODCALLTYPE OnSelectionChange(IFileDialog* pfd)
{
// get comdlg service provider
CComPtr<IServiceProvider> sp;
Check(pfd->QueryInterface(&sp));
// get explorer browser
// note this call would fail if we call it from IFileDialog* directly instead of from an event
CComPtr<IUnknown> unk;
Check(sp->QueryService(SID_STopLevelBrowser, &unk));
// get its service provider
CComPtr<IServiceProvider> sp2;
Check(unk->QueryInterface(&sp2));
// get the tree control
CComPtr<INameSpaceTreeControl> ctl;
Check(sp2->QueryService(IID_INameSpaceTreeControl, &ctl));
// get all roots, "Application Links" is a root
CComPtr<IShellItemArray> roots;
Check(ctl->GetRootItems(&roots));
DWORD count;
Check(roots->GetCount(&count));
// search for "Application Links" folder
for (DWORD i = 0; i < count; i++)
{
CComPtr<IShellItem> root;
Check(roots->GetItemAt(i, &root));
// get the normalized name, not the display (localized) name
CComHeapPtr<wchar_t> name;
Check(root->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &name));
// CLSID_AppSuggestedLocations?
if (!lstrcmpi(name, L"::{C57A6066-66A3-4D91-9EB9-41532179F0A5}"))
{
// found, expand it
ctl->SetItemState(root, NSTCIS_EXPANDED, NSTCIS_EXPANDED);
// get the first child
// TODO: loop over all suggested location (places) and use the one we're after instead of blindly taking the first one...
CComPtr<IShellItem> child;
ctl->GetNextItem(root, NSTCGNI_CHILD, &child);
if (child.p) // this will probably not succeed the first time we're called
{
// select the item
CComHeapPtr<wchar_t> childName;
ctl->SetItemState(child, NSTCIS_SELECTED, NSTCIS_SELECTED);
}
else
{
// select something so we can get back here
HRCHECK (pfd->SetFolder(location));
}
break;
}
}
return S_OK;
}
// poor-man's COM implementation for demo purposes...
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject)
{
*ppvObject = NULL;
if (riid == IID_IFileDialogEvents)
{
*ppvObject = (IFileDialogEvents*)this;
return S_OK;
}
if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown*)this;
return S_OK;
}
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef() { return 1; }
ULONG STDMETHODCALLTYPE Release() { return 1; }
HRESULT STDMETHODCALLTYPE OnFileOk(IFileDialog* pfd) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnFolderChanging(IFileDialog* pfd, IShellItem* psiFolder) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnFolderChange(IFileDialog* pfd) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnShareViolation(IFileDialog* pfd, IShellItem* psi, FDE_SHAREVIOLATION_RESPONSE* pResponse) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnTypeChange(IFileDialog* pfd) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnOverwrite(IFileDialog* pfd, IShellItem* psi, FDE_OVERWRITE_RESPONSE* pResponse) { return S_OK; }
};
注意:为了简单起见,我使用 Visual Studio 的 ATL 智能指针 类。