-
-
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); | |
} | |
} | |
} |
That's good! I'm trying now...
I haven't been able to test it in most situations, so I'd be happy to hear how it goes.
Here are the docs => https://learn.microsoft.com/en-us/windows/apps/design/style/mica#use-mica-with-the-windows-app-sdk
It seems cannot put wasdk api on maui...
Oh sorry, I'm forgot to update the nuget package, now works fine.
@LanceMcCarthy Question for you,
Does this work in shell apps? Ive tried your exact code (and a bunch of other things, i even tried going into the inheritance hierarchy) but nothing seems to work. It feels like .net Maui keeps overriding the default platform behavior.
@ajsuydam There is a ShellPage background color that is overlaying the window background. I forget the exact trick to change it, but I want to say just make sure the Shell page's background color is Transparent.
Yeah, I wasn't talking about the shell, but as you pointed out, the background of the flyout header and content. Setting those to have a background of transparent has no effect. Matter of fact, the screen shot I attached already has those properties set to transparent. I believe the internals of how those components are rendered (much like the label on a check box in Windows) is overriding the explicit background property. When inspecting the rendered UI, there are many pieces that are added by the platform, and which I cannot seem to be able to target with regular styling.
I will see if the MAUI team has any guidance for me.
Hey @kolkonos with the new approach that @LanceMcCarthy listed (just doing it in MauiProgram.cs), I'm now running into the same issue that you are. Did you ever find a solution? @LanceMcCarthy I did try setting those properties, no dice sadly.
@ajsuydam In case you haven't tried it yet, there is an official Discord server for .NET and there's a dedicated MAUI channel. Here's the invite link http://aka.ms/dotnet-discord
@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.
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.
NEW APPROACH
Hi folks, we no longer need a bunch of custom code. Microsoft now has an easy API to set the backdrop right on the Window class itself:
window.SystemBackdrop = new MicaBackdrop { Kind = MicaKind.BaseAlt };
Here's what it looks like in MauiProgram.cs
Here is the result at runtime (note MicaKind.BaseAlt is more tinted than default Mica):