MFC學習--對話框及窗口背景色 .


設置對話框背景顏色及背景圖片可在OnCtlColor(),OnEraseBkgnd(),OnPaint()里設置,對話框初始化完畢,顯示窗口時按順序調用OnSize()>OnEraseBkgnd()>OnPaint()>OnCtlColor()。

OnEraseBkgnd()中默認調用基類(CFrameWnd)的OnEraseBkgnd(), 用窗口類中注冊的Brush繪制背景。因此,繪制窗口背景時最好重寫OnEraseBkgnd(),但后面不能調用基類的OnEraseBkgnd()。

同時,MSDN關 WM_ERASEBKGND消息的說明中有寫道,DefWindowProc使用窗口類注冊的背景Brush繪制背景,如果背景Brush是NULL,則需處理處理WM_ERASEBKGND消息。言外之意,處理WM_ERASEBKGND消息但不傳給DefWindowProc就可以自己繪制背景,系統又不會重復繪制一遍。

When this member(背景畫刷) is NULL, an application must paint its own background whenever it is requested to paint in its client area. To determine whether the background must be painted, an application can either process the WM_ERASEBKGND message or test the fErase member of the PAINTSTRUCT structure filled by the BeginPaint function.

方法如下:

  1. BOOL CMainWindow::OnEraseBkgnd(CDC* pDC)  
  2. {  
  3.     // TODO: 在此添加消息處理程序代碼和/或調用默認值   
  4.     CRect rc;  
  5.     GetClientRect(&rc);  
  6.     pDC->FillSolidRect(&rc ,  RGB(0,0,255)  );   
  7.     //return CFrameWnd::OnEraseBkgnd(pDC);   
  8.     return TRUE;  
  9. }  

 當然,也可以重寫OnEraseBkgnd()直接返回TRUE,然后在OnPaint()中改變背景。

更直接的方法是修改窗口類,實現更改背景顏色

  1. BOOL CMainWindow::PreCreateWindow(CREATESTRUCT& cs)  
  2. {  
  3.     // TODO: 在此添加專用代碼和/或調用基類   
  4.     if( CFrameWnd::PreCreateWindow(cs))  
  5.     {     
  6.         //改變窗口類   
  7.         WNDCLASS   wndclass;   
  8.   
  9.         ::GetClassInfo(AfxGetInstanceHandle(),cs.lpszClass,&wndclass);   
  10.         //wndclass.hbrBackground   =   (HBRUSH)(COLOR_3DFACE   +   1);    
  11.         //wndclass.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);   
  12.         wndclass.hbrBackground=CreateSolidBrush(RGB(0,100,100));  
  13.         wndclass.hbrBackground=m_BKBrush;//m_BKBrush不能為函數局部變量   
  14.         wndclass.hbrBackground=*(new CBrush(RGB(25,25,0)));//最方便的方法   
  15.         //wndclass.hCursor   =   AfxGetApp()-> LoadCursor(IDC_CURSOR1);    
  16.         wndclass.lpszClassName   =   _T("newViewClassName ");   
  17.         VERIFY(AfxRegisterClass(&wndclass));   
  18.         cs.lpszClass=wndclass.lpszClassName;  
  19.         return TRUE;  
  20.     }  
  21.     return FALSE;  
  22. }  


 

若改變對話框大小,比如全屏顯示ShowWindow(SW_SHOWMAXIMIZED);UpdateWindow();

其中 ShowWindow會調用OnSize()->OnEraseBkgnd(),

        UpdateWindow();調用OnPaint()->OnCtlColor(),

      若對話框中沒有設置消息響應OnEraseBkgnd(),,則系統默認消息響應OnEraseBkgnd()會調用OnCtlColor()設置對話框背景(即替代OnEraseBkgnd())

      對話框的背景設置可在OnCtlColor()中進行,因為OnCtlColor()一般會被多次調用,所以要想設置的CFont,CBrush等應在OnInitDialog中初始化,若要在OnCtlColor()中設置,在設置前先調用Detach就可以了,如下示例

  1. HBRUSH CDb3Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)   
  2. {   
  3.     if(pWnd->GetDlgCtrlID()==IDC_STATIC5)  
  4.    {  
  5.        
  6.     m_font.CreatePointFont(300,"宋體");  
  7.     pDC->SelectObject(&m_font);  
  8.     m_font.Detach();              
  9.     pDC->SetBkMode(TRANSPARENT);     
  10.     return (HBRUSH)::GetStockObject(NULL_BRUSH);         
  11.    }  
  12. }  


