久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

WIN32界面開發(fā)之三:DUI雛形開發(fā)(一)

 c 資料收集 2014-03-24

前言:這部分涉及工程比較大,,所以我打算分開為兩篇來寫,第一篇完成基本框架的構(gòu)建,,第二篇添加上EVENT和NOTIFY機制。

完成目標:仿照DirectUI,,完成一個基本雛形,開發(fā)一個布局控件(Dialog),,和一個按鈕控件(Button),,通過XML來布局窗體,,最后按鈕響應(yīng)點擊、鼠標移動等事件信息,,用戶還可以通過NOTIFY機制來定制,用戶具體行為時,,界面所要做的動作。給大家看下最終界面吧,,一個背景和四個按鈕。
正常狀態(tài):

點擊按鈕狀態(tài)(點擊按鈕時,,并拖動窗體)

正文

一,、CDialogBuilder的構(gòu)建

1,、先看下我們布局的XML

  1. "<XML>"  
  2. "<Dialog bk=\"C:\\bg.png\" pos=\"0 0 390 212\">"  
  3.     "<Canvas pos=\"0 0 100 212\">"  
  4.         "<Button pos=\"0 0 60 60\" />"  
  5.         "<Button pos=\"70 70 100 100\" />"  
  6.     "</Canvas>"  
  7.     "<Canvas pos=\"100 0 300 212\">"   
  8.         "<Button pos=\"120 20 160 60\" />"  
  9.         "<Button pos=\"170 170 200 200\" />"  
  10.     "</Canvas>"  
  11. "</Dialog>"  
  12. "</XML>"  
2,、兩個文件:UIMarkup.h和UIMarkup.cpp
這兩個文件里主要完成的功能就是加載XML,并且對XML對容進行分析,,構(gòu)建出結(jié)點樹,,具體代碼就不講了,只要知道所完成的功能就可以了,,大家有興趣可以看看,,

但有幾個接口,,要注意一下:

  1. CMarkup::Load(LPCTSTR pstrXML) //加載并分析XML,構(gòu)建XML結(jié)點樹  
  2. CMarkup::GetRoot()         //獲取XML樹的根結(jié)點  
  3.   
  4. CMarkupNode CMarkupNode::GetParent();//獲取父結(jié)點  
  5. CMarkupNode CMarkupNode::GetSibling();//獲取下一緊臨的兄弟結(jié)點  
  6. CMarkupNode CMarkupNode::GetChild(); //獲取孩子結(jié)點  
  7. CMarkupNode CMarkupNode::GetChild(LPCTSTR pstrName);//根據(jù)名字獲取孩子結(jié)點  
  8.   
  9. bool CMarkupNode::HasAttributes();  
  10. bool CMarkupNode::HasAttribute(LPCTSTR pstrName);  //指定結(jié)點是否具有屬性  
  11. int CMarkupNode::GetAttributeCount();          //具有的屬性個數(shù)  
  12. LPCTSTR CMarkupNode::GetAttributeName(int iIndex);//根據(jù)索引獲取屬性名  
  13.   
  14. LPCTSTR CMarkupNode::GetAttributeValue(int iIndex);  
  15. LPCTSTR CMarkupNode::GetAttributeValue(LPCTSTR pstrName);  
  16. bool CMarkupNode::GetAttributeValue(int iIndex, LPTSTR pstrValue, SIZE_T cchMax);  
  17. bool CMarkupNode::GetAttributeValue(LPCTSTR pstrName, LPTSTR pstrValue, SIZE_T cchMax);//獲取對應(yīng)屬性名的屬性的值的幾個函數(shù)  
3,、CDialogBuilder講解

完成功能:

1,、利用上面的CMarkUp類分析XML,,構(gòu)建出XML結(jié)點樹
2,、然后根據(jù)XML中的結(jié)點,NEW 出控件,,并利用XML結(jié)點樹的屬性,構(gòu)建出控件的屬性

先看定義:

  1. class CDialogBuilder  
  2. {  
  3. public:  
  4.     CDialogBuilder();  
  5.     ~CDialogBuilder();  
  6. public:  
  7.    CControlUI* Create(LPCTSTR pstrXML);  
  8.   
  9. private:  
  10.    CControlUI* _Parse(CMarkupNode* parent, CControlUI* pParent = NULL);  
  11.   
  12.    CMarkup m_xml;  
  13. };  
可以看到,,CDialogBuilder比較簡單,,只有兩個函數(shù),Create()和_Parse(),,另一個變量m_xml是用來分析XML,并構(gòu)建XML結(jié)點樹的,。
看下Create函數(shù)
  1. CControlUI* CDialogBuilder::Create(LPCTSTR pstrXML)  
  2. {  
  3.    if( !m_xml.Load(pstrXML) ) return NULL;//加載XML,,并生成XML結(jié)點樹  
  4.    // NOTE: The root element is actually discarded since the _Parse() methods is  
  5.    //       parsing children and attaching to the current node.  
  6.    CMarkupNode root = m_xml.GetRoot();//獲取XML結(jié)點樹的根結(jié)點  
  7.    return _Parse(&root);//分析結(jié)點樹  
  8. }  
