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

MFC 系統托盤程式設計完全指南:最小化至托盤、右鍵選單與生命週期控制

     編輯:關於VC++

現代 Windows 應用程式常需支援「關閉視窗不退出程式、最小化至系統托盤、托盤圖示右鍵選單操作」等行為模式。本文系統性解析 MFC 托盤程式設計核心技術,包含 CSystemTray 類封裝、系統選單修改、視窗狀態管理、動畫效果與優雅退出機制,提供可直接套用的完整架構。

核心架構設計

1. 自訂托盤通知訊息

// 標頭檔中定義唯一訊息 ID
#define WM_USER_TRAY_NOTIFICATION (WM_USER + 0x101)

2. CSystemTray 類別成員宣告


class CVioletPlayDlg : public CDialog
{
    // ...
protected:
    CSystemTray m_trayIcon;           // 托盤圖示物件
    BOOL m_bStartMinimized;           // 啟動時最小化旗標
    
    // 訊息對應
    afx_msg LONG OnTrayNotification(WPARAM wParam, LPARAM lParam);
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    // ...
};

系統選單重構:移除預設「關閉」與「關於」


BOOL CVioletPlayDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    
    // 1. 取得系統選單控制代碼
    CMenu* pSystemMenu = GetSystemMenu(FALSE);
    if (pSystemMenu)
    {
        // 移除分隔線(第 8 項)與「關於」(第 7 項),索引從 0 開始
        pSystemMenu->DeleteMenu(8, MF_BYPOSITION);  // 分隔線
        pSystemMenu->DeleteMenu(7, MF_BYPOSITION);  // 關於
    }
    
    // 2. 移除對應的命令處理碼(OnSysCommand 中)
    // 原始碼中移除:
    // if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); }
    
    return TRUE;
}

關閉按鈕行為重定向:最小化至托盤而非退出


void CVioletPlayDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == SC_CLOSE)
    {
        // 隱藏視窗而非銷毀
        // 動畫效果需在 stdafx.h 定義 WINVER 0x500
        #undef WINVER
        #define WINVER 0x500
        
        AnimateWindow(GetSafeHwnd(), 1000, AW_HIDE | AW_BLEND);  // 淡出動畫
        KillTimer(0);                                             // 清除計時器
        ShowWindow(SW_HIDE);                                      // 隱藏視窗
        return;
    }
    
    CDialog::OnSysCommand(nID, lParam);
}

托盤圖示建立與啟動邏輯


BOOL CVioletPlayDlg::OnInitDialog()
{
    // ... 前述初始化 ...
    
    // 建立托盤圖示
    // 參數:父視窗、回調訊息、工具提示文字、圖示控制代碼、右鍵選單資源 ID
    m_trayIcon.Create(this, WM_USER_TRAY_NOTIFICATION, 
                     "VioletPlay", m_hIcon, IDR_TUOPAN);
    
    // 判斷是否啟動時最小化至托盤
    m_bStartMinimized = GetProfileInt(_T(""), _T("StartMinimized"), FALSE);
    if (m_bStartMinimized)
    {
        PostMessage(WM_CLOSE);  // 觸發上述隱藏邏輯
    }
    
    return TRUE;
}

托盤通知訊息處理:右鍵選單、左鍵還原


LONG CVioletPlayDlg::OnTrayNotification(WPARAM wParam, LPARAM lParam)
{
    switch (lParam)
    {
    case WM_RBUTTONDOWN:  // 右鍵點擊:顯示內容選單
        {
            CMenu oMenu;
            if (oMenu.LoadMenu(IDR_TUOPAN))
            {
                CMenu* pPopup = oMenu.GetSubMenu(0);
                ASSERT(pPopup != NULL);
                
                CPoint oPoint;
                GetCursorPos(&oPoint);
                
                // 動態更新「顯示/隱藏」選單文字
                if (IsWindowVisible())
                    oMenu.ModifyMenu(IDC_SHOW, MF_STRING, IDC_SHOW, "隱藏(&H)");
                else
                    oMenu.ModifyMenu(IDC_SHOW, MF_STRING, IDC_SHOW, "顯示(&S)");
                
                SetForegroundWindow();  // 關鍵:確保選單獲得焦點
                pPopup->TrackPopupMenu(
                    TPM_LEFTALIGN | TPM_RIGHTBUTTON,
                    oPoint.x, oPoint.y, this);
            }
        }
        break;
        
    case WM_LBUTTONDOWN:   // 左鍵單擊
    case WM_LBUTTONDBLCLK: // 左鍵雙擊:還原視窗
        OnShow();
        break;
    }
    return 0;
}

選單命令處理:顯示/隱藏/退出


// 顯示/隱藏切換
void CVioletPlayDlg::OnShow()
{
    if (IsWindowVisible())
    {
        AnimateWindow(GetSafeHwnd(), 1000, AW_HIDE | AW_BLEND);
        ShowWindow(SW_HIDE);
    }
    else
    {
        ShowWindow(SW_SHOW);
        AnimateWindow(GetSafeHwnd(), 1000, AW_SHOW | AW_BLEND);
        SetForegroundWindow();
    }
}

// 真正退出程式(托盤選單「退出」)
void CVioletPlayDlg::OnTrayExit()
{
    if (AfxMessageBox("確定要完全退出程式嗎?", MB_YESNO) == IDYES)
    {
        AnimateWindow(GetSafeHwnd(), 1000, AW_HIDE | AW_BLEND);
        KillTimer(0);
        ::PostQuitMessage(0);  // 真正結束訊息迴圈
    }
}

// 檔案選單「退出」僅隱藏視窗
void CVioletPlayDlg::OnFileExit()
{
    if (AfxMessageBox("確定要最小化至托盤嗎?", MB_YESNO) == IDYES)
    {
        AnimateWindow(GetSafeHwnd(), 1000, AW_HIDE | AW_BLEND);
        KillTimer(0);
        ShowWindow(SW_HIDE);
    }
}

關鍵技術細節總結

技術點關鍵實作注意事項
托盤類別CSystemTray 繼承 CWnd,封裝 Shell_NotifyIcon需包含 SystemTray.h/cpp,來源可參考 VCKBASE 等開源實作
動畫效果AnimateWindow + AW_BLEND需 #define WINVER 0x500 在 stdafx.h 最前方
選單焦點TrackPopupMenu 前呼叫 SetForegroundWindow()否則選單可能無法正確接收鍵盤/滑鼠事件
動態選單ModifyMenu 依視窗可見狀態切換文字使用 MF_BYCOMMAND 而非位置索引,避免選單結構變更失效
雙重退出語意托盤「退出」= 終止進程;標題列/檔案「退出」= 隱藏至托盤務必透過確認對話框區分使用者意圖

進階擴充建議

  1. 氣球提示支援:使用 NIF_INFO 旗標與 Shell_NotifyIcon(NIM_MODIFY) 顯示豐富通知
  2. 多圖示狀態:根據程式狀態(錄音中、下載中、錯誤)動態切換托盤圖示
  3. 單一實例強制:啟動時使用 CreateMutex 檢測既有實例,若存在則啟動既有視窗並退出新實例
  4. 設定持久化:將視窗位置、大小、最小化偏好寫入註冊表或 INI 檔

總結

專業級托盤應用程式需精心設計視窗生命週期、使用者互動語意與視覺回饋。本文提供的架構涵蓋系統選單重構、關閉行為重定向、動畫效果、動態右鍵選單與雙重退出機制,可作為 MFC 托盤應用開發的標準範本。實務上建議將托盤邏輯封裝為可重用的 CTrayAppModule 類別,降低主對話框複雜度。

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