標準 CFileDialog 功能有限,企業級應用常需加入預覽窗格、批次選取按鈕、檔案類型過濾等功能。本文完整示範如何繼承 CFileDialog、建立自訂範本資源、子類化清單控制項、處理 UI 更新機制,並解決 Windows 2000 下 GetPathName 回傳錯誤路徑的經典問題。
WM_COMMAND/WM_NOTIFY 觸發 UI 更新旗標。WM_ENTERIDLE 執行 UpdateDialogControls。GetListCtrl()、GetItemName()、IsItemFolder() 等高階介面,使用者只需在 OnInitDialog 呼叫 Init(this)。
void CMyOpenDlg::OnSelectAll() {
CListCtrl* pList = m_dlghelper.GetListCtrl();
for (int i=0; iGetItemCount(); i++) {
CString name = pList->GetItemText(i, 0);
if (IsTextFileName(name)) // 判斷副檔名 .txt
pList->SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
}
pList->SetFocus();
}
void CMyOpenDlg::OnUpdateSelectAll(CCmdUI* pCmdUI) {
CListCtrl* pList = m_dlghelper.GetListCtrl();
for (int i=0; iGetItemCount(); i++) {
if (IsTextFileName(m_dlghelper.GetItemName(i))) {
pCmdUI->Enable(TRUE); return;
}
}
pCmdUI->Enable(FALSE);
}
關鍵在於 CFileDlgHelper 內部透過 WM_ENTERIDLE + WM_COMMAND/NOTIFY 旗標機制,讓模態對話框也能正常運作 ON_UPDATE_COMMAND_UI。
CDN_SELCHANGE 通知,取得選取項目路徑,讀取檔案前 512 位元組顯示。Win2000 下 CFileDialog::GetPathName() 在選取資料夾時仍回傳檔案名稱,導致預覽區顯示錯誤內容。
對話框清單控制項實際嵌套在 SHELLDLL_DefView 視窗內 (Spy++ 可見),清單控制項 ID 為 1,父視窗 ID 為 lst2。
// 取得真正的 ListCtrl 指標
CListCtrl* pList = (CListCtrl*)GetParent()->GetDlgItem(lst2)->GetDlgItem(1);
// 取得選取項目真實路徑
POSITION pos = pList->GetFirstSelectedItemPosition();
while (pos) {
int idx = pList->GetNextSelectedItem(pos);
CString name = pList->GetItemText(idx, 0);
CString fullPath = GetFolderPath() + "\\" + name; // CDM_GETFOLDERPATH
// 判斷是否為資料夾
struct _stat st;
if (_stat(fullPath, &st) == 0 && (st.st_mode & _S_IFDIR)) {
// 是資料夾 -> 清空預覽
} else {
// 是檔案 -> 讀取內容預覽
}
}
#include
static BOOL IsFolder(LPCTSTR path) {
struct _stat st;
return _stat(path, &st) == 0 && (st.st_mode & _S_IFDIR);
}
注意:非資料夾不代表是檔案,可能是「網上鄰居」、「我的電腦」等 Shell 物件,需配合 Shell API 進一步判斷。
若使用者在資源管理器設定「隱藏已知檔案類型副檔名」,清單顯示的名稱將不含 .txt,導致 IsTextFileName 失效。解決需使用 SHGetFileInfo 取得真實副檔名或 IShellFolder 介面枚舉,屬進階主題。
本文提供生產級 CFileDialog 客製化框架,解決預覽、批次選取、UI 更新、Win2000 路徑判斷等實務難題,CFileDlgHelper 封裝可直接重用於其他專案。