如何使用MFC编写自定义UI界面【附高仿QQ 2014登陆界面范例程序】

来源:互联网 发布:php模拟ajax请求 编辑:程序博客网 时间:2024/05/02 11:18
地址: http://blog.csdn.net/hujkay
作者:Jekkay Hu(34538980@qq.com)
关键词:MFC, 编写异行窗体,自定义UI控件,VC++,异形控件,高仿QQ登陆界面, 截取QQ密码,QQ钓鱼
时间: 2014/4/12

1. 概述

    在开发客户端程序的时候,稍微有点正常审美观的人都会对微软的默认UI界面表示深恶痛绝,连最简单的默认程序ICON都用那么低的像素的ICON,质量差的令人发指啊。所以,但凡好一点的软件,都会去找一些界面库,或者干脆自己写个界面库来美化一下用户交互界面,免得用户吐血暴毙。
    网络上有许多界面库,其中不少是免费的,这里推荐一款我经常用的界面库:Skinsharp。这个界面库,动态编译版本是免费的,静态编译版本是收费的,当然网上肯定有不少破解版本,但是为了中国软件的前途,大家还是稍微掏点钱买正版吧,有点扯远了,拽回来!使用第三方提供的界面库固然能美化程序界面,但有的程序需要一些比较特殊的异形界面,那么这些界面库就很难满足需求了,这样我们就必须自己去编写UI插件了。


2. 编写自定义UI界面

     编写自定义UI插件,涉及到三方面:形状,界面绘制和事件响应。下面分别对着三个方面进行详细阐述一下。

    2.1 形状

        我们先随便找个软件,比如金山毒霸吧,如下:
      
      界面是不是很酷,相比Windows自身的界面是不是感觉是天上天下之别。而我们看到这些好看的界面形状都不是方方正正,全是不规则的形状,所以我们第一步要做的就是设定窗口的形状。微软提供了一个API函数,可以设定窗体的形状:
int SetWindowRgn(HWND hWnd, HRGN hRgn, BOOL bRedraw);
 其中HRGN是一个用于表示形状的对象,我们可以用另外一个封装好的类CRGN来进行操作。该CRGN提供了许多非常友好的接口函数,比如添加一块区域,减少一块区域等,具体的可以查看一下官网的MSDN,下面列举一些常用的操作。    
    a. 创建一个矩形的区域          CRgnrgn;          rgn.CreateRectRgn(0, 0, nWidth,nHeight);    b. 在当前的区域中添加一块的区域        rcXor.SetRect(0, 0, 1, 2);        rgn_xor.CreateRectRgn(0, y, border_offset[y], y + 1);        rgn.CombineRgn(&rgn, &rgn_xor, RGN_OR);    c. 在当前的区域中只取共同区域        rcXor.SetRect(0, 0, 1, 2);        rgn_xor.CreateRectRgn(0, y, border_offset[y], y + 1);        rgn.CombineRgn(&rgn, &rgn_xor, RGN_AND);    d. 在当前的区域中异或一块的区域        rcXor.SetRect(0, 0, 1, 2);        rgn_xor.CreateRectRgn(0, y, border_offset[y], y + 1);        rgn.CombineRgn(&rgn, &rgn_xor, RGN_XOR);
      
   区域创建了好了之后,就设定一下窗体的区域:      
SetWindowRgn((HRGN)rgn, TRUE);

