My first Duilib program

  1. Prepare for development

  打开DuiFarm项目DuiFarm.cpp文件,将除_tWinMain函数之外所有内容删除。删除后的DuiFarm.cpp内容如下:

int APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
{;}

  在DuiFarm.cpp文件首引入头文件UIlib.h,并引用namespace,及相关预编译指令。如下:

 #include "..\\..\\duilib-master\\DuiLib\\UIlib.h"
 using namespace DuiLib;
 #ifdef _DEBUG
     #ifdef _UNICODE
         #pragma comment(lib,"..\\..\\duilib-master\\Lib\\Duilib_ud.lib")
     #else
         #pragma comment(lib,"..\\..\\duilib-master\\Lib\\Duilib_d.lib")
     #endif
 #else
     #ifdef _UNICODE
         #pragma comment(lib,"..\\..\\duilib-master\\Lib\\Duilib_u.lib")
     #else
         #pragma comment(lib,"..\\..\\duilib-master\\Lib\\Duilib.lib")
     #endif
 #endif
 int APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
 {
     //TODO: Input code here...
 }

  这样,Duilib已经成功集成在项目DuiFarm项目中了。注意作者项目的文件结构,引用Duilib.h与Duilib_x.lib文件使用相对路径必须正确(,相对路径以DuiFarm下Debug目录内为参照)。

  在编写其他代码之前,需要明确几个知识点。Duilib的几个内置对象。

  1. CWindowWnd:窗口对象父类,负责创建窗口、窗口消息过程处理、提供窗口子类化与超类化接口。
  2. CPaintManagerUI:负责窗口图形绘制。CPaintManagerUI三个主要功能:绘制控件、管理消息、事件通知。
  3. CDialogBuilder:控件布局类,主要作用:解析XML,构建控件树,创建控件对象。
  4. INotifyUI:处理事件和通知。子类重载Notify(TNotify &msg)虚函数,实现处理事件通知功能。
  5. CControlUI:控件管理的父类,为控件提供通用属性。

  2.Starting coding "Hello World"

  在DuiFarm.cpp中新建类DuiFarm,继承自CWindowWnd与INotifyUI,重写CWindowWnd的虚函数GetWindowClassName、HandleMessage与INotify的虚函数Notify。代码如下:

class DuiFarm: public CWindowWnd, public INotifyUI
{
protected:
    CPaintManagerUI m_PaintManager;
public:
    virtual LPCTSTR GetWindowClassName() const
    {/*some code*/}
    virtual void    Notify(TNotifyUI& msg)
    {/*some code*/}
    virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {/*some code*/}
};

  其中三个函数的主要功能:

  1. GetWindowClassName:返回窗口名称。
  2. Notify:事件响应,如点击事件。
  3. HandleMessage:处理消息,例如创建窗口消息。

  首先补全GetWindowClassName函数的代码,在函数体内,直接返回窗体名字符串。

