http://hi.baidu.com/combojiang/item/28d7fec3cdeec553bcef6939
windows sdk编程系列文章 ---- 利用镂空制作特效界面
理论:
平时我们经常看到一些不规则的窗口界面,给人的感觉也比较好。本篇我们就做这样一个特效界面。
先贴出一个图来。
要制作这样一个不规则的窗口其实比较简单。我们先看看原理,首先我们要做一张背景图。如图所示。
我们看到背景图中,粉色的底色,我们需要处理掉。关于背景颜色的rgb值,我们可以通过GetPixel函数来得到。也可以使用一些小工具得到。windows sdk中将窗口设置为不规则形状的函数只有一个,如下:
int SetWindowRgn( HWND hWnd , // handle to window HRGN hRgn , // handle to region BOOL bRedraw // window redraw option );
从这个函数参数看,想要创建一个不规则的窗口,关键是需要一个不规则的区域。
得到不规则区域的办法,比较简单。步骤是:
1)使用CreateRectRgn函数,创建一个为0的区域。
HRGN wndRgn = ::CreateRectRgn(0,0,0,0);
2)根据平面图的长度和宽度,逐行扫描图,遇到需要非底色的颜色,则创建一个像素的区域。
//创建一个包含起点与终点间高为1像素的临时“region”
rgnTemp = CreateRectRgn(iLeftX, y, iX, y+1);
3)将该区域合并1)创建的区域上。
CombineRgn(wndRgn,wndRgn,rgnTemp, RGN_OR);
4)重复2的步骤,继续扫描,扫描完成后,就得到了我们想要得区域。
上面的步骤贴个代码如下:
void SetupRegion(HDC hDC, HBITMAP &cBitmap, COLORREF TransColor)
{
//创建与传入DC兼容的临时DC
HDC memDC= ::CreateCompatibleDC(hDC);
HBITMAP hOldMemBmp(0);
//将位图选入临时DC
hOldMemBmp = (HBITMAP)::SelectObject(memDC,cBitmap);
//创建总的窗体区域,初始region为0
HRGN wndRgn = ::CreateRectRgn(0,0,0,0);
BITMAP bit;
GetObject(cBitmap,sizeof(BITMAP),&bit);//取得位图参数,这里要用到位图的长和宽
int y;
for(y=0;y<=bit.bmHeight ;y++)
{
HRGN rgnTemp; //保存临时region
int iX = 0;
do
{
//跳过透明色找到下一个非透明色的点.
while (iX <= bit.bmWidth && GetPixel(memDC,iX, y) == TransColor)
iX++;
//记住这个起始点
int iLeftX = iX;
//寻找下个透明色的点
while (iX <= bit.bmWidth && GetPixel(memDC,iX, y) != TransColor)
++iX;
//创建一个包含起点与终点间高为1像素的临时“region”
rgnTemp = CreateRectRgn(iLeftX, y, iX, y+1);
//合并到主"region".
CombineRgn(wndRgn,wndRgn,rgnTemp, RGN_OR);
//删除临时"region",否则下次创建时和出错
DeleteObject(rgnTemp);
}while(iX <bit.bmWidth );
iX = 0;
}
if(hOldMemBmp)
SelectObject(memDC,hOldMemBmp);
HWND hWnd = WindowFromDC(hDC);
SetWindowRgn(hWnd,wndRgn,TRUE);
SetForegroundWindow(hWnd);
}
本篇例子,我们直接有了这个区域文件,我们把这个区域文件加载到rc中。
我们使用ExtCreateRegion函数,根据rc中加载的rgn资源,来创建一个HRGN.
而我们例子怎样生成一个rgn文件呢,这里给出一个链接,里面给出了一个工具。 http://www.codeproject.com/KB/GDI/rgncreator.aspx
代码:见光盘cws
#include "windows.h"
/************************************************************/
#define ButtonID 1000
#define PictureW 300
#define PictureH 300
HWND g_hwndButton;
HWND g_hWnd;
HRSRC g_RsrcHand;
HGLOBAL g_RsrcPoint;
DWORD g_RsrcSize;
HINSTANCE g_hInstance;
char ClassName[] = "cws_class";
char DisplayName[] = "custom windows shape";
char RsrcName[] = "RANGE";
char RsrcType[] = "RGN";
char ButtonClassName[] = "button";
char ButtonText[] = "Click Me!";
char Text[] = "good bye...";
/************************************************************/
LRESULT CALLBACK WindowProc( HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch(uMsg)
{
case WM_CREATE:
g_RsrcHand = FindResource(g_hInstance,RsrcName,RsrcType);
g_RsrcPoint = LoadResource(g_hInstance,g_RsrcHand);
g_RsrcSize = SizeofResource(g_hInstance,g_RsrcHand);
g_RsrcPoint = LockResource(g_RsrcPoint);
SetWindowRgn(hwnd,ExtCreateRegion(NULL,g_RsrcSize,(const struct _RGNDATA *)g_RsrcPoint),TRUE);
g_hwndButton = CreateWindowEx(NULL,ButtonClassName,ButtonText,
WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,
150,185,100,25,hwnd,(HMENU)ButtonID,g_hInstance,NULL);
break;
case WM_COMMAND:
if(LOWORD(wParam) == ButtonID)
{
MessageBox(hwnd,Text,DisplayName,MB_OK);
SendMessage(hwnd,WM_DESTROY,NULL,NULL);
}
break;
case WM_LBUTTONDOWN:
SendMessage(hwnd,WM_NCLBUTTONDOWN,HTCAPTION,lParam);
break;
case WM_DESTROY:
PostQuitMessage(NULL);
break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX wc;
MSG msg;
g_hInstance = hInstance;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW |CS_BYTEALIGNWINDOW;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra = NULL;
wc.cbWndExtra = NULL;
wc.hInstance = hInstance;
wc.hbrBackground = CreatePatternBrush(LoadBitmap(hInstance,MAKEINTRESOURCE(1000)));
wc.lpszMenuName = NULL;
wc.lpszClassName = ClassName;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL,IDC_CROSS);
wc.hIconSm = NULL;
RegisterClassEx(&wc);
g_hWnd = CreateWindowEx(WS_EX_LEFT,
ClassName,
DisplayName,
WS_POPUP,
(GetSystemMetrics(SM_CXSCREEN) - PictureW)/2,
(GetSystemMetrics(SM_CYSCREEN) - PictureH)/2,
PictureW,
PictureH,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(g_hWnd,SW_SHOWNORMAL);
UpdateWindow(g_hWnd);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
分析:
首先,我们把要镂空处理的图作为背景画刷。
wc.hbrBackground = CreatePatternBrush(LoadBitmap(hInstance,MAKEINTRESOURCE(1000)));
然后,在创建窗口的时候,我们从资源中加载rgn,并将窗口设置为不规则形状。
case WM_CREATE:
g_RsrcHand = FindResource(g_hInstance,RsrcName,RsrcType);
g_RsrcPoint = LoadResource(g_hInstance,g_RsrcHand);
g_RsrcSize = SizeofResource(g_hInstance,g_RsrcHand);
g_RsrcPoint = LockResource(g_RsrcPoint);
SetWindowRgn(hwnd,ExtCreateRegion(NULL,g_RsrcSize,(const struct _RGNDATA *)g_RsrcPoint),TRUE);
接下来,我们创建了一个按钮。
g_hwndButton = CreateWindowEx(NULL,ButtonClassName,ButtonText,
WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,
150,185,100,25,hwnd,(HMENU)ButtonID,g_hInstance,NULL);
break;
为了让窗口能够拖动,我们模拟了 WM_NCLBUTTONDOWN消息。
case WM_LBUTTONDOWN:
SendMessage(hwnd,WM_NCLBUTTONDOWN,HTCAPTION,lParam);
break;
平时我们经常看到一些不规则的窗口界面,给人的感觉也比较好。本篇我们就做这样一个特效界面。
先贴出一个图来。
要制作这样一个不规则的窗口其实比较简单。我们先看看原理,首先我们要做一张背景图。如图所示。
我们看到背景图中,粉色的底色,我们需要处理掉。关于背景颜色的rgb值,我们可以通过GetPixel函数来得到。也可以使用一些小工具得到。windows sdk中将窗口设置为不规则形状的函数只有一个,如下:
int SetWindowRgn( HWND hWnd , // handle to window HRGN hRgn , // handle to region BOOL bRedraw // window redraw option );
从这个函数参数看,想要创建一个不规则的窗口,关键是需要一个不规则的区域。
得到不规则区域的办法,比较简单。步骤是:
1)使用CreateRectRgn函数,创建一个为0的区域。
HRGN wndRgn = ::CreateRectRgn(0,0,0,0);
2)根据平面图的长度和宽度,逐行扫描图,遇到需要非底色的颜色,则创建一个像素的区域。
//创建一个包含起点与终点间高为1像素的临时“region”
rgnTemp = CreateRectRgn(iLeftX, y, iX, y+1);
3)将该区域合并1)创建的区域上。
CombineRgn(wndRgn,wndRgn,rgnTemp, RGN_OR);
4)重复2的步骤,继续扫描,扫描完成后,就得到了我们想要得区域。
上面的步骤贴个代码如下:
void SetupRegion(HDC hDC, HBITMAP &cBitmap, COLORREF TransColor)
{
//创建与传入DC兼容的临时DC
HDC memDC= ::CreateCompatibleDC(hDC);
HBITMAP hOldMemBmp(0);
//将位图选入临时DC
hOldMemBmp = (HBITMAP)::SelectObject(memDC,cBitmap);
//创建总的窗体区域,初始region为0
HRGN wndRgn = ::CreateRectRgn(0,0,0,0);
BITMAP bit;
GetObject(cBitmap,sizeof(BITMAP),&bit);//取得位图参数,这里要用到位图的长和宽
int y;
for(y=0;y<=bit.bmHeight ;y++)
{
HRGN rgnTemp; //保存临时region
int iX = 0;
do
{
//跳过透明色找到下一个非透明色的点.
while (iX <= bit.bmWidth && GetPixel(memDC,iX, y) == TransColor)
iX++;
//记住这个起始点
int iLeftX = iX;
//寻找下个透明色的点
while (iX <= bit.bmWidth && GetPixel(memDC,iX, y) != TransColor)
++iX;
//创建一个包含起点与终点间高为1像素的临时“region”
rgnTemp = CreateRectRgn(iLeftX, y, iX, y+1);
//合并到主"region".
CombineRgn(wndRgn,wndRgn,rgnTemp, RGN_OR);
//删除临时"region",否则下次创建时和出错
DeleteObject(rgnTemp);
}while(iX <bit.bmWidth );
iX = 0;
}
if(hOldMemBmp)
SelectObject(memDC,hOldMemBmp);
HWND hWnd = WindowFromDC(hDC);
SetWindowRgn(hWnd,wndRgn,TRUE);
SetForegroundWindow(hWnd);
}
本篇例子,我们直接有了这个区域文件,我们把这个区域文件加载到rc中。
我们使用ExtCreateRegion函数,根据rc中加载的rgn资源,来创建一个HRGN.
而我们例子怎样生成一个rgn文件呢,这里给出一个链接,里面给出了一个工具。 http://www.codeproject.com/KB/GDI/rgncreator.aspx
代码:见光盘cws
#include "windows.h"
/************************************************************/
#define ButtonID 1000
#define PictureW 300
#define PictureH 300
HWND g_hwndButton;
HWND g_hWnd;
HRSRC g_RsrcHand;
HGLOBAL g_RsrcPoint;
DWORD g_RsrcSize;
HINSTANCE g_hInstance;
char ClassName[] = "cws_class";
char DisplayName[] = "custom windows shape";
char RsrcName[] = "RANGE";
char RsrcType[] = "RGN";
char ButtonClassName[] = "button";
char ButtonText[] = "Click Me!";
char Text[] = "good bye...";
/************************************************************/
LRESULT CALLBACK WindowProc( HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch(uMsg)
{
case WM_CREATE:
g_RsrcHand = FindResource(g_hInstance,RsrcName,RsrcType);
g_RsrcPoint = LoadResource(g_hInstance,g_RsrcHand);
g_RsrcSize = SizeofResource(g_hInstance,g_RsrcHand);
g_RsrcPoint = LockResource(g_RsrcPoint);
SetWindowRgn(hwnd,ExtCreateRegion(NULL,g_RsrcSize,(const struct _RGNDATA *)g_RsrcPoint),TRUE);
g_hwndButton = CreateWindowEx(NULL,ButtonClassName,ButtonText,
WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,
150,185,100,25,hwnd,(HMENU)ButtonID,g_hInstance,NULL);
break;
case WM_COMMAND:
if(LOWORD(wParam) == ButtonID)
{
MessageBox(hwnd,Text,DisplayName,MB_OK);
SendMessage(hwnd,WM_DESTROY,NULL,NULL);
}
break;
case WM_LBUTTONDOWN:
SendMessage(hwnd,WM_NCLBUTTONDOWN,HTCAPTION,lParam);
break;
case WM_DESTROY:
PostQuitMessage(NULL);
break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX wc;
MSG msg;
g_hInstance = hInstance;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW |CS_BYTEALIGNWINDOW;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra = NULL;
wc.cbWndExtra = NULL;
wc.hInstance = hInstance;
wc.hbrBackground = CreatePatternBrush(LoadBitmap(hInstance,MAKEINTRESOURCE(1000)));
wc.lpszMenuName = NULL;
wc.lpszClassName = ClassName;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL,IDC_CROSS);
wc.hIconSm = NULL;
RegisterClassEx(&wc);
g_hWnd = CreateWindowEx(WS_EX_LEFT,
ClassName,
DisplayName,
WS_POPUP,
(GetSystemMetrics(SM_CXSCREEN) - PictureW)/2,
(GetSystemMetrics(SM_CYSCREEN) - PictureH)/2,
PictureW,
PictureH,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(g_hWnd,SW_SHOWNORMAL);
UpdateWindow(g_hWnd);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
分析:
首先,我们把要镂空处理的图作为背景画刷。
wc.hbrBackground = CreatePatternBrush(LoadBitmap(hInstance,MAKEINTRESOURCE(1000)));
然后,在创建窗口的时候,我们从资源中加载rgn,并将窗口设置为不规则形状。
case WM_CREATE:
g_RsrcHand = FindResource(g_hInstance,RsrcName,RsrcType);
g_RsrcPoint = LoadResource(g_hInstance,g_RsrcHand);
g_RsrcSize = SizeofResource(g_hInstance,g_RsrcHand);
g_RsrcPoint = LockResource(g_RsrcPoint);
SetWindowRgn(hwnd,ExtCreateRegion(NULL,g_RsrcSize,(const struct _RGNDATA *)g_RsrcPoint),TRUE);
接下来,我们创建了一个按钮。
g_hwndButton = CreateWindowEx(NULL,ButtonClassName,ButtonText,
WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,
150,185,100,25,hwnd,(HMENU)ButtonID,g_hInstance,NULL);
break;
为了让窗口能够拖动,我们模拟了 WM_NCLBUTTONDOWN消息。
case WM_LBUTTONDOWN:
SendMessage(hwnd,WM_NCLBUTTONDOWN,HTCAPTION,lParam);
break;