Windows界面编程-背景图片、透明特效使用
Windows界面编程第一篇 位图背景与位图画刷
可以通过WM_CTLCOLORDLG消息来设置对话框的背景,MSDN上对这个消息的说明如下:
The WM_CTLCOLORDLG message is sent to a dialog box before the system draws the dialog box. By responding to this message, the dialog box can set its text and background colors using the specified display device context handle.
当窗口消息响应函数接收这个消息时,wParam表示对话框的设备上下方即HDC,lParam表示对话框的句柄。如果程序处理了这个消息,应返回一个画刷。系统将会用这个画刷来重绘对话框背景。
因此我们在这个WM_CTLCOLORDLG消息中得到对话框的大小,并通过StretchBlt函数将位图缩放后贴到对话框的HDC中就完成了对话框背景的设置,然后返回一个空画刷给系统,这样系统就不会将位图背景给覆盖了。
代码非常简单,要注意的是在使用StretchBlt函数缩放位图时,最好先使用
SetStretchBltMode函数来设置下位图内容伸展模式,这样可以避免缩放后位图失真严重。SetStretchBltMode函数原型如下:
int SetStretchBltMode(
HDChdc, // handle to DC
int iStretchMode // bitmap stretching mode
);
第一个参数就是设备上下方即HDC。
第二个参数有四种设置:
1. BLACKONWHITE or STRETCH_ANDSCANS
如果两个或多个像素得合并成一个像素,那么StretchBlt会对像素执行一个逻辑AND运算。这样的结果是只有全部的原始像素是白色时该像素才为白色,其实际意义是黑色像素控制了白色像素。这适用于白色背景中主要是黑色的单色点阵图。
2. WHITEONBLACK or STRETCH_ORSCANS
如果两个或多个像素得合并成一个像素,那么StretchBlt会对像素执行逻辑OR运算。这样的结果是只有全部的原始像素都是黑色时该像素才为黑色,也就是说由白色像素决定颜色。这适用于黑色背景中主要是白色的单色点阵图。
3. COLORONCOLOR or STRETCH_DELETESCANS
简单地消除图素行或列,而没有任何逻辑组合。这是通常是处理彩色点阵图的最佳方法。
4. HALFTONE or STRETCH_HALFTONE
根据组合起来的来源颜色来计算目的的平均颜色。
其它技术细节可以见代码中的注释,完整代码如下(也可以下载,下载地址为:http://download.csdn.net/download/morewindows/4947377):
- // 对话框位图背景 - WM_CTLCOLORDLG中使用StretchBlt贴图
- //By MoreWindows-(http://blog.csdn.net/MoreWindows)
- #include <windows.h>
- #include "resource.h"
- const char szDlgTitle[] = "位图背景 使用StretchBlt贴图 MoreWindows-(http://blog.csdn.net/MoreWindows)";
- // 对话框消息处理函数
- BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
- int APIENTRY WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPSTR lpCmdLine,
- int nCmdShow)
- {
- DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
- return 0;
- }
- BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
- {
- RECT rcDialog;
- HBITMAP hBitmap;
- static BITMAP s_bm;
- static HDC s_hdcMem;
- switch (message)
- {
- case WM_INITDIALOG:
- // 设置对话框标题
- SetWindowText(hDlg, szDlgTitle);
- // 设置对话框大小可调节
- SetWindowLong(hDlg, GWL_STYLE, GetWindowLong(hDlg, GWL_STYLE) | WS_SIZEBOX);
- // 加载背影图片
- hBitmap = (HBITMAP)LoadImage(NULL, "005.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
- if (hBitmap == NULL)
- {
- MessageBox(hDlg, "LoadImage failed", "Error", MB_ICONERROR);
- exit(0);
- }
- else
- {
- // 将背影图片放入HDC - s_hdcMem
- HDC hdc;
- hdc = GetDC(hDlg);
- s_hdcMem = CreateCompatibleDC(hdc);
- SelectObject(s_hdcMem, hBitmap);
- ReleaseDC(hDlg, hdc);
- // 得到位图信息
- GetObject(hBitmap, sizeof(s_bm), &s_bm);
- }
- return 0;
- case WM_COMMAND:
- switch (LOWORD(wParam))
- {
- case IDCANCEL:
- DeleteDC(s_hdcMem);
- EndDialog(hDlg, LOWORD(wParam));
- return TRUE;
- }
- break;
- case WM_SIZE:
- InvalidateRect(hDlg, NULL, TRUE);
- return TRUE;
- case WM_CTLCOLORDLG:
- GetClientRect(hDlg, &rcDialog);
- //通过SetStretchBltMode的设置能使StretchBlt在缩放图像更加清晰
- SetStretchBltMode((HDC)wParam, COLORONCOLOR);
- StretchBlt((HDC)wParam, 0, 0, rcDialog.right, rcDialog.bottom, s_hdcMem, 0, 0, s_bm.bmWidth, s_bm.bmHeight, SRCCOPY);
- return (BOOL)((HBRUSH)GetStockObject(NULL_BRUSH));
- }
- return FALSE;
- }
运行结果如下:
在上面这种方法中,我们是在WM_CTLCOLORDLG中贴图来完成背景设置了,也可以创建一个位图画刷,然后在WM_CTLCOLORDLG消息中直接返回这个画刷,这样系统将完成贴图功能。
位图画刷的创建函数为CreatePatternBrush,其函数原型如下:
HBRUSHCreatePatternBrush(
HBITMAPhbmp // handle to bitmap
);
只要给这个函数传入位图句柄即可,完整代码如下(下载地址为:http://download.csdn.net/download/morewindows/4947377):
- // 对话框位图背景 - 直接在WM_CTLCOLORDLG中使用位图画刷
- //By MoreWindows-(http://blog.csdn.net/MoreWindows)
- #include <windows.h>
- #include "resource.h"
- const char szDlgTitle[] = "位图画刷 MoreWindows-(http://blog.csdn.net/MoreWindows)";
- // 对话框消息处理函数
- BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
- int APIENTRY WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPSTR lpCmdLine,
- int nCmdShow)
- {
- DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
- return 0;
- }
- BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
- {
- static HBRUSH s_hBitmapBrush; //位图画刷
- switch (message)
- {
- case WM_INITDIALOG:
- // 设置对话框标题
- SetWindowText(hDlg, szDlgTitle);
- // 设置对话框大小可调节
- SetWindowLong(hDlg, GWL_STYLE, GetWindowLong(hDlg, GWL_STYLE) | WS_SIZEBOX);
- // 加载背影图片
- HBITMAP hBitmap;
- hBitmap = (HBITMAP)LoadImage(NULL, "005.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
- if (hBitmap == NULL)
- {
- MessageBox(hDlg, "LoadImage failed", "Error", MB_ICONERROR);
- exit(0);
- }
- // 创建位图画刷
- s_hBitmapBrush = CreatePatternBrush(hBitmap);
- return 0;
- case WM_COMMAND:
- switch (LOWORD(wParam))
- {
- case IDCANCEL:
- DeleteObject(s_hBitmapBrush);
- EndDialog(hDlg, LOWORD(wParam));
- return TRUE;
- }
- break;
- case WM_CTLCOLORDLG:
- return (BOOL)s_hBitmapBrush;
- }
- return FALSE;
- }
运行结果如下:
注意,这两做法在窗口大小超过位图大小时会表现不同,前一种做法会拉伸位图以适应窗口大小,后一种做法是直接平铺。详见下图:
本篇《Windows界面编程第一篇位图背景与位图画刷》主要介绍了通过WM_CTLCOLORDLG消息来设置对话框的背景,在Windows系统中还有类似于WM_CTLCOLORDLG消息的还有WM_CTLCOLORBTN,WM_CTLCOLOREDIT,WM_CTLCOLORLISTBOX,WM_CTLCOLORSCROLLBAR,WM_CTLCOLORSTATIC这五种来分别管理按钮,编辑框,列表框,滚动条,静态框。有兴趣的读者可以试验下。
下一篇将介绍调节窗体透明度的方法,欢迎继续浏览《Windows界面编程第二篇 半透明窗体》
当窗口的背景用彩色图片来装饰时,其它控件如果还是用灰色的背景会显的比较不谐调,《Windows界面编程第五篇 静态控件背景透明化》将介绍如何为静态框设置透明背景。
Windows界面编程第二篇半透明窗体
上一篇《Windows界面编程第一篇 位图背景与位图画刷》介绍了通过WM_CTLCOLORDLG消息来来设置对话框的背景以及位图画刷的使用。本篇将介绍动态调节窗体透明度的方法。
调节窗体透明度可以先使用SetWindowLong为窗体加上WS_EX_LAYERED属性,再使用来SetLayeredWindowAttributes指定窗体的透明度。这样就可以在程序运行时动态的调节窗体的透明度了。
下面先介绍下SetWindowLong和SetLayeredWindowAttributes函数。
SetWindowLong可以用来设置窗口的一些属性,其函数原型如下:
LONGSetWindowLong(
HWNDhWnd,
int nIndex,
LONGdwNewLong
);
第一个参数表示窗口句柄。
第二个参数代表要设置的哪一种值,如GWL_STYLE表示将设置窗口的风格,这个参数还可以取GWL_EXSTYLE,GWL_WNDPROC,DWL_DLGPROC,GWL_HINSTANCE,GWL_USERDATA等等。
第三个参数表示要设置的值。
比如要设置对话框也能够可调节窗口大小,可以用:
// 设置对话框大小可调节
SetWindowLong(hDlg, GWL_STYLE, GetWindowLong(hDlg, GWL_STYLE) | WS_SIZEBOX);
SetLayeredWindowAttributes在MSDN上解释如下:
The SetLayeredWindowAttributes function sets the opacity and transparency color key of a layered window.
其函数原型为:
BOOLSetLayeredWindowAttributes(
HWNDhwnd,
COLORREFcrKey,
BYTEbAlpha,
DWORDdwFlags
);
第一个参数表示窗口句柄。
第二个参数表示透明色。
第三个参数表示透明度。
第四个参数表示函数将完成什么样的功能,设置成LWA_COLORKEY表示为窗口指定了透明色,设置成LWA_ALPHA表示将调整窗口的透明度,可以同时设置这两种功能。
程序代码还将用到Slider控件,这种控件可以有如下操作:
1.通过SendMessage + TBM_SETRANGE来设置滑块变化的范围。
2.通过SendMessage + TBM_SETPOS来设置滑块当前位置。
3.通过SendMessage + TBM_GETPOS来获得滑块当前位置。
4.当滑块位置发生变化时,在其父窗口中通过WM_HSCROLL或WM_VSCROLL来响应消息。
下面给出完整的源代码(下载地址:http://download.csdn.net/download/morewindows/4947386)
- // 可变透明度窗体 先加上WS_EX_LAYERED属性再SetLayeredWindowAttributes设置透明度
- //By MoreWindows-(http://blog.csdn.net/MoreWindows)
- #include <windows.h>
- #include <commctrl.h>
- #include "resource.h"
- const char szDlgTitle[] = "可变透明度窗体 MoreWindows-(http://blog.csdn.net/MoreWindows)";
- // 对话框消息处理函数
- BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
- int APIENTRY WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPSTR lpCmdLine,
- int nCmdShow)
- {
- DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
- return 0;
- }
- BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
- {
- const int INIT_TRANSPARENT = 200; //窗体初始透明度
- static HBRUSH s_hBitmapBrush; //位图画刷
- switch (message)
- {
- case WM_INITDIALOG:
- // 设置对话框标题
- SetWindowText(hDlg, szDlgTitle);
- // 加载背影图片
- HBITMAP hBitmap;
- hBitmap = (HBITMAP)LoadImage(NULL, "005.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
- if (hBitmap == NULL)
- {
- MessageBox(hDlg, "LoadImage failed", "Error", MB_ICONERROR);
- exit(0);
- }
- // 创建位图画刷
- s_hBitmapBrush = CreatePatternBrush(hBitmap);
- // 设置分层属性
- SetWindowLong(hDlg, GWL_EXSTYLE, GetWindowLong(hDlg, GWL_EXSTYLE) | WS_EX_LAYERED);
- // 设置透明度 0 - completely transparent 255 - opaque
- SetLayeredWindowAttributes(hDlg, 0, INIT_TRANSPARENT, LWA_ALPHA);
- // 设置滑动条变化范围
- SendMessage(GetDlgItem(hDlg, IDC_SLIDER_TRANSPARENT), TBM_SETRANGE, (WPARAM)FALSE, MAKELONG(0, 255));
- // 设置滑块初始位置
- SendMessage(GetDlgItem(hDlg, IDC_SLIDER_TRANSPARENT), TBM_SETPOS, (WPARAM)TRUE, INIT_TRANSPARENT);
- return 0;
- case WM_COMMAND:
- switch (LOWORD(wParam))
- {
- case IDCANCEL:
- DeleteObject(s_hBitmapBrush);
- EndDialog(hDlg, LOWORD(wParam));
- return TRUE;
- }
- break;
- case WM_HSCROLL: // slider control 滑块位置有变化时的响应函数
- {
- // 获取当前滑块位置
- int nTransparent = SendMessage(GetDlgItem(hDlg, IDC_SLIDER_TRANSPARENT), TBM_GETPOS, 0, 0);
- // 设置新透明度
- SetLayeredWindowAttributes(hDlg, 0, nTransparent, LWA_ALPHA);
- }
- break;
- case WM_CTLCOLORDLG: //对话框背影
- return (BOOL)s_hBitmapBrush;
- }
- return FALSE;
- }
运行结果如下:
在本篇文章中我们通过SetWindowLong为窗体加上WS_EX_LAYERED属性,再使用SetLayeredWindowAttributes完成窗体的透明度调节。下面两篇也将使用这两个函数来完成另一个功能——异形窗口(异形窗体)。异形窗口可以使得窗口更加美观,它能灵活的变动窗口的形状。欢迎继续浏览《Windows界面编程 第三篇异形窗体 普通版》和《Windows界面编程 第四篇异形窗体 高富帅版》
上一篇《Windows界面编程第二篇 半透明窗体》介绍了先使用SetWindowLong为窗体加上WS_EX_LAYERED属性,再使用SetLayeredWindowAttributes调整窗体的透明度。本篇将使用这二个函数来完成另一个实用的功能——异形窗口(异形窗体)。
异形窗口的原理很简单,首先加载一张位图画刷作窗口背景,然后设置透明色就可以使得窗口由矩形变成任意形状。这样使得窗口的显示更加美观。
下面先简单介绍下Win32 SDK方式创建窗口的流程(按《Windows程序设计》中的大体流程):
1.设置WNDCLASS类型的变量,这个变量将描述窗口的风格、窗口消息处理函数、程序图标、光标、窗口背景画刷等待。
2.使用RegisterClass将WNDCLASS类型的变量注册下。
3.使用CreateWindow或CreateWindowEx创建窗口,这里可以设置窗口的初始位置、初始大小、扩展属性等等。
4.通过ShowWindow和UpdateWindow将窗口显示出来。
5.窗口显示完成后,在WinMain()函数中添加消息处理:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
通过这五步就完成了一个窗口的创建了。
对于窗口消息函数,一般只要在WM_DESTROY消息中调用下PostQuitMessage(0);其它的消息都可以使用默认消息处理函数DefWindowProc。
对于异形窗口,可以在WM_CREATE即窗口初始化时设置好透明色就完成了窗口形状的改变,不过为了支持鼠标对窗口的拖曳,还要在WM_LBUTTONDOWN消息中加入:
PostMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, 0);
这样,异形窗口就可以在屏幕上像普通窗口一样的移动了。
完整的源代码如下所示(下载地址:http://download.csdn.net/download/morewindows/4966815):
- // 异形窗口1 窗口背景使用位图画刷再指定透明色
- //By MoreWindows-(http://blog.csdn.net/MoreWindows)
- #include <windows.h>
- const char szAppName[] = "异形窗口1 MoreWindows-(http://blog.csdn.net/MoreWindows)";
- /*
- * 函数名称: GetWindowSize
- * 函数功能: 得到窗口的宽高
- * hwnd 窗口句柄
- * pnWidth 窗口宽
- * pnHeight 窗口高
- */
- void GetWindowSize(HWND hwnd, int *pnWidth, int *pnHeight);
- /*
- * 函数名称: InitBitmapWindow
- * 函数功能: 位图窗口初始化
- * hinstance 进程实例
- * hBitmap 位图句柄
- * nCmdshow 显示方式-与ShowWindow函数的第二个参数相同
- */
- BOOL InitBitmapWindow(HINSTANCE hinstance, HBITMAP hBitmap, int nCmdshow);
- // 位图窗口消息处理函数
- LRESULT CALLBACK BitmapWindowWndPrco(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParm);
- int APIENTRY WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPSTR lpCmdLine,
- int nCmdShow)
- {
- //设置窗口背景画刷为图片画刷,再指定透明颜色即可以创建透明区域。
- HBITMAP hBitmap;
- hBitmap = (HBITMAP)LoadImage(NULL, "Kitty.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
- if (hBitmap == NULL)
- {
- MessageBox(NULL, "位图加载失败", "Error", MB_ICONERROR);
- return 0;
- }
- if (!InitBitmapWindow(hInstance, hBitmap, nCmdShow))
- return 0;
- MSG msg;
- while (GetMessage(&msg, NULL, 0, 0))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- DeleteObject(hBitmap);
- return msg.wParam;
- }
- BOOL InitBitmapWindow(HINSTANCE hinstance, HBITMAP hBitmap, int nCmdshow)
- {
- HWND hwnd;
- WNDCLASS wndclass;
- wndclass.style = CS_VREDRAW | CS_HREDRAW;
- wndclass.lpfnWndProc = BitmapWindowWndPrco;
- wndclass.cbClsExtra = 0;
- wndclass.cbWndExtra = 0;
- wndclass.hInstance = hinstance;
- wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
- wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
- wndclass.hbrBackground = CreatePatternBrush(hBitmap);//位图画刷
- wndclass.lpszMenuName = NULL;
- wndclass.lpszClassName = szAppName;
- if (!RegisterClass(&wndclass))
- {
- MessageBox(NULL, "Program Need Windows NT!", "Error", MB_ICONERROR);
- return FALSE;
- }
- BITMAP bm;
- GetObject(hBitmap, sizeof(bm), &bm);
- hwnd = CreateWindowEx(WS_EX_TOPMOST,
- szAppName,
- szAppName,
- WS_POPUP,
- CW_USEDEFAULT,
- CW_USEDEFAULT,
- bm.bmWidth,
- bm.bmHeight,
- NULL,
- NULL,
- hinstance,
- NULL);
- if (hwnd == NULL)
- return FALSE;
- ShowWindow(hwnd, nCmdshow);
- UpdateWindow(hwnd);
- return TRUE;
- }
- LRESULT CALLBACK BitmapWindowWndPrco(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParm)
- {
- static HDC s_hdcMem;
- static HBRUSH s_hBackBrush;
- switch (message)
- {
- case WM_CREATE:
- {
- // 设置分层属性
- SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
- // 设置透明色
- COLORREF clTransparent = RGB(0, 0, 0);
- SetLayeredWindowAttributes(hwnd, clTransparent, 0, LWA_COLORKEY);
- }
- return 0;
- case WM_KEYDOWN:
- switch (wParam)
- {
- case VK_ESCAPE: //按下Esc键时退出
- SendMessage(hwnd, WM_DESTROY, 0, 0);
- return 0;
- }
- break;
- case WM_LBUTTONDOWN: //当鼠标左键点击时可以拖曳窗口
- PostMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, 0);
- return 0;
- case WM_DESTROY:
- PostQuitMessage(0);
- return 0;
- }
- return DefWindowProc(hwnd, message, wParam, lParm);
- }
- void GetWindowSize(HWND hwnd, int *pnWidth, int *pnHeight)
- {
- RECT rc;
- GetWindowRect(hwnd, &rc);
- *pnWidth = rc.right - rc.left;
- *pnHeight = rc.bottom - rc.top;
- }
运行结果如下:
现在总结下异形窗口的创建,先通过创建位图画刷来做窗口的背景画刷,再通过SetWindowLong为窗体加上WS_EX_LAYERED属性,然后使用SetLayeredWindowAttributes指定窗口的透明色来完成窗口形状的调整。为了支持鼠标的拖曳,在WM_LBUTTONDOWN消息中作了特殊处理,使得异形窗口可以像普通窗口一样在屏幕上移动。
本篇程序中的异形窗口的大小是无法设置的,只能和位图一样。下一篇《Windows界面编程第四篇 异形窗体 高富帅版》将介绍窗口大小可以指定的异形窗口。欢迎继续浏览。
上一篇《Windows界面编程第三篇 异形窗体 普通版》介绍了异形窗口(异形窗体)的创建,其主要步骤为——先通过创建位图画刷来做窗口的背景画刷,再通过SetWindowLong为窗体加上WS_EX_LAYERED属性,然后使用SetLayeredWindowAttributes指定窗口的透明色来完成窗口形状的调整。并且为了使异形窗口支持鼠标的拖曳,在WM_LBUTTONDOWN消息中作了特殊处理。
然后在下图中有非常相似的两个异形窗体,只不过,左边的异形窗体小,右边的异形窗体大。这个可以怎么实现了?
先通过其它软件来缩放位图,然后再让程序加载这种方式来指定异形窗口的大小。这种方法虽然可以完成任务,但毕竟太OUT了。
由《Windows界面编程第一篇位图背景与位图画刷》可以想到不用位图画刷,而直接在窗口背景绘制时使用StretchBlt来缩放位图至窗口大小,这样就可以达到指定窗口大小的功能。
由于异形窗口运行后无法通过鼠标来动态调整窗口大小,因此可以窗口初始化时就可以先缩放位图并加载到一个缓冲HDC中,然后再在窗口背景绘制时使用BitBlt来贴图。这种做法只需要缩放位图一次,在每次背景绘制时只须拷贝位图,对程序的效率会有提高。下面给出完整源代码(下载地址:http://download.csdn.net/download/morewindows/4966819)
- // 异形窗口2 在WM_ERASEBKGND消息中自贴图
- //By MoreWindows-(http://blog.csdn.net/MoreWindows)
- #include <windows.h>
- const char szAppName[] = "异形窗口2 MoreWindows-(http://blog.csdn.net/MoreWindows)";
- /*
- * 函数名称: GetWindowSize
- * 函数功能: 得到窗口的宽高
- * hwnd 窗口句柄
- * pnWidth 窗口宽
- * pnHeight 窗口高
- */
- void GetWindowSize(HWND hwnd, int *pnWidth, int *pnHeight);
- /*
- * 函数名称: InitBitmapWindow
- * 函数功能: 位图窗口初始化
- * hinstance 进程实例
- * nWidth 窗口宽
- * nHeight 窗口高
- * nCmdshow 显示方式-与ShowWindow函数的第二个参数相同
- */
- BOOL InitBitmapWindow(HINSTANCE hinstance, int nWidth, int nHeight, int nCmdshow);
- // 位图窗口消息处理函数
- LRESULT CALLBACK BitmapWindowWndPrco(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParm);
- HBITMAP g_hBitmap;
- int APIENTRY WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPSTR lpCmdLine,
- int nCmdShow)
- {
- //先创建一个无背影画刷窗口,
- //然后在WM_CREATE中并指定透明颜色, 缩放位图后加载至s_hdcMem中.
- //最后在WM_ERASEBKGND中用s_hdcMem贴图即可
- g_hBitmap = (HBITMAP)LoadImage(NULL, "Kitty.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
- if (g_hBitmap == NULL)
- {
- MessageBox(NULL, "位图加载失败", "Error", MB_ICONERROR);
- return 0;
- }
- // 设置异形窗口大小
- BITMAP bm;
- GetObject(g_hBitmap, sizeof(bm), &bm);
- int nWindowWidth = bm.bmWidth;
- int nWindowHeight = bm.bmHeight + 100; //拉高100高度
- if (!InitBitmapWindow(hInstance, nWindowWidth, nWindowHeight, nCmdShow))
- return 0;
- MSG msg;
- while (GetMessage(&msg, NULL, 0, 0))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- DeleteObject(g_hBitmap);
- return msg.wParam;
- }
- BOOL InitBitmapWindow(HINSTANCE hinstance, int nWidth, int nHeight, int nCmdshow)
- {
- HWND hwnd;
- WNDCLASS wndclass;
- wndclass.style = CS_VREDRAW | CS_HREDRAW;
- wndclass.lpfnWndProc = BitmapWindowWndPrco;
- wndclass.cbClsExtra = 0;
- wndclass.cbWndExtra = 0;
- wndclass.hInstance = hinstance;
- wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
- wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
- wndclass.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);//窗口背影画刷为空
- wndclass.lpszMenuName = NULL;
- wndclass.lpszClassName = szAppName;
- if (!RegisterClass(&wndclass))
- {
- MessageBox(NULL, "Program Need Windows NT!", "Error", MB_ICONERROR);
- return FALSE;
- }
- hwnd = CreateWindowEx(WS_EX_TOPMOST,
- szAppName,
- szAppName,
- WS_POPUP,
- CW_USEDEFAULT,
- CW_USEDEFAULT,
- nWidth,
- nHeight,
- NULL,
- NULL,
- hinstance,
- NULL);
- if (hwnd == NULL)
- return FALSE;
- ShowWindow(hwnd, nCmdshow);
- UpdateWindow(hwnd);
- return TRUE;
- }
- LRESULT CALLBACK BitmapWindowWndPrco(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParm)
- {
- static HDC s_hdcMem; //放置缩放后的位图
- switch (message)
- {
- case WM_CREATE:
- {
- // 设置分层属性
- SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
- // 设置透明色
- COLORREF clTransparent = RGB(0, 0, 0);
- SetLayeredWindowAttributes(hwnd, clTransparent, 0, LWA_COLORKEY);
- // 缩放位图
- // 加载位图到hdcTemp中
- HDC hdc = GetDC(hwnd);
- HDC hdcTemp = CreateCompatibleDC(hdc);
- SelectObject(hdcTemp, g_hBitmap);
- // 得到窗口大小
- int nWidth, nHeight;
- GetWindowSize(hwnd, &nWidth, &nHeight);
- // 创建与窗口大小相等且能容纳位图的HDC - s_hdcMem
- s_hdcMem = CreateCompatibleDC(hdc);
- HBITMAP hbmp = CreateCompatibleBitmap(hdc, nWidth, nHeight);
- SelectObject(s_hdcMem, hbmp);
- // 将原位图缩放到窗口大小
- BITMAP bm;
- GetObject(g_hBitmap, sizeof(bm), &bm);
- StretchBlt(s_hdcMem, 0, 0, nWidth, nHeight, hdcTemp, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
- // 释放资源
- DeleteDC(hdcTemp);
- ReleaseDC(hwnd, hdc);
- }
- return 0;
- case WM_KEYDOWN:
- switch (wParam)
- {
- case VK_ESCAPE: //按下Esc键时退出
- SendMessage(hwnd, WM_DESTROY, 0, 0);
- return TRUE;
- }
- break;
- case WM_LBUTTONDOWN: //当鼠标左键点击时可以拖曳窗口
- PostMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, 0);
- return TRUE;
- case WM_ERASEBKGND: //在窗口背景中直接贴图
- {
- HDC hdc = (HDC)wParam;
- int nWidth, nHeight;
- GetWindowSize(hwnd, &nWidth, &nHeight);
- BitBlt(hdc, 0, 0, nWidth, nHeight, s_hdcMem, 0, 0, SRCCOPY);
- return TRUE;
- }
- case WM_DESTROY:
- DeleteDC(s_hdcMem);
- PostQuitMessage(0);
- return 0;
- }
- return DefWindowProc(hwnd, message, wParam, lParm);
- }
- void GetWindowSize(HWND hwnd, int *pnWidth, int *pnHeight)
- {
- RECT rc;
- GetWindowRect(hwnd, &rc);
- *pnWidth = rc.right - rc.left;
- *pnHeight = rc.bottom - rc.top;
- }
运行程序将得到如文章中每一张图右边所示的异形窗口。
最后总结一下异形窗口的“三要素”:
1.WS_EX_LAYERED属性
2.以位图为窗口背景(自贴图或位图画刷)
3.指定透明色
本文配套程序下载地址为:http://download.csdn.net/download/morewindows/4966819
当窗口的背景用彩色图片来装饰时,其它控件如果还是用灰色的背景会显的比较不谐调,《Windows界面编程第五篇 静态控件背景透明化》将介绍如何为静态框设置透明背景。
上一篇《Windows界面编程第三篇 异形窗体 普通版》和《Windows界面编程第四篇异形窗体 高富帅版》介绍了异形窗口(异形窗体)的创建,并总结出了异形窗口的“三要素”:
1.WS_EX_LAYERED属性
2.指定透明色
3.以位图为窗口背景
本篇文章将主要介绍Windows编程中如何实现静态控件背景的透明化,这将进一步的美化界面。下面先看一张没有做静态控件背景透明化的对话框程序的运行画面,对话框的彩色图片背景可以参考《Windows界面编程第一篇位图背景与位图画刷》。
可以看出静态控件的灰度背景在对话框的彩色图片背景中显得很不谐调,因此有必要对其美化一下。
在第一篇《Windows界面编程第一篇位图背景与位图画刷》中介绍了通过WM_CTLCOLORDLG消息来设置对话框的背景,而在Windows系统中还有类似于WM_CTLCOLORDLG消息的还有WM_CTLCOLORBTN,WM_CTLCOLOREDIT,WM_CTLCOLORLISTBOX,WM_CTLCOLORSCROLLBAR,WM_CTLCOLORSTATIC这五种来分别管理按钮,编辑框,列表框,滚动条,静态框。因此我们首先在WM_CTLCOLORSTATIC消息中返回一个空画刷看看能不能达到背景透明化的要求。另外对于文字区域的背景透明可以通过SetBkMode来设置。其函数原型如下:
int SetBkMode(
HDC hdc, // handle to DC
int iBkMode // background mode
);
这个函数的第二个参数设置为TRANSPARENT时就可以将文字区域的前景设置成透明。
下面给出完整的源代码(下载地址:http://download.csdn.net/download/morewindows/4966826):
- // 静态控件背景透明化WM_CTLCOLORSTATIC中返回空画刷
- //By MoreWindows-(http://blog.csdn.net/MoreWindows)
- #include <windows.h>
- #include "resource.h"
- const char szDlgTitle[] = "静态控件背景透明化MoreWindows-(http://blog.csdn.net/MoreWindows)";
- // 对话框消息处理函数
- BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
- int APIENTRY WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPSTR lpCmdLine,
- int nCmdShow)
- {
- DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
- return 0;
- }
- BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
- {
- RECT rcDialog;
- HBITMAP hBitmap;
- static BITMAP s_bm;
- static HDC s_hdcMem;
- switch (message)
- {
- case WM_INITDIALOG:
- // 设置对话框标题
- SetWindowText(hDlg, szDlgTitle);
- // 设置对话框大小可调节
- SetWindowLong(hDlg, GWL_STYLE, GetWindowLong(hDlg, GWL_STYLE) | WS_SIZEBOX);
- // 加载背影图片
- hBitmap = (HBITMAP)LoadImage(NULL, "006.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
- if (hBitmap == NULL)
- {
- MessageBox(hDlg, "LoadImage failed", "Error", MB_ICONERROR);
- exit(0);
- }
- else
- {
- // 将背影图片放入HDC - s_hdcMem
- HDC hdc;
- hdc = GetDC(hDlg);
- s_hdcMem = CreateCompatibleDC(hdc);
- SelectObject(s_hdcMem, hBitmap);
- ReleaseDC(hDlg, hdc);
- // 得到位图信息
- GetObject(hBitmap, sizeof(s_bm), &s_bm);
- }
- return 0;
- case WM_COMMAND:
- switch (LOWORD(wParam))
- {
- case IDCANCEL:
- DeleteDC(s_hdcMem);
- EndDialog(hDlg, LOWORD(wParam));
- return TRUE;
- }
- break;
- case WM_SIZE:
- InvalidateRect(hDlg, NULL, TRUE);
- return TRUE;
- case WM_CTLCOLORSTATIC:
- SetBkMode((HDC)wParam, TRANSPARENT);
- return (BOOL)((HBRUSH)GetStockObject(NULL_BRUSH));
- case WM_CTLCOLORDLG:
- GetClientRect(hDlg, &rcDialog);
- //通过SetStretchBltMode的设置能使StretchBlt在缩放图像更加清晰
- SetStretchBltMode((HDC)wParam, COLORONCOLOR);
- StretchBlt((HDC)wParam, 0, 0, rcDialog.right, rcDialog.bottom, s_hdcMem, 0, 0, s_bm.bmWidth, s_bm.bmHeight, SRCCOPY);
- return (BOOL)((HBRUSH)GetStockObject(NULL_BRUSH));
- }
- return FALSE;
- }
这份代码也是在《Windows界面编程第一篇位图背景与位图画刷》文章的代码上增加了
case WM_CTLCOLORSTATIC:
SetBkMode((HDC)wParam, TRANSPARENT);
return (BOOL)((HBRUSH)GetStockObject(NULL_BRUSH));
来达到静态控件背景透明化的效果的,程序运行效果如下:
由图可以看出,虽然Static Text控件的是达到了背景透明化的要求,但是Group Box控件的描述文字的显示却显得很不美观。
要解决这一问题,可以试下位图画刷,我们在WM_CTLCOLORSTATIC消息中像WM_CTLCOLORDLG消息一样也返回一个位图画刷来试试。
- // 静态控件背景透明化- 在WM_CTLCOLORDLG返回窗口背景的位图画刷
- //By MoreWindows-(http://blog.csdn.net/MoreWindows)
- #include <windows.h>
- #include "resource.h"
- const char szDlgTitle[] = "静态控件背景透明化MoreWindows-(http://blog.csdn.net/MoreWindows)";
- // 对话框消息处理函数
- BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
- int APIENTRY WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPSTR lpCmdLine,
- int nCmdShow)
- {
- DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
- return 0;
- }
- BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
- {
- static HBRUSH s_hBitmapBrush; //位图画刷
- switch (message)
- {
- case WM_INITDIALOG:
- // 设置对话框标题
- SetWindowText(hDlg, szDlgTitle);
- // 设置对话框大小可调节
- SetWindowLong(hDlg, GWL_STYLE, GetWindowLong(hDlg, GWL_STYLE) | WS_SIZEBOX);
- // 加载背影图片
- HBITMAP hBitmap;
- hBitmap = (HBITMAP)LoadImage(NULL, "006.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
- if (hBitmap == NULL)
- {
- MessageBox(hDlg, "LoadImage failed", "Error", MB_ICONERROR);
- exit(0);
- }
- // 创建位图画刷
- s_hBitmapBrush = CreatePatternBrush(hBitmap);
- return 0;
- case WM_COMMAND:
- switch (LOWORD(wParam))
- {
- case IDCANCEL:
- DeleteObject(s_hBitmapBrush);
- EndDialog(hDlg, LOWORD(wParam));
- return TRUE;
- }
- break;
- case WM_CTLCOLORSTATIC:
- SetBkMode((HDC)wParam, TRANSPARENT);
- case WM_CTLCOLORDLG:
- return (BOOL)s_hBitmapBrush;
- }
- return FALSE;
- }
与上一个程序一样,这个程序也只是在在《Windows界面编程第一篇位图背景与位图画刷》文章的代码上增加了
case WM_CTLCOLORSTATIC:
SetBkMode((HDC)wParam, TRANSPARENT);
来设置静态控件背景透明化,程序运行效果如下:
由图可以看出,静态控件的透明化还是非常方便的,只要在WM_CTLCOLORSTATIC消息中完成二个步骤即可:
1.通过SetBkMode((HDC)wParam, TRANSPARENT);来设置文字区域背景透明。
2.返回空画刷或与父窗口相同的画刷。
自绘实现半透明水晶按钮
运行效果
实现方法
1.给按钮加上BS_OWNERDRAW样式
2.重载DrawItem函数,在这里绘制按钮
3.关键之处就是把父窗口的背景复制到按钮上,实现视觉上的透明
4.最后通过AlphaBlend实现半透明.
源码下载:
http://blog.csdn.net/cometnet/article/details/8464693
实现源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 |
// MyButton.h #pragma once // CMyButton class CMyButton : public CButton { DECLARE_DYNAMIC(CMyButton) public : CMyButton(); virtual ~CMyButton(); public : void SetBkColor( COLORREF color); void SetTextColor( COLORREF color); private : bool m_bOver; bool m_bDown; bool m_bDisable; bool m_bTracking; COLORREF m_bkColor; COLORREF m_textColor; protected : DECLARE_MESSAGE_MAP() virtual BOOL PreCreateWindow(CREATESTRUCT& cs); virtual void PreSubclassWindow(); public : virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/ ); afx_msg void OnMouseMove( UINT nFlags, CPoint point); afx_msg void OnLButtonDown( UINT nFlags, CPoint point); afx_msg void OnLButtonUp( UINT nFlags, CPoint point); afx_msg LRESULT OnMouseLeave( WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnMouseHover( WPARAM wParam, LPARAM lParam); afx_msg void OnEnable( BOOL bEnable); private : void ButtonInit(); void DrawButton(); void DrawButton( HDC hDestDC); }; |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199 |
// MyButton.cpp : 实现文件 // #include "stdafx.h" #include "AlphaButton.h" #include "MyButton.h" #include "MainDlg.h" // CMyButton IMPLEMENT_DYNAMIC(CMyButton, CButton) CMyButton::CMyButton() { m_bkColor=0xFFFFFF; m_textColor=0x000000; } CMyButton::~CMyButton() { } BEGIN_MESSAGE_MAP(CMyButton, CButton) ON_WM_MOUSEMOVE() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave) ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover) ON_WM_ENABLE() END_MESSAGE_MAP() // CMyButton 消息处理程序 void CMyButton::SetBkColor( COLORREF color) { m_bkColor=color; } void CMyButton::SetTextColor( COLORREF color) { m_textColor=color; } BOOL CMyButton::PreCreateWindow(CREATESTRUCT& cs) { BOOL bRet=CButton::PreCreateWindow(cs); ButtonInit(); return bRet; } void CMyButton::PreSubclassWindow() { CButton::PreSubclassWindow(); ButtonInit(); } void CMyButton::ButtonInit() { m_bTracking= false ; m_bOver=m_bDown=m_bDisable= false ; m_bDisable=IsWindowEnabled()?FALSE:TRUE; ModifyStyle(NULL,BS_OWNERDRAW); } void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { DrawButton(lpDrawItemStruct->hDC); } void CMyButton::OnMouseMove( UINT nFlags, CPoint point) { if (!m_bTracking) { m_bOver = TRUE; TRACKMOUSEEVENT tme; tme.cbSize = sizeof (tme); tme.hwndTrack = m_hWnd; tme.dwFlags = TME_LEAVE | TME_HOVER; tme.dwHoverTime = 50; m_bTracking = ( bool )_TrackMouseEvent(&tme); } CButton::OnMouseMove(nFlags, point); } void CMyButton::OnLButtonDown( UINT nFlags, CPoint point) { m_bDown=TRUE; CButton::OnLButtonDown(nFlags, point); } void CMyButton::OnLButtonUp( UINT nFlags, CPoint point) { m_bDown=FALSE; CButton::OnLButtonUp(nFlags, point); } LRESULT CMyButton::OnMouseLeave( WPARAM wParam, LPARAM lParam) { m_bOver = FALSE; m_bTracking = FALSE; m_bDown=FALSE; InvalidateRect(NULL, FALSE); return 0; } LRESULT CMyButton::OnMouseHover( WPARAM wParam, LPARAM lParam) { m_bOver = TRUE; InvalidateRect(NULL); return 0; } void CMyButton::DrawButton() { HDC hDC=::GetDC(m_hWnd); DrawButton(hDC); ::ReleaseDC(m_hWnd,hDC); } void CMyButton::DrawButton( HDC hDestDC) { CRect rc; GetClientRect(rc); int nWindth=rc.Width(); int nHeight=rc.Height(); HDC hDC=CreateCompatibleDC(hDestDC); //创建兼容DC,采用双缓冲画出 HDC hMaskDC=CreateCompatibleDC(hDestDC); HBITMAP hBitmap=CreateCompatibleBitmap(hDestDC,nWindth,nHeight); HBITMAP hMaskBitmap=CreateCompatibleBitmap(hDestDC,nWindth,nHeight); HBITMAP hOldBitmap=( HBITMAP )SelectObject(hDC,hBitmap); HBITMAP hOldMaskBitmap=( HBITMAP )SelectObject(hMaskDC,hMaskBitmap); SetBkMode(hDC,TRANSPARENT); //把父窗口的背景图复制到按钮的DC上,实现视觉透明---------------- CMainDlg* pParent=(CMainDlg*)GetParent(); CPoint pt(0,0); MapWindowPoints(pParent,&pt,1); pParent->m_bkImage.BitBlt(hDC,rc,pt,SRCCOPY); //------------------------------------------------------------- int nAlpha=100; //0--255 int nOffset=0; HBRUSH hbr=CreateSolidBrush(m_bkColor); FillRect(hMaskDC,&rc,hbr); DeleteObject(hbr); if (m_bDisable){ nAlpha=100; } else if (m_bDown){ nAlpha=180; nOffset=1; } else if (m_bOver){ nAlpha=150; } else { nAlpha=100; } BLENDFUNCTION blend; memset ( &blend, 0, sizeof ( blend) ); blend.BlendOp= AC_SRC_OVER; blend.SourceConstantAlpha= nAlpha; // 透明度 最大255 HRGN hRgn=CreateRoundRectRgn(0,0,nWindth,nHeight,3,3); SelectClipRgn (hDC,hRgn); AlphaBlend (hDC,0,0,nWindth,nHeight,hMaskDC, 0,0,nWindth,nHeight,blend); CString strText; GetWindowText(strText); if (strText!=_T( "" )){ rc.InflateRect(-2,-2); rc.OffsetRect(nOffset,nOffset); HFONT hFont=( HFONT )SendMessage(WM_GETFONT); if (!hFont)hFont=( HFONT )GetStockObject(DEFAULT_GUI_FONT); HFONT hOldFont=( HFONT )SelectObject(hDC,hFont); ::SetTextColor(hDC,m_textColor); ::DrawText(hDC,strText,-1,&rc,DT_SINGLELINE|DT_CENTER|DT_VCENTER|DT_WORD_ELLIPSIS); ::SelectObject(hDC,hOldFont); } SelectClipRgn (hDC,NULL); DeleteObject(hRgn); //复制到控件的DC上------------------------ BitBlt(hDestDC,0,0,nWindth,nHeight,hDC,0,0,SRCCOPY); //删除资源,释放内存----------------------- SelectObject(hDC,hOldBitmap); DeleteObject(hBitmap); DeleteDC(hDC); SelectObject(hMaskDC,hOldMaskBitmap); DeleteObject(hMaskBitmap); DeleteDC(hMaskDC); } void CMyButton::OnEnable( BOOL bEnable) { CButton::OnEnable(bEnable); m_bDisable=IsWindowEnabled()?FALSE:TRUE; DrawButton(); } |