下面看看_Parse函數(shù)完成的功能:
  1. CControlUI* CDialogBuilder::_Parse(CMarkupNode* pRoot, CControlUI* pParent)  
  2. {  
  3.    IContainerUI* pContainer = NULL;  
  4.    CControlUI* pReturn = NULL;  
  5.    for( CMarkupNode node = pRoot->GetChild() ; node.IsValid(); node = node.GetSibling() ) {  
  6.       LPCTSTR pstrClass = node.GetName();  
  7.       SIZE_T cchLen = _tcslen(pstrClass);  
  8.       CControlUI* pControl = NULL;  
  9.       switch( cchLen ) {  
  10.       case 6:  
  11.          if( _tcscmp(pstrClass, _T("Canvas")) == 0 )                pControl = new CContainerUI;  
  12.          else if( _tcscmp(pstrClass, _T("Button")) == 0 )           pControl = new CButtonUI;  
  13.          else if ( _tcscmp(pstrClass, _T("Dialog")) == 0)           pControl=new CDialogUI;  
  14.          break;  
  15.       }/////根據(jù)XML樹,,生成對應(yīng)的控件  
  16.   
  17.       ASSERT(pControl);  
  18.       if( pControl == NULL ) return NULL;  
  19.       // Add children  
  20.       if( node.HasChildren() ) {  
  21.          _Parse(&node, pControl);//利用遞規(guī),,遍歷XML樹  
  22.       }  
  23.       // Attach to parent  
  24.       if( pParent != NULL ) {//如果它的父親不為空,,說明它的父結(jié)點肯定具有CONTAINER屬性,所以把它加到它的父結(jié)點的CONTAINER中  
  25.          if( pContainer == NULL ) pContainer = static_cast<IContainerUI*>(pParent->GetInterface(_T("Container")));  
  26.          ASSERT(pContainer);  
  27.          if( pContainer == NULL ) return NULL;  
  28.          pContainer->Add(pControl);  
  29.       }  
  30.       // Process attributes  
  31.       if( node.HasAttributes() ) {//分析屬性,然后設(shè)置控件屬性  
  32.          TCHAR szValue[500] = { 0 };  
  33.          SIZE_T cchLen = lengthof(szValue) - 1;  
  34.          // Set ordinary attributes  
  35.          int nAttributes = node.GetAttributeCount();  
  36.          for( int i = 0; i < nAttributes; i++ ) {  
  37.             pControl->SetAttribute(node.GetAttributeName(i), node.GetAttributeValue(i));  
  38.          }  
  39.       }  
  40.       // 返回根結(jié)點,對于我們的XML,返回的是CDialogUI的實例指針  
  41.       if( pReturn == NULL ) pReturn = pControl;  
  42.    }  
  43.    return pReturn;  
  44. }  

二,、控件與容器的構(gòu)建

一、總體關(guān)系

這里我們先講下,,控件類和布局類的區(qū)別,,在這里我們在構(gòu)建幾個類,,先看下繼承圖(這篇文章先不管INotifyUI的事)

1、一個控件基類(CControlUI)和一個按鈕控件類(CButtonUI)
控件基類,,包含有控件繪制的最基本的幾個函數(shù),,當然都是虛函數(shù),,比如:SetAttribute(設(shè)置控件屬性),,GetInterface(獲取控件的this指針),,DoPaint(繪圖)
2,、一個容器基類,,故名思義,,容器是用來盛控件的,,所以它必須具有的幾個函數(shù):
Add(將控件添加到此容器的子隊列中),,Remove(刪除某個子控件),、RemoveAll(清空子控件隊列)、GetItem(獲取某個子控件),、GetCount(獲取子控件的個數(shù))
所以這幾個函數(shù)也是容器最基類IContainerUI的函數(shù),

3,、CContainerUI,這個才是稍微有意義點的布局類,,它用于在窗體上劃分出不同的區(qū)域,,然后在區(qū)域中布局控件.
這里最值得注意的地方是派生出它的基類,它是兩個基類的派生類(IContainerUI和CControlUI),,所以容器也是控件,,這一點在后面繪圖代碼中特點重要,在繪圖時,,我們將所有的容器和控件全部都定義為CControlUI,,所以如果當前的如果是CDialogUI的指針的話,它就會根據(jù)繼承關(guān)系,,先到CDialogUI中執(zhí)行相關(guān)函數(shù),,這點尤其注意!?。,。?/SPAN>(這里暫時搞不明白也沒關(guān)系,,后面遇到的時候,,我會重新講)
4、CDialogUI,,這是窗體的構(gòu)建類,,里面包含所在構(gòu)建窗體的大小、背景圖案,,現(xiàn)在只加了這兩個屬性,,其它都沒加

二、控件類的實現(xiàn)
1,、CContolUI的實現(xiàn)

  1. class CControlUI  
  2. {  
  3. public:  
  4.     CControlUI();  
  5.     virtual ~CControlUI();  
  6. public:  
  7.     virtual void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue);//設(shè)置屬性  
  8.     virtual LPVOID GetInterface(LPCTSTR pstrName);//獲取當前control的this指針  
  9.     virtual void DoPaint(HWND hwnd,HDC hDC)=0;//控件的繪制函數(shù),,注意這里把它聲明為純虛函數(shù),強制子類中必須對其進行實現(xiàn)  
  10.       
  11. //設(shè)置控件屬性的幾個函數(shù)  
  12.     void SetPos(RECT rc);///設(shè)置控件位置  
  13.     RECT GetPos();  
  14.     virtual CControlUI* GetParent();//設(shè)置當前控件的父結(jié)點  
  15.     virtual void SetParent(CControlUI* parent);  
  16.       
  17.     virtual void SetHwnd(HWND hwnd);//設(shè)置窗體句柄  
  18.   
  19. public:  
  20.     void Invalidate();  
  21. protected:  
  22.     RECT m_RectItem;///控件位置  
  23.     CControlUI *m_pParent;//父結(jié)點指針  
  24.   
  25.     HWND m_hwnd;//保存?zhèn)鬟^來的窗體的句柄  
  26. };  
