为子控件添加自定义绘图方式
在MFC應用程序中,有時會遇到需要讓指定的控件實現自繪。但是看該控件的事件,沒有一個像是能承擔這種責任的。
我們都知道控件也是窗口,也都有消息循環。所以:
方案一:寫個新類,繼承自某個窗口類,在它的WM_PAINT消息中實現自繪。這種方法需要定義一個新類,不是太方便。
方法二:利用SetWindowLong修改該控件的消息處理函數,在WM_PAINT消息中實現自繪。本文就采用此方案。
------------------------------------------------------------
方案二實現過程,環境:Win XP + VC2010(MFC)
完整源碼
1. 界面:
2. 為Button1添加點擊事件
void CdelDlg::OnBnClickedButton1() {m_bgIndex = (m_bgIndex + 1) % 7;Invalidate(); // 刷新背景 }3. 添加2個全局變量和2個全局函數(繪圖函數和子控件新的消息處理函數)
// 定義全局變量和全局函數 WNDPROC oldProc_PIC1 = 0; // 保存IDC_PIC1控件默認的消息處理函數地址 CString imgPath; // 保存背景圖片地址BOOL DrawPic(HWND hWnd) // 在指定的控件上畫圖 {CImage img;if(SUCCEEDED(img.Load(imgPath))){CWnd *pWnd = CWnd::FromHandle(hWnd);CPaintDC dc(pWnd); // dc必須用指定的控件窗口來初始化,否則將看不到繪圖結果CRect rect;pWnd->GetClientRect(rect); // 獲取控件的大小img.Draw(dc.m_hDC, rect);return TRUE;}return FALSE; // 繪圖失敗 }LRESULT NewProc_PIC1(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // IDC_PIC1控件對應的新的消息函數 {switch (message){case WM_PAINT:if(DrawPic(hWnd))return S_OK;elsebreak;default:break;}return CallWindowProc(oldProc_PIC1, hWnd, message, wParam, lParam); // 調用默認的消息函數 }4. 在對話框的OnInitDialog()中為子控件IDC_PIC1 指定新的消息循環函數 BOOL CdelDlg::OnInitDialog() {CDialogEx::OnInitDialog();// 設置此對話框的圖標。當應用程序主窗口不是對話框時,框架將自動// 執行此操作SetIcon(m_hIcon, TRUE); // 設置大圖標SetIcon(m_hIcon, FALSE); // 設置小圖標oldProc_PIC1 = (WNDPROC)SetWindowLong(GetDlgItem(IDC_PIC1)->m_hWnd, GWL_WNDPROC, (LONG)NewProc_PIC1); // 為控件設置新的消息處理函數return TRUE; // 除非將焦點設置到控件,否則返回 TRUE }
5. 處理對話框的WM_PAINT和子控件(IDC_PIC1)的WM_PAINT消息
5.1 主對話框的WM_PAINT消息處理
void CdelDlg::OnPaint() {if (IsIconic()){CPaintDC dc(this); // 用于繪制的設備上下文SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);// 使圖標在工作區矩形中居中int cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// 繪制圖標dc.DrawIcon(x, y, m_hIcon);}else{CString file[] = {"1.jpg", "2.jpg", "3.jpg", "11.jpg", "12.bmp", "Smiley.png", "11.gif"};m_imgPath.Format("res\\%s", file[m_bgIndex]);imgPath = m_imgPath;DrawPic(this->m_hWnd); // 在控件上繪圖//CDialogEx::OnPaint();} }5.2 子控件(IDC_PIC1)的WM_PAINT消息處理,即步驟3中的NewProc_PIC1()
點滴經驗:
1. 本來我想在對話框的PreTranslateMessage()中攔截子控件的WM_PAINT消息的,但是經過試驗才知道,窗口在第一次啟動時不會觸發WM_PAINT消息的,只有失去焦點或重新獲得焦點時才會觸發WM_PAINT消息。所以放棄使用該方法。
2. CPaintDC的構造函數有個參數的,一般看到的代碼中都是用dc(this)來初始化的,因為這個代碼是在類的成員函數中,所以沒問題。另外在進行繪圖時,所指定的rect要與初始化dc的這個參數(如pWnd)對應的坐標(pWnd->GetClientRect(rect))相對應,否則可能看不到繪圖結果或繪圖的位置不對。
3. 在對話框的自繪處理中,要屏蔽掉其基類的OnPaint(),否則自繪上去的圖形將被覆蓋。但是,將基類的OnPaint()語句放在自繪語句的最后,是可以的,即我文中的代碼可以取消對CDialogEx::OnPaint()的注釋。
總結
以上是生活随笔為你收集整理的为子控件添加自定义绘图方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 智慧水利大屏可视化决策系统
- 下一篇: MQ消息队列之MSMQ