承接上一篇文章,我們已經完成了 MP3 播放器的基本架構與音樂清單管理。本篇將深入探討播放器的核心邏輯:對話框初始化、系統托盤整合、播放進度條拖曳控制,以及自動循環播放的定時器機制。完整掌握這些技術,即可打造一款隱藏於托盤、無需人工干預即可定時循環播放的企業級音樂播放器。
OnInitDialog() 是對話框啟動的關鍵入口。在此階段,我們不僅要完成基本的圖示設定,還需建立定時器、隱藏工作列按鈕、將程式最小化至系統托盤。以下是關鍵步驟解析:
BOOL CMp3PlayerDlg::OnInitDialog()
{
// 設定視窗標題
m_DlgStyle.sTitleName = _T("音樂播放器");
COwnerDrawDialog::OnInitDialog();
// 設定大小圖示
SetIcon(m_hIcon, TRUE); // 大圖示
SetIcon(m_hIcon, FALSE); // 小圖示
...
}
啟動兩個定時器分別服務於不同用途:
m_bAutoRun 為真時啟動,負責檢查排程時間、觸發自動播放/停止邏輯。SetTimer(4, 100, NULL); // 介面更新定時器
if (m_bAutoRun)
SetTimer(1, 1000, NULL); // 自動排程定時器
透過修改擴充視窗風格 WS_EX_TOOLWINDOW 可隱藏工作列按鈕;接著使用 Shell_NotifyIcon API 在系統托盤區建立圖示,並指定自訂訊息 WM_DLG_SHOW 作為回呼,以處理滑鼠互動。
// 修改視窗風格:隱藏工作列按鈕
long wl = GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE);
SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, WS_EX_TOOLWINDOW);
// 建立托盤圖示資料結構
NOTIFYICONDATA tnd = {0};
tnd.cbSize = sizeof(NOTIFYICONDATA);
tnd.hWnd = m_hWnd;
tnd.uID = IDI_MAINFRAME;
tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
tnd.uCallbackMessage = WM_DLG_SHOW; // 自訂回呼訊息
tnd.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_MAINFRAME));
_strcpy(tnd.szTip, _T("音樂播放器"));
// 加入托盤
Shell_NotifyIcon(NIM_ADD, &tnd);
OnHScroll)使用者拖曳進度條時,需即時更新播放位置。OnHScroll 處理 CScrollBar 事件,重點在於 SB_THUMBTRACK(拖曳中)與 SB_THUMBPOSITION / SB_ENDSCROLL(拖曳結束)的區別處理。
void CMp3PlayerDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
if (pScrollBar == &m_MPBar) // 確認是播放進度條
{
switch (nSBCode)
{
case SB_THUMBTRACK: // 正在拖曳
m_nPos = nPos; // 暫存目標位置,不立即跳轉播放
break;
case SB_THUMBPOSITION:
case SB_ENDSCROLL: // 拖曳結束
// 真正設定播放位置 (毫秒)
m_Music.SetPosition(m_nPos * 1000);
break;
}
}
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
關鍵點:
SB_THUMBTRACK) 僅更新介面顯示位置,避免頻繁呼叫 SetPosition 造成卡頓。SetScrollRange) 在 Play() 函式中依音樂總長度動態設定。定時器 1 每秒觸發一次,檢查目前時間是否落於任一排程時段(如上班、午休、下班)。若符合且播放清單非空,則呼叫 PlayNext() 循環播放;時段結束時自動停止。完整排程判斷邏輯可參考專案原始碼中的 OnTimer(UINT nIDEvent) 實作。
本篇完整呈現了 MP3 播放器從初始化、托盤駐留、進度條互動到自動排程的關鍵技術。結合上一篇的音樂清單管理與多檔案過濾,已構成一個功能完整的企業級自動播放系統。後續可擴充:
完整原始碼與專案檔可至原文連結下載。若有任何技術疑問,歡迎留言交流。