熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

創建有個性的對話框之MFC篇

2013-11-13 09:41:16  來源: .NET編程 
想使自己的軟件與眾不同就要給軟件加點一個顏色搭配協調的窗口要比windows千篇一律的灰底黑字更能吸引別人的眼球設想如果html浏覽器顯示的網頁都是白底黑字還會有那麼多的mm喜歡上網嗎?可能互聯網的人氣將下降一半做個出色的界面對於老手來說可能不在話下但是對於新手來說還是無從下手使用BCGControlBar和Xtreme Toolkit是個很好的選擇不過對於一個小程序使用這麼大的庫未免有頭重腳輕的感覺其實不使用這些龐然大物一樣可以做個很的界面本文就結合CSDN論壇上經常被問起的問題介紹幾個給對話框上色的方法本文的方法都是針對MFC程序的其他方法請參看創建有個性的對話框之ATL/WTL篇

  第一步改變對話框的背景顏色

  如何改變對話框的背景顏色這個問題常常出現在論壇上可見大家對Windows默認的灰色對話框是多麼不滿MFC程序修改對話框的背景和文字顏色最簡單的方法就是調用SetDialogBkColor函數SetDialogBkColor是CWinApp類的成員函數以下是該函數的原型

void CWinApp::SetDialogBkColor(COLORREF clrCtlBk COLORREF clrCtlText);

  請注意SetDialogBkColor函數並不是對Windows的某個API的封裝他是MFC框架的一部分所以不使用MFC的程序也就不能享受這種方便這個函數的使用很簡單在程序的CWinApp派生類的InitInstance函數中添加一行代碼就行了

SetDialogBkColor(RGB()RGB());

  圖 就是運行效果

  

  圖 SetDialogBkColor效果圖

  使用SetDialogBkColor也有局限的地方那就是所有的控件文字顏色都一樣不能針對不同的控件設置不同的文字顏色還有就是不能設置Edit控件的顏色不使用SetDialogBkColor函數直接編寫代碼控制對話框的背景顏色和控件文字顏色也不是很困難的事情並且這種方法能夠提供更靈活的顏色設置方案比如對不同類型的控件使用不同的文字顏色使用高亮度的背景顏色突出某個控件等等最重要的是能夠控制Edit控件的文字和背景顏色下面就介紹這種方法

  首先是改變對話框的背景顏色當Windows系統需要重畫某個窗口客戶區的背景的時候就會向該窗口發送WM_ERASEBKGND 消息窗口的處理過程響應這個消息重新畫窗口的背景這個過程稱之為自畫改變對話框的背景顏色的原理很簡單就是響應這個消息用自定義的顏色填充對話框的客戶區背景代替對話框窗口默認的背景填充動作許多新手經常問為什麼在class wizard中找不到對話框的WM_ERASEBKGND消息是不是對話框沒有這個消息?其實對話框也是窗口它也有WM_ERASEBKGND消息只是MFC的class wizard使用的dialog過濾器將其過濾掉了(只是在message窗口的顯示中過濾了並不是真的不響應這個消息)為的是代碼編寫過程中突出對話框專有的消息和控件事件如圖 所示只要在class wizard中的class info table標簽下將消息過濾器改成Windows就可以在對話框的消息列表中看到WM_ERASEBKGND了

  

  圖 修改消息過濾器

  現在通過class wizard添加WM_ERASEBKGND的消息響應函數並如下所示修改這個函數

