Share via


Windows 11용 데스크톱 앱에서 Mica 또는 Acrylic 자료 적용

Windows 11의 자료는 실제 인공물을 닮은 UX 표면에 적용된 시각 효과입니다. 마이카 및 아크릴과 같은 폐색 재료는 대화형 UI 컨트롤 아래에서 기본 레이어로 사용됩니다.

Mica는 사용자의 테마와 데스크톱 배경 화면을 통합하여 고도로 개인 설정된 모양을 만드는 불투명 재질입니다. Mica는 배경 배경 화면을 한 번만 캡처하여 시각화를 만들 수 있으므로 특히 제목 표시줄 영역에서 앱의 기본 계층에 사용하는 것이 좋습니다.

아크릴은 반투명 유리의 효과를 복제하는 반투명 재질입니다. 이건 플라이아웃 및 상황에 맞는 메뉴와 같은 일시적으로 조명이 없는 화면에서만 사용됩니다.

이 문서에서는 Mica 또는 Acrylic을 Windows 앱 SDK/WinUI 3 XAML 앱의 기본 계층으로 적용하는 방법을 설명합니다.

참고 항목

배경 자료를 사용하는 방법

WinUI 3 갤러리 앱에는 대부분의 WinUI 3 컨트롤, 특징, 기능의 대화형 예제가 포함되어 있습니다. 해당 Microsoft Store에서 앱을 다운로드하거나 GitHub에서 소스 코드를 가져오세요.

앱에 Mica 또는 Acrylic 재질을 적용하려면 Window.SystemBackdrop 속성을 XAML SystemBackdrop (일반적으로 기본 제공 배경 중 하나인 MicaBackdrop 또는 DesktopAcrylicBackdrop)으로 설정합니다.

다음 예제에서는 XAML 및 코드에서 이 작업을 수행하는 방법을 보여 줍니다.

Mica

<Window
    ... >

    <Window.SystemBackdrop>
        <MicaBackdrop Kind="BaseAlt"/>
    </Window.SystemBackdrop>

</Window>
public MainWindow()
{
    this.InitializeComponent();

    SystemBackdrop = new MicaBackdrop() 
                        { Kind = MicaKind.BaseAlt };
}

Acrylic

<Window
    ... >

    <Window.SystemBackdrop>
        <DesktopAcrylicBackdrop/>
    </Window.SystemBackdrop>

</Window>
public MainWindow()
{
    this.InitializeComponent();

    SystemBackdrop = new DesktopAcrylicBackdrop();
}

시스템 배경 컨트롤러를 사용하는 방법

참고 항목

Windows 앱 SDK 1.3부터 이전 섹션에서 설명한 대로 속성을 XAML Window.SystemBackdrop 로 설정 SystemBackdrop 하여 자료를 적용할 수 있습니다. 이는 재질을 적용하는 데 권장되는 방법입니다.

이 글의 나머지 부분은 Composition MicaControllerDesktopAcrylicController API의 사용 방법을 보여줍니다.

앱에서 배경 자료를 사용하려면 ISystemBackdropController 인터페이스(MicaController 또는 DesktopAcrylicController)를 구현하는 컨트롤러 중 하나를 사용할 수 있습니다. 이러한 클래스는 시스템 배경 자료의 렌더링과 자료에 대한 시스템 정책 처리를 모두 관리합니다.

Mica를 배경 자료로 사용하려면 MicaController 개체를 만듭니다. Acrylic을 사용하려면 DesktopAcrylicController 개체를 만듭니다. 설정 및 지원 코드는 시스템 배경 자료의 각 유형에 대해 동일합니다.

이 코드는 MicaController을 만드는 방법을 보여줍니다.

MicaController m_backdropController;

bool TrySetSystemBackdrop()
{
    if (MicaController.IsSupported())
    {
        ...
        m_backdropController = new MicaController();
        ...
    }
}
// namespace MUCSB = Microsoft::UI::Composition::SystemBackdrops;

winrt::MUCSB::MicaController m_backdropController{ nullptr };

void SetBackground()
{
    if (winrt::MUCSB::MicaController::IsSupported())
    {
        ...
        m_backdropController = winrt::MUCSB::MicaController();
        ...
    }
}

Mica의 Mica Alt 변형을 사용하려면 개체를 MicaController 만들고 Kind 속성을 MicaKind.BaseAlt로 설정합니다.

MicaController m_backdropController;

bool TrySetSystemBackdrop()
{
    if (MicaController.IsSupported())
    {
        ...
        m_backdropController = new MicaController()
        {
            Kind = MicaKind.BaseAlt
        };
        ...
    }
}
// namespace MUCSB = Microsoft::UI::Composition::SystemBackdrops;

winrt::MUCSB::MicaController m_backdropController{ nullptr };

void SetBackground()
{
    if (winrt::MUCSB::MicaController::IsSupported())
    {
        ...
        m_backdropController = winrt::MUCSB::MicaController();
        m_backdropController.Kind(winrt::MUCSB::MicaKind::BaseAlt);
        ...
    }
}

이 코드는 DesktopAcrylicController을 만드는 방법을 보여줍니다.

DesktopAcrylicController m_backdropController;

bool TrySetSystemBackdrop()
{
    if (DesktopAcrylicController.IsSupported())
    {
        ...
        m_backdropController = new DesktopAcrylicController();
        ...
    }
}
// namespace MUCSB = Microsoft::UI::Composition::SystemBackdrops;

winrt::MUCSB::DesktopAcrylicController m_backdropController{ nullptr };

void SetBackground()
{
    if (winrt::MUCSB::DesktopAcrylicController::IsSupported())
    {
        ...
        m_backdropController = winrt::MUCSB::DesktopAcrylicController();
        ...
    }
}

controller는 기본적으로 시스템 밝은 테마 및 어두운 테마에 반응합니다. 이 동작을 재정의하려면 컨트롤러에서 다음 속성을 설정할 수 있습니다:

앱에서 배경 자료를 사용하려면 다음 항목이 필요합니다.

예: Windows AppSDK/WinUI 3 앱에서 Mica 사용

이 예제에서는 XAML 앱에서 Mica 배경 자료를 설정하는 방법을 보여줍니다.

또한 GitHub에서 다음 예제 프로젝트를 참조하세요.

C#: WinUI3 갤러리의 SampleSystemBackdropsWindow입니다.

C++/WinRT: Windows 앱 SDK Mica 샘플입니다.

using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Xaml;
using System.Runtime.InteropServices; // For DllImport
using WinRT; // required to support Window.As<ICompositionSupportsSystemBackdrop>()

public sealed partial class MainWindow : Window
{
    WindowsSystemDispatcherQueueHelper m_wsdqHelper; // See below for implementation.
    MicaController m_backdropController;
    SystemBackdropConfiguration m_configurationSource;

    public MainWindow()
    {
        this.InitializeComponent();

        TrySetSystemBackdrop();
    }

    bool TrySetSystemBackdrop()
    {
        if (Microsoft.UI.Composition.SystemBackdrops.MicaController.IsSupported())
        {
            m_wsdqHelper = new WindowsSystemDispatcherQueueHelper();
            m_wsdqHelper.EnsureWindowsSystemDispatcherQueueController();

            // Create the policy object.
            m_configurationSource = new SystemBackdropConfiguration();
            this.Activated += Window_Activated;
            this.Closed += Window_Closed;
            ((FrameworkElement)this.Content).ActualThemeChanged += Window_ThemeChanged;

            // Initial configuration state.
            m_configurationSource.IsInputActive = true;
            SetConfigurationSourceTheme();

            m_backdropController = new Microsoft.UI.Composition.SystemBackdrops.MicaController();

            // Enable the system backdrop.
            // Note: Be sure to have "using WinRT;" to support the Window.As<...>() call.
            m_backdropController.AddSystemBackdropTarget(this.As<Microsoft.UI.Composition.ICompositionSupportsSystemBackdrop>());
            m_backdropController.SetSystemBackdropConfiguration(m_configurationSource);
            return true; // succeeded
        }

        return false; // Mica is not supported on this system
    }

    private void Window_Activated(object sender, WindowActivatedEventArgs args)
        {
            m_configurationSource.IsInputActive = args.WindowActivationState != WindowActivationState.Deactivated;
        }

    private void Window_Closed(object sender, WindowEventArgs args)
        {
            // Make sure any Mica/Acrylic controller is disposed
            // so it doesn't try to use this closed window.
            if (m_backdropController != null)
            {
                m_backdropController.Dispose();
                m_backdropController = null;
            }
            this.Activated -= Window_Activated;
            m_configurationSource = null;
        }

    private void Window_ThemeChanged(FrameworkElement sender, object args)
        {
            if (m_configurationSource != null)
            {
                SetConfigurationSourceTheme();
            }
        }

    private void SetConfigurationSourceTheme()
        {
            switch (((FrameworkElement)this.Content).ActualTheme)
            {
                case ElementTheme.Dark: m_configurationSource.Theme = Microsoft.UI.Composition.SystemBackdrops.SystemBackdropTheme.Dark; break;
                case ElementTheme.Light: m_configurationSource.Theme = Microsoft.UI.Composition.SystemBackdrops.SystemBackdropTheme.Light; break;
                case ElementTheme.Default: m_configurationSource.Theme = Microsoft.UI.Composition.SystemBackdrops.SystemBackdropTheme.Default; break;
            }
        }
}

class WindowsSystemDispatcherQueueHelper
{
    [StructLayout(LayoutKind.Sequential)]
    struct DispatcherQueueOptions
    {
        internal int dwSize;
        internal int threadType;
        internal int apartmentType;
    }

