在上一篇文章 分层窗口的说明与使用(1) 中我们已经初步掌握了分层窗口的创建,本文将会基于上文中的知识试着制作出好看的窗口特效
我会尽量将代码放在一个文件里防止有些人看的稀里糊涂
本文篇幅较长,希望能够耐心看完
本文适合有一定win32基础的人
小小的改动
我们先拿出上篇文章的代码并找到CreateLayeredWindow函数
//创建分层窗口
HWND CreateLayeredWindow(HINSTANCE hInstance, HWND hWnd, int iWidth, int iHeight, int iPosX, int iPosY, COLORREF* colBGRA)
{
//注册分层窗口
RegWindow(hInstance, L"LayeredWindow", DefWindowProc, BLACK_BRUSH);
//创建分层窗口
HWND hLayeredWindow = CreateWindowEx(WS_EX_LAYERED, L"LayeredWindow", 0,
WS_POPUP | WS_BORDER, 0, 0, iWidth, iHeight, hWnd, NULL, hInstance, NULL);
//将分层窗口设为本窗口的子窗口
SetParent(hLayeredWindow, hWnd);
//更新分层窗口//
//创建分层窗口的DC
HDC hLayeredWindowDC = GetDC(hLayeredWindow);
HDC hCompatibleDC = CreateCompatibleDC(hLayeredWindowDC);
//填充BLENDFUNCTION结构
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
//控制显示位置
POINT ptDst = { iPosX, iPosY };
//控制窗口大小
SIZE sizeWnd = { iWidth, iHeight };
//为0就行
POINT pSrc = { 0, 0 };
//创建一副与当前DC兼容的位图
HBITMAP hCustomBmp = NULL;
hCustomBmp = CreateCompatibleBitmap(hLayeredWindowDC, iWidth, iHeight);
//将hCustomBmp指定到hCompatibleDC中
SelectObject(hCompatibleDC, hCustomBmp);
//填充bmpInfo
BITMAPINFO bmpInfo = { 0 };
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfo.bmiHeader.biWidth = iWidth;
bmpInfo.bmiHeader.biHeight = -iHeight;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biBitCount = 32;
//设置位图中的像素
SetDIBits(NULL, hCustomBmp, 0, iHeight, colBGRA, &bmpInfo, DIB_RGB_COLORS);
//更新分层窗口
UpdateLayeredWindow(hLayeredWindow, hLayeredWindowDC, &ptDst, &sizeWnd, hCompatibleDC, &pSrc, NULL, &blend, ULW_ALPHA);
//释放DC
DeleteDC(hLayeredWindowDC);
return hLayeredWindow;
}
为了方便,我们需要将colBGRA中的像素点数据从BGRA转为RGBA,若不知道RGBA和BGRA请看上一篇文章
因此我们需要一个翻转RGB值的函数,代码如下(我想我的注释应该够详细了,不需要多说什么)
void TurnRGB(COLORREF* srcCols, COLORREF* tagCols, DWORD dwSize, DWORD dwWidth)
{
if (!srcCols || !tagCols || dwSize <= dwWidth || dwSize % dwWidth != 0)
/*
若srcCols或tagCols为NULL,意为没有源数据能翻转或没有存储翻转后数据的存放处
dwSize <= dwWidth 意味着数据的宽度大于数据总大小,怎么可能0w0
dwSize % dwWidth != 0 意味着像素点数据若以dwWidth为一行的个数,则无法拼成一个矩形,无法形成一个完整的窗口
*/
return;
DWORD dwHeight = dwSize / dwWidth;
//计算高度
for (DWORD index = 0; index < dwSize; index++)
//遍历每个像素点并执行翻转操作
{
//col用于存储当前像素点的数据
COLORREF col = srcCols[index];
//翻转RGB值
tagCols[index] = RGBA((BYTE)(col >> 16), (BYTE)(col >> 8), (BYTE)col, (BYTE)(col >> 24));
}
}
srcCols:像素点数据
tagCols:接收像素点翻转后的数据的变量
dwSize:像素大小(总数量)
dwWidth:像素点数据宽度(就是分层窗口的宽)
将上面的函数放在CreateLayeredWindow之前便于调用,然后就可以对CreateLayeredWindow进行小小的改动
//创建分层窗口
HWND CreateLayeredWindow(HINSTANCE hInstance, HWND hWnd, int iWidth, int iHeight, int iPosX, int iPosY, COLORREF* colRGBA)
//将变量名colBGRA改为colRGBA
{
DWORD dwColSize = iWidth * iHeight;
COLORREF* colBGRA = new COLORREF[dwColSize];
TurnRGB(colRGBA, colBGRA, dwColSize, iWidth);
//注册分层窗口
RegWindow(hInstance, L"LayeredWindow", DefWindowProc, BLACK_BRUSH);
//创建分层窗口
HWND hLayeredWindow = CreateWindowEx(WS_EX_LAYERED, L"LayeredWindow", 0,
WS_POPUP | WS_BORDER, 0, 0, iWidth, iHeight, hWnd, NULL, hInstance, NULL);
//将分层窗口设为本窗口的子窗口
SetParent(hLayeredWindow, hWnd);
//更新分层窗口//
//创建分层窗口的DC
HDC hLayeredWindowDC = GetDC(hLayeredWindow);
HDC hCompatibleDC = CreateCompatibleDC(hLayeredWindowDC);
//填充BLENDFUNCTION结构
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
//控制显示位置
POINT ptDst = { iPosX, iPosY };
//控制窗口大小
SIZE sizeWnd = { iWidth, iHeight };
//为0就行
POINT pSrc = { 0, 0 };
//创建一副与当前DC兼容的位图
HBITMAP hCustomBmp = NULL;
hCustomBmp = CreateCompatibleBitmap(hLayeredWindowDC, iWidth, iHeight);
//将hCustomBmp指定到hCompatibleDC中
SelectObject(hCompatibleDC, hCustomBmp);
//填充bmpInfo
BITMAPINFO bmpInfo = { 0 };
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfo.bmiHeader.biWidth = iWidth;
bmpInfo.bmiHeader.biHeight = -iHeight;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biBitCount = 32;
//设置位图中的像素
SetDIBits(NULL, hCustomBmp, 0, iHeight, colBGRA, &bmpInfo, DIB_RGB_COLORS);
//更新分层窗口
UpdateLayeredWindow(hLayeredWindow, hLayeredWindowDC, &ptDst, &sizeWnd, hCompatibleDC, &pSrc, NULL, &blend, ULW_ALPHA);
//释放DC
DeleteDC(hLayeredWindowDC);
return hLayeredWindow;
}
这样就可以直接传入RGBA值而不必先翻转RGBA值为BGRA值再传入了。
然后我们在找到CreateLayeredWindow函数里的这一行代码
//注册分层窗口
RegWindow(hInstance, L"LayeredWindow", WindowProc, BLACK_BRUSH);
将其中的"WindowProc"改为"DefWindowProc"
//注册分层窗口
RegWindow(hInstance, L"LayeredWindow", DefWindowProc, BLACK_BRUSH);
分层窗口并不需要自己的窗口过程函数,用微软提供的就行。
然后找到这一行
//创建分层窗口
HWND hLayeredWindow = CreateWindowEx(WS_EX_LAYERED, L"LayeredWindow", 0,
WS_POPUP | WS_BORDER, 0, 0, iWidth, iHeight, NULL, NULL, hInstance, NULL);
将倒数第四个参数改为hWnd
//创建分层窗口
HWND hLayeredWindow = CreateWindowEx(WS_EX_LAYERED, L"LayeredWindow", 0,
WS_POPUP | WS_BORDER, 0, 0, iWidth, iHeight, hWnd, NULL, hInstance, NULL);
这样调用 GetParent函数 时就不会出错返回NULL了
接着找到WinMain函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE PrevhInstance, LPSTR lpCmdLine, int nCmdShow)
{
//注册窗口
RegWindow(hInstance, L"WINDOW", WindowProc, WHITE_BRUSH);
//创建窗口
HWND hWnd = CreateWindow(L"WINDOW", 0,
WS_OVERLAPPEDWINDOW, 100, 100, 800, 500, NULL, NULL, hInstance, NULL);
//显示窗口
ShowWindow(hWnd, SW_SHOW);
//更新窗口
UpdateWindow(hWnd);
//创建BGRA数据
COLORREF* colBGRA = new COLORREF[100 * 100];
//初始化
ZeroMemory(colBGRA, 100 * 100 * sizeof(COLORREF));
//填充半透明蓝色
for (int i = 0; i < 100 * 100; i++)
{
//虽然用的是RGBA,但实际上是BGRA
// R G B A
colBGRA[i] = RGBA(233, 0, 0, 128);
}
//创建分层窗口
HWND hLW = CreateLayeredWindow(hInstance, hWnd, 100, 100, 0, 0, colBGRA);
//消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return 0;
}
将14~28行的代码删除或注释掉
...
//更新窗口
UpdateWindow(hWnd);
//创建BGRA数据
//COLORREF* colBGRA = new COLORREF[100 * 100];
//初始化
//ZeroMemory(colBGRA, 100 * 100 * sizeof(COLORREF));
//填充半透明蓝色
//for (int i = 0; i < 100 * 100; i++)
//{
//虽然用的是RGBA,但实际上是BGRA
// R G B A
//colBGRA[i] = RGBA(233, 0, 0, 128);
//}
//创建分层窗口
//HWND hLW = CreateLayeredWindow(hInstance, hWnd, 100, 100, 0, 0, colBGRA);
//消息循环
MSG msg;
...
现在的完整代码应该是这样的
#include <windows.h>
#define RGBA(r,g,b,a) (COLORREF)(((BYTE)(r) |((WORD)((BYTE)(g)) << 8)) |(((DWORD)((BYTE)(b)) << 16)) |(((DWORD)((BYTE)(a)) << 24)))
//窗口过程函数
LRESULT CALLBACK WindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return 0;
}
//注册窗口函数
void RegWindow(HINSTANCE hInstance, LPCWSTR lpClassName, WNDPROC wndProc, DWORD dwColor)
{
WNDCLASS wnd;
wnd.cbClsExtra = 0;
wnd.cbWndExtra = 0;
wnd.hbrBackground = (HBRUSH)(GetStockObject(dwColor));
wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
wnd.hIcon = LoadCursor(NULL, IDI_APPLICATION);
wnd.lpfnWndProc = wndProc;
wnd.lpszClassName = lpClassName;
wnd.lpszMenuName = NULL;
wnd.style = CS_HREDRAW;
wnd.hInstance = hInstance;
RegisterClass(&wnd);
}
void TurnRGB(COLORREF* srcCols, COLORREF* tagCols, DWORD dwSize, DWORD dwWidth)
{
if (!srcCols || !tagCols || dwSize <= dwWidth || dwSize % dwWidth != 0)
/*
若srcCols或tagCols为NULL,意为没有源数据能翻转或没有存储翻转后数据的存放处
dwSize <= dwWidth 意味着数据的宽度大于数据总大小,怎么可能0w0
dwSize % dwWidth != 0 意味着像素点数据若以dwWidth为一行的个数,则无法拼成一个矩形,无法形成一个完整的窗口
*/
return;
DWORD dwHeight = dwSize / dwWidth;
//计算高度
for (DWORD index = 0; index < dwSize; index++)
//遍历每个像素点并执行翻转操作
{
//col用于存储当前像素点的数据
COLORREF col = srcCols[index];
//翻转RGB值
tagCols[index] = RGBA((BYTE)(col >> 16), (BYTE)(col >> 8), (BYTE)col, (BYTE)(col >> 24));
}
}
//创建分层窗口
HWND CreateLayeredWindow(HINSTANCE hInstance, HWND hWnd, int iWidth, int iHeight, int iPosX, int iPosY, COLORREF* colRGBA)
//将变量名colBGRA改为colRGBA
{
DWORD dwColSize = iWidth * iHeight;
COLORREF* colBGRA = new COLORREF[dwColSize];
TurnRGB(colRGBA, colBGRA, dwColSize, iWidth);
//注册分层窗口
RegWindow(hInstance, L"LayeredWindow", DefWindowProc, BLACK_BRUSH);
//创建分层窗口
HWND hLayeredWindow = CreateWindowEx(WS_EX_LAYERED, L"LayeredWindow", 0,
WS_POPUP | WS_BORDER, 0, 0, iWidth, iHeight, hWnd, NULL, hInstance, NULL);
//将分层窗口设为本窗口的子窗口
SetParent(hLayeredWindow, hWnd);
//更新分层窗口//
//创建分层窗口的DC
HDC hLayeredWindowDC = GetDC(hLayeredWindow);
HDC hCompatibleDC = CreateCompatibleDC(hLayeredWindowDC);
//填充BLENDFUNCTION结构
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
//控制显示位置
POINT ptDst = { iPosX, iPosY };
//控制窗口大小
SIZE sizeWnd = { iWidth, iHeight };
//为0就行
POINT pSrc = { 0, 0 };
//创建一副与当前DC兼容的位图
HBITMAP hCustomBmp = NULL;
hCustomBmp = CreateCompatibleBitmap(hLayeredWindowDC, iWidth, iHeight);
//将hCustomBmp指定到hCompatibleDC中
SelectObject(hCompatibleDC, hCustomBmp);
//填充bmpInfo
BITMAPINFO bmpInfo = { 0 };
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfo.bmiHeader.biWidth = iWidth;
bmpInfo.bmiHeader.biHeight = -iHeight;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biBitCount = 32;
//设置位图中的像素
SetDIBits(NULL, hCustomBmp, 0, iHeight, colBGRA, &bmpInfo, DIB_RGB_COLORS);
//更新分层窗口
UpdateLayeredWindow(hLayeredWindow, hLayeredWindowDC, &ptDst, &sizeWnd, hCompatibleDC, &pSrc, NULL, &blend, ULW_ALPHA);
//释放DC
DeleteDC(hLayeredWindowDC);
return hLayeredWindow;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE PrevhInstance, LPSTR lpCmdLine, int nCmdShow)
{
//注册窗口
RegWindow(hInstance, L"WINDOW", WindowProc, WHITE_BRUSH);
//创建窗口
HWND hWnd = CreateWindow(L"WINDOW", 0,
WS_OVERLAPPEDWINDOW, 100, 100, 800, 500, NULL, NULL, hInstance, NULL);
//显示窗口
ShowWindow(hWnd, SW_SHOW);
//更新窗口
UpdateWindow(hWnd);
//创建BGRA数据
//COLORREF* colBGRA = new COLORREF[100 * 100];
//初始化
//ZeroMemory(colBGRA, 100 * 100 * sizeof(COLORREF));
//填充半透明蓝色
//for (int i = 0; i < 100 * 100; i++)
//{
//虽然用的是RGBA,但实际上是BGRA
// R G B A
//colBGRA[i] = RGBA(233, 0, 0, 128);
//}
//创建分层窗口
//HWND hLW = CreateLayeredWindow(hInstance, hWnd, 100, 100, 0, 0, colBGRA);
//消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return 0;
}
现在我们就可以正式进入本文主题了。
悬停效果
拿360做例子
其实就是点击一个按钮后有一个框框住了按钮的效果,这种效果用于实现三态按钮。如果用纯win32按照正常的思路设计会比较麻烦,但用分层窗口就会比较容易
因为win32的按钮要检测鼠标悬停,无论是原版按钮还是自绘按钮,都比较麻烦,讲起来可能比较乱,所以本文不加以论述,也就是做不出如下效果
思路
我们要先创建几个按钮,然后检测到按钮按下后就将分层窗口移动到该按钮上,而动窗口需要MoveWindow函数
BOOL MoveWindow(
[in] HWND hWnd,
[in] int X,
[in] int Y,
[in] int nWidth,
[in] int nHeight,
[in] BOOL bRepaint
);
hWnd:要移动的窗口的hWnd
x,y:要移动到哪里(新位置的坐标)
nWidth,nHeight:窗口移动后的大小
bRepaint:是否重绘
实践
这里我们先创建4个按钮,而创建按钮需要在WM_CREATE消息里创建
而处理按钮事件需要接收WM_COMMAND消息
我们找到主窗口的窗口过程函数WindowProc,添加WM_CREATE与WM_COMMAND消息并处理
(若不知道为什么要处理WM_CREATE与WM_COMMAND消息,请先去学习win32编程基础)
LRESULT CALLBACK WindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_CREATE:
{
LPCREATESTRUCT pc = (LPCREATESTRUCT)lParam;
break;
}
case WM_COMMAND:
{
WORD id = LOWORD(wParam);
WORD code = HIWORD(wParam);
HWND hwnd = (HWND)lParam;
break;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return 0;
}
接着我们创建4个宽为120,高为150的按钮,分别命名为choice1、choice2、choice3、choice4,对应的id分别为1,、2、3、4
...
case WM_CREATE:
{
LPCREATESTRUCT pc = (LPCREATESTRUCT)lParam;
HWND hButton1 = CreateWindow(L"BUTTON", L"choice1", WS_CHILD | WS_VISIBLE, 10, 10, 120, 150, hWnd, (HMENU)1, pc->hInstance, NULL);
HWND hButton2 = CreateWindow(L"BUTTON", L"choice2", WS_CHILD | WS_VISIBLE, 140, 10, 120, 150, hWnd, (HMENU)2, pc->hInstance, NULL);
HWND hButton3 = CreateWindow(L"BUTTON", L"choice3", WS_CHILD | WS_VISIBLE, 270, 10, 120, 150, hWnd, (HMENU)3, pc->hInstance, NULL);
HWND hButton4 = CreateWindow(L"BUTTON", L"choice4", WS_CHILD | WS_VISIBLE, 400, 10, 120, 150, hWnd, (HMENU)4, pc->hInstance, NULL);
break;
}
...
接着我们在WM_COMMAND消息里处理按钮按下的消息
...
HWND hwnd = (HWND)lParam;
if (id == 1 && code == BN_CLICKED)
{
MessageBox(hWnd, L"choice1", L"CLICK", 0);
}
else if (id == 2 && code == BN_CLICKED)
{
MessageBox(hWnd, L"choice2", L"CLICK", 0);
}
else if (id == 3 && code == BN_CLICKED)
{
MessageBox(hWnd, L"choice3", L"CLICK", 0);
}
else if (id == 4 && code == BN_CLICKED)
{
MessageBox(hWnd, L"choice4", L"CLICK", 0);
}
break;
...
现在点击任意一个按钮就会弹出一个对话框,总体效果如下
(颜色有点奇怪是因为gif本身的问题,虽然有按钮点丑...以后如果有时间我会专门写几篇关于按钮美化的文章)
接着我们要创建一个分层窗口用于实现悬停的效果
先声明全局变量并提前声明CreateLayeredWindow函数,还要定义一些关于分层窗口的宏便于使用
...
#define RGBA(r,g,b,a) (COLORREF)(((BYTE)(r) |((WORD)((BYTE)(g)) << 8)) |(((DWORD)((BYTE)(b)) << 16)) |(((DWORD)((BYTE)(a)) << 24)))
#define LAYERED_WINDOW_WIDTH 120 //分层窗口的宽
#define LAYERED_WINDOW_HEIGHT 150 //分层窗口的高
//声明全局变量用于存放分层窗口的句柄
HWND hLayeredWindow = NULL;
//也要提前声明TurnRGB函数否则会出错
void TurnRGB(COLORREF* srcCols, COLORREF* tagCols, DWORD dwSize, DWORD dwWidth);
//声明CreateLayeredWindow函数便于在WindowProc函数里使用
HWND CreateLayeredWindow(HINSTANCE hInstance, HWND hWnd, int iWidth, int iHeight, int iPosX, int iPosY, COLORREF* colRGBA);
//窗口过程函数
LRESULT CALLBACK WindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
...
再在WM_CREATE消息里创建分层窗口
...
HWND hButton4 = CreateWindow(L"BUTTON", L"choice4", WS_CHILD | WS_VISIBLE, 400, 10, 120, 150, hWnd, (HMENU)4, pc->hInstance, NULL);
//创建一个红色半透明的像素矩阵
COLORREF* col = new COLORREF[120 * 150];
for (size_t i = 0; i < 120 * 150; i++)
//遍历填充颜色
{
col[i] = RGBA(255, 0, 0, 20);
}
//创建分层窗口
//在(10,10)位置创建能使分层窗口与按钮重合
hLayeredWindow = CreateLayeredWindow(pc->hInstance, hWnd, LAYERED_WINDOW_WIDTH, LAYERED_WINDOW_HEIGHT, 10, 10, col);
if (!hLayeredWindow)
//创建失败就取消创建直接结束窗口
SendMessage(hWnd, WM_DESTROY, 0, 0);
break;
...
效果如图
现在我们只需要使用MoveWindow函数移动分层窗口就行了
回到WM_COMMAND消息里,将MessageBox函数删除或注释,并使用MoveWindow函数将分层窗口移动到当前按钮的位置,代码如下
...
HWND hwnd = (HWND)lParam;
if (id == 1 && code == BN_CLICKED)
{
//MessageBox(hWnd, L"choice1", L"CLICK", 0);
//移动分层窗口到当前按钮位置,保持宽高不变,重绘窗口,下同
MoveWindow(hLayeredWindow, 10, 10, LAYERED_WINDOW_WIDTH, LAYERED_WINDOW_HEIGHT, TRUE);
}
else if (id == 2 && code == BN_CLICKED)
{
//MessageBox(hWnd, L"choice2", L"CLICK", 0);
MoveWindow(hLayeredWindow, 140, 10, LAYERED_WINDOW_WIDTH, LAYERED_WINDOW_HEIGHT, TRUE);
}
else if (id == 3 && code == BN_CLICKED)
{
//MessageBox(hWnd, L"choice3", L"CLICK", 0);
MoveWindow(hLayeredWindow, 270, 10, LAYERED_WINDOW_WIDTH, LAYERED_WINDOW_HEIGHT, TRUE);
}
else if (id == 4 && code == BN_CLICKED)
{
//MessageBox(hWnd, L"choice4", L"CLICK", 0);
MoveWindow(hLayeredWindow, 400, 10, LAYERED_WINDOW_WIDTH, LAYERED_WINDOW_HEIGHT, TRUE);
}
break;
...
至此,一般的悬停效果就做好了,效果如下
完整代码:
#include <windows.h>
#define RGBA(r,g,b,a) (COLORREF)(((BYTE)(r) |((WORD)((BYTE)(g)) << 8)) |(((DWORD)((BYTE)(b)) << 16)) |(((DWORD)((BYTE)(a)) << 24)))
#define LAYERED_WINDOW_WIDTH 120 //分层窗口的宽
#define LAYERED_WINDOW_HEIGHT 150 //分层窗口的高
//声明全局变量用于存放分层窗口的句柄
HWND hLayeredWindow = NULL;
//也要提前声明TurnRGB函数否则会出错
void TurnRGB(COLORREF* srcCols, COLORREF* tagCols, DWORD dwSize, DWORD dwWidth);
//声明CreateLayeredWindow函数便于在WindowProc函数里使用
HWND CreateLayeredWindow(HINSTANCE hInstance, HWND hWnd, int iWidth, int iHeight, int iPosX, int iPosY, COLORREF* colRGBA);
//窗口过程函数
LRESULT CALLBACK WindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_CREATE:
{
LPCREATESTRUCT pc = (LPCREATESTRUCT)lParam;
HWND hButton1 = CreateWindow(L"BUTTON", L"choice1", WS_CHILD | WS_VISIBLE, 10, 10, 120, 150, hWnd, (HMENU)1, pc->hInstance, NULL);
HWND hButton2 = CreateWindow(L"BUTTON", L"choice2", WS_CHILD | WS_VISIBLE, 140, 10, 120, 150, hWnd, (HMENU)2, pc->hInstance, NULL);
HWND hButton3 = CreateWindow(L"BUTTON", L"choice3", WS_CHILD | WS_VISIBLE, 270, 10, 120, 150, hWnd, (HMENU)3, pc->hInstance, NULL);
HWND hButton4 = CreateWindow(L"BUTTON", L"choice4", WS_CHILD | WS_VISIBLE, 400, 10, 120, 150, hWnd, (HMENU)4, pc->hInstance, NULL);
//创建一个红色半透明的像素矩阵
COLORREF* col = new COLORREF[120 * 150];
for (size_t i = 0; i < 120 * 150; i++)
//遍历填充颜色
{
col[i] = RGBA(255, 0, 0, 20);
}
//创建分层窗口
//在(10,10)位置创建能使分层窗口与按钮重合
hLayeredWindow = CreateLayeredWindow(pc->hInstance, hWnd, LAYERED_WINDOW_WIDTH, LAYERED_WINDOW_HEIGHT, 10, 10, col);
if (!hLayeredWindow)
//创建失败就取消创建直接结束窗口
SendMessage(hWnd, WM_DESTROY, 0, 0);
break;
}
case WM_COMMAND:
{
WORD id = LOWORD(wParam);
WORD code = HIWORD(wParam);
HWND hwnd = (HWND)lParam;
if (id == 1 && code == BN_CLICKED)
{
//MessageBox(hWnd, L"choice1", L"CLICK", 0);
//移动分层窗口到当前按钮位置,保持宽高不变,重绘窗口,下同
MoveWindow(hLayeredWindow, 10, 10, LAYERED_WINDOW_WIDTH, LAYERED_WINDOW_HEIGHT, TRUE);
}
else if (id == 2 && code == BN_CLICKED)
{
//MessageBox(hWnd, L"choice2", L"CLICK", 0);
MoveWindow(hLayeredWindow, 140, 10, LAYERED_WINDOW_WIDTH, LAYERED_WINDOW_HEIGHT, TRUE);
}
else if (id == 3 && code == BN_CLICKED)
{
//MessageBox(hWnd, L"choice3", L"CLICK", 0);
MoveWindow(hLayeredWindow, 270, 10, LAYERED_WINDOW_WIDTH, LAYERED_WINDOW_HEIGHT, TRUE);
}
else if (id == 4 && code == BN_CLICKED)
{
//MessageBox(hWnd, L"choice4", L"CLICK", 0);
MoveWindow(hLayeredWindow, 400, 10, LAYERED_WINDOW_WIDTH, LAYERED_WINDOW_HEIGHT, TRUE);
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return 0;
}
//注册窗口函数
void RegWindow(HINSTANCE hInstance, LPCWSTR lpClassName, WNDPROC wndProc, DWORD dwColor)
{
WNDCLASS wnd;
wnd.cbClsExtra = 0;
wnd.cbWndExtra = 0;
wnd.hbrBackground = (HBRUSH)(GetStockObject(dwColor));
wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
wnd.hIcon = LoadCursor(NULL, IDI_APPLICATION);
wnd.lpfnWndProc = wndProc;
wnd.lpszClassName = lpClassName;
wnd.lpszMenuName = NULL;
wnd.style = CS_HREDRAW;
wnd.hInstance = hInstance;
RegisterClass(&wnd);
}
void TurnRGB(COLORREF* srcCols, COLORREF* tagCols, DWORD dwSize, DWORD dwWidth)
{
if (!srcCols || !tagCols || dwSize <= dwWidth || dwSize % dwWidth != 0)
/*
若srcCols或tagCols为NULL,意为没有源数据能翻转或没有存储翻转后数据的存放处
dwSize <= dwWidth 意味着数据的宽度大于数据总大小,怎么可能0w0
dwSize % dwWidth != 0 意味着像素点数据若以dwWidth为一行的个数,则无法拼成一个矩形,无法形成一个完整的窗口
*/
return;
DWORD dwHeight = dwSize / dwWidth;
//计算高度
for (DWORD index = 0; index < dwSize; index++)
//遍历每个像素点并执行翻转操作
{
//col用于存储当前像素点的数据
COLORREF col = srcCols[index];
//翻转RGB值
tagCols[index] = RGBA((BYTE)(col >> 16), (BYTE)(col >> 8), (BYTE)col, (BYTE)(col >> 24));
}
}
//创建分层窗口
HWND CreateLayeredWindow(HINSTANCE hInstance, HWND hWnd, int iWidth, int iHeight, int iPosX, int iPosY, COLORREF* colRGBA)
//将变量名colBGRA改为colRGBA
{
DWORD dwColSize = iWidth * iHeight;
COLORREF* colBGRA = new COLORREF[dwColSize];
TurnRGB(colRGBA, colBGRA, dwColSize, iWidth);
//注册分层窗口
RegWindow(hInstance, L"LayeredWindow", DefWindowProc, BLACK_BRUSH);
//创建分层窗口
HWND hLayeredWindow = CreateWindowEx(WS_EX_LAYERED, L"LayeredWindow", 0,
WS_POPUP | WS_BORDER, 0, 0, iWidth, iHeight, hWnd, NULL, hInstance, NULL);
//将分层窗口设为本窗口的子窗口
SetParent(hLayeredWindow, hWnd);
//更新分层窗口//
//创建分层窗口的DC
HDC hLayeredWindowDC = GetDC(hLayeredWindow);
HDC hCompatibleDC = CreateCompatibleDC(hLayeredWindowDC);
//填充BLENDFUNCTION结构
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
//控制显示位置
POINT ptDst = { iPosX, iPosY };
//控制窗口大小
SIZE sizeWnd = { iWidth, iHeight };
//为0就行
POINT pSrc = { 0, 0 };
//创建一副与当前DC兼容的位图
HBITMAP hCustomBmp = NULL;
hCustomBmp = CreateCompatibleBitmap(hLayeredWindowDC, iWidth, iHeight);
//将hCustomBmp指定到hCompatibleDC中
SelectObject(hCompatibleDC, hCustomBmp);
//填充bmpInfo
BITMAPINFO bmpInfo = { 0 };
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfo.bmiHeader.biWidth = iWidth;
bmpInfo.bmiHeader.biHeight = -iHeight;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biBitCount = 32;
//设置位图中的像素
SetDIBits(NULL, hCustomBmp, 0, iHeight, colBGRA, &bmpInfo, DIB_RGB_COLORS);
//更新分层窗口
UpdateLayeredWindow(hLayeredWindow, hLayeredWindowDC, &ptDst, &sizeWnd, hCompatibleDC, &pSrc, NULL, &blend, ULW_ALPHA);
//释放DC
DeleteDC(hLayeredWindowDC);
return hLayeredWindow;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE PrevhInstance, LPSTR lpCmdLine, int nCmdShow)
{
//注册窗口
RegWindow(hInstance, L"WINDOW", WindowProc, WHITE_BRUSH);
//创建窗口
HWND hWnd = CreateWindow(L"WINDOW", 0,
WS_OVERLAPPEDWINDOW, 100, 100, 800, 500, NULL, NULL, hInstance, NULL);
//显示窗口
ShowWindow(hWnd, SW_SHOW);
//更新窗口
UpdateWindow(hWnd);
//创建BGRA数据
//COLORREF* colBGRA = new COLORREF[100 * 100];
//初始化
//ZeroMemory(colBGRA, 100 * 100 * sizeof(COLORREF));
//填充半透明蓝色
//for (int i = 0; i < 100 * 100; i++)
//{
//虽然用的是RGBA,但实际上是BGRA
// R G B A
//colBGRA[i] = RGBA(233, 0, 0, 128);
//}
//创建分层窗口
//HWND hLW = CreateLayeredWindow(hInstance, hWnd, 100, 100, 0, 0, colBGRA);
//消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return 0;
}
当然,我们还可以添加滑动效果让它变高级一点,如下
(有卡顿是因为gif本身原因)
碍于篇幅有限,要是我写太多肯定没人看,所以剩下的就留到下次再讲吧