Windows 應用程式幾乎所有工作都建立在訊息處理機制之上,系統訊息分為標準視窗訊息、控制項通知訊息與命令訊息三大類。然而實際開發中,我們常需要定義專屬訊息來通知程式特定事件發生,這就是「使用者自訂訊息」。由於 ClassWizard 不支援直接新增自訂訊息,必須手動撰寫程式碼,完成後 ClassWizard 才能像處理標準訊息一樣管理自訂訊息。
Microsoft 建議自訂訊息編號至少從 WM_USER + 100 開始,避免與新版控制項使用的 WM_USER 範圍衝突。範例:
#define WM_MY_MESSAGE (WM_USER + 100)
處理函式必須採用標準簽名,回傳 LRESULT,接受 WPARAM 與 LPARAM 兩個參數:
LRESULT CMainFrame::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
// TODO: 處理自訂訊息邏輯
AfxMessageBox(_T("收到自訂訊息"));
return 0;
}
於 AFX_MSG 區塊中加入函式宣告:
class CMainFrame : public CMDIFrameWnd
{
// ...
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
使用 ON_MESSAGE 巨集將訊息 ID 對應至處理函式:
BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_TIMER()
ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
進階技巧: 若需系統全域唯一訊息,可改用RegisterWindowMessageSDK 函式搭配ON_REGISTERED_MESSAGE巨集,其他步驟相同。
VC++ 雖提供強大的視窗生成工具,但預設樣式千篇一律。以下介紹多種客製化手法,打造專屬程式風格。
在 CMyMDIApp::InitInstance() 中,命令列解析程式碼之後加入:
cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing;
執行後程式僅顯示主框架視窗,不再自動開啟子視窗。
方法一: 呼叫 CWnd::SetWindowText() 設定固定標題。
方法二(保留文件名): 使用資源編輯器修改字串表 IDR_MAINFRAME 的 caption 欄位,第一個參數即為主視窗標題。
方法三(移除文件名後綴): 在 CMainFrame::PreCreateWindow 中移除 FWS_ADDTOTITLE 樣式旗標:
cs.style &= ~FWS_ADDTOTITLE;
透過覆寫 PreCreateWindow(CREATESTRUCT& cs) 修改建立參數:
主框架視窗範例:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CFrameWnd::PreCreateWindow(cs))
return FALSE;
cs.cx = 450; cs.cy = 300; // 設定初始尺寸
cs.style = WS_POPUPWINDOW; // 移除標題列與最大化按鈕
return CFrameWnd::PreCreateWindow(cs);
}
子視窗範例:
BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
{
// 修改 cs 結構調整子視窗屬性
return CMDIChildWnd::PreCreateWindow(cs);
}
檢視視窗範例(設定白底、無游標、重繪):
BOOL CMyView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.lpszClass = AfxRegisterWndClass(
CS_HREDRAW | CS_VREDRAW,
0,
(HBRUSH)::GetStockObject(WHITE_BRUSH),
0);
return CScrollView::PreCreateWindow(cs);
}
AfxRegisterWndClass 參數依序為:類別風格、游標控制碼、背景筆刷、圖示控制碼。
使用 CScrollView 簡化捲動邏輯,於 OnInitialUpdate 設定捲動範圍:
void CMyScrollView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
CSize sizePage(400, 400); // 水平/垂直捲動閾值
SetScrollSizes(MM_TEXT, sizePage);
}
視窗尺寸小於 400×400 像素時自動顯示捲動條;若修改為 sizePage(600, 800),則 600×800 以下才出現捲動條。
在 MainFrm.h 加入分割器成員與覆寫函式:
CSplitterWnd m_wndSplitter;
virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext);
於 MainFrm.cpp 實作:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
return m_wndSplitter.Create(this, 2, 2, CSize(20, 20), pContext);
}
參數說明:父視窗、列數(2)、行數(2)、最小格尺寸、建立內容。分割後需呼叫 pDoc->UpdateAllViews(NULL) 同步各格內容。