搭建DirectUi開發平台


DirectUi的效果可以使用GDI、GDI+、DirectX、OpenGL實現,常用的有GDI和GDI+,后兩種有殺雞用牛刀的感覺。在網絡上能找到此方面的教材

現在的軟件越來越多的有很炫目的界面,看來商家是越來越重視用戶體驗了,這個一個流行趨勢呀。從技術上來說,美化界面基本有兩種方式:

1. DirectUi 無句柄自繪控件方式

2. 繼承MFC控件類進行自繪

兩種各有優缺點,前者:實現復雜,控制復雜(如:消息控制、各個控件的基本設置),但自由度很大,你可以實現你能想象到的任何控件。后者:實現簡單、但受制於MFC現有的控件功能,最重要的是窗口一旦多,窗口背景的繪制和子窗口的繪制如若處理不當很容易造成局部貼圖殘缺、拖拽窗口閃爍。所以一般在子窗口控件不隨着主窗口拖拽而發生位置變化時采用后者的方式,其他建議采用前者的方式來完成。

 

由於以后會經常用到DirectUi進行界面美化,於是抽空打了一個DirectUi的開發平台,方便以后開發,DirectUi的開發平台要求如下:

1. 建立在VS2005的MFC Dialog工程之上

2. 實現最基本的一個空的Dialog的皮膚

3. 皮膚實現后,必須保留最基本的Dialog的功能,如:最大化、最小化、雙擊標題欄、單擊任務欄按鈕、拖拽等

4. 建立DirecrUi的引擎,已最簡便的方式便於以后的程序擴展

 

OK,開工了。先建立MFC的Dialog的工程,保持所有屬性都默認,去掉【確定】和【退出】按鈕,如下:

之后我們必須解決一個又一個問題:

 

問題1:我們在什么地方重繪窗口

有3個消息處理可以重繪窗口:WM_ERASEBKGND、WM_PAINT、WM_NCPAINT,第一個只重繪窗口整個背景,包括客戶區和非客戶區,不重繪子窗口;第二個只重繪客戶區,無法重繪非客戶區;第三個重繪非客戶區,也可以重繪客戶區。很明顯,我們應該處理第三個消息,但第一個消息我們也需要處理,整個函數,直接 return TRUE 即可。

 

問題2:頑固的系統默認標題欄

繪制第一步當然是重繪標題欄,在WM_NCPAINT里重繪標題欄后,發現那幾個系統按鈕在窗口激活或者拖動的時候是不是閃現在界面上,相當的頑固,如下方法即可解決:

1. 截獲 WM_NCACTIVATE 消息,此消息函數修改如下:

BOOL CSkinTestDlg::OnNcActivate(BOOL bActive)
{
 this->SendMessage(WM_NCPAINT, 0, 0);
 return TRUE;
}
2. 在 WindowProc 函數中截獲繪制標題欄的消息,代碼如下:

LRESULT CSkinTestDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
 if(message == 0x00AE || // WM_NCUAHDRAWCAPTION
 message == 0x00AF)  // WM_NCUAHDRAWFRAME
 {
  return WM_NCPAINT;
 }

 return __super::WindowProc(message, wParam, lParam);
}

 以上兩步,可以很完美的解決頑固的標題欄按鈕問題。

 

問題3:變態的標題欄消息處理

系統自帶的標題欄會隨着桌面主題的變化,標題欄的高度、系統按鈕的位置都會發生變化,這個相當煩人,咱們自定義的按鈕的位置大小一般都不會和系統按鈕相同。在處理這個問題的過程中,發現了一些導致了一些矛盾之處,幾乎很難調和(抱歉,時間太久了,很多的矛盾忘了),比如:客戶區坐標和非客戶區坐標轉換問題(兩套坐標系,維護比較麻煩)、鼠標在標題欄的雙擊區域、最大化的邊框問題... ... 結合這些問題,最后的處理方式是:截獲 WM_NCCALCSIZE 消息,修改非客戶區大小,讓非客戶區大小為0,所有自繪的東東都在客戶區實現,包括標題欄和邊框。代碼如下:

// 截獲此消息為了讓窗口沒有標題欄和邊框
void CSkinTestDlg::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
 // __super::OnNcCalcSize(bCalcValidRects, lpncsp);
}

 

問題4:沒有邊框的拖拽

問題3的衍生問題,沒有了邊框,當然就不能拖拽了,那我們自己處理拖拽吧,很簡單,截獲 WM_NCHITTEST 消息,代碼如下:


LRESULT CSkinTestDlg::OnNcHitTest(CPoint point)
{
  // 注意:不是全屏的情況下,才可以拖拽,需要用戶自己處理
  int nCheckPos = 2;
  int nRDPos = nCheckPos * 2;
  CRect WndRect(0, 0, 0, 0);
  GetWindowRect(&WndRect);

  m_nMouseSizeType = -1;
  if(point.x >= WndRect.right - nRDPos && point.y >= WndRect.bottom - nRDPos)
  {
   // 右下角
   return HTBOTTOMRIGHT;
  }
  else if(point.x >= WndRect.right - nRDPos && point.y <= WndRect.top + nRDPos)
  {
   // 右上角
   return HTTOPRIGHT;
  }
  else if(point.x <= WndRect.left + nRDPos && point.y <= WndRect.top + nRDPos)
  {
   // 左上角
   return HTTOPLEFT;
  }
  else if(point.x <= WndRect.left + nRDPos && point.y >= WndRect.bottom - nRDPos)
  {
   // 左下角
   return HTBOTTOMLEFT;
  }
  else if(point.x >= WndRect.right - nCheckPos)
  {
   // 右邊線
   return HTRIGHT;
  }
  else if(point.x <= WndRect.left + nCheckPos)
  {
   // 左邊線
   return HTLEFT;
  }
  else if(point.y <= WndRect.top + nCheckPos)
  {
   // 上邊線
   return HTTOP;
  }
  else if(point.y >= WndRect.bottom - nCheckPos)
  {
   // 下邊線
   return HTBOTTOM;
  }

 return __super::OnNcHitTest(point);
}

 問題5:最大化的邊框問題

 一個正常的窗口,最大化后總是比當前屏幕大,剛好能將軟件的邊框蓋住,我實在不想要這個效果,那我只能自己處理最大化的效果了。

我自定一個了最大化消息,當自繪的最大化按鈕按下時,觸發這個消息,接受到消息后,取得屏幕的工作區域,然后將窗口改變到工作區域大小即可,代碼如下:


    // 先記錄最大化前的窗口位置,以便恢復的時候用。
    this->GetWindowRect(&m_MaxBeforeRect);
    CRect WndRect(0, 0, 0, 0);
    ::SystemParametersInfo(SPI_GETWORKAREA, 0, &WndRect, 0);

    this->MoveWindow(&WndRect);

    

問題又來了:最大化動畫沒了,這個簡單,再加一句代碼,播放動畫:


    // 先記錄最大化前的窗口位置,以便恢復的時候用。
    this->GetWindowRect(&m_MaxBeforeRect);
    CRect WndRect(0, 0, 0, 0);
    ::SystemParametersInfo(SPI_GETWORKAREA, 0, &WndRect, 0);

    // 播放動畫
    DrawAnimatedRects(IDANI_CAPTION, &m_MaxBeforeRect, &WndRect);

    this->MoveWindow(&WndRect);

    

同樣恢復窗口的代碼如下:


    // 恢復
    CRect WndRect(0, 0, 0, 0);
    ::SystemParametersInfo(SPI_GETWORKAREA, 0, &WndRect, 0);

    // 播放動畫
    DrawAnimatedRects(IDANI_CAPTION, &WndRect, &m_MaxBeforeRect);

    this->MoveWindow(&m_MaxBeforeRect);

    

問題6:自定義系統菜單

系統默認的系統菜單(鼠標右擊任務欄按鈕的菜單),不能修改,那我們自己做一個,查了很多消息,終於找到啦,消息 0x0313 就是鼠標右擊任務欄按鈕彈出菜單的消息,代碼如下:

定義消息:

#define WM_POPUPSYSTEMMENU    0x0313

截獲消息:

 ON_MESSAGE(WM_POPUPSYSTEMMENU, OnPopupSystemMenu)

處理消息:

afx_msg LRESULT OnPopupSystemMenu(WPARAM wParam, LPARAM lParam);


LRESULT CSkinTestDlg::OnPopupSystemMenu(WPARAM wParam, LPARAM lParam)
{
 CMenu PopMenu;
 CPoint point;
 GetCursorPos(&point);

 PopMenu.CreatePopupMenu();

 PopMenu.AppendMenu(MF_STRING, 111111, _T("關於界面測試"));
 PopMenu.AppendMenu(MF_SEPARATOR);
 PopMenu.AppendMenu(MF_STRING, IDCANCEL, _T("退出\tAlt+F4"));

 PopMenu.TrackPopupMenu(TPM_RIGHTBUTTON, point.x, point.y, this);

 PopMenu.DestroyMenu();

 return 0L;
}
這樣,想怎么處理就怎么處理。

 

最后,我們貼上皮膚,基本的DirecrUi的平台就構建好了,效果如下:

 

http://blog.csdn.net/qing666888/article/details/49734897


免責聲明!

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



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