    [DllImport("CoreMessaging.dll")]
    private static extern int CreateDispatcherQueueController([In] DispatcherQueueOptions options, [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object dispatcherQueueController);

    object m_dispatcherQueueController = null;
    public void EnsureWindowsSystemDispatcherQueueController()
    {
        if (Windows.System.DispatcherQueue.GetForCurrentThread() != null)
        {
            // one already exists, so we'll just use it.
            return;
        }

        if (m_dispatcherQueueController == null)
        {
            DispatcherQueueOptions options;
            options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions));
            options.threadType = 2;    // DQTYPE_THREAD_CURRENT
            options.apartmentType = 2; // DQTAT_COM_STA

            CreateDispatcherQueueController(options, ref m_dispatcherQueueController);
        }
    }
}
// pch.h
...
#include <winrt/Microsoft.UI.Composition.SystemBackdrops.h>
#include <winrt/Windows.System.h>
#include <dispatcherqueue.h>

// MainWindow.xaml.h
...
namespace winrt
{
    namespace MUC = Microsoft::UI::Composition;
    namespace MUCSB = Microsoft::UI::Composition::SystemBackdrops;
    namespace MUX = Microsoft::UI::Xaml;
    namespace WS = Windows::System;
}
...
struct MainWindow : MainWindowT<MainWindow>
{
    winrt::MUCSB::SystemBackdropConfiguration m_configuration{ nullptr };
    winrt::MUCSB::MicaController m_backdropController{ nullptr };
    winrt::MUX::Window::Activated_revoker m_activatedRevoker;
    winrt::MUX::Window::Closed_revoker m_closedRevoker;
    winrt::MUX::FrameworkElement::ActualThemeChanged_revoker m_themeChangedRevoker;
    winrt::MUX::FrameworkElement m_rootElement{ nullptr };
    winrt::WS::DispatcherQueueController m_dispatcherQueueController{ nullptr };

    MainWindow::MainWindow()
    {
        InitializeComponent();

        SetBackground();

        m_closedRevoker = this->Closed(winrt::auto_revoke, [&](auto&&, auto&&)
        {
            if (nullptr != m_backdropController)
            {
                m_backdropController.Close();
                m_backdropController = nullptr;
            }

            if (nullptr != m_dispatcherQueueController)
            {
                m_dispatcherQueueController.ShutdownQueueAsync();
                m_dispatcherQueueController = nullptr;
            }
        });
    }

    void SetBackground()
    {
        if (winrt::MUCSB::MicaController::IsSupported())
        {
            // We ensure that there is a Windows.System.DispatcherQueue on the current thread.
            // Always check if one already exists before attempting to create a new one.
            if (nullptr == winrt::WS::DispatcherQueue::GetForCurrentThread() &&
                nullptr == m_dispatcherQueueController)
            {
                m_dispatcherQueueController = CreateSystemDispatcherQueueController();
            }

            // Setup the SystemBackdropConfiguration object.
            SetupSystemBackdropConfiguration();

            // Setup Mica on the current Window.
            m_backdropController = winrt::MUCSB::MicaController();
            m_backdropController.SetSystemBackdropConfiguration(m_configuration);
            m_backdropController.AddSystemBackdropTarget(
                this->m_inner.as<winrt::MUC::ICompositionSupportsSystemBackdrop>());
        }
        else
        {
            // The backdrop material is not supported.
        }
    }

    winrt::WS::DispatcherQueueController CreateSystemDispatcherQueueController()
    {
        DispatcherQueueOptions options
        {
            sizeof(DispatcherQueueOptions),
            DQTYPE_THREAD_CURRENT,
            DQTAT_COM_NONE
        };

        ::ABI::Windows::System::IDispatcherQueueController* ptr{ nullptr };
        winrt::check_hresult(CreateDispatcherQueueController(options, &ptr));
        return { ptr, take_ownership_from_abi };
    }

    void SetupSystemBackdropConfiguration()
    {
        m_configuration = winrt::MUCSB::SystemBackdropConfiguration();

        // Activation state.
        m_activatedRevoker = this->Activated(winrt::auto_revoke,
            [&](auto&&, MUX::WindowActivatedEventArgs const& args)
            {
                m_configuration.IsInputActive(
                    winrt::MUX::WindowActivationState::Deactivated != args.WindowActivationState());
            });

        // Initial state.
        m_configuration.IsInputActive(true);

        // Application theme.
        m_rootElement = this->Content().try_as<winrt::MUX::FrameworkElement>();
        if (nullptr != m_rootElement)
        {
            m_themeChangedRevoker = m_rootElement.ActualThemeChanged(winrt::auto_revoke,
                [&](auto&&, auto&&)
                {
                    m_configuration.Theme(
                        ConvertToSystemBackdropTheme(m_rootElement.ActualTheme()));
                });

            // Initial state.
            m_configuration.Theme(
                ConvertToSystemBackdropTheme(m_rootElement.ActualTheme()));
        }
    }

    winrt::MUCSB::SystemBackdropTheme ConvertToSystemBackdropTheme(
        winrt::MUX::ElementTheme const& theme)
    {
        switch (theme)
        {
        case winrt::MUX::ElementTheme::Dark:
            return winrt::MUCSB::SystemBackdropTheme::Dark;
        case winrt::MUX::ElementTheme::Light:
            return winrt::MUCSB::SystemBackdropTheme::Light;
        default:
            return winrt::MUCSB::SystemBackdropTheme::Default;
        }
    }
    ...
};
...