這里大家可能會有個疑問,,控件要窗體句柄干嘛,,這里呢,因為我們只有一個窗體,,也就是CreateWindowEx函數(shù)創(chuàng)建后,,返回的那個HWND,我們這里保存的就是這個HWND,,那我們要它有什么用呢,,這是因為我們這里的控件全部都是模擬的控件的行為而已,,是并沒有句柄的,所以也不可能像MFC那樣根據(jù)控件的句柄單獨刷新了,,所以我們要刷新控件的話,,就得刷新整個窗體(當然,我們會利用緩存技術(shù)局部繪制來提高效率),,我們要刷新窗體就得利用SendMessage(hwnd,msg,wparam,lparam),注意這里的第一個變量就是窗體的句柄,,當然這里要保存了,就這樣的道理,。大笑

看各個函數(shù)的具體實現(xiàn)

  1. void CControlUI::SetPos(RECT rc)  
  2. {  
  3.     m_RectItem = rc;  
  4. }  
  5. RECT CControlUI::GetPos()  
  6. {  
  7.     return m_RectItem;  
  8. }  
這倆函數(shù)很簡單,就是設(shè)置控件位置和獲取控件位置的,。
  1. void CControlUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)  
  2. {  
  3.     if( _tcscmp(pstrName, _T("pos")) == 0 ) {  
  4.         RECT rcPos = { 0 };  
  5.         LPTSTR pstr = NULL;  
  6.         rcPos.left = _tcstol(pstrValue, &pstr, 10);  ASSERT(pstr);      
  7.         rcPos.top = _tcstol(pstr + 1, &pstr, 10);    ASSERT(pstr);      
  8.         rcPos.right = _tcstol(pstr + 1, &pstr, 10);  ASSERT(pstr);      
  9.         rcPos.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr);      
  10.         SetPos(rcPos);  
  11.     }  
  12. }  
這個函數(shù)是設(shè)置控件屬性,,哪里用到這個函數(shù)了呢,嘿嘿,,還記得不,,CDialogBuilder的_Parse函數(shù)里
  1. void CControlUI::SetParent(CControlUI* parent)  
  2. {  
  3.     m_pParent=parent;  
  4. }  
  5. CControlUI* CControlUI::GetParent()  
  6. {  
  7.     return m_pParent;  
  8. }  
設(shè)置父結(jié)點,也是在CDialogBuilder的_Parse函數(shù)里用到
  1. LPVOID CControlUI::GetInterface(LPCTSTR pstrName)  
  2. {  
  3.     if( _tcscmp(pstrName, _T("Control")) == 0 ) return this;  
  4.     return NULL;  
  5. }  
根據(jù)要獲取的指針名,,返回當前實例的this指針,,哪里用到了呢,也是CDialogBuilder的_Parse函數(shù)里,,下面是_Parse里的,,我摘過來
  1. if( pParent != NULL ) {  
  2.    if( pContainer == NULL ) pContainer = static_cast<IContainerUI*>(pParent->GetInterface(_T("Container")));  
  3.    ASSERT(pContainer);  
  4.    if( pContainer == NULL ) return NULL;  
  5.    pContainer->Add(pControl);  
  6. }  
看到了吧,獲取的是父結(jié)點的Container的指針,,然后將控件加到父結(jié)點中,,下面繼續(xù)
  1. void CControlUI::SetHwnd(HWND hwnd)  
  2. {  
  3.     m_hwnd=hwnd;  
  4. }  
  5.   
  6. void CControlUI::Invalidate()  
  7. {  
  8.     SendMessage(m_hwnd,WM_PAINT,NULL,NULL);  
  9. }  
SetHwnd用來保存窗體的HWND,然后是Invalidate函數(shù)的實現(xiàn),,也就是向窗體發(fā)送WM_PAINT消息
2,、CButtionUI的實現(xiàn)
定義:
  1. class CButtonUI:public CControlUI  
  2. {  
  3. public:  
  4.     CButtonUI();  
  5.     ~CButtonUI();  
  6. public:  
  7.     virtual void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue);  
  8.     virtual LPVOID GetInterface(LPCTSTR pstrName);  
  9.     virtual void DoPaint(HWND hwnd,HDC hDC);  
  10. };  
可以看到,很簡單,,設(shè)置屬性,、獲取THIS指針,然后就是繪圖類,,看具體實現(xiàn)
  1. void CButtonUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)  
  2. {//因為實現(xiàn)的是最簡易版,,Button里也就不設(shè)置什么獨有的屬性了,直接返回基類的SetAttribute(),設(shè)置Pos屬性  
  3.     return CControlUI::SetAttribute(pstrName,pstrValue);  
  4. }  
  5. LPVOID CButtonUI::GetInterface(LPCTSTR pstrName)  
  6. {  
  7.     if( _tcscmp(pstrName, _T("Button")) == 0 ) return this;  
  8.     return CControlUI::GetInterface(pstrName);  
  9.   
  10. }  
  11. void CButtonUI::DoPaint(HWND hwnd,HDC hDC)  
  12. {  
  13.     assert(hDC);  
  14.     Graphics graph(hDC);  
  15.     graph.FillRectangle(&SolidBrush(Color::Green),m_RectItem.left,m_RectItem.top,m_RectItem.right-m_RectItem.left,m_RectItem.bottom-m_RectItem.top);  
  16.     graph.ReleaseHDC(hDC);  
  17.   
  18. }  
這里要說明的一點就是,,DoPaint()函數(shù),,因為我們這篇文章里面還沒有添加EVENT事件通知功能,所以我們就先初始化的時候,,把按鈕統(tǒng)一畫成綠色,。
三,、容器類的實現(xiàn)
1、容器最基類IContainerUI的實現(xiàn)
  1. class IContainerUI  
  2. {  
  3. public:  
  4.     virtual CControlUI* GetItem(int iIndex) const = 0;  
  5.     virtual int GetCount() const = 0;  
  6.     virtual bool Add(CControlUI* pControl) = 0;  
  7.     virtual bool Remove(CControlUI* pControl) = 0;  
  8.     virtual void RemoveAll() = 0;  
  9. };  
其實這個類沒有任何的實際意義,,就是個接口類,,它的這幾個函數(shù)全部都聲明為純虛函數(shù)
2、CContainerUI的實現(xiàn)
定義:
  1. class CContainerUI : public CControlUI, public IContainerUI  
  2. {  
  3. public:  
  4.     CContainerUI();  
  5.     virtual ~CContainerUI();  
  6. public:  
  7.     ///先實現(xiàn)繼承的純虛函數(shù)  
  8.     virtual CControlUI* GetItem(int iIndex) const;  
  9.     virtual int GetCount() const;  
  10.     virtual bool Add(CControlUI* pControl);  
  11.     virtual bool Remove(CControlUI* pControl);  
  12.     virtual void RemoveAll();   
  13. public:  
  14.     void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue);///設(shè)置屬性  
  15.     virtual LPVOID GetInterface(LPCTSTR pstrName);//獲取THIS指針  
  16.     virtual  void DoPaint(HWND hwnd,HDC hDC);//繪圖函數(shù)  
  17.   
  18.     void LoadBackground(LPCTSTR Parth);//根據(jù)路徑加載圖像,,并保存在m_pImage中  
  19. protected:  
  20.     CStdPtrArray m_items;   //當前容器中的子變量隊列  
  21.   
  22.     Gdiplus::Image* m_pImage;//容器的背景  
  23. };  
大家看到了吧,,這里除了實現(xiàn)IContainerUI幾個純虛函數(shù)以外,其它幾個函數(shù)SetAttribute,、GetInterface,、DoPaint也都是控件所具有的??聪戮唧w實現(xiàn):
  1. CControlUI* CContainerUI::GetItem(int iIndex) const  
  2. {  
  3.     if( iIndex < 0 || iIndex >= m_items.GetSize() ) return NULL;  
  4.     return static_cast<CControlUI*>(m_items[iIndex]);  
  5. }  
  6.   
  7. int CContainerUI::GetCount() const  
  8. {  
  9.     return m_items.GetSize();  
  10. }  
  11.   
  12. bool CContainerUI::Add(CControlUI* pControl)  
  13. {  
  14.     return m_items.Add(pControl);  
  15. }  
  16.   
  17. bool CContainerUI::Remove(CControlUI* pControl)  
  18. {  
  19.     for( int it = 0;it < m_items.GetSize(); it++ ) {  
  20.         if( static_cast<CControlUI*>(m_items[it]) == pControl ) {  
  21.             delete pControl;  
  22.             return m_items.Remove(it);  
  23.         }  
  24.     }  
  25.     return false;  
  26. }  
  27.   
  28. void CContainerUI::RemoveAll()  
  29. {  
  30.     for( int it = 0;it < m_items.GetSize(); it++ ) delete static_cast<CControlUI*>(m_items[it]);  
  31.     m_items.Empty();  
  32. }  
這幾個就不講了,,就是向隊列里添加、刪除變量的操作,。
  1. LPVOID CContainerUI::GetInterface(LPCTSTR pstrName)  
  2. {  
  3.     if( _tcscmp(pstrName, _T("Container")) == 0 ) return static_cast<IContainerUI*>(this);  
  4.     return CControlUI::GetInterface(pstrName);  
  5. }  
獲取this指針
  1. void CContainerUI::LoadBackground(LPCTSTR Parth)  
  2. {  
  3.     m_pImage = Gdiplus::Image::FromFile(Parth);  
  4. }  
  5.   
  6. void CContainerUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)  
  7. {  
  8.     if( _tcscmp(pstrName, _T("bk")) == 0 ) LoadBackground(pstrValue);  
  9.     else CControlUI::SetAttribute(pstrName, pstrValue);  
  10. }  
設(shè)置屬性,,這里只在CControlUI的基礎(chǔ)上增加了一個屬性,bk-----背景
  1. void CContainerUI::DoPaint(HWND hwnd,HDC hDC)  
  2. {  
  3.     for( int it = 0; it < m_items.GetSize(); it++ ) {  
  4.         CControlUI* pControl = static_cast<CControlUI*>(m_items[it]);  
  5.         pControl->DoPaint(hwnd,hDC);  
  6.     }  
  7. }  
最重要的,,繪制函數(shù),,我這里沒有對它本身進行繪制,只是遍歷當前容器中所有的控件,,然后再它里面的控件進行逐個繪制,,這里我要留下一個疑問,如果容器里嵌套有容器是怎么完成繪制的呢,?,??,??。。,?!
3、CDialogUI實現(xiàn)
定義:
  1. class CDialogUI:public CContainerUI  
  2. {  
  3. public:  
  4.     CDialogUI();  
  5.     ~CDialogUI();  
  6. public:  
  7.     void DoPaintBackground(HDC hdc);//畫背景  
  8.     virtual LPVOID GetInterface(LPCTSTR pstrName);//獲取this指針  
  9. };  

大家可以看到這個CDialogUI的實現(xiàn)是非常簡單的,,只有一個DoPaintBackground(),,這個函數(shù)只是用來畫背景的

實現(xiàn):

  1. void CDialogUI::DoPaintBackground(HDC hdc)  
  2. {  
  3.     Gdiplus::Graphics graph(hdc);  
  4.     graph.SetSmoothingMode(Gdiplus::SmoothingModeNone);  
  5.     graph.DrawImage(m_pImage, 0, 0, m_RectItem.right-m_RectItem.left, m_RectItem.bottom-m_RectItem.top);  
  6.     graph.ReleaseHDC(hdc);  
  7. }  
  8. LPVOID CDialogUI::GetInterface(LPCTSTR pstrName)  
  9. {  
  10.     if( _tcscmp(pstrName, _T("Dialog")) == 0 ) return static_cast<IContainerUI*>(this);  
  11.     return CContainerUI::GetInterface(pstrName);  
  12. }  
好了,到這幾個控件的實現(xiàn)就全部實現(xiàn)了,,下面看窗體的創(chuàng)建,。

三、窗體創(chuàng)建

基本思想,,我們?yōu)榱俗層脩舨槐刈鎏嗟墓ぷ?,我們建一個基類,,這個基類完成窗口類注冊、創(chuàng)建窗口,、消息響應(yīng)等功能,,而用戶只需要從我們的基類派生,并且簡單調(diào)用Create()函數(shù)就可以了,。
一,、創(chuàng)建窗體基類(CWindowWnd)

定義

  1. class CWindowWnd{  
  2. public:  
  3.     CWindowWnd();  
  4.     ~CWindowWnd();  
  5.   
  6.     HWND GetHWND() const{return m_hWnd;}  
  7.   
  8.     bool RegisterWindowClass(WNDPROC fWndProc,HINSTANCE hInstance,LPCTSTR szClassName);//注冊窗口類  
  9.   
  10.     HWND Create();//創(chuàng)建窗體  
  11.   
  12.     void ShowWindow(bool bShow = true, bool bTakeFocus = true);  
  13.     void SetInstance(HINSTANCE instance){m_instance=instance;}  
  14.   
  15. protected:  
  16.     virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);  
  17.   
  18.     static LRESULT CALLBACK __WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);  
  19. protected:  
  20.     HWND m_hWnd;// 保存所創(chuàng)建窗體的句柄  
  21.   
  22.     HINSTANCE m_instance;//保存WinMain入口,參數(shù)里的hInstance,,因為注冊窗口類要用到  
  23. };  
注意一點:這里必須把窗口消息處理函數(shù)(_WndProc)定義為靜態(tài)函數(shù),,否則就定義為全局函數(shù)
我們看下具體實現(xiàn),由簡到難講:
  1. bool CWindowWnd::RegisterWindowClass(WNDPROC fWndProc,HINSTANCE hInstance,LPCTSTR szClassName)  
  2. {  
  3.     WNDCLASSEX wce={0};  
  4.     wce.cbSize=sizeof(wce);  
  5.     wce.style=CS_HREDRAW|CS_VREDRAW;  
  6.     wce.lpfnWndProc=fWndProc;  
  7.     wce.cbClsExtra=0;  
  8.     wce.cbWndExtra=0;  
  9.     wce.hInstance=hInstance;  
  10.     wce.hIcon=NULL;  
  11.     wce.hCursor=LoadCursor(NULL,IDC_ARROW);  
  12.     wce.hbrBackground=(HBRUSH)(6);//(HBRUSH)(COLOR_WINDOW+1);  
  13.     wce.lpszMenuName=NULL;  
  14.     wce.lpszClassName=szClassName;  
  15.     wce.hIconSm=NULL;  
  16.     ATOM nAtom=RegisterClassEx(&wce);  
  17.     if(nAtom==0) return false;  
  18.     return true;  
  19.   
  20. }  
冊窗口類,,沒什么好講的了,,標準流程。不過這里要注意一下,,fWndProc是消息處理函數(shù),,這個函數(shù)必須是靜態(tài)的或是全局的?。,。。,?!
  1. void CWindowWnd::ShowWindow(bool bShow /*= true*/, bool bTakeFocus /*= false*/)  
  2. {  
  3.     ASSERT(::IsWindow(m_hWnd));  
  4.     if( !::IsWindow(m_hWnd) ) return;  
  5.     ::ShowWindow(m_hWnd, bShow ? (bTakeFocus ? SW_SHOWNORMAL : SW_SHOWNOACTIVATE) : SW_HIDE);  
  6. }  
顯示窗口,調(diào)用系統(tǒng)API ----ShowWindow()
  1. HWND CWindowWnd::Create()  
  2. {  
  3.     if (!RegisterWindowClass(__WndProc,m_instance,L"transparent"))  
  4.     {//注冊窗口類,,看到了吧,,窗口處理函數(shù)的函數(shù)名為:_WndProc,后面講  
  5.         assert(L"注冊窗口失敗");  
  6.     }  
  7.   
  8.     assert(this);  
  9.     m_hWnd = ::CreateWindowEx(WS_EX_LAYERED, L"transparent", _T(""),WS_POPUP|WS_MAXIMIZE|WS_MINIMIZE|WS_SYSMENU,  
  10.         CW_USEDEFAULT, CW_USEDEFAULT,   
  11.         CW_USEDEFAULT, CW_USEDEFAULT,   
  12.         NULL, NULL, (HINSTANCE)::GetModuleHandle(NULL),(LPVOID)this);   
  13.   
  14.     if(m_hWnd == NULL || !::IsWindow(m_hWnd))  
  15.         return NULL;  
  16.   
  17.     return m_hWnd;    
  18. }  
創(chuàng)建窗口,,流程是,,先注冊窗口類,如果注冊成功,,就調(diào)用CreateWindowEx創(chuàng)建窗口,。這里最注意的一個地方,單獨把CreateWindowEx調(diào)出來
  1. m_hWnd = ::CreateWindowEx(WS_EX_LAYERED, L"transparent", _T(""),WS_POPUP|WS_MAXIMIZE|WS_MINIMIZE|WS_SYSMENU,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, (HINSTANCE)::GetModuleHandle(NULL),(LPVOID)this);   
