不规则窗口——GDI+实现

来源:互联网 发布:ssh框架项目源码 编辑:程序博客网 时间:2024/04/29 17:41

  今天终于完成一个小心结,以前看到鱼鱼桌面秀那里那么炫的界面就想自己做一回,但是苦于没有实现思路,今天终于有了点眉目了。其实那些桌面秀的界面都是不规则的窗口,以前也有想过实现做不规则窗口,但由于各种原因没动手都没有完成。经过这两天来的搜索,终于完全明白怎么回事了。

  要实现不规则窗口,有几种方法。

  第一种是基于GDI的,这种方法是根据图片或者其他图案生成一个窗口区域也就是RGN,然后通过SetWindowRgn来得到不规则窗口的区域,然后在重绘窗口的时候就画那张图就可以了,这种方法实现起来比较复杂,因为要得到不规则区域比较困难。

  第二方法是基于GDI+的,GDI+是在增强的GDI,扩展了很多的功能,支持多种图片的格式,不再是只有BITMAP让你苦恼,是一套C++的接口,所以操作都是通过类实现的。利用GDI+的方法跟第一种方法有一点是相似的,就是要在重绘窗口的时候画图,但不同的是它不需要创建不规则窗口区域,还有重绘的时候也不是DC间的直接拷贝,而是通过一个叫做UpdateLayerWindow的函数来实现的,这个函数从WINDOWS2000后的系统才有,而且使用的时候如果是VC6而没有装SDK的话要用LoadLibrary的方法得到函数指针调用。这种方法其实是利用WINDOWS的一种叫层次窗口的东西,这种窗口,通过它可以实现半透明效果。最常操作这种窗口的API就是SetLayeredWindowAttributes和UpdateLayerWindow函数。之前我有接过这两个函数,但没深入研究到不规则窗口上,遗憾!另外要实现不规则窗口还要把窗口的样式属性设置成WS_EX_LAYERED, 不然的话是不能成功的,WS_EX_LAYERED的值是0x80000,在网上看到很多文章都是直接写上这个值,根本不知道它实际的含义,估计都是在用VC6编程,呵呵。

  好了,下面来用一个实例来讲讲我如何实现不规则窗口的,这东西还是从一些前辈上学到的,我只不过把原理再说一下,根据自身的体会跟大家分享一下而已,在这里要感谢那些前辈们了。这里只是实现一个不规则的窗口,其他功能暂不提供。下面是窗口的截图,这图从一前辈那弄的,具体的原创是谁就搞不清楚了。


  从图中可以看到半透明效果,图片是PNG格式的,我另外加上了鼠标单击移动窗口,这是个基于WINDOWS的SDK应用程序,不是使用MFC,这做的过程也遇到窗口样式设置的问题,下面一些关键代码:

 

  //窗口句柄

  HWND g_hWnd = NULL;

  //背景图

  Image *g_pImageBack;

  //透明度颜色混合选项

  BLENDFUNCTION g_Blend;

  //背景图的宽度和高度

  int g_BakWidth, g_BakHeight;

//初始化
g_Blend.SourceConstantAlpha = int(10 * 2.55);//1~255
g_Blend.BlendOp=0; //theonlyBlendOpdefinedinWindows2000
g_Blend.BlendFlags=0; //nothingelseisspecial...
g_Blend.AlphaFormat=1; //...
g_Blend.SourceConstantAlpha=255;//AC_SRC_ALPHA
DWORD dwExStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
//设置成工具窗口,无标题栏
SetWindowLong(hWnd, GWL_STYLE, dwExStyle ^ WS_EX_TOOLWINDOW);
  //设置成层次窗口
dwExStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE);
if((dwExStyle & 0x80000) != WS_EX_LAYERED)
SetWindowLong(hWnd, GWL_EXSTYLE, dwExStyle^WS_EX_LAYERED);
//加载图像
ImageFromIDResource(IDR_CLOCK, L"PNG", g_pImageBack);
g_BakWidth = g_pImageBack->GetWidth();
g_BakHeight = g_pImageBack->GetHeight();
//显示和更新窗口
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
  //窗口句柄赋值