BOOL CCustDlgDlg::OnEraseBkgnd(CDC* pDC)
{
  CRect rcClient;
  GetClientRect(&rcClient);
  pDC>FillRect(&rcClient&m_brBkgnd);

  return TRUE;

//  return CDialog::OnEraseBkgnd(pDC);
}

  m_brBkgnd是個CBrush在此之前已經初始化過了關鍵代碼是最後返回TRUE而不是默認的調用基類函數返回TRUE意在告訴Windows我已經畫過背景了你不要再畫了現在來看看運行的效果

  

  圖 重畫背景的效果

  使用位圖作為對話框的背景也不難就是在整個客戶區畫一個位圖背景

   

  第二步改變控件的顏色

  看起來不如剛才效果好控件文字的顏色和背景色都沒有改變這是因為我們還沒有處理WM_CTLCOLOR消息WM_CTLCOLOR是Windows的控件向其父窗口發送最頻繁的通知消息之一例如許多控件發送WM_CTLCOLOR消息給父窗口讓父窗口提供畫刷來畫自己的背景MFC的窗口類對這個通知消息特殊對待如果父窗口沒有處理這個通知消息MFC的窗口類就根據WM_CTLCOLOR通知消息的來源將這個WM_CTLCOLOR消息發送回控件讓控件自己處理這就是所謂的消息反射不僅是WM_CTLCOLORMFC對很多通知消息都做了反射不過我們今天的例子沒有使用消息反射我們在控件的父窗口也就是對話框窗口處理這個通知消息還有一點需要說明的是WM_CTLCOLOR消息是位的Windows平台的消息位的Windows平台上取而代之的是一系列更明確的通知消息

  WM_CTLCOLORBTN   按鈕控件
WM_CTLCOLORDLG   對話框
WM_CTLCOLOREDIT   編輯控件
WM_CTLCOLORLISTBOX   列表框控件
WM_CTLCOLORSCROLLBAR   滾動條控件
WM_CTLCOLORSTATIC   靜態文本控件

  MFC為了兼容性考慮仍舊使用OnCtlColor響應這些消息但是通過參數nCtlColor來具體的區分他們在這個函數中我們可以通過改變pDC參數的屬性來改變控件的繪制並返回相應的畫刷句柄給控件控件使用這個畫刷畫自己的背景下面是我們修改後的OnCtlColor函數

HBRUSH CCustDlgDlg::OnCtlColor(CDC* pDC CWnd* pWnd UINT nCtlColor)
{
  HBRUSH hbr = CDialog::OnCtlColor(pDC pWnd nCtlColor);
  
  pDC>SetTextColor(m_clrText);
  pDC>SetBkMode(TRANSPARENT);

  return (HBRUSH)m_brBkgnd; //因為CBrush類實現了HBRUSH類型轉換操作符
  
//  return hbr;
}

  圖 就是這段代碼的效果在這裡我們不分青紅皂白向所有的控件返回我們自己的畫刷看起來不錯Edit控件的文字顏色也改了但是好像多行Edit控件有了麻煩看來需要對多行Edit控件特殊對待

  

  圖 重載OnCtlColor之後的效果

  對於多行Edit控件特殊處理如下所示上面的問題解決了

HBRUSH CCustDlgDlg::OnCtlColor(CDC* pDC CWnd* pWnd UINT nCtlColor)
{
  HBRUSH hbr = CDialog::OnCtlColor(pDC pWnd nCtlColor);
  
  if(pWnd>GetDlgCtrlID() == IDC_EDIT_MULTI_LINE) //IDC_EDIT_MULTI_LINE是多行Edir控件的ID
  {
    pDC>SetTextColor(m_clrText);
    return hbr;
  }
  else
  {
    pDC>SetTextColor(m_clrText);
    pDC>SetBkMode(TRANSPARENT);

    return (HBRUSH)m_brBkgnd;
  }
}

  上面的代碼解決了IDC_EDIT_MULTI_LINE的問題但是對每個多行Edit控件都要判斷ID下面的方法可以一勞永逸地解決多行編輯控件的問題

HBRUSH CCustDlgDlg::OnCtlColor(CDC* pDC CWnd* pWnd UINT nCtlColor)
{
  HBRUSH hbr = CDialog::OnCtlColor(pDC pWnd nCtlColor);
  TCHAR szClassName[];

  ::GetClassName(pWnd>GetSafeHwnd()szClassName);
  if(lstrcmpi(szClassName_T(Edit)) == ) //是Edit 控件
  {
    DWORD dwStyle = pWnd>GetStyle();
    if((dwStyle & ES_MULTILINE)  == ES_MULTILINE) //多行edit控件
    {
      pDC>SetTextColor(m_clrText);
      return hbr;
    }
    else
    {
      pDC>SetTextColor(m_clrText);
      pDC>SetBkMode(TRANSPARENT);

      return (HBRUSH)m_brBkgnd;
    }
  }
  else //不是編輯控件
  {
    pDC>SetTextColor(m_clrText);
    pDC>SetBkMode(TRANSPARENT);

    return (HBRUSH)m_brBkgnd;
  }
}

  下面我們針對每個控件設置特殊的顏色區分控件可以通過控件的ID修改控件背景也很簡單直接返回相應的畫刷就可以了下面就是顏色設置的完整代碼


