DUI总结

  DUI好处在于可以很方便的构建高效,绚丽的,非常易于扩展的界面。从而很好的将界面和逻辑分离,同时易于实现各种超炫的界面效果如换色,换肤,透明等。DUI使用的是GDI+核心.DirectUI可以理解为一个轻量级的WPF,可以让C++做出C#般绚丽的界面。

DUI核心的大体结构图如下:

/

分为几个大部分:

1.    控件

2.    容器

3.    UI构建解析器(XML解析)

4.    窗体管理器

5.    渲染引擎

一、构建

1CDialogBuilder的创建

         在DUI中加载xml界面配置文件的工作由CMarkUp类完成,CMarkUp类对xml内容进行分析构建出结点树。CDialogBuilder利用CMarkUp构建的结点树New出控件,并利用xml结点树的属性,构建出控件的属性。

下面由一张图来描述如何进行界面的模块划分。

/

2、控件与容器的构建

  这里我们先讲下,控件类和布局类的区别,在这里我们在构建几个类,先看下继承图

/

 (1)、一个控件基类(CControlUI)和一个按钮控件类(CButtonUI)

控件基类,包含有控件绘制的最基本的几个函数,当然都是虚函数,比如:SetAttribute(设置控件属性),GetInterface(获取控件的this指针),DoPaint(绘图)

(2)、一个容器基类,故名思义,容器是用来盛控件的,所以它必须具有的几个函数:

Add(将控件添加到此容器的子队列中),Remove(删除某个子控件)、RemoveAll(清空子控件队列)、GetItem(获取某个子控件)、GetCount(获取子控件的个数).所以这几个函数也是容器最基类IContainerUI的函数,

(3)、CContainerUI,这个才是稍微有意义点的布局类,它用于在窗体上划分出不同的区域,然后在区域中布局控件.这里最值得注意的地方是派生出它的基类,它是两个基类的派生类(IContainerUI和CControlUI),所以容器也是控件,这一点在后面绘图代码中特点重要,在绘图时,我们将所有的容器和控件全部都定义为CControlUI,所以如果当前的如果是CDialogUI的指针的话,它就会根据继承关系,先到CDialogUI中执行相关函数,这点尤其注意!!!!(这里暂时搞不明白也没关系,后面遇到的时候,我会重新讲)

(4)、CDialogUI,这是窗体的构建类,里面包含所在构建窗体的大小、背景图案等属性。

在控件基类中保存了窗口的句柄,因为我们只有一个窗体,也就是CreateWindowEx函数创建后,返回的那个HWND,我们这里保存的就是这个HWND,那我们要它有什么用呢,这是因为我们这里的控件全部都是模拟的控件的行为而已,是并没有句柄的,所以也不可能像MFC那样根据控件的句柄单独刷新       了,所以我们要刷新控件的话,就得刷新整个窗体(当然,我们会利用缓存技术局部绘制来提高效率),我们要刷新窗体就得利用SendMessage(hwnd,msg,wparam,lparam),所以要在控件基类中保存窗口的句柄。

 

3、窗体创建

创建窗体的一个基本思想,使用户不必做更多的工作,在创建窗口的基类CWindowWnd中完成窗口类注册、创建窗口、消息响应等功能,用户只需要从基类派生,并且简单调用Create()函数就可以了。

 

二、消息机制

1、添加EVENT机制

以通知某个按钮LBUTTONDOWN为例,我们首先在HandMessage()中,截获WM_LBUTTONDOWN消息,然后根据点击位置,由FindControlFromPoint找到控件,然后发送EVENT消息通知它,EVENT消息包括消息类型、发送消息控件、如果点击消息发送鼠标位置等。在这个控件收到EVNET通知后,然    后根据事件类型,重新绘图。就是根据当前不同的控件状态,给按钮绘制不同的颜色。当点击时,是红色,放开时,是绿色。

2、添加NOTIFY机制

  试想一下,上面的EVENT机制能够通知我们的控件,当前控件应该处于哪种状态,并完成哪种绘制。但是,当用户想点击控件的时候托动窗体,或者在点击控件的时候弹出另一个窗体该怎么办呢这,就是NOTIFY通知机制,比如当前控件处理BUTTON-DWON的状态时,我们就向用户发送“click”通知消息,又比如,当我们的控件处于"BUTTON-DOWN"同时又具有"MOUSE-MOVE"状态时,我们就向用户发送“drag”(拖动)通知消息。

    EVENT相似,要发送通知消息,总是以结构体为单位的,这样能包含的信息多一些,而这里的消息通知的结构体定义围绕着我们感兴趣的几个变量,如消息发送者(控件)、消息类型、时间戳、鼠标信息等。

    DUINOTIOFY机制是作为一个功能独立的模块,所以把它单独抽象为一个类INotifyUI,这个类只包含一个纯虚函数,Notify(),参数为一个TNotifyUI的结构体。在创建窗口时,我们将窗口类CMyWnd派生自INotifyUI,在窗口类中实现Notify()函数。

用下述步骤来描述Notify的工作流程

  STEP01. 消息第一次会由CPaintManagerUI类的TranslateMessage消息接收到.
  STEP02. 2. 调用CWindowWnd::Create创建窗口完成以下操作:

  1) 如果要子类下Window的控件(就是系统的控件, 而不是duilib的模拟控件), 就设置ControlProc函数为消息回调函数.

  2)不子类化, 就注册窗口类. 此时设置WndProc为窗口消息处理回调函数

  3)CreateWindowExAPI函数创建窗口.

  这里先不看子类化相关的, 我要先看明白标准的窗口创建过程这也操作后消息就会分发到WndProc了。消息第二次就由WndProc接收到.

  STEP03. 之后要传到CPaintManagerUI类对象的MessageHandler函数. 未处理的消息就要返回给CWindowWnd类的默认消息处理函数来处理了

  STEP04. MessageHandler中利用多态性来调用不同控件的事件方法:pControl -> Event: pControl为控件对象实例之一。

  STEP5. CPaintManagerUI::SendNotify: 注意以下实现是完成控制和业务分离的关键

  m_CPaintManagerUI::AddNotifier 保存监听对象到m_aNotifiers中,利用重载特性调用注册的监听对象(窗口)的消息处理函数Notify

for (  int  i  =   0 ; i  <  m_aNotifiers.GetSize();  ++ i )

{    

static_cast < INotifyUI *> (m_aNotifiers[i]) -> Notify(Msg); 

}

posted on 2016-04-06 14:26  ccueyt  阅读(1231)  评论(0编辑  收藏  举报