virtual LPCTSTR GetWindowClassName() const
{return _T("DuiFramFrame");}

  HandleMessage函数中补全对窗体创建等消息的处理。

 virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
     LRESULT lRes = ;
     if( uMsg == WM_CREATE )
     {
         m_PaintManager.Init(m_hWnd);    //init dialog paint
         CDialogBuilder builder;
         /* load xml, create widget obj, draw dialog */
         CControlUI *pRoot = builder.Create(_T(,NULL,&m_PaintManager);
         ASSERT(pRoot&&"### XML error!");
         m_PaintManager.AttachDialog(pRoot);    //widget attach to dialog
         m_PaintManager.AddNotifier(this);      //add msg notify to window
         return lRes;
     }
     if( m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes))
     {
         return lRes;
     }
     return __super::HandleMessage(uMsg, wParam, lParam);
 }

  第4行在消息为窗体创建(WM_CREATE)时,执行:

  1. 初始化当前窗口绘制(line 6)
  2. 加载窗口设计xml文件,初始化窗口部件(line 9)
  3. 窗口部件对象附着对话框(line 11)
  4. 添加消息通知(line 12)

  执行完上述代码,窗体即绘制完成,事件通知已可用。在上述代码中,第8行加载xml文件即为duilib的界面设计xml。犹如html之于浏览器,dui通过解析xml将控件绘制到窗体上。

  xml文件如下:

 <?xml version="1.0" encoding="utf-8"?>
 <Window size="100,100">
   <HorizontalLayout bkcolor="#FFFFFF00">
     <Button name="btnHello" text="Hello World :-)"/>
   </HorizontalLayout>
 </Window>

  将文件保存到项目Debug目录下(因当前项目以Debug模式运行)。这段xml描述了窗体的整体布局:

  • 第二行定义了窗体大小,长宽各500像素。
  • 第三行定义了一个横向布局,背景黄色(bkcolor为背景色设置)。
  • 第四行定义了一个按钮,名称是btnHello,显示文字为Hello World :-)

  现在窗体已经定义好,需要添加对按钮事件的处理。根据上一篇介绍,按钮事件处理要在重载的Notify函数中编写。

 virtual void Notify(TNotifyUI& msg)
 {
     if(msg.sType==_T("click"))
     {
         if(msg.pSender->GetName()==_T("btnHello"))
             ::MessageBox(NULL,_T("Hello World :-)"),_T("Hallo Welt :-D"),NULL);
     }
 }
  • 第2行代码表示:“此处要接收类型为‘click’的事件”。
  • 第5行代码表示:“在所有click事件中,此处仅接收名为‘btnHello’这家伙的事件”。
  • 第6行代码表示:“提示英德对照的Hello World,以及它们对应的口型”。

  编写到这里,项目已经具备窗口绘制、窗口布局和事件处理的基本功能,下面需要在入口函数中为之初始化与分配资源:

 int APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
 {
     CPaintManagerUI::SetInstance(hInstance);
     CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath());
     DuiFarm duiFarm;
     duiFarm.Create(NULL, _T("Dui Farm"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
     duiFarm.CenterWindow();
     duiFarm.ShowModal();
     ;
 }
  • 第3行~第4行,入口函数对实例和资源进行分配,第4行实参为当前项目路径。
  • 第6行:创建窗口。
  • 第7行:设置窗口在屏幕居中。
  • 第8行:显示窗口。

  在调试运行前,需要确认DuiLib[x].dll是否位于DuiFarm项目的Debug目录下(x为_d,_ud或不存在,根据项目编码以及是否为Debug模式而定)。

  运行效果如下:

  

图2.2.1 显示窗体

  如图,窗体显示黄色,而按钮实际上占据了整个窗体。点击按钮如下:

图2.2.2 点击按钮(按钮占据了整个窗体)

  通过以上代码,笔者实现了一个最简单的Duilib窗体界面——一个窗体,一个铺满了整个窗口区域的黄色按钮(由xml定义可知,窗体显示“HelloWorld :-)”的部分是Windows按钮)。

  Duilib三部分分割业务与显示:XML、(XML-)UI引擎、Win32,并且Duilib可以脱离传统Windows界面限制,开发出独具特色的UI界面风格。我们通过重写HandleMessage函数来实现简单的无边界窗口:

 virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
     LRESULT lRes = ;
     if( uMsg == WM_CREATE )
     {
         m_PaintManager.Init(m_hWnd);    //init dialog paint
         CDialogBuilder builder;
         /* load xml, create widget obj, draw dialog */
         CControlUI *pRoot = builder.Create(_T(,NULL,&m_PaintManager);
         ASSERT(pRoot&&"### XML error!");
         m_PaintManager.AttachDialog(pRoot);    //widget attach to dialog
         m_PaintManager.AddNotifier(this)    ;//add msg to window
         return lRes;
     }
     /* blocked message: WM_NCACTIVATE,WM_NCCALCSIZE,WM_NCPAINT*/
     else if(uMsg == WM_NCACTIVATE)
     {
         if(!::IsIconic(m_hWnd))
             )?TRUE:FALSE;
     }
     else if(uMsg == WM_NCCALCSIZE || uMsg == WM_NCPAINT)
     {
         ;
     }
     if( m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes))
     {
         return lRes;
     }
     return __super::HandleMessage(uMsg, wParam, lParam);
 }

  通过上面代码第15行~28行,屏蔽WM_NCACTIVATE,WM_NCCALCSIZE,WM_NCPAINT三个消息,实现无边界窗体设计。显示和点击效果如图:

  在实际软件开发过程中,显示与业务分离是软件开发的指导性思想,UI与业务分离使得软件开发工作更好的划分业务范围。与原始的开发模式对比,XML定义界面相比C++代码定义界面更加简洁便利,后台开发人员可以从界面设计、图片图标呈现等非本专业工作中解放出来,专注于业务逻辑和数据处理;美工与UI设计人员专注于界面美化而不必顾及后台兼容性问题。Duilib通过xml解析、对象创建、部件附着将复杂的UI设计封装到可配置的库中,完全基于win32平台使开发人员不必另学一种技术,从而避免了“重新造轮子”给程序员带来额外的学习成本开销。