g_hWnd = hWnd;
Update();
窗口过程的处理:
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
//更新窗口
    Update();
EndPaint(hWnd, &ps);
break;
case WM_LBUTTONDOWN:
//禁止显示移动矩形窗体框
::SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,TRUE,NULL,0);
//非标题栏移动整个窗口
::SendMessage(hWnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, 0);

 

 

更新窗口函数:

void Update()

{

HDC hdcTemp= ::GetDC(g_hWnd);

HDC hdcMemory=CreateCompatibleDC(hdcTemp);

HBITMAP hBitMap=CreateCompatibleBitmap(hdcTemp, g_BakWidth, g_BakHeight);

SelectObject(hdcMemory, hBitMap);

 

HDC hdcScreen=::GetDC (g_hWnd);

RECT rct;

::GetWindowRect(g_hWnd, &rct);

POINT ptWinPos={rct.left,rct.top};

Graphics graph(hdcMemory);

 

Point points[] = { Point(0, 0),

Point(g_BakWidth, 0),

Point(0, g_BakHeight)

  };

 

graph.DrawImage(g_pImageBack, points, 3);

 

POINT ptDst;

ptDst.x = rct.left;

ptDst.y = rct.top;

SIZE size={g_BakWidth, g_BakHeight};

 

POINT pt;

pt.x = 0;

pt.y = 0;

 

//设置成层次窗口

DWORD dwExStyle = GetWindowLong(g_hWnd,GWL_EXSTYLE);

if((dwExStyle & WS_EX_LAYERED) != WS_EX_LAYERED)

{//WS_EX_LAYERED是0x00080000

SetWindowLong(g_hWnd, GWL_EXSTYLE,dwExStyle ^ WS_EX_LAYERED);

}

 

//更新窗口

if (!UpdateLayeredWindow( g_hWnd, hdcScreen, &ptWinPos,

&size, hdcMemory, &pt, 0, &g_Blend, 2))

{

DWORD dwError = ::GetLastError();

printf("failed");

}

 

//释放资源

graph.ReleaseHDC(hdcMemory);

::DeleteObject(hBitMap);

::DeleteDC(hdcMemory);;

::ReleaseDC(g_hWnd, hdcTemp);

::ReleaseDC(g_hWnd, hdcScreen);

}

  要注意上面两个地方:

  (1)

       //设置成工具窗口,无标题栏
       SetWindowLong(hWnd, GWL_STYLE, dwExStyle ^ WS_EX_TOOLWINDOW);
  //设置成层次窗口
      dwExStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE);
      if((dwExStyle & 0x80000) != WS_EX_LAYERED)
             SetWindowLong(hWnd, GWL_EXSTYLE, dwExStyle^WS_EX_LAYERED);
  这里要设置为工具窗口,然后设置层次窗口的时候是与上WS_EX_LAYERED即0x80000.
  (2)
      //非标题栏移动整个窗口
     ::SendMessage(hWnd, WM_SYSCOMMAND, SC_MOVE   | HTCAPTION, 0);
  这里发送一个系统消息,具体是SC_MOVE加上HTCAPTION,它加起来的值是0xF012,SC_MOVE是0xF010而HTCAPTION就是2,意思是单击非标题栏进行移动窗口,网上很多人直接写0xF012搞得我一直不知道是什么意思,最后GOOGLE后好不容易找到,呵呵,估计直接写的也是用VC6的,这样的程序可读性太差了,至少我是一眼看不出来是什么意思的。最后是关于GDI+的,要使用GDI+的话要加上:
#include <Gdiplus.h>
#pragma comment(lib, "Gdiplus.lib")
using namespace Gdiplus;
还要在前面去掉
#define WIN32_LEAN_AND_MEAN// 从 Windows 头中排除极少使用的资料
不然的话会出现一堆error C4430: 缺少类型说明符 - 假定为 int。注意: C++ 不支持默认 int的错误。
注:文章来源 http://hi.baidu.com/cmdmac_scut_edu_cn/blog/item/f7d0f05c2d366548faf2c070.html
原创粉丝点击