HBRUSH CCustDlgDlg::OnCtlColor(CDC* pDC CWnd* pWnd UINT nCtlColor)
{
 HBRUSH hbr = CDialog::OnCtlColor(pDC pWnd nCtlColor);
 TCHAR szClassName[];

 ::GetClassName(pWnd>GetSafeHwnd()szClassName);
 if(lstrcmpi(szClassName_T(Edit)) == ) //是Edit 控件
 {
  DWORD dwStyle = pWnd>GetStyle();
  if((dwStyle & ES_MULTILINE)  == ES_MULTILINE) //多行edit控件
  {
   pDC>SetTextColor(m_clrText);
   return hbr;
  }
  else
  {
   pDC>SetTextColor(m_clrText);
   pDC>SetBkMode(TRANSPARENT);

   return (HBRUSH)m_brBkgnd;
  }
 }
 else //不是編輯控件
 {
  if(pWnd>GetDlgCtrlID() == IDC_STC_REDTEXT)
  {
   pDC>SetTextColor(RGB());
   pDC>SetBkMode(TRANSPARENT);
   return (HBRUSH)m_brBkgnd;
  }
  else if(pWnd>GetDlgCtrlID() == IDC_STC_BLUETEXT)
  {
   pDC>SetTextColor(RGB());
   pDC>SetBkMode(TRANSPARENT);
   return (HBRUSH)m_brBkgnd;
  }
  else if(pWnd>GetDlgCtrlID() == IDC_STC_BLUETEXTWHITEBACK)
  {
   pDC>SetTextColor(RGB());
   pDC>SetBkMode(TRANSPARENT);
   return (HBRUSH)m_brControlBkgnd;
  }
  else if(pWnd>GetDlgCtrlID() == IDC_CHK_GREEN)
  {
   pDC>SetTextColor(RGB());
   pDC>SetBkMode(TRANSPARENT);
   return (HBRUSH)m_brBkgnd;
  }
  else if(pWnd>GetDlgCtrlID() == IDC_RAD_BLUE)
  {
   pDC>SetTextColor(RGB());
   pDC>SetBkMode(TRANSPARENT);
   return (HBRUSH)m_brBkgnd;
  }
  else if(pWnd>GetDlgCtrlID() == IDC_CHK_GREEN)
  {
   pDC>SetTextColor(RGB());
   pDC>SetBkMode(TRANSPARENT);
   return (HBRUSH)m_brControlBkgnd;
  }
  else if(pWnd>GetDlgCtrlID() == IDC_RADIO)
  {
   pDC>SetTextColor(RGB());
   pDC>SetBkMode(TRANSPARENT);
   return (HBRUSH)m_brControlBkgnd;
  }
  else
  {
   pDC>SetTextColor(m_clrText);
   pDC>SetBkMode(TRANSPARENT);
   return (HBRUSH)m_brBkgnd;
  }
 }
}

  現在看看效果

  

  圖 修改OnCtlColor之後的效果

  上面的代碼是根據控件ID來設置顏色還可以根據控件的類型統一設置某種控件的顏色這就要用到nCtlColor參數nCtlColor參數用來指明發送這個通知消息的控件的類型nCtlColor可以是以下取值

  CTLCOLOR_BTN
CTLCOLOR_DLG
CTLCOLOR_EDIT
CTLCOLOR_LISTBOX
CTLCOLOR_MSGBOX
CTLCOLOR_SCROLLBAR
CTLCOLOR_STATIC

  第三步使用位圖作對話框的背景

  使用位圖作為對話框的背景也很簡單就是在OnEraseBkgnd中用位圖填充客戶區只是在OnCtlColor中需要注意返回空畫刷代替原來的畫刷返回空畫刷是為了阻止控件繪制自己的背景從而破壞位圖背景的完整性但是有時候返回空畫刷會對其他控件產生不良影響所以我們只處理了CTLCOLOR_BTN和CTLCOLOR_STATIC兩種類型的消息

HBRUSH CBmpBkgndDlg::OnCtlColor(CDC* pDC CWnd* pWnd UINT nCtlColor)
{
 HBRUSH hbr = CDialog::OnCtlColor(pDC pWnd nCtlColor);
 
 if(nCtlColor == CTLCOLOR_BTN || nCtlColor == CTLCOLOR_STATIC)
 {
  pDC>SetTextColor(RGB());
  pDC>SetBkMode(TRANSPARENT);
  return (HBRUSH)m_HollowBrush;
 }
 
 pDC>SetTextColor(RGB());
 pDC>SetBkMode(TRANSPARENT);
 return hbr;
}

  下面是使用位圖背景和空畫刷的效果

  

  圖 使用位圖背景的效果

   

  第四步單獨處理按鈕控件

  現在看來按鈕控件還是影響整體效果WM_CTLCOLORBTN好像對於push button類型的按鈕控件沒有效果不過push button也是支持自畫的在使用自畫按鈕之前我們先來看看控件自畫的原理Windows的控件都有默認的外觀但是許多控件有支持自畫也就是讓用戶定制控件的外觀當給一個控件指定自畫的樣式之後控件在重畫自己的時候向父窗口發送WM_MEASUREITEM和WM_DRAWITEM消息父窗口響應這兩個消息定位控件的大小並繪制控件從而使控件有定制的外觀但是每個控件的自畫都由父窗口完成加重了父窗口的負擔也不利於代碼重用所以MFC對這些消息進行了反射處理就是將消息發還位控件由控件響應消息自己繪制這樣將自畫代碼封裝在控件類中提高了代碼的重用性很多MFC的控件類都自己處理這兩個消息派生類可以重載MeasureItem和DrawItem自己畫控件的外觀CButton就是這樣的控件類

  現在就來做一個自畫的按鈕類首先從CButton派生一個類我們命名為CSMButton然後重載DrawItem和PreSubclassWindow重載PreSubclassWindow的原因是在CSMButton子類化按鈕控件之前先給按鈕添加BS_OWNERDRAW樣式否則按鈕就不會向父窗口發送WM_DRAWITEM消息MFC的消息反射就不會發生我們的DrawItem就不會被調用後果很嚴重當然也可以讓CSMButton的使用者自己給按鈕添加BS_OWNERDRAW樣式但是會讓人覺得沒水平後果也很嚴重接下來添加對WM_CAPTURECHANGEDWM_MOUSEMOVEWM_SETCURSOR和WM_KILLFOCUS四個消息的響應函數對這四個消息的響應是為了給按鈕增加更多的功能比如使按鈕看起來象工具欄的按鈕改變鼠標的形狀等等

  關於CSMButton類的使用就像CButton一樣為按鈕添加變量就行了演示代碼中包含了這個類的源代碼以及用法這裡不在贅述CSMButton類的功能很簡單但是完成了一個自畫按鈕的框架大家可以修改代碼實現自己的風格網上也有很多這樣的類功能更強大比如STButton等現在看看CSMButton的效果

  

  圖 使用自畫按鈕後的效果

  第五步使用Picture Box控件

  想要在對話框上顯示位圖可以使用很復雜的控件或CxImage之類的庫也可以很簡單地使用Picture BoxPicture Box默認的樣式使Frame需要手工改成Bitmap如下圖所示

  

  圖 使用位圖

  VC的集成環境不支持位位圖的浏覽和編輯但是並不影響使用本例使用的位圖都是位的為的是省去調色板的處理本人比較懶使用如下代碼就可以更改Picture Box中的位圖

m_hCat = (HBITMAP)::LoadImage(AfxGetResourceHandle()MAKEINTRESOURCE(IDB_BITMAP)IMAGE_BITMAPLR_CREATEDIBSECTION);
GetDlgItem(IDC_STC_PICTURE)>SendMessage(STM_SETIMAGEIMAGE_BITMAP (LPARAM)m_hCat);

  裝載位圖還可以這樣

m_hCat = (HBITMAP)::LoadImage(AfxGetResourceHandle()(LPCTSTR)IDB_BITMAPIMAGE_BITMAPLR_CREATEDIBSECTION);

  這是最終的效果

  

  圖 對話框的最終效果


From:http://tw.wingwit.com/Article/program/net/201311/11335.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.