Duilib第一步(II)-Hello World的更多相关文章

  1. Duilib第一步(I)-简介与环境搭建

    Primus gradus et cognoscetis veritatem et veritas liberabit vos.  --Johannes 8:32 Introduction Duili ...

  2. Duilib第一步(III)-知识进阶

    核心模块 CWindowWnd:窗口对象管理父类 创建窗口. 窗口消息过程处理. 提供窗口子类化.超类化接口. CDialogBuilder:空间布局类 解析XML界面布局文件,构建控件树 创建控件对 ...

  3. 开发thinkphp的第一步就是给Application目录(不包括其下的文件)777权限, 关闭selinux

    开发thinkphp的时候, 总是会出现各种个样 的奇怪的毛病, 比如: 说什么Application目录不可写, 比如: 说什么 _STORAGE_WRITE_ERROR, 不能生成 Runtime ...

  4. ElasticSearch第一步-环境配置

    ElasticSearch第一步-环境配置 ElasticSearch第二步-CRUD之Sense ElasticSearch第三步-中文分词 ElasticSearch第四步-查询详解 Elasti ...

  5. Scala的第一步

    第一步:学习使用Scala解释器 开始Scala最简单的方法是使用Scala解释器,它是一个编写Scala表达式和程序的交互式“shell”.在使用Scala之前需要安装Scala,可以参考 Firs ...

  6. UE4蓝图编程的第一步

    认识UE4蓝图中颜色与变量类型: UE4中各个颜色对应着不同的变量,连接点和连线的颜色都在表示此处是什么类型的变量.对于初学者来说一开始看到那么多连接点, 可能会很茫然,搞不清还怎么连,如果知道了颜色 ...

  7. STM32F407第一步之点亮LED

    STM32F407第一步之点亮LED. 要点亮LED,首先了解一下F4的GPIO模块.首先看一下STM32F4数据手册,GPIO模块的内部结构图 看上去有点复杂,不要怕,慢慢理解就可以了.对外引脚那里 ...

  8. 重制AdvanceWars第一步 -- 搞定地图

    首先来聊下高级战争吧Advance Wars,由任天堂旗下的Intelligent Systems开发的战棋游戏.初作诞生于GBA上,后来继续跟进了高战2黑洞崛,而后在下一代掌机DS上也出了三代续作高 ...

  9. 高德携手阿里云发布“LBS云”,账户打通只是第一步

    位置.游戏.视频,是公认的基于云计算的三大移动端应用方向.而今,LBS云有了更多进展,在高价值应用与云平台之间实现了资源打通和融合,高德迈出了实质性的一步. 高德地图副总裁郄建军(左)与阿里云业务总经 ...

随机推荐

  1. Maven初步

    Maven初入 maven 是一个项目管理工具, 它包含了一个 项目对象模型(Project Object Model POM), 一组标准集合, 一个项目生命周期(Project Lifecycle ...

  2. mybatis 中文文档

    http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html

  3. tomcat中session在两个webapp中实现共享

    现在遇到一个需求就是要求完成简单的单点登录,通过在一个tomcat实例中放置两个webapps应用ROOT应用和CEO应用来完成在ROOT应用登录后,在CEO可以直接使用,而未在ROOT应用登录时,不 ...

  4. 【Java入门提高篇】Day13 Java中的反射机制

    前一段时间一直忙,所以没什么时间写博客,拖了这么久,也该更新更新了.最近看到各种知识付费的推出,感觉是好事,也是坏事,好事是对知识沉淀的认可与推动,坏事是感觉很多人忙于把自己的知识变现,相对的在沉淀上 ...

  5. C语言打印不出百分号'%'(以解决)

    1. 问题描述 今天,我需要把百分号'%'打印出来,考虑到它是特殊符号,我就用转义字符'\',和它组合,结果是漆黑的屏幕什么也没有. 2. 解决办法    我问度娘, 她告诉我要打印百分号需要在它的前 ...

  6. linux shell 中的 2>&1 用法说明

    linux中有三种标准输入输出,分别是 STDIN,STDOUT,STDERR,对应的数字是 0,1,2. STDIN 是标准输入,默认从键盘读取信息: STDOUT 是标准输出,默认将输出结果输出至 ...

  7. WebService学习------小实例开发(号码归属地查询)

    1.WebService简介: WebService是一种跨平台,跨语言的,可以接收从Internet或者Intranet上的其它系统中传递过来的请求,轻量级的独立的通讯技术.通过SOAP在Web上提 ...

  8. JavaScript 基本语法 -- 运算符的优先级

    在所有的运算里,都是有运算顺序的.小时候学四则运算的时候,我们都知道这么一个规则:先乘除后加减,有括号要先算括号! 同样的,在JavaScript里面,运算符也是有相应的优先级的.其优先级如下表所示, ...

  9. ABP官方文档翻译 3.3 仓储

     仓储 默认仓储 自定义仓储 自定义仓储接口 自定义仓储实现 基础仓储方法管理数据库连接 查询 获取单个实体 获取实体列表 关于IQueryable 自定义返回值 插入 更新 删除 其他 关于异步方法 ...

  10. ABP官方文档翻译 2.2 ABP会话

    ABP会话 介绍 关于IAbpSession 注入会话 会话属性 覆盖当前会话值 警告! 用户标示 介绍 如果应用需要登录的话,同样也需要知道当前用户可以执行哪些操作.ABP在展现层提供了会话对象,同 ...