一般專案對話方塊
從 Windows Vista 開始,通用專案對話方塊會在用來開啟或儲存檔案時取代較舊的通用檔案對話方塊。 [一般專案對話方塊] 用於兩種變化:[ 開啟 ] 對話方塊和 [ 儲存 ] 對話方塊。 這兩個對話方塊會共用大部分的功能,但每個對話都有自己的唯一方法。
雖然這個較新版本的名稱為通用專案對話方塊,但大部分檔中仍會繼續呼叫 Common File Dialog。 除非您特別處理舊版 Windows,否則您應該假設任何提及通用檔案對話方塊都參考此通用專案對話方塊。
這裡將討論下列主題:
IFileDialog、IFileOpenDialog 和 IFileSaveDialog
Windows Vista 提供 [開啟 和 儲存 ] 對話方塊的實作 :CLSID_FileOpenDialog和CLSID_FileSaveDialog。 這些對話方塊會顯示在這裡。
IFileOpenDialog 和 IFileSaveDialog 繼承自 IFileDialog ,並共用其大部分功能。 此外,[ 開啟 ] 對話方塊支援 IFileOpenDialog ,而 [ 儲存 ] 對話方塊則支援 IFileSaveDialog 。
在 Windows Vista 中找到的一般專案對話方塊實作提供幾個優點,優於舊版中提供的實作:
- 支援透過 IShellItem 直接使用 Shell 命名空間,而不是使用檔案系統路徑。
- 啟用對話方塊的簡單自訂,例如在 [確定 ] 按鈕上 設定標籤,而不需要勾點程式。
- 藉由新增一組沒有 Win32 對話方塊範本運作的資料驅動控制項,支援對對話方塊進行更廣泛的自訂。 此自訂配置會從 UI 配置釋放呼叫程式。 由於對話設計的任何變更會繼續使用此資料模型,因此對話實作不會系結至對話的特定目前版本。
- 支援對話方塊內事件的呼叫端通知,例如選取範圍變更或檔案類型變更。 此外,也可讓呼叫進程攔截對話方塊中的特定事件,例如剖析。
- 引進新的對話方塊功能,例如將呼叫端指定的位置新增至 Places 列。
- 在 [ 儲存 ] 對話方塊中,開發人員可以利用 Windows Vista Shell 的新中繼資料功能。
此外,開發人員可以選擇實作下列介面:
- IFileDialogEvents 可接收對話方塊內事件的通知。
- IFileDialogCustomize 將控制項新增至對話方塊。
- IFileDialogControlEvents 會通知這些新增控制項中的事件。
[開啟 ] 或 [ 儲存 ] 對話方塊會將 IShellItem 或 IShellItemArray 物件傳回呼叫進程。 呼叫端接著可以使用個別 的 IShellItem 物件來取得檔案系統路徑,或開啟專案上的資料流程來讀取或寫入資訊。
新對話方塊方法可用的旗標和選項與 OPENFILENAME 結構中找到 的舊 OFN 旗標非常類似,而且用於 GetOpenFileName 和 GetSaveFileName。 其中許多都完全相同,不同之處在于它們開頭為 FOS 前置詞。 您可以在 IFileDialog::GetOptions 和 IFileDialog::SetOptions 主題中找到 完整清單。 預設會使用最常見的旗標來建立 [開啟 ] 和 [儲存 ] 對話方塊。 針對 [ 開啟] 對話方塊,這是 (FOS_PATHMUSTEXIST |FOS_FILEMUSTEXIST |FOS_NOCHANGEDIR] 和 [ 儲存 ] 對話方塊為 (FOS_OVERWRITEPROMPT |FOS_NOREADONLYRETURN |FOS_PATHMUSTEXIST |FOS_NOCHANGEDIR)。
IFileDialog 及其子代介面繼承自 並擴充 IModalWindow 。 顯示為 父視窗控制碼的唯一參數。 如果 Show 成功傳回,則會產生有效的結果。 如果傳回 HRESULT_FROM_WIN32(ERROR_CANCELLED)
,表示使用者已取消對話方塊。 它也可能會合法地傳回另一個錯誤碼,例如 E_OUTOFMEMORY 。
範例使用方式
下列各節顯示各種對話工作的範例程式碼。
大部分範例程式碼都可以在 Windows SDK 通用檔案對話方塊範例 中找到。
基本使用方式
下列範例說明如何啟動 [ 開啟 ] 對話方塊。 在此範例中,它僅限於 Microsoft Word 檔。
注意
本主題中的數個範例會使用 CDialogEventHandler_CreateInstance
協助程式函式來建立 IFileDialogEvents 實作的 實例。 若要在您自己的程式碼中使用此函式,請從 通用檔案對話方塊範例 複製函式的原始程式碼 CDialogEventHandler_CreateInstance
,本主題中的所有範例都會從中取得。
HRESULT BasicFileOpen()
{
// CoCreate the File Open Dialog object.
IFileDialog *pfd = NULL;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
// Create an event handling object, and hook it up to the dialog.
IFileDialogEvents *pfde = NULL;
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
if (SUCCEEDED(hr))
{
// Hook up the event handler.
DWORD dwCookie;
hr = pfd->Advise(pfde, &dwCookie);
if (SUCCEEDED(hr))
{
// Set the options on the dialog.
DWORD dwFlags;
// Before setting, always get the options first in order
// not to override existing options.
hr = pfd->GetOptions(&dwFlags);
if (SUCCEEDED(hr))
{
// In this case, get shell items only for file system items.
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
if (SUCCEEDED(hr))
{
// Set the file types to display only.
// Notice that this is a 1-based array.
hr = pfd->SetFileTypes(ARRAYSIZE(c_rgSaveTypes), c_rgSaveTypes);
if (SUCCEEDED(hr))
{
// Set the selected file type index to Word Docs for this example.
hr = pfd->SetFileTypeIndex(INDEX_WORDDOC);
if (SUCCEEDED(hr))
{
// Set the default extension to be ".doc" file.
hr = pfd->SetDefaultExtension(L"doc;docx");
if (SUCCEEDED(hr))
{
// Show the dialog
hr = pfd->Show(NULL);
if (SUCCEEDED(hr))
{
// Obtain the result once the user clicks
// the 'Open' button.
// The result is an IShellItem object.
IShellItem *psiResult;
hr = pfd->GetResult(&psiResult);
if (SUCCEEDED(hr))
{
// We are just going to print out the
// name of the file for sample sake.
PWSTR pszFilePath = NULL;
hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH,
&pszFilePath);
if (SUCCEEDED(hr))
{
TaskDialog(NULL,
NULL,
L"CommonFileDialogApp",
pszFilePath,
NULL,
TDCBF_OK_BUTTON,
TD_INFORMATION_ICON,
NULL);
CoTaskMemFree(pszFilePath);
}
psiResult->Release();
}
}
}
}
}
}
}
// Unhook the event handler.
pfd->Unadvise(dwCookie);
}
pfde->Release();
}
pfd->Release();
}
return hr;
}
將結果限制為檔案系統專案
上述範例示範如何將結果限制為檔案系統專案。 請注意, IFileDialog::SetOptions 會將新旗標新增至透過 IFileDialog::GetOptions 取得的值。 這是建議的方法。
// Set the options on the dialog.
DWORD dwFlags;
// Before setting, always get the options first in order
// not to override existing options.
hr = pfd->GetOptions(&dwFlags);
if (SUCCEEDED(hr))
{
// In this case, get shell items only for file system items.
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
指定對話方塊的檔案類型
若要設定對話方塊可以處理的特定檔案類型,請使用 IFileDialog::SetFileTypes 方法。 該方法接受COMDLG_FILTERSPEC 結構的陣列 ,每個結構都代表檔案類型。
對話方塊中的預設擴充機制與 GetOpenFileName 和 GetSaveFileName 保持不變。 在對話方塊開啟時,會初始化 [檔案名] 編輯方塊中使用者輸入之文字的副檔名。 它應該符合預設檔案類型(在對話方塊開啟時選取的檔案類型)。 如果預設檔案類型是 「*.*」 (所有檔案),則檔案可以是您選擇的副檔名。 如果使用者選擇不同的檔案類型,副檔名會自動更新為與該檔案類型相關聯的第一個副檔名。 如果使用者選擇 「*.*」 (所有檔案),則副檔名會還原為其原始值。
下列範例說明上述作業的完成方式。
// Set the file types to display only.
// Notice that this is a 1-based array.
hr = pfd->SetFileTypes(ARRAYSIZE(c_rgSaveTypes), c_rgSaveTypes);
if (SUCCEEDED(hr))
{
// Set the selected file type index to Word Docs for this example.
hr = pfd->SetFileTypeIndex(INDEX_WORDDOC);
if (SUCCEEDED(hr))
{
// Set the default extension to be ".doc" file.
hr = pfd->SetDefaultExtension(L"doc;docx");
控制預設資料夾
Shell 命名空間中幾乎任何資料夾都可以作為對話方塊的預設資料夾(當使用者選擇開啟或儲存檔案時顯示的資料夾)。 呼叫 Show 之前,請先呼叫 IFileDialog::SetDefaultFolder 以執行此動作。
預設資料夾是對話方塊第一次從您的應用程式開啟對話方塊時啟動的資料夾。 之後,對話方塊會在使用者開啟的最後一個資料夾中開啟,或用來儲存專案的最後一個資料夾。 如需詳細資訊,請參閱 狀態持續性 。
您可以呼叫 IFileDialog::SetFolder ,強制對話方塊在開啟時一律顯示相同的資料夾,而不論先前的使用者動作為何。 不過,我們不建議這麼做。 如果您在顯示對話方塊之前呼叫 SetFolder ,則不會顯示使用者儲存或開啟的最新位置。 除非此行為有非常具體的原因,否則這不是良好的或預期的使用者體驗,因此應該避免。 在幾乎所有的實例中, IFileDialog::SetDefaultFolder 是更好的方法。
第一次在 [儲存] 對話方塊中儲存檔時,您應該遵循與在 [開啟 ] 對話方塊中判斷初始資料夾 相同的指導方針。 如果使用者正在編輯先前存在的檔,請在儲存該檔的資料夾中開啟對話方塊,並以該檔的名稱填入編輯方塊。 在呼叫 Show 之前,使用目前的專案呼叫 IFileSaveDialog::SetSaveAsItem。
將專案新增至放置列
下列範例示範將專案新增至 Places 列:
HRESULT AddItemsToCommonPlaces()
{
// CoCreate the File Open Dialog object.
IFileDialog *pfd = NULL;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
// Always use known folders instead of hard-coding physical file paths.
// In this case we are using Public Music KnownFolder.
IKnownFolderManager *pkfm = NULL;
hr = CoCreateInstance(CLSID_KnownFolderManager,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pkfm));
if (SUCCEEDED(hr))
{
// Get the known folder.
IKnownFolder *pKnownFolder = NULL;
hr = pkfm->GetFolder(FOLDERID_PublicMusic, &pKnownFolder);
if (SUCCEEDED(hr))
{
// File Dialog APIs need an IShellItem that represents the location.
IShellItem *psi = NULL;
hr = pKnownFolder->GetShellItem(0, IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
// Add the place to the bottom of default list in Common File Dialog.
hr = pfd->AddPlace(psi, FDAP_BOTTOM);
if (SUCCEEDED(hr))
{
// Show the File Dialog.
hr = pfd->Show(NULL);
if (SUCCEEDED(hr))
{
//
// You can add your own code here to handle the results.
//
}
}
psi->Release();
}
pKnownFolder->Release();
}
pkfm->Release();
}
pfd->Release();
}
return hr;
}
狀態持續性
在 Windows Vista 之前,狀態,例如上次流覽的資料夾,會根據每個進程儲存。 不過,不論特定動作為何,都會使用該資訊。 例如,視訊編輯應用程式會在 [轉譯為 ] 對話方塊中呈現相同的資料夾 ,就像在 [ 匯入媒體 ] 對話方塊中一樣。 在 Windows Vista 中,您可以透過使用 GUID 更具體。 若要將 GUID 指派 給對話方塊,請呼叫 iFileDialog::SetClientGuid 。
多重選取功能
您可以使用 GetResults 方法, 在 [開啟 ] 對話方塊中 取得多重選取功能,如下所示。
HRESULT MultiselectInvoke()
{
IFileOpenDialog *pfd;
// CoCreate the dialog object.
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
DWORD dwOptions;
// Specify multiselect.
hr = pfd->GetOptions(&dwOptions);
if (SUCCEEDED(hr))
{
hr = pfd->SetOptions(dwOptions | FOS_ALLOWMULTISELECT);
}
if (SUCCEEDED(hr))
{
// Show the Open dialog.
hr = pfd->Show(NULL);
if (SUCCEEDED(hr))
{
// Obtain the result of the user interaction.
IShellItemArray *psiaResults;
hr = pfd->GetResults(&psiaResults);
if (SUCCEEDED(hr))
{
//
// You can add your own code here to handle the results.
//
psiaResults->Release();
}
}
}
pfd->Release();
}
return hr;
}
接聽來自對話方塊的事件
呼叫程式可以使用 IFileDialog::Advise 和 IFileDialog::Unadvise 方法,向對話方塊 註冊 IFileDialogEvents 介面,如下所示。
這是取自 基本使用 方式範例。
// Create an event handling object, and hook it up to the dialog.
IFileDialogEvents *pfde = NULL;
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
if (SUCCEEDED(hr))
{
// Hook up the event handler.
DWORD dwCookie;
hr = pfd->Advise(pfde, &dwCookie);
大部分的對話方塊處理都會放在這裡。
// Unhook the event handler.
pfd->Unadvise(dwCookie);
}
pfde->Release();
}
pfd->Release();
}
return hr;
}
當使用者變更資料夾、檔案類型或選取專案時,呼叫程式可以使用事件來通知。 當呼叫程式已將控制項新增至對話方塊時,這些事件特別有用(請參閱 自訂對話方塊 ),而且必須變更這些控制項的狀態以回應這些事件。 在所有情況下都很有用的是呼叫程式提供自訂程式碼來處理共用違規、覆寫檔案,或判斷對話方塊關閉前檔案是否有效等情況。 本節將說明其中一些案例。
OnFileOk
這個方法會在使用者選擇專案之後呼叫,就在對話方塊關閉之前。 然後,應用程式可以呼叫 IFileDialog::GetResult 或 IFileOpenDialog::GetResults ,就像對話方塊關閉一樣。 如果選擇的專案是可接受的,則可以傳回S_OK。 否則,他們會傳回S_FALSE並顯示 UI,告知使用者選擇的專案不正確原因。 如果傳回S_FALSE,對話方塊就不會關閉。
呼叫進程可以使用對話方塊本身的視窗控制碼做為 UI 的父代。 您可以先呼叫 IOleWindow::QueryInterface ,然後使用控制碼呼叫 IOleWindow::GetWindow 來取得該控制碼,如此範例所示。
HRESULT CDialogEventHandler::OnFileOk(IFileDialog *pfd)
{
IShellItem *psiResult;
HRESULT hr = pfd->GetResult(&psiResult);
if (SUCCEEDED(hr))
{
SFGAOF attributes;
hr = psiResult->GetAttributes(SFGAO_COMPRESSED, &attributes);
if (SUCCEEDED(hr))
{
if (attributes & SFGAO_COMPRESSED)
{
// Accept the file.
hr = S_OK;
}
else
{
// Refuse the file.
hr = S_FALSE;
_DisplayMessage(pfd, L"Not a compressed file.");
}
}
psiResult->Release();
}
return hr;
};
HRESULT CDialogEventHandler::_DisplayMessage(IFileDialog *pfd, PCWSTR pszMessage)
{
IOleWindow *pWindow;
HRESULT hr = pfd->QueryInterface(IID_PPV_ARGS(&pWindow));
if (SUCCEEDED(hr))
{
HWND hwndDialog;
hr = pWindow->GetWindow(&hwndDialog);
if (SUCCEEDED(hr))
{
MessageBox(hwndDialog, pszMessage, L"An error has occurred", MB_OK);
}
pWindow->Release();
}
return hr;
}
OnShareViolation 和 OnOverwrite
如果使用者選擇覆寫 [儲存] 對話方塊中的檔案,或正在儲存或取代的檔案 正在使用中且無法寫入 (共用違規),應用程式可以提供自訂功能來覆寫對話方塊的預設行為。 根據預設,覆寫檔案時,對話方塊會顯示提示,允許使用者驗證此動作。 針對共用違規,對話方塊預設會顯示錯誤訊息、不會關閉,而且使用者必須做出其他選擇。 呼叫進程可以覆寫這些預設值,並視需要顯示自己的 UI。 對話方塊可以指示拒絕檔案,並保持開啟或接受檔案並成功關閉。
自訂對話方塊
您可以新增各種控制項至對話方塊,而不需提供 Win32 對話方塊範本。 這些控制項包括 PushButton、ComboBox、EditBox、CheckButton、RadioButton 清單、群組、分隔符號和靜態文字控制項。 在對話方塊物件上呼叫 QueryInterface ( IFileDialog、 IFileOpenDialog 或 IFileSaveDialog ),以取得 IFileDialogCustomize 指標。 使用該介面來新增控制項。 每個控制項都有相關聯的呼叫端提供識別碼,以及 可由呼叫程式設定的可見 和 啟用 狀態。 某些控制項,例如 PushButton,也有與其相關聯的文字。
您可以將多個控制項新增至「視覺群組」,以在對話方塊的版面配置中以單一單位的形式移動。 群組可以有與其相關聯的標籤。
控制項只能在顯示對話方塊之前新增。 不過,一旦顯示對話方塊,控制項就可以隱藏或視需要顯示,也許是為了回應使用者動作。 下列範例示範如何將選項按鈕清單新增至對話方塊。
// Controls
#define CONTROL_GROUP 2000
#define CONTROL_RADIOBUTTONLIST 2
#define CONTROL_RADIOBUTTON1 1
#define CONTROL_RADIOBUTTON2 2 // It is OK for this to have the same ID
// as CONTROL_RADIOBUTTONLIST, because it
// is a child control under CONTROL_RADIOBUTTONLIST
// This code snippet demonstrates how to add custom controls in the Common File Dialog.
HRESULT AddCustomControls()
{
// CoCreate the File Open Dialog object.
IFileDialog *pfd = NULL;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
// Create an event handling object, and hook it up to the dialog.
IFileDialogEvents *pfde = NULL;
DWORD dwCookie = 0;
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
if (SUCCEEDED(hr))
{
// Hook up the event handler.
hr = pfd->Advise(pfde, &dwCookie);
if (SUCCEEDED(hr))
{
// Set up a Customization.
IFileDialogCustomize *pfdc = NULL;
hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdc));
if (SUCCEEDED(hr))
{
// Create a Visual Group.
hr = pfdc->StartVisualGroup(CONTROL_GROUP, L"Sample Group");
if (SUCCEEDED(hr))
{
// Add a radio-button list.
hr = pfdc->AddRadioButtonList(CONTROL_RADIOBUTTONLIST);
if (SUCCEEDED(hr))
{
// Set the state of the added radio-button list.
hr = pfdc->SetControlState(CONTROL_RADIOBUTTONLIST,
CDCS_VISIBLE | CDCS_ENABLED);
if (SUCCEEDED(hr))
{
// Add individual buttons to the radio-button list.
hr = pfdc->AddControlItem(CONTROL_RADIOBUTTONLIST,
CONTROL_RADIOBUTTON1,
L"Change Title to ABC");
if (SUCCEEDED(hr))
{
hr = pfdc->AddControlItem(CONTROL_RADIOBUTTONLIST,
CONTROL_RADIOBUTTON2,
L"Change Title to XYZ");
if (SUCCEEDED(hr))
{
// Set the default selection to option 1.
hr = pfdc->SetSelectedControlItem(CONTROL_RADIOBUTTONLIST,
CONTROL_RADIOBUTTON1);
}
}
}
}
// End the visual group.
pfdc->EndVisualGroup();
}
pfdc->Release();
}
if (FAILED(hr))
{
// Unadvise here in case we encounter failures
// before we get a chance to show the dialog.
pfd->Unadvise(dwCookie);
}
}
pfde->Release();
}
if (SUCCEEDED(hr))
{
// Now show the dialog.
hr = pfd->Show(NULL);
if (SUCCEEDED(hr))
{
//
// You can add your own code here to handle the results.
//
}
// Unhook the event handler.
pfd->Unadvise(dwCookie);
}
pfd->Release();
}
return hr;
}
將選項新增至 [確定] 按鈕
同樣地,選項可以新增至 [開啟 ] 或 [儲存 ] 按鈕,也就是 其個別對話方塊類型的 [確定 ] 按鈕。 選項可透過附加至按鈕的下拉式清單方塊來存取。 清單中的第一個專案會成為按鈕的文字。 下列範例示範如何提供具有兩種可能性的 [開啟 ] 按鈕:「Open」 和 「Open as read-only」。
// OpenChoices options
#define OPENCHOICES 0
#define OPEN 0
#define OPEN_AS_READONLY 1
HRESULT AddOpenChoices()
{
// CoCreate the File Open Dialog object.
IFileDialog *pfd = NULL;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
// Create an event handling object, and hook it up to the dialog.
IFileDialogEvents *pfde = NULL;
DWORD dwCookie = 0;
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
if (SUCCEEDED(hr))
{
// Hook up the event handler.
hr = pfd->Advise(pfde, &dwCookie);
if (SUCCEEDED(hr))
{
// Set up a Customization.
IFileDialogCustomize *pfdc = NULL;
hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdc));
if (SUCCEEDED(hr))
{
hr = pfdc->EnableOpenDropDown(OPENCHOICES);
if (SUCCEEDED(hr))
{
hr = pfdc->AddControlItem(OPENCHOICES, OPEN, L"&Open");
}
if (SUCCEEDED(hr))
{
hr = pfdc->AddControlItem(OPENCHOICES,
OPEN_AS_READONLY,
L"Open as &read-only");
}
if (SUCCEEDED(hr))
{
pfd->Show(NULL);
}
}
pfdc->Release();
}
pfd->Unadvise(dwCookie);
}
pfde->Release();
}
pfd->Release();
return hr;
}
使用者可以在 [顯示方法] 傳回 [ComboBox ] 之後驗證使用者的選擇,也可以驗證 為 IFileDialogEvents::OnFileOk 處理的一部分。
回應已新增控制項中的事件
呼叫進程所提供的事件處理常式除了 IFileDialogEvents 之外,還可以實 作 IFileDialogControlEvents 。 IFileDialogControlEvents 可讓呼叫程式回應這些事件:
- 按下 PushButton
- CheckButton 狀態已變更
- 從功能表、ComboBox 或 RadioButton 清單選取的專案
- 控制啟動。 當功能表即將顯示下拉式清單時,就會傳送此專案,以防呼叫程式想要變更清單中的專案。
完整範例
以下是 Windows 軟體發展工具組 (SDK) 中完整的可下載 C++ 範例,示範如何使用和與通用專案對話方塊互動。
相關主題