WIN32界面开发之三:DUI雏形开发(一)
http://blog.csdn.net/harvic880925/article/details/9491387
前言:这部分涉及工程比较大,所以我打算分开为两篇来写,第一篇完成基本框架的构建,第二篇添加上EVENT和NOTIFY机制。
完成目标:仿照DirectUI,完成一个基本雏形,开发一个布局控件(Dialog),和一个按钮控件(Button),通过XML来布局窗体,最后按钮响应点击、鼠标移动等事件信息,用户还可以通过NOTIFY机制来定制,用户具体行为时,界面所要做的动作。给大家看下最终界面吧,一个背景和四个按钮。
正常状态:
点击按钮状态(点击按钮时,并拖动窗体)
正文
一、CDialogBuilder的构建
1、先看下我们布局的XML
- "<XML>"
- "<Dialog bk=\"C:\\bg.png\" pos=\"0 0 390 212\">"
- "<Canvas pos=\"0 0 100 212\">"
- "<Button pos=\"0 0 60 60\" />"
- "<Button pos=\"70 70 100 100\" />"
- "</Canvas>"
- "<Canvas pos=\"100 0 300 212\">"
- "<Button pos=\"120 20 160 60\" />"
- "<Button pos=\"170 170 200 200\" />"
- "</Canvas>"
- "</Dialog>"
- "</XML>"
这两个文件里主要完成的功能就是加载XML,并且对XML对容进行分析,构建出结点树,具体代码就不讲了,只要知道所完成的功能就可以了,大家有兴趣可以看看,
但有几个接口,要注意一下:
- CMarkup::Load(LPCTSTR pstrXML) //加载并分析XML,构建XML结点树
- CMarkup::GetRoot() //获取XML树的根结点
- CMarkupNode CMarkupNode::GetParent();//获取父结点
- CMarkupNode CMarkupNode::GetSibling();//获取下一紧临的兄弟结点
- CMarkupNode CMarkupNode::GetChild(); //获取孩子结点
- CMarkupNode CMarkupNode::GetChild(LPCTSTR pstrName);//根据名字获取孩子结点
- bool CMarkupNode::HasAttributes();
- bool CMarkupNode::HasAttribute(LPCTSTR pstrName); //指定结点是否具有属性
- int CMarkupNode::GetAttributeCount(); //具有的属性个数
- LPCTSTR CMarkupNode::GetAttributeName(int iIndex);//根据索引获取属性名
- LPCTSTR CMarkupNode::GetAttributeValue(int iIndex);
- LPCTSTR CMarkupNode::GetAttributeValue(LPCTSTR pstrName);
- bool CMarkupNode::GetAttributeValue(int iIndex, LPTSTR pstrValue, SIZE_T cchMax);
- bool CMarkupNode::GetAttributeValue(LPCTSTR pstrName, LPTSTR pstrValue, SIZE_T cchMax);//获取对应属性名的属性的值的几个函数
完成功能:
1、利用上面的CMarkUp类分析XML,构建出XML结点树
2、然后根据XML中的结点,NEW 出控件,并利用XML结点树的属性,构建出控件的属性
先看定义:
- class CDialogBuilder
- {
- public:
- CDialogBuilder();
- ~CDialogBuilder();
- public:
- CControlUI* Create(LPCTSTR pstrXML);
- private:
- CControlUI* _Parse(CMarkupNode* parent, CControlUI* pParent = NULL);
- CMarkup m_xml;
- };
看下Create函数
- CControlUI* CDialogBuilder::Create(LPCTSTR pstrXML)
- {
- if( !m_xml.Load(pstrXML) ) return NULL;//加载XML,并生成XML结点树
- // NOTE: The root element is actually discarded since the _Parse() methods is
- // parsing children and attaching to the current node.
- CMarkupNode root = m_xml.GetRoot();//获取XML结点树的根结点
- return _Parse(&root);//分析结点树
- }
- CControlUI* CDialogBuilder::_Parse(CMarkupNode* pRoot, CControlUI* pParent)
- {
- IContainerUI* pContainer = NULL;
- CControlUI* pReturn = NULL;
- for( CMarkupNode node = pRoot->GetChild() ; node.IsValid(); node = node.GetSibling() ) {
- LPCTSTR pstrClass = node.GetName();
- SIZE_T cchLen = _tcslen(pstrClass);
- CControlUI* pControl = NULL;
- switch( cchLen ) {
- case 6:
- if( _tcscmp(pstrClass, _T("Canvas")) == 0 ) pControl = new CContainerUI;
- else if( _tcscmp(pstrClass, _T("Button")) == 0 ) pControl = new CButtonUI;
- else if ( _tcscmp(pstrClass, _T("Dialog")) == 0) pControl=new CDialogUI;
- break;
- }/////根据XML树,生成对应的控件
- ASSERT(pControl);
- if( pControl == NULL ) return NULL;
- // Add children
- if( node.HasChildren() ) {
- _Parse(&node, pControl);//利用递规,遍历XML树
- }
- // Attach to parent
- if( pParent != NULL ) {//如果它的父亲不为空,说明它的父结点肯定具有CONTAINER属性,所以把它加到它的父结点的CONTAINER中
- if( pContainer == NULL ) pContainer = static_cast<IContainerUI*>(pParent->GetInterface(_T("Container")));
- ASSERT(pContainer);
- if( pContainer == NULL ) return NULL;
- pContainer->Add(pControl);
- }
- // Process attributes
- if( node.HasAttributes() ) {//分析属性,然后设置控件属性
- TCHAR szValue[500] = { 0 };
- SIZE_T cchLen = lengthof(szValue) - 1;
- // Set ordinary attributes
- int nAttributes = node.GetAttributeCount();
- for( int i = 0; i < nAttributes; i++ ) {
- pControl->SetAttribute(node.GetAttributeName(i), node.GetAttributeValue(i));
- }
- }
- // 返回根结点,对于我们的XML,返回的是CDialogUI的实例指针
- if( pReturn == NULL ) pReturn = pControl;
- }
- return pReturn;
- }
二、控件与容器的构建
一、总体关系
这里我们先讲下,控件类和布局类的区别,在这里我们在构建几个类,先看下继承图(这篇文章先不管INotifyUI的事)
1、一个控件基类(CControlUI)和一个按钮控件类(CButtonUI)
控件基类,包含有控件绘制的最基本的几个函数,当然都是虚函数,比如:SetAttribute(设置控件属性),GetInterface(获取控件的this指针),DoPaint(绘图)
2、一个容器基类,故名思义,容器是用来盛控件的,所以它必须具有的几个函数:
Add(将控件添加到此容器的子队列中),Remove(删除某个子控件)、RemoveAll(清空子控件队列)、GetItem(获取某个子控件)、GetCount(获取子控件的个数)
所以这几个函数也是容器最基类IContainerUI的函数,
3、CContainerUI,这个才是稍微有意义点的布局类,它用于在窗体上划分出不同的区域,然后在区域中布局控件.这里最值得注意的地方是派生出它的基类,它是两个基类的派生类(IContainerUI和CControlUI),所以容器也是控件,这一点在后面绘图代码中特点重要,在绘图时,我们将所有的容器和控件全部都定义为CControlUI,所以如果当前的如果是CDialogUI的指针的话,它就会根据继承关系,先到CDialogUI中执行相关函数,这点尤其注意!!!!(这里暂时搞不明白也没关系,后面遇到的时候,我会重新讲)
4、CDialogUI,这是窗体的构建类,里面包含所在构建窗体的大小、背景图案,现在只加了这两个属性,其它都没加
二、控件类的实现
1、CContolUI的实现
- class CControlUI
- {
- public:
- CControlUI();
- virtual ~CControlUI();
- public:
- virtual void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue);//设置属性
- virtual LPVOID GetInterface(LPCTSTR pstrName);//获取当前control的this指针
- virtual void DoPaint(HWND hwnd,HDC hDC)=0;//控件的绘制函数,注意这里把它声明为纯虚函数,强制子类中必须对其进行实现
- //设置控件属性的几个函数
- void SetPos(RECT rc);///设置控件位置
- RECT GetPos();
- virtual CControlUI* GetParent();//设置当前控件的父结点
- virtual void SetParent(CControlUI* parent);
- virtual void SetHwnd(HWND hwnd);//设置窗体句柄
- public:
- void Invalidate();
- protected:
- RECT m_RectItem;///控件位置
- CControlUI *m_pParent;//父结点指针
- HWND m_hwnd;//保存传过来的窗体的句柄
- };
看各个函数的具体实现
- void CControlUI::SetPos(RECT rc)
- {
- m_RectItem = rc;
- }
- RECT CControlUI::GetPos()
- {
- return m_RectItem;
- }
- void CControlUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
- {
- if( _tcscmp(pstrName, _T("pos")) == 0 ) {
- RECT rcPos = { 0 };
- LPTSTR pstr = NULL;
- rcPos.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr);
- rcPos.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr);
- rcPos.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr);
- rcPos.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr);
- SetPos(rcPos);
- }
- }
- void CControlUI::SetParent(CControlUI* parent)
- {
- m_pParent=parent;
- }
- CControlUI* CControlUI::GetParent()
- {
- return m_pParent;
- }
- LPVOID CControlUI::GetInterface(LPCTSTR pstrName)
- {
- if( _tcscmp(pstrName, _T("Control")) == 0 ) return this;
- return NULL;
- }
- if( pParent != NULL ) {
- if( pContainer == NULL ) pContainer = static_cast<IContainerUI*>(pParent->GetInterface(_T("Container")));
- ASSERT(pContainer);
- if( pContainer == NULL ) return NULL;
- pContainer->Add(pControl);
- }
- void CControlUI::SetHwnd(HWND hwnd)
- {
- m_hwnd=hwnd;
- }
- void CControlUI::Invalidate()
- {
- SendMessage(m_hwnd,WM_PAINT,NULL,NULL);
- }
2、CButtionUI的实现
定义:
- class CButtonUI:public CControlUI
- {
- public:
- CButtonUI();
- ~CButtonUI();
- public:
- virtual void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue);
- virtual LPVOID GetInterface(LPCTSTR pstrName);
- virtual void DoPaint(HWND hwnd,HDC hDC);
- };
- void CButtonUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
- {//因为实现的是最简易版,Button里也就不设置什么独有的属性了,直接返回基类的SetAttribute(),设置Pos属性
- return CControlUI::SetAttribute(pstrName,pstrValue);
- }
- LPVOID CButtonUI::GetInterface(LPCTSTR pstrName)
- {
- if( _tcscmp(pstrName, _T("Button")) == 0 ) return this;
- return CControlUI::GetInterface(pstrName);
- }
- void CButtonUI::DoPaint(HWND hwnd,HDC hDC)
- {
- assert(hDC);
- Graphics graph(hDC);
- graph.FillRectangle(&SolidBrush(Color::Green),m_RectItem.left,m_RectItem.top,m_RectItem.right-m_RectItem.left,m_RectItem.bottom-m_RectItem.top);
- graph.ReleaseHDC(hDC);
- }
三、容器类的实现
1、容器最基类IContainerUI的实现
- class IContainerUI
- {
- public:
- virtual CControlUI* GetItem(int iIndex) const = 0;
- virtual int GetCount() const = 0;
- virtual bool Add(CControlUI* pControl) = 0;
- virtual bool Remove(CControlUI* pControl) = 0;
- virtual void RemoveAll() = 0;
- };
2、CContainerUI的实现
定义:
- class CContainerUI : public CControlUI, public IContainerUI
- {
- public:
- CContainerUI();
- virtual ~CContainerUI();
- public:
- ///先实现继承的纯虚函数
- virtual CControlUI* GetItem(int iIndex) const;
- virtual int GetCount() const;
- virtual bool Add(CControlUI* pControl);
- virtual bool Remove(CControlUI* pControl);
- virtual void RemoveAll();
- public:
- void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue);///设置属性
- virtual LPVOID GetInterface(LPCTSTR pstrName);//获取THIS指针
- virtual void DoPaint(HWND hwnd,HDC hDC);//绘图函数
- void LoadBackground(LPCTSTR Parth);//根据路径加载图像,并保存在m_pImage中
- protected:
- CStdPtrArray m_items; //当前容器中的子变量队列
- Gdiplus::Image* m_pImage;//容器的背景
- };
- CControlUI* CContainerUI::GetItem(int iIndex) const
- {
- if( iIndex < 0 || iIndex >= m_items.GetSize() ) return NULL;
- return static_cast<CControlUI*>(m_items[iIndex]);
- }
- int CContainerUI::GetCount() const
- {
- return m_items.GetSize();
- }
- bool CContainerUI::Add(CControlUI* pControl)
- {
- return m_items.Add(pControl);
- }
- bool CContainerUI::Remove(CControlUI* pControl)
- {
- for( int it = 0;it < m_items.GetSize(); it++ ) {
- if( static_cast<CControlUI*>(m_items[it]) == pControl ) {
- delete pControl;
- return m_items.Remove(it);
- }
- }
- return false;
- }
- void CContainerUI::RemoveAll()
- {
- for( int it = 0;it < m_items.GetSize(); it++ ) delete static_cast<CControlUI*>(m_items[it]);
- m_items.Empty();
- }
- LPVOID CContainerUI::GetInterface(LPCTSTR pstrName)
- {
- if( _tcscmp(pstrName, _T("Container")) == 0 ) return static_cast<IContainerUI*>(this);
- return CControlUI::GetInterface(pstrName);
- }
- void CContainerUI::LoadBackground(LPCTSTR Parth)
- {
- m_pImage = Gdiplus::Image::FromFile(Parth);
- }
- void CContainerUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
- {
- if( _tcscmp(pstrName, _T("bk")) == 0 ) LoadBackground(pstrValue);
- else CControlUI::SetAttribute(pstrName, pstrValue);
- }
- void CContainerUI::DoPaint(HWND hwnd,HDC hDC)
- {
- for( int it = 0; it < m_items.GetSize(); it++ ) {
- CControlUI* pControl = static_cast<CControlUI*>(m_items[it]);
- pControl->DoPaint(hwnd,hDC);
- }
- }
3、CDialogUI实现
定义:
- class CDialogUI:public CContainerUI
- {
- public:
- CDialogUI();
- ~CDialogUI();
- public:
- void DoPaintBackground(HDC hdc);//画背景
- virtual LPVOID GetInterface(LPCTSTR pstrName);//获取this指针
- };
大家可以看到这个CDialogUI的实现是非常简单的,只有一个DoPaintBackground(),这个函数只是用来画背景的
实现:
- void CDialogUI::DoPaintBackground(HDC hdc)
- {
- Gdiplus::Graphics graph(hdc);
- graph.SetSmoothingMode(Gdiplus::SmoothingModeNone);
- graph.DrawImage(m_pImage, 0, 0, m_RectItem.right-m_RectItem.left, m_RectItem.bottom-m_RectItem.top);
- graph.ReleaseHDC(hdc);
- }
- LPVOID CDialogUI::GetInterface(LPCTSTR pstrName)
- {
- if( _tcscmp(pstrName, _T("Dialog")) == 0 ) return static_cast<IContainerUI*>(this);
- return CContainerUI::GetInterface(pstrName);
- }
三、窗体创建
基本思想,我们为了让用户不必做太多的工作,我们建一个基类,这个基类完成窗口类注册、创建窗口、消息响应等功能,而用户只需要从我们的基类派生,并且简单调用Create()函数就可以了。一、创建窗体基类(CWindowWnd)
定义
- class CWindowWnd{
- public:
- CWindowWnd();
- ~CWindowWnd();
- HWND GetHWND() const{return m_hWnd;}
- bool RegisterWindowClass(WNDPROC fWndProc,HINSTANCE hInstance,LPCTSTR szClassName);//注册窗口类
- HWND Create();//创建窗体
- void ShowWindow(bool bShow = true, bool bTakeFocus = true);
- void SetInstance(HINSTANCE instance){m_instance=instance;}
- protected:
- virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
- static LRESULT CALLBACK __WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
- protected:
- HWND m_hWnd;// 保存所创建窗体的句柄
- HINSTANCE m_instance;//保存WinMain入口,参数里的hInstance,因为注册窗口类要用到
- };
我们看下具体实现,由简到难讲:
- bool CWindowWnd::RegisterWindowClass(WNDPROC fWndProc,HINSTANCE hInstance,LPCTSTR szClassName)
- {
- WNDCLASSEX wce={0};
- wce.cbSize=sizeof(wce);
- wce.style=CS_HREDRAW|CS_VREDRAW;
- wce.lpfnWndProc=fWndProc;
- wce.cbClsExtra=0;
- wce.cbWndExtra=0;
- wce.hInstance=hInstance;
- wce.hIcon=NULL;
- wce.hCursor=LoadCursor(NULL,IDC_ARROW);
- wce.hbrBackground=(HBRUSH)(6);//(HBRUSH)(COLOR_WINDOW+1);
- wce.lpszMenuName=NULL;
- wce.lpszClassName=szClassName;
- wce.hIconSm=NULL;
- ATOM nAtom=RegisterClassEx(&wce);
- if(nAtom==0) return false;
- return true;
- }
- void CWindowWnd::ShowWindow(bool bShow /*= true*/, bool bTakeFocus /*= false*/)
- {
- ASSERT(::IsWindow(m_hWnd));
- if( !::IsWindow(m_hWnd) ) return;
- ::ShowWindow(m_hWnd, bShow ? (bTakeFocus ? SW_SHOWNORMAL : SW_SHOWNOACTIVATE) : SW_HIDE);
- }
- HWND CWindowWnd::Create()
- {
- if (!RegisterWindowClass(__WndProc,m_instance,L"transparent"))
- {//注册窗口类,看到了吧,窗口处理函数的函数名为:_WndProc,后面讲
- assert(L"注册窗口失败");
- }
- assert(this);
- 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);
- if(m_hWnd == NULL || !::IsWindow(m_hWnd))
- return NULL;
- return m_hWnd;
- }
- 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);
- LRESULT CWindowWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- return ::CallWindowProc(::DefWindowProc, m_hWnd, uMsg, wParam, lParam);
- }
- LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- CWindowWnd* pThis = NULL;
- if( uMsg == WM_NCCREATE ) {
- LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
- pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);
- pThis->m_hWnd = hWnd;
- ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));
- }
- else {
- pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
- if( uMsg == WM_NCDESTROY && pThis != NULL ) {
- ::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L);
- pThis->m_hWnd = NULL;
- return true;
- }
- }
- if( pThis != NULL ) {
- return pThis->HandleMessage(uMsg, wParam, lParam);
- }
- else {
- return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
- }
- }
首先将lParam强转成LPCREATESTRUCT 变量,我们上面说过我们传过来的CWindowWnd的this指针,保存在CREATESTRUCT结构体的lpCreateParams参数里
- pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);
- ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));
在其它消息到来的是,我们先通过GetWindowLongPtr得到THIS指针,然后调用虚函数pThis->HandleMessage(uMsg, wParam, lParam);把消息转到HandleMessage函数中处理,而用户的函数是派生自CWindowWnd的,所以只需要在HandleMessage函数中处理消息就可以了。
二、用户窗口类
- class CStartPage: public CWindowWnd
- {
- public:
- CStartPage();
- ~CStartPage();
- public:
- virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
- LPCTSTR GetDialogResource();//传进去XML字符串
- void Paint(HWND m_hWnd);//响应WM_PAINT消息的函数
- private:
- WCHAR m_resource[556];//XML字符串
- CDialogBuilder m_dialogBuilder;
- CControlUI *m_root;
- HDC hdcBKMemory;//内存DC,参见《之二----GDI+中的局部刷新技术》
- HBITMAP hBKBitmap;
- HGDIOBJ hBKBitmapOld;
- };
- LPCTSTR CStartPage::GetDialogResource()
- {
- char temp[]=
- "<XML>"
- "<Dialog bk=\"C:\\bg.png\" pos=\"0 0 390 212\">"
- "<Canvas pos=\"0 0 100 212\">"
- "<Button pos=\"0 0 60 60\" />"
- "<Button pos=\"70 70 100 100\" />"
- "</Canvas>"
- "<Canvas pos=\"100 0 300 212\">" //一定要给canvas加上POS,因为我们根据鼠标点来查找时,首先看是否在CANVAS的区域内,如果不在,就直接返回NULL了
- "<Button pos=\"120 20 160 60\" />"
- "<Button pos=\"170 170 200 200\" />"
- "</Canvas>"
- "</Dialog>"
- "</XML>";
- int iStrlen=sizeof(temp);
- MultiByteToWideChar(CP_ACP,0,(LPCSTR)(temp),iStrlen,m_resource,iStrlen);
- return m_resource;
- }
- LRESULT CStartPage::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- switch(uMsg){
- case WM_DESTROY:
- {
- PostQuitMessage(100);
- ::SelectObject( hdcBKMemory, hBKBitmapOld); //不要把默认的位图选回来,如果选回来的话,我们新建的位图就被替换掉了,当然我们上面画的东东也就没有了
- ::DeleteObject(hBKBitmapOld);//这三个在清除的时候,一块清除
- ::DeleteObject(hBKBitmap); //先不要删除,先保存起来,后面再跟hmdmDC一起删除
- ::DeleteDC(hdcBKMemory);
- }
- break;
- case WM_CREATE:
- {
- m_root=m_dialogBuilder.Create(GetDialogResource());///传入XML文档,让其分析
- SendMessage(GetHWND(),WM_PAINT,NULL,NULL);
- }
- break;
- case WM_PAINT:
- {
- Paint(GetHWND());
- }
- break;
- }
- return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
- void CStartPage::Paint(HWND m_hWnd)
- {
- RECT rcWindow;
- GetWindowRect(m_hWnd,&rcWindow);
- SIZE sizeWindow;
- /////因为根结点,必是DLG结点,肯定是个容器,所以将它强制转换成CContainerUI*,定位到
- CDialogUI *pdlg=(CDialogUI*)m_root;
- RECT Pos=pdlg->GetPos();
- sizeWindow.cx=Pos.right-Pos.left;
- sizeWindow.cy=Pos.bottom-Pos.top;
- HDC hDC = ::GetDC(m_hWnd);
- if (hdcBKMemory==NULL)
- {
- hdcBKMemory = CreateCompatibleDC(hDC);
- //创建背景画布
- BITMAPINFOHEADER stBmpInfoHeader = { 0 };
- int nBytesPerLine = ((sizeWindow.cx * 32 + 31) & (~31)) >> 3;
- stBmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
- stBmpInfoHeader.biWidth = sizeWindow.cx;
- stBmpInfoHeader.biHeight = sizeWindow.cy;
- stBmpInfoHeader.biPlanes = 1;
- stBmpInfoHeader.biBitCount = 32;
- stBmpInfoHeader.biCompression = BI_RGB;
- stBmpInfoHeader.biClrUsed = 0;
- stBmpInfoHeader.biSizeImage = nBytesPerLine * sizeWindow.cy;
- PVOID pvBits = NULL;
- hBKBitmap = ::CreateDIBSection(NULL, (PBITMAPINFO)&stBmpInfoHeader, DIB_RGB_COLORS, &pvBits, NULL, 0);
- assert(hBKBitmap != NULL);
- hBKBitmapOld = ::SelectObject( hdcBKMemory, hBKBitmap);
- pdlg->DoPaintBackground(hdcBKMemory);//绘制窗体背景
- }
- HDC hdcEnd = CreateCompatibleDC(hDC);//新建兼容DC
- BITMAPINFOHEADER stBmpInfoHeader2 = { 0 };
- int nBytesPerLine2 = ((sizeWindow.cx * 32 + 31) & (~31)) >> 3;
- stBmpInfoHeader2.biSize = sizeof(BITMAPINFOHEADER);
- stBmpInfoHeader2.biWidth = sizeWindow.cx;
- stBmpInfoHeader2.biHeight = sizeWindow.cy;
- stBmpInfoHeader2.biPlanes = 1;
- stBmpInfoHeader2.biBitCount = 32;
- stBmpInfoHeader2.biCompression = BI_RGB;
- stBmpInfoHeader2.biClrUsed = 0;
- stBmpInfoHeader2.biSizeImage = nBytesPerLine2 * sizeWindow.cy;
- PVOID pvBits2 = NULL;
- HBITMAP hbmpMem2 = ::CreateDIBSection(NULL, (PBITMAPINFO)&stBmpInfoHeader2, DIB_RGB_COLORS, &pvBits2, NULL, 0);//新建画布
- HGDIOBJ hEndBitmapOld=SelectObject(hdcEnd,hbmpMem2);
- POINT ptSrc = { 0, 0};
- BLENDFUNCTION blendFunc;
- blendFunc.BlendOp = 0;
- blendFunc.BlendFlags = 0;
- blendFunc.AlphaFormat = 1;
- blendFunc.SourceConstantAlpha = 255;//AC_SRC_ALPHA
- ::AlphaBlend(hdcEnd,0,0,sizeWindow.cx,sizeWindow.cy,hdcBKMemory,0,0,sizeWindow.cx,sizeWindow.cy,blendFunc);//将背景复制到新画布上
- m_root->DoPaint(m_hWnd,hdcEnd);/////////////绘制画布和控件
- POINT ptWinPos = { rcWindow.left, rcWindow.top };
- //UpdateLayeredWindow
- HMODULE hFuncInst = LoadLibrary(_T("User32.DLL"));
- typedef BOOL (WINAPI *MYFUNC)(HWND, HDC, POINT*, SIZE*, HDC, POINT*, COLORREF, BLENDFUNCTION*, DWORD);
- MYFUNC UpdateLayeredWindow;
- UpdateLayeredWindow = (MYFUNC)::GetProcAddress(hFuncInst, "UpdateLayeredWindow");
- if(!UpdateLayeredWindow(m_hWnd, hDC, &ptWinPos, &sizeWindow, hdcEnd, &ptSrc, 0, &blendFunc, ULW_ALPHA))
- {
- assert(L"UpdateLayeredWindow 调用失败");
- TCHAR tmp[255] = {_T('\0')};
- }//使用UpdateLayeredWindow更新到当前窗体上
- //释放资源
- SelectObject(hdcEnd,hEndBitmapOld);
- ::DeleteObject(hFuncInst);
- ::DeleteObject(hEndBitmapOld);
- ::DeleteObject(hbmpMem2);
- ::DeleteDC(hdcEnd);
- ::DeleteDC(hDC);
- }
这里我只讲两个地方:
1、窗体背景的绘制
- CDialogUI *pdlg=(CDialogUI*)m_root;
- pdlg->DoPaintBackground(hdcBKMemory);//绘制窗体背景
2、容器及控件的绘制
- m_root->DoPaint(m_hWnd,hdcEnd);/////////////绘制画布和控件
这个得着重讲一下,看它是怎么完成绘制的。我们先看一下根据XML生成的控件树
从上面这个图就能看得出来,CDialogUI包含两个子控件(Canvas),这也就是我说的容器里包含容器,而每个Canvas又包含两个Button控件。下面我们看DoPaint的调用顺序,首先m_root虽然被定义为CControlUI的变量,但实际是CDialogUI的实例指针,所以会沿着继承图去找CDialogUI的DoPaint,而CDialogUI是没有DoPaint的,所以会去找它的子类CContainerUI的DoPaint,并且执行,我们重新看下这个DoPaint方法
- void CContainerUI::DoPaint(HWND hwnd,HDC hDC)
- {
- for( int it = 0; it < m_items.GetSize(); it++ ) {
- CControlUI* pControl = static_cast<CControlUI*>(m_items[it]);
- pControl->DoPaint(hwnd,hDC);
- }
- }
1、找到CDialogUI的第一个孩子结点(一个CContainerUI实例,假设为ContainerA),然后调用这个实例的DoPaint
2、执行ContainerA->DoPaint(),依然是这个函数,也就是说,它会挨个执行那两个Button的DoPaint,完成之后返回。
3、找到CDialogUI的第二个孩子结点(同样,一个CContainerUI实例,假设为ContainerB),然后调用这个实例的DoPaint
4、执行ContainerB>DoPaint(),同样,它也是同样执行这个函数,然后逐个执行它的那个Button的DoPaint,之后返回。
三、WinMain函数
- void Message(){
- MSG msg={0};
- while(GetMessage(&msg,NULL,0,0)){
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- ULONG_PTR gdiplusToken = 0;
- int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd){
- Gdiplus::GdiplusStartupInput gdiplusStartupInput;
- Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
- CStartPage *startPage=new CStartPage();
- startPage->SetInstance(hInstance);
- startPage->Create();
- startPage->ShowWindow(true);
- Message();
- delete startPage;
- Gdiplus::GdiplusShutdown(gdiplusToken);
- return 0;
- }
本文由HARVIC完成,转载请标明出处(http://blog.csdn.net/harvic880925/article/details/9491387),谢谢
源码地址:http://download.csdn.net/detail/harvic880925/5820507
声明:本文只仅交流,转载请标明出处,感谢金山影音漂亮的界面图片。