下面一段完整的代码,供大家参考一下:  

  CDC* pDC = GetDC(); CRectrc; GetWindowRect(rc); rc.OffsetRect(-rc.left, -rc.top); CRgnrgn; rgn.CreateRectRgn(0, 0, rc.Width(), rc.Height()); CRgnrgn_xor; CRectrcXor;  for (int y = 0; y < nSize; ++y) {  rcXor.SetRect(0, y, border_offset[y], y + 1);  rgn_xor.CreateRectRgn(0, y, border_offset[y], y + 1);  rgn.CombineRgn(&rgn, &rgn_xor, RGN_XOR);  rgn_xor.DeleteObject(); } for (int y = 0; y < nSize; ++y) {  rcXor.SetRect(rc.right - border_offset[y], y, rc.right, y + 1);  rgn_xor.CreateRectRgn(rc.right - border_offset[y], y, rc.right, y + 1);  rgn.CombineRgn(&rgn, &rgn_xor, RGN_XOR);  rgn_xor.DeleteObject(); } for (int y = 0; y < nSize; ++y) {  rcXor.SetRect(0, rc.bottom - y - 1, border_offset[y], rc.bottom - y);  rgn_xor.CreateRectRgn(0, rc.bottom - y - 1, border_offset[y], rc.bottom - y);  rgn.CombineRgn(&rgn, &rgn_xor, RGN_XOR);  rgn_xor.DeleteObject(); } for (int y = 0; y < nSize; ++y) {  rcXor.SetRect(rc.right - border_offset[y], rc.bottom - y - 1, rc.right, rc.bottom - y);  rgn_xor.CreateRectRgn(rc.right - border_offset[y], rc.bottom - y - 1, rc.right,rc.bottom - y);  rgn.CombineRgn(&rgn, &rgn_xor, RGN_XOR);  rgn_xor.DeleteObject(); } SetWindowRgn((HRGN)rgn, TRUE); m_Rgn.DeleteObject(); m_Rgn.Attach(rgn.Detach()); ReleaseDC(pDC);

   2.2 界面绘制

        界面绘制是繁琐的工作,推荐使用Gdiplus库来支持一下,在stdafx.h加入如下的代码,将其包含进来即可:
#include <gdiplus.h> using namespace Gdiplus; #pragma comment(lib, "gdiplus.lib")
    自定义绘制界面需要重载一下主要下面的几个函数:
     
绘制客户区域的函数:ON_WM_PAINT()绘制非客户区域的函数:ON_WM_NCPAINT()背景擦除函数:ON_WM_ERASEBKGND()限定窗体大小的函数:ON_WM_GETMINMAXINFO()
   一般来说,自绘窗体控件最主要的就是ON_WM_PAINT()方法,大部分时间都耗在就是在里面,这个活比较细致,像素一个一个地调,没有耐心的人估计会砸电脑。
    在这里有技巧需要提一下,如果绘制的界面插件老一闪一闪的,可以先在内存里面创建一块画板,在里面画好了之后然后在显示到界面上,可参考如下的代码:
     
   CRectrcClient;   GetClientRect(&rcClient);  CPaintDCdc(this);  CDC MemDC;  MemDC.CreateCompatibleDC(&dc);  CBitmap memBmp;  memBmp.CreateCompatibleBitmap(&dc, rcClient.Width(), rcClient.Height());  CBitmap *pOldmap = MemDC.SelectObject(&memBmp);  DrawImageStyle(MemDC, rcClient);  dc.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &MemDC, 0, 0, SRCCOPY);  MemDC.SelectObject(pOldmap);  MemDC.DeleteDC();

  2.3 事件

     事件是指根据用户的交互动作而触发某些动作,主要是鼠标和键盘两种。我这里主要介绍下鼠标事件,鼠标事件可分为:移动,按下,弹起,移入,移出,单击,双击和滚动。在创建自定义创建窗口的时候,千万不要重载单击和双击的事件,这是非常不好的习惯,而且容易引发各种问题,最好的办法是自己在按下按钮的时候模拟单击或者双击的信息,所以一般来说,重载下面几个方法即可:
 ON_WM_MOUSEMOVE() ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave) ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover) ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_LBUTTONDBLCLK()
     事件和窗体绘制有着紧密的关系,比如一个按钮,用户鼠标移入时是一种状态,按下时又是一种状态,弹起式又是一状态。因此,在绘制窗体时,要根据当前的用于的交互状态绘制不同的形态,非常考验一个人的变成能力。

3. 小结

    绘制自定义UI插件是个细活,必须如加工艺术品一样有耐心,所以我但凡见到做UI插件的同仁,都心生佩服。周末闲来无事,根据其他高手的一些代码,高仿了一下QQ的登陆界面,很酷吧,呵呵。




    高仿的QQ 2014登陆程序的下载地址:http://download.csdn.net/user/hujkay

    别干坏事啊~~~


胡杨, Jekkay Hu

2014/4/12










0 0