但是如果在OnCtlColor()在設置背景圖片,則圖片不會隨對話框大小按比例縮放

所以可調用StretchBlt()函數設置,如下示例:

  1. void CDb3Dlg::OnPaint()   
  2. {  
  3. CClientDC cdc(this); CDC comdc;  
  4. comdc.CreateCompatibleDC(&cdc);  
  5. CBitmap bitmap;  
  6. bitmap.LoadBitmap(IDB_BITMAP2);  
  7. comdc.SelectObject(&bitmap);  
  8. CRect rect;  
  9. GetClientRect(rect);  
  10. BITMAP bit;  
  11. bitmap.GetBitmap(&bit);  
  12. cdc.StretchBlt(0,0,rect.Width(),rect.Height(),&comdc,0,0,bit.bmWidth,bit.bmHeight,SRCCOPY);  
  13. }//全屏顯示對話框背景圖片(限bmp格式)  

對於窗口程序,一般有個特點:窗口大部分的區域保持不變,只有不分區域需要重新繪制。如果將整個窗口全部刷新的畫,就做了許多不必要的工作,因而,MFC采用了一套基於無效區的處理機制。在分析無效區處理之前,我們要明白一個現實,現在的機器還不夠牛,如果夠牛的話,我們干脆將整個窗口不斷的重新繪制好了。事實上即使夠牛也不行,對於一個單線程程序,通過一個while循環不斷的刷新窗口,程序也無法相應其他消息(除非使用多線程),看來使用無效區的處理機制還是有其必然性的。

VC程序是基於消息機制的,你所做的任何操作,比如點擊鼠標,拖動窗口,首先進入系統的消息隊列。這里的系統消息隊列包括多個程序的消息,系統再將消息發送給相應的程序。既然是隊列,這就有一個先進先出的問題,屏幕上的無效區更新消息出現的頻率就會特別高。比如當左上角更新的消息還沒有處理,右下角更新的消息已經過來了。為了避免多次處理WM_PAINT消息,系統就將這些窗口更新消息合並到一條,只是將無效區范圍變成包括這兩次更新無效區范圍在內的矩形區域。這樣就減少了WM_PAINT消息的處理次數,提高了效率。

那么,在OnPaint消息處理函數中,又是怎樣實現更新無效區的呢?首先,要明白MFC中所有繪圖操作都是基於設備描述表(Device Context,簡稱DC)的,具體信息可參看任何一本VC教材。DC中包含了繪圖設備的各種信息,對於屏幕繪圖,其實就是有一塊內存(顯存),專門用來存放要顯示到屏幕上的信息,顯示器以85HZ的頻率(我以前的顯示器)將其內容刷新的屏幕上。這里就到了關鍵點,顯示器的刷新是將顯存中的內容完全更新到顯示器上,不存在無效區處理的問題,那么,無效區的處理一定發生在DC的繪圖處理上。事實確實如此,當程序調用OnPaint消息時,首先將無效區范圍傳遞給DC,DC在進行繪圖操作時,就只更新無效區范圍內的信息,其他地方的不管,這就提高了效率。

現在你明白OnPaint的處理是怎么一回事了吧?這里還想說一下Invalidate和UpdateWindow的區別。Invalidate在消息隊列中加入一條WM_PAINT消息,其無效區為整個客戶區。而UpdateWindow直接發送一個WM_PAINT消息,其無效區范圍就是消息隊列中WM_PAINT消息(最多只有一條)的無效區。效果很明顯,調用Invalidate之后,屏幕不一定馬上更新,因為WM_PAINT消息不一定在隊列頭部,而調用UpdateWindow會使WM_PAINT消息馬上執行的,繞過了消息隊列。如果你調用Invalidate之后想馬上更新屏幕,那就加上UpdateWindow()這條語句。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM