搭建DirectUi开发平台
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开发平台的更多相关文章
- 搭建J2EE开发平台-Eclipse+MySql+tomcat
搭建J2EE开发平台-Eclipse+MySql+tomcat 分类: ·Java 2010-10-10 15:45 2596人阅读 评论(3) 收藏 举报 mysql平台eclipsetomcatj ...
- 在Windows下用Eclipse+CDT+MinGW搭建C++开发平台
本文提供了在Windows下用Eclipse+CDT+MinGW搭建C / C++开发平台的方法, 测试平台为Windows XP Sp2 CHS. 以下软件均为Windows平台下的版本. 1. ...
- Windows搭建golang开发平台
Golang是谷歌开发的一款开源性语言,暂时比较方便的IDE有Inteillj Idea.LiteIDE.Eclipse(Golipse)等,使用起来比较方便的IDE:LiteIDE和Inteillj ...
- Ubuntu下qemu环境搭建vexpress开发平台
在查找资料过程中,发现自己搭建虚拟的arm环境的话,有一个比较好的软件就是qemu了,当然还有其他的,大家各投所好就好. 接下来说一下qemu环境搭建过程. 其实搭建很简单,作为小白,我还是捣鼓了两三 ...
- 搭建python开发平台
转:http://www.cnblogs.com/xuqiang/archive/2011/04/18/2019484.html <1>. 建立Python的开发环境; 这里使用的Pyth ...
- 简单的搭建php开发平台 WAMP
下载wamp,地址http://www.wampserver.com/en/#download-wrapper 和正常软件安装下就行了. 修改WAMP中mysql默认空密码 WAMP安装好后,mysq ...
- 搭建基于 STM32 和 rt-thread 的开发平台
我们需要平台 如果说,SharePoint 的价值之一在于提供了几乎开箱即用的 innovation 环境,那么,智能设备的开发平台也一样.不必每次都从头开始,所以需要固定的工作室和开发平台作为创新的 ...
- Ubuntu 14.04下搭建Python3.4 + PyQt5.3.2 + Eric6.0开发平台
引言 找了很多Python GUI工具集,还是觉得PyQt比较理想,功能强大跨平台,还支持界面设计器.花一天时间折腾了Ubuntu14.04(32位)+ Python3.4 + Qt5.3.2 + P ...
- 开发指南专题4:JEECG高速微云开发平台--JEECG开发环境的搭建
开发指南专题4:JEECG微云高速开发平台开发环境搭建 1. JEECG开发环境搭建 JEECG推荐的开发环境为Myeclipse8.5/Eclipse3.7+JDK1.6+Tomcat6.0 1.1 ...
随机推荐
- CentOS6.5一键安装MySQL5.5.32(源码编译)
###################################################mysql_install.sh ...
- centos6.7 install chrome
1.yum仓库 (如果用rpm包安装 可以忽略此步) vim /etc/yum.repos.d/google-chrome.repo [google-chrome] name=google-chrom ...
- RedHat7搭建Nginx+Apache+PHP
Nginx做为前端服务器(本机IP:192.168.136.104),将访问PHP页面的动态请求转发给Apache服务器(只监听本地回环地址172.0.0.1:80) 安装Apache# yum -y ...
- Linux终端Ctrl相关快捷键
快速跳至行首:Ctrl+A 快速跳至行尾:Ctrl+E 向前删除至行首:Ctrl+U 向后删除至行尾:Ctrl+K 向后删一个单词:Ctrl+D 清屏:Crtl+L(clear)
- 【bzoj1212】 [HNOI2004]L语言
题目描述 标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的.现在你要处理的就是一段没有标点的文章. 一段文章T是由若干小写字母构成.一个单词W也是由若干小写字母构成.一个字典D是若干个单词的 ...
- ECshop--导航栏模块细究
花了一下午的时间,总算解决了. 本来想在前台界面上分析看看ecshop导航栏设置,在浏览器里面是定位到了"nav-manu"下面,然后子目录是在m_left下可以看到是动态生成一系 ...
- javascript 和 jquery 语法上的一些区别
jQuery 能大大简化 Javascript 程序的编写,我最近花时间了解了一下 jQuery,把我上手过程中的笔记和大家分享出来,希望对大家有所帮助.要使用 jQuery,首先要在 HTML 代码 ...
- linux下查找文件
1,find 经常在linux下工作,总要查找一些文件,于是就搜索的学习了一下 find 指定目录 指定条件 指定动作 举例:find . -name "my*" 查找 当前目录下 ...
- Canvas保存图片保存到本地
使用Canvas绘图,将图片保存到本地方法 一.使用HTML5 a标签的download属性,将图片保存到本地,不需要链接服务器 关于download属性:HTML5 <a>标签downl ...
- 利用case when 减少表扫描次数
数据库环境:SQL SERVER 2008R2 有网友希望有人帮他优化一下他的SQL,SQL语句如下: WITH T AS ( SELECT B.O_Money MON,B.O_States STAT ...