注意最后一個參數(shù):傳進去的當前CWindowWnd的this指針?。,。?!這個參數(shù)會做為lparam傳進CREATESTRUCT結(jié)構(gòu)體里的lpCreateParams參數(shù)里,,在WM_NCCREATE消息里可以獲取到。那傳進去他有什么用呢,?往下看,,_WndProc消息處理函數(shù)
  1. LRESULT CWindowWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)  
  2. {  
  3.     return ::CallWindowProc(::DefWindowProc, m_hWnd, uMsg, wParam, lParam);  
  4. }  
  5. LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)  
  6. {  
  7.     CWindowWnd* pThis = NULL;  
  8.     if( uMsg == WM_NCCREATE ) {  
  9.         LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);  
  10.         pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);  
  11.         pThis->m_hWnd = hWnd;  
  12.         ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));  
  13.     }   
  14.     else {  
  15.         pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));  
  16.         if( uMsg == WM_NCDESTROY && pThis != NULL ) {  
  17.             ::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L);  
  18.   
  19.             pThis->m_hWnd = NULL;  
  20.             return true;  
  21.         }  
  22.     }  
  23.     if( pThis != NULL ) {  
  24.         return pThis->HandleMessage(uMsg, wParam, lParam);  
  25.     }   
  26.     else {  
  27.         return ::DefWindowProc(hWnd, uMsg, wParam, lParam);  
  28.     }  
  29. }  
注意在遇到WM_NCCREATE消息時,,
首先將lParam強轉(zhuǎn)成LPCREATESTRUCT 變量,我們上面說過我們傳過來的CWindowWnd的this指針,,保存在CREATESTRUCT結(jié)構(gòu)體的lpCreateParams參數(shù)里

  1. pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);  
這句,,就是取出this指針!,,有同學可能會問,,為什么非要這樣得到this指針,_WndProc不是CWindowWnd的成員函數(shù)么,,不是直接有this么,?注意,_WndProc是靜態(tài)函數(shù),,會在CWindowWnd創(chuàng)建之前編譯,,所以,由于在編譯_WndProc時,,沒有CWindowWnd還沒有被實例化,,當然要報錯了。所以我們要通過傳參的方式得到this指針,,那接下來的問題就是,,那下次我怎么再在這個函數(shù)里得到this指針呢,這里我們用SetWindowLongPtr把他保存在GWLP_USERDATA域,,然后調(diào)用GetWindowLongPtr就可以得到了,。

  1. ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));  
這句的意思就是把THIS指針保存在GWLP_USERDATA域。

在其它消息到來的是,,我們先通過GetWindowLongPtr得到THIS指針,,然后調(diào)用虛函數(shù)pThis->HandleMessage(uMsg, wParam, lParam);把消息轉(zhuǎn)到HandleMessage函數(shù)中處理,而用戶的函數(shù)是派生自CWindowWnd的,,所以只需要在HandleMessage函數(shù)中處理消息就可以了,。

二、用戶窗口類

  1. class CStartPage: public CWindowWnd  
  2. {  
  3. public:  
  4.     CStartPage();  
  5.     ~CStartPage();  
  6. public:  
  7.     virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);  
  8.   
  9.     LPCTSTR GetDialogResource();//傳進去XML字符串  
  10.   
  11.     void Paint(HWND m_hWnd);//響應(yīng)WM_PAINT消息的函數(shù)  
  12. private:  
  13.     WCHAR m_resource[556];//XML字符串  
  14.   
  15.     CDialogBuilder m_dialogBuilder;  
  16.     CControlUI *m_root;  
  17.   
  18.     HDC hdcBKMemory;//內(nèi)存DC,,參見《之二----GDI+中的局部刷新技術(shù)》  
  19.     HBITMAP hBKBitmap;  
  20.     HGDIOBJ hBKBitmapOld;  
  21. };  
看下具體實現(xiàn):
  1. LPCTSTR CStartPage::GetDialogResource()    
  2. {  
  3.     char temp[]=  
  4.         "<XML>"  
  5.         "<Dialog bk=\"C:\\bg.png\" pos=\"0 0 390 212\">"  
  6.         "<Canvas pos=\"0 0 100 212\">"  
  7.         "<Button pos=\"0 0 60 60\" />"  
  8.         "<Button pos=\"70 70 100 100\" />"  
  9.         "</Canvas>"  
  10.         "<Canvas pos=\"100 0 300 212\">" //一定要給canvas加上POS,,因為我們根據(jù)鼠標點來查找時,首先看是否在CANVAS的區(qū)域內(nèi),,如果不在,,就直接返回NULL了  
  11.         "<Button pos=\"120 20 160 60\" />"  
  12.         "<Button pos=\"170 170 200 200\" />"  
  13.         "</Canvas>"  
  14.         "</Dialog>"  
  15.         "</XML>";  
  16.     int iStrlen=sizeof(temp);  
  17.     MultiByteToWideChar(CP_ACP,0,(LPCSTR)(temp),iStrlen,m_resource,iStrlen);  
  18.     return  m_resource;  
  19. }  
這個函數(shù)很簡單,就是加載XML,,并保存在m_resource字符串中
  1. LRESULT CStartPage::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)  
  2. {  
  3.     switch(uMsg){  
  4. case WM_DESTROY:  
  5.     {  
  6.         PostQuitMessage(100);  
  7.   
  8.         ::SelectObject( hdcBKMemory, hBKBitmapOld); //不要把默認的位圖選回來,,如果選回來的話,我們新建的位圖就被替換掉了,,當然我們上面畫的東東也就沒有了  
  9.         ::DeleteObject(hBKBitmapOld);//這三個在清除的時候,,一塊清除  
  10.         ::DeleteObject(hBKBitmap); //先不要刪除,,先保存起來,后面再跟hmdmDC一起刪除  
  11.         ::DeleteDC(hdcBKMemory);  
  12.     }  
  13.     break;  
  14. case WM_CREATE:  
  15.     {  
  16.         m_root=m_dialogBuilder.Create(GetDialogResource());///傳入XML文檔,,讓其分析  
  17.   
  18.         SendMessage(GetHWND(),WM_PAINT,NULL,NULL);  
  19.     }  
  20.     break;  
  21. case WM_PAINT:  
  22.     {         
  23.         Paint(GetHWND());  
  24.     }  
  25.     break;  
  26.     }  
  27.     return CWindowWnd::HandleMessage(uMsg, wParam, lParam);  
注意兩個地方,,在CREATE的時候,傳入XML,,然后發(fā)送WM_PAIT消息,。然后在WM_PAIT中,Paint函數(shù)繪圖
  1. void CStartPage::Paint(HWND m_hWnd)  
  2. {  
  3.     RECT rcWindow;  
  4.     GetWindowRect(m_hWnd,&rcWindow);  
  5.     SIZE sizeWindow;  
  6.     /////因為根結(jié)點,,必是DLG結(jié)點,,肯定是個容器,所以將它強制轉(zhuǎn)換成CContainerUI*,,定位到  
  7.     CDialogUI *pdlg=(CDialogUI*)m_root;  
  8.   
  9.     RECT Pos=pdlg->GetPos();  
  10.     sizeWindow.cx=Pos.right-Pos.left;  
  11.     sizeWindow.cy=Pos.bottom-Pos.top;  
  12.   
  13.   
  14.     HDC hDC = ::GetDC(m_hWnd);  
  15.     if (hdcBKMemory==NULL)  
  16.     {  
  17.         hdcBKMemory = CreateCompatibleDC(hDC);  
  18.         //創(chuàng)建背景畫布  
  19.         BITMAPINFOHEADER stBmpInfoHeader = { 0 };     
  20.         int nBytesPerLine = ((sizeWindow.cx * 32 + 31) & (~31)) >> 3;  
  21.         stBmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);     
  22.         stBmpInfoHeader.biWidth = sizeWindow.cx;     
  23.         stBmpInfoHeader.biHeight = sizeWindow.cy;     
  24.         stBmpInfoHeader.biPlanes = 1;     
  25.         stBmpInfoHeader.biBitCount = 32;     
  26.         stBmpInfoHeader.biCompression = BI_RGB;     
  27.         stBmpInfoHeader.biClrUsed = 0;     
  28.         stBmpInfoHeader.biSizeImage = nBytesPerLine * sizeWindow.cy;     
  29.         PVOID pvBits = NULL;     
  30.         hBKBitmap = ::CreateDIBSection(NULL, (PBITMAPINFO)&stBmpInfoHeader, DIB_RGB_COLORS, &pvBits, NULL, 0);  
  31.         assert(hBKBitmap != NULL);  
  32.         hBKBitmapOld = ::SelectObject( hdcBKMemory, hBKBitmap);  
  33.   
  34.         pdlg->DoPaintBackground(hdcBKMemory);//繪制窗體背景  
  35.   
  36.     }  
  37.   
  38.   
  39.     HDC hdcEnd = CreateCompatibleDC(hDC);//新建兼容DC  
  40.     BITMAPINFOHEADER stBmpInfoHeader2 = { 0 };     
  41.     int nBytesPerLine2 = ((sizeWindow.cx * 32 + 31) & (~31)) >> 3;  
  42.     stBmpInfoHeader2.biSize = sizeof(BITMAPINFOHEADER);     
  43.     stBmpInfoHeader2.biWidth = sizeWindow.cx;     
  44.     stBmpInfoHeader2.biHeight = sizeWindow.cy;     
  45.     stBmpInfoHeader2.biPlanes = 1;     
  46.     stBmpInfoHeader2.biBitCount = 32;     
  47.     stBmpInfoHeader2.biCompression = BI_RGB;     
  48.     stBmpInfoHeader2.biClrUsed = 0;     
  49.     stBmpInfoHeader2.biSizeImage = nBytesPerLine2 * sizeWindow.cy;     
  50.     PVOID pvBits2 = NULL;     
  51.     HBITMAP hbmpMem2 = ::CreateDIBSection(NULL, (PBITMAPINFO)&stBmpInfoHeader2, DIB_RGB_COLORS, &pvBits2, NULL, 0);//新建畫布  
  52.   
  53.     HGDIOBJ hEndBitmapOld=SelectObject(hdcEnd,hbmpMem2);  
  54.     POINT ptSrc = { 0, 0};  
  55.     BLENDFUNCTION blendFunc;  
  56.     blendFunc.BlendOp = 0;  
  57.     blendFunc.BlendFlags = 0;  
  58.     blendFunc.AlphaFormat = 1;  
  59.     blendFunc.SourceConstantAlpha = 255;//AC_SRC_ALPHA  
  60.     ::AlphaBlend(hdcEnd,0,0,sizeWindow.cx,sizeWindow.cy,hdcBKMemory,0,0,sizeWindow.cx,sizeWindow.cy,blendFunc);//將背景復(fù)制到新畫布上  
  61.   
  62.     m_root->DoPaint(m_hWnd,hdcEnd);/////////////繪制畫布和控件  
  63.   
  64.     POINT ptWinPos = { rcWindow.left, rcWindow.top };  
  65.   
  66.     //UpdateLayeredWindow  
  67.     HMODULE hFuncInst = LoadLibrary(_T("User32.DLL"));  
  68.     typedef BOOL (WINAPI *MYFUNC)(HWND, HDC, POINT*, SIZE*, HDC, POINT*, COLORREF, BLENDFUNCTION*, DWORD);            
  69.     MYFUNC UpdateLayeredWindow;  
  70.     UpdateLayeredWindow = (MYFUNC)::GetProcAddress(hFuncInst, "UpdateLayeredWindow");  
  71.     if(!UpdateLayeredWindow(m_hWnd, hDC, &ptWinPos, &sizeWindow, hdcEnd, &ptSrc, 0, &blendFunc, ULW_ALPHA))  
  72.     {  
  73.         assert(L"UpdateLayeredWindow 調(diào)用失敗");  
  74.         TCHAR tmp[255] = {_T('\0')};  
  75.     }//使用UpdateLayeredWindow更新到當前窗體上  
  76.     //釋放資源  
  77.   
  78.     SelectObject(hdcEnd,hEndBitmapOld);  
  79.   
  80.     ::DeleteObject(hFuncInst);  
  81.     ::DeleteObject(hEndBitmapOld);  
  82.     ::DeleteObject(hbmpMem2);  
  83.     ::DeleteDC(hdcEnd);  
  84.   
  85.     ::DeleteDC(hDC);  
  86.   
  87. }  
Paint()函數(shù)中用了雙緩沖,,具體講解,參看《WIN32界面開發(fā)之二:GDI+中的局部刷新技術(shù)
這里我只講兩個地方:
1,、窗體背景的繪制
  1. CDialogUI *pdlg=(CDialogUI*)m_root;  
因為我們在定義m_root時,,定義的是CControlUI,但它實際是CDialogUI的實例,,所以將它強轉(zhuǎn)成CDialogUI指針,,然后內(nèi)存內(nèi)存DC里調(diào)用
  1. pdlg->DoPaintBackground(hdcBKMemory);//繪制窗體背景  
來繪制窗體背景。
2,、容器及控件的繪制
  1. m_root->DoPaint(m_hWnd,hdcEnd);/////////////繪制畫布和控件  
主要是靠這個函數(shù)來完成的,!
這個得著重講一下,,看它是怎么完成繪制的,。我們先看一下根據(jù)XML生成的控件樹

從上面這個圖就能看得出來,CDialogUI包含兩個子控件(Canvas),這也就是我說的容器里包含容器,,而每個Canvas又包含兩個Button控件,。下面我們看DoPaint的調(diào)用順序,首先m_root雖然被定義為CControlUI的變量,,但實際是CDialogUI的實例指針,,所以會沿著繼承圖去找CDialogUI的DoPaint,而CDialogUI是沒有DoPaint的,,所以會去找它的子類CContainerUI的DoPaint,,并且執(zhí)行,我們重新看下這個DoPaint方法

  1. void CContainerUI::DoPaint(HWND hwnd,HDC hDC)  
  2. {  
  3.     for( int it = 0; it < m_items.GetSize(); it++ ) {  
  4.         CControlUI* pControl = static_cast<CControlUI*>(m_items[it]);  
  5.         pControl->DoPaint(hwnd,hDC);  
  6.     }  
  7. }  
從這里就可以看到CDialogUI的DoPaint執(zhí)行順序了:
1,、找到CDialogUI的第一個孩子結(jié)點(一個CContainerUI實例,假設(shè)為ContainerA),,然后調(diào)用這個實例的DoPaint
2、執(zhí)行ContainerA->DoPaint(),,依然是這個函數(shù),,也就是說,,它會挨個執(zhí)行那兩個Button的DoPaint,完成之后返回,。
3,、找到CDialogUI的第二個孩子結(jié)點(同樣,一個CContainerUI實例,假設(shè)為ContainerB),,然后調(diào)用這個實例的DoPaint
4,、執(zhí)行ContainerB>DoPaint(),同樣,,它也是同樣執(zhí)行這個函數(shù),,然后逐個執(zhí)行它的那個Button的DoPaint,之后返回,。

三,、WinMain函數(shù)

  1. void Message(){  
  2.     MSG msg={0};  
  3.     while(GetMessage(&msg,NULL,0,0)){  
  4.         TranslateMessage(&msg);  
  5.         DispatchMessage(&msg);  
  6.     }  
  7. }  
  8.   
  9. ULONG_PTR gdiplusToken = 0;  
  10. int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd){  
  11.     Gdiplus::GdiplusStartupInput gdiplusStartupInput;  
  12.     Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);  
  13.   
  14.     CStartPage *startPage=new CStartPage();  
  15.   
  16.     startPage->SetInstance(hInstance);  
  17.     startPage->Create();  
  18.     startPage->ShowWindow(true);  
  19.   
  20.     Message();  
  21.   
  22.     delete startPage;  
  23.   
  24.     Gdiplus::GdiplusShutdown(gdiplusToken);  
  25.     return 0;  
  26. }  
最終的程序界面:(現(xiàn)在還沒有添加EVENT和NOTIFY,還不具有事件響應(yīng)功能,,下篇實現(xiàn))


本文由HARVIC完成,,轉(zhuǎn)載請標明出處(http://blog.csdn.net/harvic880925/article/details/9491387),謝謝

源碼地址:http://download.csdn.net/detail/harvic880925/5820507


聲明:本文只僅交流,,轉(zhuǎn)載請標明出處,,感謝金山影音漂亮的界面圖片。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多