9
0

在Windows窗口中使用Mica云母和Acrylic亚克力材质

2024-10-15
2026-08-31
C++

Mica & Acrylic backdrop in Windows apps

为Windows app启用 Mica(云母)、Acrylic(亚克力) 等材质背景

Inroduction

Windows中的Mica云母是一种在窗口背景上显示的“材质”,在未启用时,软件窗口的背景通常是静态、纯色的,本文将教导如何在自己的软件中启用Mica材质,且主要以 C++ 的API调用为主。

像Mica这样材质还有 Acrylic(亚克力)、Aero(Windows Vista/7 时期大量采用),在Windows10中主要以Acrylic为主,Windows11中,主要以Mica为主。

Code

窗口会被DWM进行装饰,通常使用dwmapi对指定窗口操作即可更换样式。Windows11中,不仅包括背景材质,还包括窗口圆角、深色模式等等。

快速开始

在原生Win32应用、Qt(QWidget)应用中快速使用

//MSVC (visual C++) 环境中
#include <windows.h>
#include <dwmapi.h>

#pragma comment(lib, "dwmapi.lib")

/* 前置条件
需要有窗口背景透明操作。若窗口没有透明化,则会遮挡特殊背景材质
*/

void applyMicaEffect(HWND hwnd)
{
    MARGINS margins {-1, -1, -1, -1};
    DWM_SYSTEMBACKDROP_TYPE type = DWM_SYSTEMBACKDROP_TYPE::DWMSBT_MAINWINDOW;
    DwmExtendFrameIntoClientArea(hwnd, &margins);
    DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &type, sizeof(type))
}

// 对Qt应用 (QWidget)
#include <QWidget>
void QtWidget(QWidget* widget)
{
    HWND hwnd = reinterpreter_cast<HWND>(widget->winId());
    applyMicaEffect(hwnd);
}
// 前置条件:透明背景窗口操作

LRESULT NativeWindows(HWND hwnd)
{
    //...

    switch (/*?窗口消息类别?*/) {
        //消息处理循环中 在处理窗口的消息 WM_PAINT 和 WM_ERASEBKGND 时
        case WM_ERASEBKGND: {
            return 1;
        }
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            RECT clientRect;
            GetClientRect(hwnd, &clientRect);
            HBRUSH brush = CreateSolidBrush(RGB(0, 0, 0));
            FillRect(hdc, &clientRect, brush);
            DeleteObject(brush);
            EndPaint(hwnd, &ps);

            break;
        }
    }

    // ...
}

void QtWidget(QWidget* widget)
{
    widget->setAttribute(Qt::WA_TranslucentBackground);
}

API

核心函数:DwmSetWindowAttribute 微软官方文档

HRESULT DwmSetWindowAttribute(
    HWND targetWindowHandle,
    DWORD attributeType,
    LPCVOID attributeValuePointer,
    DWORD attributeValueSize
);

in header dwmapi.h
函数参数解释

Value

Description

targetWindowHandle

目标窗口句柄

attributeType

属性类型(从枚举DWMWINDOWATTRIBUTE中选择)

attributeValuePointer

目标属性值(指针)

attributeValueSize

属性值的类型大小(sizeof

返回值

是否成功(S_OK为成功)

HWND句柄,原生控制句柄,通常框架也好获得;属性类型是需要修改的属性,在枚举中DWMWA_SYSTEMBACKDROP_TYPE就是背景类型属性;背景属性值也是在枚举DWM_SYSTEMBACKDROP_TYPE 中为DWMSBT_AUTO, DWMSBT_NONE, DWMSBT_MAINWINDOW, DWMSBT_TRANSIENTWINDOW, DWMSBT_TABBEDWINDOW。在Windows11中,一般有三个直接值,分别为 MainWindow:Mica、TransientWindow:Acrylic、TabbedWindow:MicaAlt(特殊云母)。其中剩余的两个枚举值中NONE为关闭,AUTO为自动控制,通常会因ExtendFrameIntoClientArea使窗口变为NONE。

typedef enum DWM_SYSTEMBACKDROP_TYPE {
  DWMSBT_AUTO, // 无样式
  DWMSBT_NONE, // 自动样式(默认值)
  DWMSBT_MAINWINDOW,      // 主窗口:Mica(云母)
  DWMSBT_TRANSIENTWINDOW, // 透明窗口:Acrylic(亚克力)(后续可能改变)
  DWMSBT_TABBEDWINDOW     // 选项卡窗口:MicaAlt(云母变体)(后续可能改变)
};

void applyBackdrop(HWND hwnd, DWM_SYSTEMBACKDROP_TYPE type)
{
    DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &type, sizeof(type))
}

取值:DwmGetWindowAttribute (指针获取) - 微软官方文档

必要设置

直接使用该API并不能直接看到效果,因为两点:1. 背景类型只直接作用于边框,使用DwmExtendFrameIntoClientArea控制边框实际位置至整个窗口即可;2. 窗口通常都会在表面绘制一层纯色背景,而这就遮挡了实际背景。

1. 将背景样式作用于整个窗口

API DwmExtendFrameIntoClientArea in header dwmapi.h
将margins设为负值,整个窗口都会成为边框区,从而整个窗口都能显示样式背景。

void extendFrameIntoClientArea(HWND hwnd)
{
   MARGINS margins{ -1, -1, -1, -1 };
   DwmExtendFrameIntoClientArea(hWnd, &margins);
}
margin为50时
margin为负时 - 扩展至整个窗口

如果margins成员没有负数,则按实际值显示,若其中有一个为负则全窗口都是背景样式区域

2. 取消/移除 自带纯色背景

移除自带纯色背景(Windows11通常在浅色模式下为白色)是必要的,否则会在设置的材质上在次绘制一层,导致等同于无效果。对于原生Win32窗口,处理WM_ERASEBKGNDWM_PAINT两个消息即可

LRESULT CALLBACK windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        //case WM_COMMAND等 其他消息
        case WM_ERASEBKGND:
            return 1;
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            RECT clientRect;
            GetClientRect(hWnd, &clientRect);
            HBRUSH brush = CreateSolidBrush(RGB(0, 0, 0));
            FillRect(hdc, &clientRect, brush);
            DeleteObject(brush);
            EndPaint(hWnd, &ps);
            break;
        }
        //case WM_DESTROY等其他消息 
    }

    return 0;
}

若是使用一些GUI框架,如Qt 通常都有较为方便的办法

void EraseQWidgetBackgroud(QWidget* w)
{
    w->setAttribute(Qt::WA_TranslucentBackground);
}

Final

总结为三个点

  1. 透明背景窗口 - 放置材质被覆盖看不见

  2. 扩展边框 - 使材质能应用在整个窗口

  3. 应用材质 - 枚举中选择,可选Mica云母的同时还可以选择主动关闭、使用亚克力等材质

需要注意的是本文主要在Windows11环境中使用,Windows10等API会有些差别,比如根本没有Mica云母材质。

Dwm作为Windows的窗口管理器,可玩性很高,更多可以查阅官方文档 包括DwmSetWindowAttribute 的可选属性


References in Microsoft Learn

DwmExtendFrameIntoClientArea 函数 - Win32 apps | Microsoft Learn
DwmSetWindowAttribute 函数 - Win32 apps | Microsoft Learn

评论