前言:前面我们完成了对DUI雏形的构建,但大家有没有注意到一个问题,我们对消息的处理都是在用户构建的类(CStartPage)中,还有,为了在控件中可以实时刷新,所以每个控件都必须带有一个变量m_hwnd,来保存当前窗体的句柄,而且在每次发送EVENT消息时都要赋值,相当麻烦,所以,我们将这些控件都具有的一些操作和变量,全部都集合起来,封装成一个类,这个类就是CPaintManagerUI;
我带大家想想,我们应该把什么功能集成到CPaintManagerUI中呢,也就是哪些功能是不需要用户做的呢,又有哪些功能是控件所共有的呢:
1、CStartPage中的HandleMessage中,对具体消息的处理部分,比如WM_PAITN,WM_LBUTTONDOWN……
2、控件的Notify机制,上节中,每个控件为了可以NOTIFY到当前窗体,每个控件都要保存一个当前窗体的this指针,相当烦琐
CPaintManagerUI类就是对于公共的成员变量及成员方法的集成,以减少代码量,提高代码重用性,所写的一个类;既然是将原有代码进行独立,所以,独立的同时,也必须将原代码中的变量根据CPaintManagerUI的需要传进来,有几个变量是必须需要的;
3、m_hwnd:当前窗口的句柄,SendMessage()时用到;
4、m_hInstance:当前应用程序的HINSTANCE实例,在创建m_ToolTip(提示条)时用到
5、this指针:SendNotify()时,要将侦听NOTIFY消息的窗体发送NOTIFY消息,所以要将当前窗体的THIS指针,传到CPaintManangerUI中,通过CPaintManangerUI::SetNotifier(this)设置;CPaintManangerUI在发送NOTIFY时,会将消息发送给所有的侦听窗体;
我贴出CPaintManagerUI的核心定义:
注意:在源码中定义的变量要比这里的多些,多余的那些变量主要是为了双缓冲绘图所定义的:
class CPaintManagerUI { public: CPaintManagerUI(); ~CPaintManagerUI(); public: void Init(HWND hWnd);//set hwnd of the window HWND GetHwnd(){return m_hWnd;} //NOTIFY部分 the part of Notify void SendNotify(CControlUI* pControl, LPCTSTR pstrMessage, WPARAM wParam= 0, LPARAM lParam= 0);//send notify messages to all listerners; bool AddNotifier(INotifyUI* pControl); bool RemoveNotifier(INotifyUI* pControl); //控件树部分 control-tree set part bool AttachDialog(CControlUI* pControl);//set the root of the control-tree bool InitControls(CControlUI* pControl, CControlUI* pParent = NULL);//set the current manager virable to controls //消息处理部分 message-handler bool MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes); private: CStdPtrArray m_aNotifiers;//NOTIFY消息接收的指针队列 listerners array HWND m_hWnd;//主窗体的句柄 the hwnd of main window CControlUI* m_pRoot;//XML控件树的根结点 the root of the DLG control tree,in our app this variable always point to CDlialogUI };讲解:
void CPaintManagerUI::Init(HWND hWnd) { ASSERT(::IsWindow(hWnd)); // Remember the window context we came from m_hWnd = hWnd; } HWND CPaintManagerUI::GetHwnd() { return m_hWnd; }没什么难度,就是赋值与返回值;
(1)、m_aNotifiers,要接收NOTIFY消息的窗体指针队列
(2)、AddNotifier(),增加到队列中;RemoveNotifier()从队列中删除
(3)、SendNotify(),发送消息到接收窗体的Notify()函数中。
实现代码讲解:
bool CPaintManagerUI::AddNotifier(INotifyUI* pControl) { ASSERT(m_aNotifiers.Find(pControl)<0); return m_aNotifiers.Add(pControl); } bool CPaintManagerUI::RemoveNotifier(INotifyUI* pControl) { for( int i = 0; i < m_aNotifiers.GetSize(); i++ ) { if( static_cast<INotifyUI*>(m_aNotifiers[i]) == pControl ) { return m_aNotifiers.Remove(i); } } return false; }讲解:这两个函数比较简单,AddNotifyer()就是把当前要接收NOTIFY消息的用户窗体类的this指针添加到数组里;RemoveNotifier()相反,就是从数组中删除指定的窗体类的this指针;
void CPaintManagerUI::SendNotify(CControlUI* pControl, LPCTSTR pstrMessage, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/) { TNotifyUI Msg; Msg.pSender = pControl; Msg.sType = pstrMessage; Msg.wParam = 0; Msg.lParam = 0; Msg.ptMouse =m_ptLastMousePos; Msg.dwTimestamp = ::GetTickCount(); // Send to all listeners for( int i = 0; i < m_aNotifiers.GetSize(); i++ ) { static_cast<INotifyUI*>(m_aNotifiers[i])->Notify(Msg); } }讲解:发送消息函数(SendNotify),非常重要,可以看到,我们先对MSG结构体进行封装,然后发送给监听者。最后一句m_aNotifiers[i])->Notify(Msg),实际相当于CStartPage->Notify(MSG);
bool CPaintManagerUI::AttachDialog(CControlUI* pControl) { ASSERT(::IsWindow(m_hWnd)); // Remove the existing control-tree. We might have gotten inside this function as // a result of an event fired or similar, so we cannot just delete the objects and // pull the internal memory of the calling code. We'll delay the cleanup. if( m_pRoot != NULL ) { m_aDelayedCleanup.Add(m_pRoot); ::PostMessage(m_hWnd, WM_APP + 1, 0, 0L); } // Set the dialog root element m_pRoot = pControl; return InitControls(pControl); } bool CPaintManagerUI::InitControls(CControlUI* pControl, CControlUI* pParent /*= NULL*/) { ASSERT(pControl); if( pControl == NULL ) return false; pControl->SetManager(this, pParent != NULL ? pParent : pControl->GetParent()); // We're usually initializing the control after adding some more of them to the tree, // and thus this would be a good time to request the name-map rebuilt. return true; }讲解:
void CContainerUI::SetManager(CPaintManagerUI* pManager, CControlUI* pParent) { for( int it = 0; it < m_items.GetSize(); it++ ) { static_cast<CControlUI*>(m_items[it])->SetManager(pManager, this); }//先设置它所有的子结点 set all his children nodes' manager first //然后再设置自己 and then set his own's manager CControlUI::SetManager(pManager, pParent); }讲解:大家可以看到,先设置所有的子结点的PaintManager,然后再设置自己;当然如果子结点仍然是CContainerUI的派生类,同样会执行这个函数;所以这样就实现了遍历设置所有控件结点;这里还用到了CControlUI的SetManager,看下具体实现:
void CControlUI::SetManager(CPaintManagerUI* pManager, CControlUI* pParent) { m_pManager = pManager; m_pParent = pParent; }4、消息处理部分,这部分没什么特殊的,就是把原来CStartPage中的消息响应移到了这里来。这部分代码就不讲了,大家可以去看看,不难懂;
void CButtonUI::Event(TEventUI& event) { if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK ) { if( ::PtInRect(&m_RectItem, event.ptMouse)) { m_uButtonState |= UISTATE_PUSHED | UISTATE_CAPTURED; m_pManager->SendNotify(this,L"click"); Invalidate(); } } if( event.Type == UIEVENT_MOUSEMOVE ) { if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) { m_pManager->SendNotify(this,L"drag"); Invalidate(); } } }这里直接调用m_pManager的SendNotify函数,由于在SendNotify中实现了MSG的封装,所以这里的代码就显得格外清爽。我这里只列举了发送NOTIFY消息的两个事件,其它事件没有列举出来,看源码就好。
由于我们将消息处理函数封装到了CPaintManagerUI中,所以我们要对CStartPage的HandleMessage重新实现,具体代码如下:
LRESULT CStartPage::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) { if( uMsg == WM_CREATE ) { m_pm.Init(m_hWnd); CDialogBuilder builder; CControlUI* pRoot = builder.Create(GetDialogResource()); ASSERT(pRoot && "Failed to parse XML"); m_pm.AttachDialog(pRoot); m_pm.AddNotifier(this); SendMessage(GetHWND(),WM_PAINT,NULL,NULL); //return 0; } LRESULT lRes = 0; if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes; return CWindowWnd::HandleMessage(uMsg, wParam, lParam); }讲解:不难理解,在这里用户可以对消息进行拦截,然后再进入到MessageHandler中处理,对于我们都不做处理的消息传递给系统,让系统默认处理。
最后就是消除原来放在控件里的m_hwnd和Notify相关的函数了;这里就不一一列举了,代码里我都加了将原来的代码加上双斜线作为删除了,一看就明白,就不说了。看源码吧。
由于本文只是集成类的实现,并没有涉及功能的改变,所以也就不再贴图了。
本文由HARVIC完成,如若转载,请标明出处,请大家尊重初创者的版权,谢谢!!
源文地址:http://blog.csdn.net/harvic880925/article/details/9632613
源码地址:http://download.csdn.net/detail/harvic880925/5843901
声明:感谢金山影音漂亮的界面图片,该图片来自网络。