Win32 使用 SetLayeredWindowAttributes 制作透明窗体
函数原型
SetLayeredWindowAttributes 用来设置分层窗口的透明度
BOOL SetLayeredWindowAttributes(
	HWND hwnd, // 指定分层窗口句柄
	COLORREF crKey, // 指定需要透明的背景颜色值,可用RGB()宏
	BYTE bAlpha, // 设置透明度,0表示完全透明,255表示不透明
	DWORD dwFlags // 透明方式
);
dwFlags
dwFlags参数用来指明窗口的透明方式,可取以下值
- LWA_ALPHAcrKey参数无效,bAlpha参数有效
- LWA_COLORKEY窗体中的所有颜色为crKey的地方将变为透明,bAlpha参数无效。其常量值为1
- LWA_ALPHA | LWA_COLORKEYcrKey的地方将变为全透明,而其它地方根据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)