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