首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

CFileDialog 客製化完整實戰:預覽窗格、全選按鈕、資料夾偵測與 Win2000 相容性修正

     編輯:關於VC++

前言

標準 CFileDialog 功能有限,企業級應用常需加入預覽窗格、批次選取按鈕、檔案類型過濾等功能。本文完整示範如何繼承 CFileDialog、建立自訂範本資源、子類化清單控制項、處理 UI 更新機制,並解決 Windows 2000 下 GetPathName 回傳錯誤路徑的經典問題。

架構設計:三大輔助類別

  • CFileDialogHook:子類化檔案對話框本身,攔截 WM_COMMAND/WM_NOTIFY 觸發 UI 更新旗標。
  • CFileDialogOwnerHook:子類化父視窗 (框架),攔截 WM_ENTERIDLE 執行 UpdateDialogControls。
  • CFileDlgHelper:封裝上述兩類別,提供 GetListCtrl()、GetItemName()、IsItemFolder() 等高階介面,使用者只需在 OnInitDialog 呼叫 Init(this)。

核心功能實作

1. 「全部」按鈕 - 選取所有 .txt 檔案


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

2. UI 更新 - 無 .txt 時停用「全部」按鈕


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。

3. 預覽窗格 - 選取單一 .txt 顯示前幾行

  • 自訂對話框範本加入靜態文字控制項做預覽區。
  • 處理 CDN_SELCHANGE 通知,取得選取項目路徑,讀取檔案前 512 位元組顯示。
  • 若選取為資料夾則清空預覽區。

Windows 2000 相容性難題與解決

問題

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 {
        // 是檔案 -> 讀取內容預覽
    }
}

IsFolder 輔助函數


#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 封裝可直接重用於其他專案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved