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. Android 解决调用系统相册打不开图片 DecodeServices报解码错误

    这是由于系统相册不知道你图片目录是一个相册.打开前需要向系统相册“注册一下”,说白了就是让系统相册知道你这个图片所在的文件夹是个相册. private static void scanImageFil ...

  2. 微信上传图文消息invalid media_id hint,thumb_media_id怎么获取

    微信上传图文消息thumb_media_id, thumb_media_id怎么获取, 微信群发图文消息invalid media_id hint, 微信群发图文消息40007, 40007,inva ...

  3. 防御SQL注入的方法总结

    这篇文章主要讲解了防御SQL注入的方法,介绍了什么是注入,注入的原因是什么,以及如何防御,需要的朋友可以参考下   SQL 注入是一类危害极大的攻击形式.虽然危害很大,但是防御却远远没有XSS那么困难 ...

  4. javascript通过字典思想操作数据

    作为一名前端程序猿,相对于后端操作数据的机会较少.然而,有些时候因为一些特殊的原因(如:需要构造成对应插件需要的数据格式,需要返回特定的数据格式等)而不得不对数据进行筛选.重构.相对于后端语言,我们没 ...

  5. ManualResetEvent的使用与介绍

    它可以通知一个或多个正在等待的线程已发生事件,允许线程通过发信号互相通信,来控制线程是否可心访问资源 当一个线程开始一个活动(此活动必须完成后,其他线程才能开始)时,它调用 Reset 以将 Manu ...

  6. 造一个Badge Service(徽章)的轮子

    什么是Badge Service 细心的读者朋友一定在很多Github的Repo,npm的package页面看到过诸如 的徽章.这些徽章是干什么用的? 大家看到上文中我引用的Badge的左侧,是Dow ...

  7. css - a:hover变色问题

    今天在帮我们学校做网站的时候,由于在css这里不是很擅长,过程中发现一个问题,a:hover的时候,字体的颜色不变.后来才发现将a和div的嵌套的问题, 我的css代码为: .left_box .lb ...

  8. html 中设置span的width完美解决方法

    在默认情况下,由于span是行标签,设置width是无效的.只有改变display的属性,才可以实现设置宽度. 1.初步想法 span{ background-color:#ccc; display: ...

  9. MySQL常见问题汇总(原创)

    本文记录了使用Mysql时遇到的问题,持续更新中... 1.在windows命令行下登录mysql时报错: C:\Program Files\MySQL\MySQL Server 5.0\bin> ...

  10. ArcGIS JS Demo

    最近在用 ArcGIS 写了一个Demo 效果如下: 1. 引用 ArcGIS JS API arcgis_js_api/init.js 2. 引用 样式 arcgis_js_api/js/esri/ ...