在 Windows 11 的 Win32 桌面应用中应用 Mica

Mica 是一种不透明的材质,可以将用户的主题和桌面壁纸结合起来,从而创建高度个性化的外观。 随着用户在屏幕上移动窗口,Mica 材质会动态调整,以便使用应用程序下的壁纸创建丰富的可视化效果。 此外,如果应用处于非活动状态,该材质可以通过回退到中性色来帮助用户专注于当前任务。

本文介绍如何使用 Mica 并将它作为 Win32 应用的基本层,用于在标题栏区域中确定应用程序的优先级和可见性。 有关使用 Mica 的应用分层的详细信息,请参阅 Mica 材料

先决条件

要将 Mica 应用于 Windows 11 的 Win32 应用,需要使用 Windows 应用 SDK。 需要准备好以下各项:

如何在 Win32 应用中使用 Mica

要在应用中使用 Mica,需要使用 MicaController 类。 此类管理系统背景材料的呈现以及 Mica 材料系统策略的处理。

默认情况下,MicaController 会响应系统浅色和深色主题。 若要替代此行为,可以将以下属性传递给 MicaController:

提示

本节中的代码取自 GitHub 上的 Windows 应用 SDK Win32 Mica 示例。 有关完整代码,请参阅 GitHub 存储库。 这些示例使用 C++/WinRT

要启用 Mica,需要引用 Windows 应用 SDK、CompositorDispatcherQueue

此示例演示如何执行以下操作来设置未打包的应用:

WinMain.cpp

int __stdcall WinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE,  _In_ PSTR, _In_ int)
{
    // Initialize WinRt Instance
    winrt::init_apartment();

    // Enable referencing the WindowsAppSDK from an unpackaged app.
    Utilities::WindowsAppSDKBootstrapperContext sdkContext;

    // Register Window class before making the window.
    MicaWindow::RegisterWindowClass();

    // Mica requires a compositor, which also requires a dispatcher queue.
    auto controller = Utilities::CreateDispatcherQueueControllerForCurrentThread();
    auto compositor = winrt::Compositor();

    // Create your window...
    ...
}

MicaWindow.cpp

void MicaWindow::RegisterWindowClass()
{
    auto instance = winrt::check_pointer(GetModuleHandleW(nullptr));
    WNDCLASSEX wcex = { sizeof(wcex) };
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = instance;
    wcex.hIcon = LoadIconW(instance, IDI_APPLICATION);
    wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszClassName = ClassName.c_str();
    wcex.hIconSm = LoadIconW(wcex.hInstance, IDI_APPLICATION);
    winrt::check_bool(RegisterClassExW(&wcex)); // check if the window class was registered successfully
}

winrt::init_apartment 方法默认为多线程。 如果应用需要单线程(如 WebView2 示例),则可以轻松设置类型。

winrt::init_apartment(winrt::apartment_type::single_threaded);

现在可使用 CreateWindowEx() 函数创建窗口。 然后,必须创建一个窗口目标并将其设置为根,以指定要应用 Mica 的层。 最后,声明窗口和目标支持 Mica。

Win32 Mica 示例 创建 DesktopWindowMicaWindow 类来完成这项工作。 这些类包括 ClassNamewindowTitlem_targetm_micaControllerm_isMicaSupported

WinMain.cpp

// Mica window is inherited from the MicaWindow class, which is an extension of the DesktopWindow Class.
// Here, we initialize the main window and set the title.
   auto window = MicaWindow(compositor, L"Hello, Mica!");

MicaWindow.cpp

// Create the main window and enable Mica.
MicaWindow::MicaWindow(const winrt::Compositor& compositor, const std::wstring& windowTitle)
{
    auto instance = winrt::check_pointer(GetModuleHandleW(nullptr));
    WINRT_ASSERT(!m_window); // check that window is not initialized
    WINRT_VERIFY(
        // Window Properties
        CreateWindowExW(
            WS_EX_COMPOSITED,
            ClassName.c_str(), // declared in MicaWindow.h and defined above
            windowTitle.c_str(),
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT, 
            800, 600, 
            nullptr, 
            nullptr, 
            instance, 
            this
        ));

    // Check that the window was created successfully.
    WINRT_ASSERT(m_window);

    ShowWindow(m_window, SW_SHOWDEFAULT);
    UpdateWindow(m_window);

    // The Mica controller needs to set a target with a root to recognize the visual base layer.
    m_target = CreateWindowTarget(compositor);

    // Need to set a root before we can enable Mica.
    m_target.Root(compositor.CreateContainerVisual());

    m_micaController = winrt::MicaController();
    m_isMicaSupported = m_micaController.SetTarget(winrt::Microsoft::UI::WindowId{ reinterpret_cast<uint64_t>(m_window) }, m_target);
}

如何在 Win32 WebView2 应用中使用 Mica

对于大多数 Win32 应用程序而言,应用 Mica 的基本原则都是一致。 WebView2 的过程遵循前面显示的 Win32 说明中的基本步骤。 但是,在这种情况下,需要从 WinRT 的 init_apartment 功能中指定一个单线程进程。

提示

本节中的代码取自 GitHub 上的 Windows 应用 WebView2 Mica 示例。 有关完整代码,请参阅 GitHub 存储库。

若要开始,请设置所需的单元、控制器、合成器、目标和根。 默认情况下,WinRT init_apartment 函数是多线程的,但 WebView2 本质上是单线程的。 要将 init_apartment 设为单线程,请传递 winrt::apartment_type::single_threaded 参数。 在 Mica WebView2 示例中,我们为以下代码中引用的 Web 视图函数创建了单独的类,以简化语法。

Main.cpp

int __stdcall WinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PSTR, _In_ int)
{
    winrt::init_apartment(winrt::apartment_type::single_threaded);
    // Enable referencing the WindowsAppSDK from an unpackaged app.
    // Remember to have a matching Microsoft.WindowsAppRuntime.Redist installed.
    // https://learn.microsoft.com/windows/apps/windows-app-sdk/deploy-unpackaged-apps
    Utilities::WindowsAppSDKBootstrapperContext sdkContext;
    CompositionWindow::RegisterWindowClass();
    // A dispatcher queue is required to be able to create a compositor.
    auto controller = Utilities::CreateDispatcherQueueControllerForCurrentThread();
    auto compositor = winrt::Compositor();
    auto window = WebView2Window(compositor, L"Hello, WebView2!");

    ...
}

有关 WebView2Window 类及其与 Mica 的集成的完整演示,请参阅 GitHub 上的 Windows 应用 SDK WebView2 Mica 示例。 请注意 CompositionWindowWebView2Window 类处理消息、初始化 Web 视图环境,并在窗口关闭后删除窗口控制器的方式。

材料分层和提升Windows 应用 SDK Mica 示例