DirectUi的效果可以使用GDI、GDI+、DirectX、OpenGL实现,常用的有GDI和GDI+,后两种有杀鸡用牛刀的感觉。在网络上能找到此方面的教材

现在的软件越来越多的有很炫目的界面,看来商家是越来越重视用户体验了,这个一个流行趋势呀。从技术上来说,美化界面基本有两种方式:

1. DirectUi 无句柄自绘控件方式

2. 继承MFC控件类进行自绘

两种各有优缺点,前者:实现复杂,控制复杂(如:消息控制、各个控件的基本设置),但自由度很大,你可以实现你能想象到的任何控件。后者:实现简单、但受制于MFC现有的控件功能,最重要的是窗口一旦多,窗口背景的绘制和子窗口的绘制如若处理不当很容易造成局部贴图残缺、拖拽窗口闪烁。所以一般在子窗口控件不随着主窗口拖拽而发生位置变化时采用后者的方式,其他建议采用前者的方式来完成。

由于以后会经常用到DirectUi进行界面美化,于是抽空打了一个DirectUi的开发平台,方便以后开发,DirectUi的开发平台要求如下:

1. 建立在VS2005的MFC Dialog工程之上

2. 实现最基本的一个空的Dialog的皮肤

3. 皮肤实现后,必须保留最基本的Dialog的功能,如:最大化、最小化、双击标题栏、单击任务栏按钮、拖拽等

4. 建立DirecrUi的引擎,已最简便的方式便于以后的程序扩展

OK,开工了。先建立MFC的Dialog的工程,保持所有属性都默认,去掉【确定】和【退出】按钮,如下:

之后我们必须解决一个又一个问题:

问题1:我们在什么地方重绘窗口

有3个消息处理可以重绘窗口:WM_ERASEBKGND、WM_PAINT、WM_NCPAINT,第一个只重绘窗口整个背景,包括客户区和非客户区,不重绘子窗口;第二个只重绘客户区,无法重绘非客户区;第三个重绘非客户区,也可以重绘客户区。很明显,我们应该处理第三个消息,但第一个消息我们也需要处理,整个函数,直接 return TRUE 即可。

问题2:顽固的系统默认标题栏

绘制第一步当然是重绘标题栏,在WM_NCPAINT里重绘标题栏后,发现那几个系统按钮在窗口激活或者拖动的时候是不是闪现在界面上,相当的顽固,如下方法即可解决:

1. 截获 WM_NCACTIVATE 消息,此消息函数修改如下:

BOOL CSkinTestDlg::OnNcActivate(BOOL bActive)
{
 this->SendMessage(WM_NCPAINT, 0, 0);
 return TRUE;
}
2. 在 WindowProc 函数中截获绘制标题栏的消息,代码如下:

LRESULT CSkinTestDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
 if(message == 0x00AE || // WM_NCUAHDRAWCAPTION
 message == 0x00AF)  // WM_NCUAHDRAWFRAME
 {
  return WM_NCPAINT;
 }

return __super::WindowProc(message, wParam, lParam);
}

以上两步,可以很完美的解决顽固的标题栏按钮问题。

问题3:变态的标题栏消息处理

系统自带的标题栏会随着桌面主题的变化,标题栏的高度、系统按钮的位置都会发生变化,这个相当烦人,咱们自定义的按钮的位置大小一般都不会和系统按钮相同。在处理这个问题的过程中,发现了一些导致了一些矛盾之处,几乎很难调和(抱歉,时间太久了,很多的矛盾忘了),比如:客户区坐标和非客户区坐标转换问题(两套坐标系,维护比较麻烦)、鼠标在标题栏的双击区域、最大化的边框问题... ... 结合这些问题,最后的处理方式是:截获 WM_NCCALCSIZE 消息,修改非客户区大小,让非客户区大小为0,所有自绘的东东都在客户区实现,包括标题栏和边框。代码如下:

// 截获此消息为了让窗口没有标题栏和边框
void CSkinTestDlg::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
 // __super::OnNcCalcSize(bCalcValidRects, lpncsp);
}

问题4:没有边框的拖拽

问题3的衍生问题,没有了边框,当然就不能拖拽了,那我们自己处理拖拽吧,很简单,截获 WM_NCHITTEST 消息,代码如下:

LRESULT CSkinTestDlg::OnNcHitTest(CPoint point)
{
  // 注意:不是全屏的情况下,才可以拖拽,需要用户自己处理
  int nCheckPos = 2;
  int nRDPos = nCheckPos * 2;
  CRect WndRect(0, 0, 0, 0);
  GetWindowRect(&WndRect);

m_nMouseSizeType = -1;
  if(point.x >= WndRect.right - nRDPos && point.y >= WndRect.bottom - nRDPos)
  {
   // 右下角
   return HTBOTTOMRIGHT;
  }
  else if(point.x >= WndRect.right - nRDPos && point.y <= WndRect.top + nRDPos)
  {
   // 右上角
   return HTTOPRIGHT;
  }
  else if(point.x <= WndRect.left + nRDPos && point.y <= WndRect.top + nRDPos)
  {
   // 左上角
   return HTTOPLEFT;
  }
  else if(point.x <= WndRect.left + nRDPos && point.y >= WndRect.bottom - nRDPos)
  {
   // 左下角
   return HTBOTTOMLEFT;
  }
  else if(point.x >= WndRect.right - nCheckPos)
  {
   // 右边线
   return HTRIGHT;
  }
  else if(point.x <= WndRect.left + nCheckPos)
  {
   // 左边线
   return HTLEFT;
  }
  else if(point.y <= WndRect.top + nCheckPos)
  {
   // 上边线
   return HTTOP;
  }
  else if(point.y >= WndRect.bottom - nCheckPos)
  {
   // 下边线
   return HTBOTTOM;
  }

return __super::OnNcHitTest(point);
}

问题5:最大化的边框问题

一个正常的窗口,最大化后总是比当前屏幕大,刚好能将软件的边框盖住,我实在不想要这个效果,那我只能自己处理最大化的效果了。

我自定一个了最大化消息,当自绘的最大化按钮按下时,触发这个消息,接受到消息后,取得屏幕的工作区域,然后将窗口改变到工作区域大小即可,代码如下:

// 先记录最大化前的窗口位置,以便恢复的时候用。
    this->GetWindowRect(&m_MaxBeforeRect);
    CRect WndRect(0, 0, 0, 0);
    ::SystemParametersInfo(SPI_GETWORKAREA, 0, &WndRect, 0);

this->MoveWindow(&WndRect);

问题又来了:最大化动画没了,这个简单,再加一句代码,播放动画:

// 先记录最大化前的窗口位置,以便恢复的时候用。
    this->GetWindowRect(&m_MaxBeforeRect);
    CRect WndRect(0, 0, 0, 0);
    ::SystemParametersInfo(SPI_GETWORKAREA, 0, &WndRect, 0);

// 播放动画
    DrawAnimatedRects(IDANI_CAPTION, &m_MaxBeforeRect, &WndRect);

this->MoveWindow(&WndRect);

同样恢复窗口的代码如下:

// 恢复
    CRect WndRect(0, 0, 0, 0);
    ::SystemParametersInfo(SPI_GETWORKAREA, 0, &WndRect, 0);

// 播放动画
    DrawAnimatedRects(IDANI_CAPTION, &WndRect, &m_MaxBeforeRect);

this->MoveWindow(&m_MaxBeforeRect);

问题6:自定义系统菜单

系统默认的系统菜单(鼠标右击任务栏按钮的菜单),不能修改,那我们自己做一个,查了很多消息,终于找到啦,消息 0x0313 就是鼠标右击任务栏按钮弹出菜单的消息,代码如下:

定义消息:

#define WM_POPUPSYSTEMMENU    0x0313

截获消息:

ON_MESSAGE(WM_POPUPSYSTEMMENU, OnPopupSystemMenu)

处理消息:

afx_msg LRESULT OnPopupSystemMenu(WPARAM wParam, LPARAM lParam);

LRESULT CSkinTestDlg::OnPopupSystemMenu(WPARAM wParam, LPARAM lParam)
{
 CMenu PopMenu;
 CPoint point;
 GetCursorPos(&point);

PopMenu.CreatePopupMenu();

PopMenu.AppendMenu(MF_STRING, 111111, _T("关于界面测试"));
 PopMenu.AppendMenu(MF_SEPARATOR);
 PopMenu.AppendMenu(MF_STRING, IDCANCEL, _T("退出\tAlt+F4"));

PopMenu.TrackPopupMenu(TPM_RIGHTBUTTON, point.x, point.y, this);

PopMenu.DestroyMenu();

return 0L;
}
这样,想怎么处理就怎么处理。

最后,我们贴上皮肤,基本的DirecrUi的平台就构建好了,效果如下:

http://blog.csdn.net/qing666888/article/details/49734897

搭建DirectUi开发平台的更多相关文章

  1. 搭建J2EE开发平台-Eclipse+MySql+tomcat

    搭建J2EE开发平台-Eclipse+MySql+tomcat 分类: ·Java 2010-10-10 15:45 2596人阅读 评论(3) 收藏 举报 mysql平台eclipsetomcatj ...

  2. 在Windows下用Eclipse+CDT+MinGW搭建C++开发平台

    本文提供了在Windows下用Eclipse+CDT+MinGW搭建C / C++开发平台的方法, 测试平台为Windows XP Sp2 CHS.   以下软件均为Windows平台下的版本. 1. ...

  3. Windows搭建golang开发平台

    Golang是谷歌开发的一款开源性语言,暂时比较方便的IDE有Inteillj Idea.LiteIDE.Eclipse(Golipse)等,使用起来比较方便的IDE:LiteIDE和Inteillj ...

  4. Ubuntu下qemu环境搭建vexpress开发平台

    在查找资料过程中,发现自己搭建虚拟的arm环境的话,有一个比较好的软件就是qemu了,当然还有其他的,大家各投所好就好. 接下来说一下qemu环境搭建过程. 其实搭建很简单,作为小白,我还是捣鼓了两三 ...

  5. 搭建python开发平台

    转:http://www.cnblogs.com/xuqiang/archive/2011/04/18/2019484.html <1>. 建立Python的开发环境; 这里使用的Pyth ...

  6. 简单的搭建php开发平台 WAMP

    下载wamp,地址http://www.wampserver.com/en/#download-wrapper 和正常软件安装下就行了. 修改WAMP中mysql默认空密码 WAMP安装好后,mysq ...

  7. 搭建基于 STM32 和 rt-thread 的开发平台

    我们需要平台 如果说,SharePoint 的价值之一在于提供了几乎开箱即用的 innovation 环境,那么,智能设备的开发平台也一样.不必每次都从头开始,所以需要固定的工作室和开发平台作为创新的 ...

  8. Ubuntu 14.04下搭建Python3.4 + PyQt5.3.2 + Eric6.0开发平台

    引言 找了很多Python GUI工具集,还是觉得PyQt比较理想,功能强大跨平台,还支持界面设计器.花一天时间折腾了Ubuntu14.04(32位)+ Python3.4 + Qt5.3.2 + P ...

  9. 开发指南专题4:JEECG高速微云开发平台--JEECG开发环境的搭建

    开发指南专题4:JEECG微云高速开发平台开发环境搭建 1. JEECG开发环境搭建 JEECG推荐的开发环境为Myeclipse8.5/Eclipse3.7+JDK1.6+Tomcat6.0 1.1 ...

随机推荐

  1. sharepoint中的YesNo字段

    sharepoint中的YesNo字段实际上是一个Boolean字段,性格有点特别,如果IsShow是一个YesNo字段,使用caml查询的时候值为”1“(Yes)”0“(No),Item[IsSho ...

  2. TCP服务器端和客服端(一)

    就是一个客服端(Socket)和服务器(ServerSocket)端的链接间.我的理解是一个服务端可以链接多个客服端. 在客服端有输入流outPutStream. 用于发送数据 在服务器端有输出流.i ...

  3. ROW_NUMBER () 与 PARTITION组合拳

    --在一个Book表里面里有字段AuthorID与Author表关联,现在要求按PublishDate字段倒序排列,列出每个作者的前五本书.要求有没有一条语句搞定的--可用游标或者临时表--最好解决方 ...

  4. c#equals相关

    1.==是直接比较值类型的值或引用类型的引用地址,但==不能用于struct,struct只能用equals来比较.==一般情况下与object.equals得到的结果是相等的. 2.Referenc ...

  5. cookie : 存储数据

    cookie : 存储数据,当用户访问了某个网站(网页)的时候,我们就可以通过cookie来像访问者电脑上存储数据 1.不同的浏览器存放的cookie位置不一样,也是不能通用的 2.cookie的存储 ...

  6. Centos安装编译环境

    众所周知,在安装完Centos完了以后,是没有编译环境的,我自己挂上光盘,手动安装解决了几十的依赖关系,在配置些软件时,还是经常报些依赖软件包的缺失,很是头疼,后来百度了下centos需要装的编译环境 ...

  7. 「译」如何正确学习JavaScript

    原文:How to Learn JavaScript Properly 目录 不要这样学习JavaScript 本课程资源 1-2周(简介,数据类型,表达式和操作符) 3~4周(对象,数组,函数,DO ...

  8. mysql5.7.14安装与配置

    参考文章链接: http://jingyan.baidu.com/article/afd8f4de9006d934e286e9fd.html http://www.cnblogs.com/wenthi ...

  9. ie9以上浏览器input文本框/密码框后面的小叉子/小眼睛问题

    找了很久不知什么属性控制的这个东西,经过群友的指点重要找到.

  10. apache2.4配置虚拟目录

    刚开始学习,跟着韩顺平老师的视频课件学习ing~ 这是自己在配置虚拟目录时遇到的问题以及解决办法,记录下来~ ---------------------------分割线君-------------- ...