Win32 使用 SetLayeredWindowAttributes 制作透明窗体
函数原型
SetLayeredWindowAttributes 用来设置分层窗口的透明度
BOOL SetLayeredWindowAttributes(
HWND hwnd, // 指定分层窗口句柄
COLORREF crKey, // 指定需要透明的背景颜色值,可用RGB()宏
BYTE bAlpha, // 设置透明度,0表示完全透明,255表示不透明
DWORD dwFlags // 透明方式
);
dwFlags
dwFlags
参数用来指明窗口的透明方式,可取以下值
LWA_ALPHA
crKey参数无效,bAlpha参数有效LWA_COLORKEY
窗体中的所有颜色为crKey的地方将变为透明,bAlpha参数无效。其常量值为1LWA_ALPHA | LWA_COLORKEY
crKey的地方将变为全透明,而其它地方根据bAlpha参数确定透明度。
窗口居中
要实现窗口居中,要先获取屏幕宽高
//获取屏幕宽高
int scWidth = GetSystemMetrics(SM_CXSCREEN);
int scHeight = GetSystemMetrics(SM_CYSCREEN);
窗体宽高使用图片宽高
//从项目目录下,加载图片文件
HBITMAP hBmp = (HBITMAP)LoadImageA(NULL, IMG_PATH, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
//创建并获取图片信息
BITMAP bmp;
GetObject(hBmp, sizeof(BITMAP), &bmp);
//获取图片宽高
int width = bmp.bmWidth;
int height = bmp.bmHeight;
然后计算窗口居中时的xy坐标
//计算窗口居中时,左上角的xy坐标
int x = (scWidth - width) / 2;
int y = (scHeight - height) / 2;
去掉窗口标题栏
要去掉窗口标题栏,只需将默认的窗口样式WS_OVERLAPPEDWINDOW
改为WS_POPUP
,即可。同时要填入居中时,窗口的xy坐标和窗口宽高
HWND hWnd = CreateWindowW(szWindowClass, szTitle,
//WS_OVERLAPPEDWINDOW,
WS_POPUP,
x, y, width, height, nullptr, nullptr, hInstance, nullptr);
将图片绘制到内存句柄
首先要创建一个与指定图片大小相等的内存设备上下文环境,然后将窗口显示出来,最后进行绘制。用完后,释放之前创建的上下文环境
注意:如果在showWindow
之前调用BitBlt
,则绘制不会生效
//指定窗口客户端区域的DC的句柄。若hWnd=NULL,则提取整个屏幕的DC
HDC hdc = GetDC(hWnd);
//该函数创建一个与指定设备兼容的内存设备上下文环境(DC)
HDC hdcMem = CreateCompatibleDC(hdc);
//这里把hbmp的位图选择到兼容DC memdc,之后这个兼容DC就拥有和hbmp同样大小的绘图区域,注意超出位图返回的GDI输出都是无效的
SelectObject(hdcMem, hBmp);
//显示窗口
ShowWindow(hWnd, nCmdShow);
//绘制一定要在ShowWindow之后调用,否则绘图无效
BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
//函数释放设备上下文环境(DC)供其他应用程序使用
ReleaseDC(hWnd, hdc);
ReleaseDC(hWnd, hdcMem);
为窗口添加透明样式
为样式添加附加层,用来支持透明样式,dwFlags
为变量,可以填入下面三个参数,下面分别来看下实际的效果
//为样式添加附加层,用来支持透明样式
LONG nExStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
nExStyle |= WS_EX_LAYERED;
SetWindowLong(hWnd, GWL_EXSTYLE, nExStyle);
//设置窗体属性
SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 128, dwFlags);
LWA_ALPHA
crKey参数无效,bAlpha参数有效
LWA_COLORKEY
窗体中的所有颜色为crKey的地方将变为透明,bAlpha参数无效。其常量值为1
LWA_ALPHA | LWA_COLORKEY
crKey的地方将变为全透明,而其它地方根据bAlpha参数确定透明度
让窗口允许被拖动
首先普及一个知识,哪里是客户区域,哪里又算是非客户区?
NC
的英文全称叫Not ClientRect
,即非客户区。在C++窗体中,标题栏(Caption那栏)就是非客户区,标题栏以外的窗体就叫客户区。
注意:超出窗体的区域,既不是“客户区域”,也不是“非客户区域”
这个,可以在WndProc
中打印WM_MOUSEMOVE
和WM_NCMOUSEMOVE
进行测试
首先,还是打开控制台
#include <stdio.h>
void ShowConsole()
{
AllocConsole();
FILE* stream;
freopen_s(&stream, "CON", "r", stdin);//重定向输入流
freopen_s(&stream, "CON", "w", stdout);//重定向输入流
}
然后在监听事件处,添加打印。可以看到,鼠标移出窗体时不打印,窗体标题栏处打印NC事件,窗体内打印WM_MOUSEMOVE
事件
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目标: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
ShowConsole();
break;
case WM_MOUSEMOVE:
printf("WM_MOUSEMOVE\n");
break;
case WM_NCMOUSEMOVE:
printf("WM_NCMOUSEMOVE\n");
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
因此,让WS_POPUP
风格的窗口支持拖动,可以在鼠标按下时,发送一个非客户区事件WM_NCLBUTTONDOWN
,事件的内容是 —— 标题栏被按下HTCAPTION
。事件通过SendMessageA
发送
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目标: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN:
//模拟按下标题栏区域,让窗口可移动
SendMessageA(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
完整代码
// test.cpp : 定义应用程序的入口点。
//
#include "framework.h"
#include "test.h"
#include <stdio.h>
#define MAX_LOADSTRING 100
#define IMG_PATH "alpha32_black.bmp"
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此处放置代码。
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_TEST, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST));
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TEST));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
void ShowConsole()
{
AllocConsole();
FILE* stream;
freopen_s(&stream, "CON", "r", stdin);//重定向输入流
freopen_s(&stream, "CON", "w", stdout);//重定向输入流
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目标: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
//从项目目录下,加载图片文件
HBITMAP hBmp = (HBITMAP)LoadImageA(NULL, IMG_PATH, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
//创建并获取图片信息
BITMAP bmp;
GetObject(hBmp, sizeof(BITMAP), &bmp);
//获取图片宽高
int width = bmp.bmWidth;
int height = bmp.bmHeight;
//获取屏幕宽高
int scWidth = GetSystemMetrics(SM_CXSCREEN);
int scHeight = GetSystemMetrics(SM_CYSCREEN);
//计算窗口居中时,左上角的xy坐标
int x = (scWidth - width) / 2;
int y = (scHeight - height) / 2;
//创建窗口,并将窗体样式设置为无标题栏的弹窗
HWND hWnd = CreateWindowW(szWindowClass, szTitle,
//WS_OVERLAPPEDWINDOW,
WS_POPUP,
x, y, width, height, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
//指定窗口客户端区域的DC的句柄。若hWnd=NULL,则提取整个屏幕的DC
HDC hdc = GetDC(hWnd);
//该函数创建一个与指定设备兼容的内存设备上下文环境(DC)
HDC hdcMem = CreateCompatibleDC(hdc);
//这里把hbmp的位图选择到兼容DC memdc,之后这个兼容DC就拥有和hbmp同样大小的绘图区域,注意超出位图返回的GDI输出都是无效的
SelectObject(hdcMem, hBmp);
//显示窗口
ShowWindow(hWnd, nCmdShow);
//绘制一定要在ShowWindow之后调用,否则绘图无效
BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
//为样式添加附加层,用来支持透明样式
LONG nExStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
nExStyle |= WS_EX_LAYERED;
SetWindowLong(hWnd, GWL_EXSTYLE, nExStyle);
//设置窗体属性
SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 128, LWA_COLORKEY);
//函数释放设备上下文环境(DC)供其他应用程序使用
ReleaseDC(hWnd, hdc);
ReleaseDC(hWnd, hdcMem);
UpdateWindow(hWnd);
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目标: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
ShowConsole();
case WM_MOUSEMOVE:
printf("WM_MOUSEMOVE\n");
break;
case WM_NCMOUSEMOVE:
printf("WM_NCMOUSEMOVE\n");
break;
case WM_LBUTTONDOWN:
//模拟按下标题栏区域,让窗口可移动
SendMessageA(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
- 点赞
- 收藏
- 关注作者
评论(0)