DVLUP
March 13, 2023

Mica or Acrylic with WinUI and .NET MAUI

Posted on March 13, 2023  •  4 minutes  • 678 words

When using .NET MAUI, we have some great capabilities to affect the way the native platform works. We can take advantage of this part of lifecycle to check if:

Here’s what the end result looks like in your MauiProgram.cs code, nice and clean thanks to using lifecycle builder extension methods.

builder.ConfigureLifecycleEvents(events =>
{
#if WINDOWS10\_0\_17763\_0\_OR\_GREATER
    
    events.AddWindows(wndLifeCycleBuilder =>
    {
        wndLifeCycleBuilder.OnWindowCreated(window =>
        {
            // \*\*\* For Mica or Acrylic support \*\* //
            window.TryMicaOrAcrylic();
        });
    });
#endif
});

To set this up, open the folder for the WinUI project (Platforms/Windows). You will see the standard Windows-specific files in there, like App.xaml. Add two new classes; WindowsHelpers.cs and WindowsSystemDispatcherQueueHelpers.cs., it should look like this:

Let’s first work on the dispatcher queue helper class, because the window helper class depends on it. Use the following code and save the file (don’t forget to fix the namespace so that it matches your project):

using System.Runtime.InteropServices;
using Windows.System; // For DllImport
using WinRT; // required to support Window.As<ICompositionSupportsSystemBackdrop>()

namespace YourProjectName.Platforms.Windows;

public 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 (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);
        }
    }
}

Next, open the window helpers class and use this code:

using Microsoft.UI.Composition;
using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Xaml;
using WinRT;

namespace YourProjectName.Platforms.Windows
{
    public static class WindowHelpers
    {
        public static void TryMicaOrAcrylic(this Microsoft.UI.Xaml.Window window)
        {
            var dispatcherQueueHelper = new WindowsSystemDispatcherQueueHelper(); // in Platforms.Windows folder
            dispatcherQueueHelper.EnsureWindowsSystemDispatcherQueueController();

            // Hooking up the policy object
            var configurationSource = new SystemBackdropConfiguration();
            configurationSource.IsInputActive = true;

            switch (((FrameworkElement)window.Content).ActualTheme)
            {
                case ElementTheme.Dark:
                    configurationSource.Theme = SystemBackdropTheme.Dark; 
                    break;
                case ElementTheme.Light:
                    configurationSource.Theme = SystemBackdropTheme.Light;
                    break;
                case ElementTheme.Default:
                    configurationSource.Theme = SystemBackdropTheme.Default;
                    break;
            }
            
            // Let's try Mica first
            if (MicaController.IsSupported()) 
            {
                var micaController = new MicaController();
                micaController.AddSystemBackdropTarget(window.As<ICompositionSupportsSystemBackdrop>());
                micaController.SetSystemBackdropConfiguration(configurationSource);
                
                window.Activated += (object sender, WindowActivatedEventArgs args) =>
                {
                    if (args.WindowActivationState is WindowActivationState.CodeActivated or WindowActivationState.PointerActivated)
                    {
                        // Handle situation where a window is activated and placed on top of other active windows.
                        if (micaController == null)
                        {
                            micaController = new MicaController();
                            micaController.AddSystemBackdropTarget(window.As<ICompositionSupportsSystemBackdrop>());
                            micaController.SetSystemBackdropConfiguration(configurationSource);
                        }

                        if (configurationSource != null)
                            configurationSource.IsInputActive = args.WindowActivationState != WindowActivationState.Deactivated;
                    }
                };

                window.Closed += (object sender, WindowEventArgs args) =>
                {
                    if (micaController != null)
                    {
                        micaController.Dispose();
                        micaController = null;
                    }

                    configurationSource = null;
                };
            }
            // If no Mica, maybe we can use Acrylic instead
            else if (DesktopAcrylicController.IsSupported())
            {
                var acrylicController = new DesktopAcrylicController();
                acrylicController.AddSystemBackdropTarget(window.As<ICompositionSupportsSystemBackdrop>());
                acrylicController.SetSystemBackdropConfiguration(configurationSource);

                window.Activated += (object sender, WindowActivatedEventArgs args) =>
                {
                    if (args.WindowActivationState is WindowActivationState.CodeActivated or WindowActivationState.PointerActivated)
                    {
                        // Handle situation where a window is activated and placed on top of other active windows.
                        if (acrylicController == null)
                        {
                            acrylicController = new DesktopAcrylicController();
                            acrylicController.AddSystemBackdropTarget(window.As<ICompositionSupportsSystemBackdrop>());
                            acrylicController.SetSystemBackdropConfiguration(configurationSource);
                        }
                    }

                    if (configurationSource != null)
                        configurationSource.IsInputActive = args.WindowActivationState != WindowActivationState.Deactivated;
                };

                window.Closed += (object sender, WindowEventArgs args) =>
                {
                    if (acrylicController != null)
                    {
                        acrylicController.Dispose();
                        acrylicController = null;
                    }

                    configurationSource = null;
                };
            }
        }
    }
}

With both of them done, you can now use it in your MauiProgram.cs CreateMauiApp method.

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            })
            .RegisterLifecycleEvents();

        builder.ConfigureLifecycleEvents(events =>
        {
#if WINDOWS10\_0\_17763\_0\_OR\_GREATER

            events.AddWindows(wndLifeCycleBuilder =>
            {
                wndLifeCycleBuilder.OnWindowCreated(window =>
                {
                    // \*\*\* For Mica or Acrylic support \*\* //
                    window.TryMicaOrAcrylic();
                });
            });
        });
#endif
        return builder.Build();
    }
}

Your application now has automatic support for Mica or Acrylic!

Tip: If you don’t see it at first, make sure you do not have another UI element covering the window’s backdrop. It’s not uncommon that a default style will set the background color of a page or root element. This will have a higher Z index and cover the window’s natural backdrop.

Follow me on your preferred social network =>