-
-
Save LanceMcCarthy/4954ab92ca44c19eb4316d9d683efd50 to your computer and use it in GitHub Desktop.
| // ************************************************************************ // | |
| // This implementation is inspired from the official WinUI3 backdrops examples | |
| // https://github.com/microsoft/WinUI-Gallery/blob/winui3/XamlControlsGallery/ControlPagesSampleCode/SystemBackdrops/SystemBackdropsSample2.txt | |
| // *********************************************************************** // | |
| using Microsoft.Maui.LifecycleEvents; | |
| #if WINDOWS10_0_17763_0_OR_GREATER | |
| // The namespace of where the WindowsHelpers class resides | |
| // In older versions of .NET MAUI, this was | |
| //using YOUR_APP.Platforms.Windows | |
| // In newer versions of .NET MAUI, it is now | |
| using YOUR_APP.WinUI; | |
| #endif | |
| namespace YOUR_APP.Maui | |
| { | |
| 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"); | |
| }); | |
| // **** THIS SECTION IS WHAT IS RELEVANT FOR YOU ************ // | |
| builder.ConfigureLifecycleEvents(events => | |
| { | |
| #if WINDOWS10_0_17763_0_OR_GREATER | |
| events.AddWindows(wndLifeCycleBuilder => | |
| { | |
| wndLifeCycleBuilder.OnWindowCreated(window => | |
| { | |
| window.TryMicaOrAcrylic(); // requires 'using YOUR_APP.WinUI;' | |
| }); | |
| }); | |
| #endif | |
| }); | |
| // ************* END OF RELEVANT SECTION *********** // | |
| return builder.Build(); | |
| } | |
| } | |
| } |
| // **************************************************** // | |
| // Place this class file in your Platforms/Windows folder | |
| // **************************************************** // | |
| using Microsoft.UI.Composition; | |
| using Microsoft.UI.Composition.SystemBackdrops; | |
| using Microsoft.UI.Xaml; | |
| using WinRT; | |
| namespace YOUR_APP.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>()); // Requires 'using WinRT;' | |
| 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>()); // Requires 'using WinRT;' | |
| 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; | |
| }; | |
| } | |
| } | |
| } | |
| } |
| // **************************************************** // | |
| // Place this class file in your Platforms/Windows folder | |
| // **************************************************** // | |
| using System.Runtime.InteropServices; | |
| using Windows.System; // Required for DllImport attribute and DispatcherQueue | |
| namespace YOUR_APP.Maui.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); | |
| } | |
| } | |
| } |
Just watched this vid, and it seems to point in the right direction for overriding or defining custom behavior on default controls or custom ones.
https://www.youtube.com/live/tOCh0d4PpOw?si=H4LClSKD37epxtzb
This has been a long time coming. To all who are still trying to solve this issue... it's done.
full sample: https://github.com/kolkonos/MicaBackdropMAUI
As Lance mentioned, add the mica effect during the lifecycle event. I prefer the MikaKind.BaseAlt as it is more pronounced (more vivid color).
THEN in the AppShell.xaml.cs in the class constructor:
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
this.Loaded += (_, _) => {
#if WINDOWS
var shellView = `Shell.Current?.Handler?.PlatformView` as ShellView;
var navigationView = shellView?.Content as MauiNavigationView;
var contentGrid = navigationView?.GetType()
.GetProperty("ContentGrid", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?
.GetValue(navigationView) as Microsoft.UI.Xaml.Controls.Grid;
contentGrid!.Background.Opacity = 0;
#endif
};
}
It's crucial that you set the opacity to 0 rather than setting the Background to null, as this would cause the framework to fallback to the default.
@ajsuydam


@kolkonos BOOM! Looks great.
@ajsuydam While I was still working on that project, I found some references to creating custom renderes for components. In those articles, which I don’t have handy, they mentioned that you can override the rendering of a built in component by inheriting from it. However I have since had to move off that project, and never tested the suggested approach.
perhaps you can dive into that rabbit hole and keep us posted.