Skip to content

Instantly share code, notes, and snippets.

@LanceMcCarthy
Last active January 25, 2024 21:33
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LanceMcCarthy/4954ab92ca44c19eb4316d9d683efd50 to your computer and use it in GitHub Desktop.
Save LanceMcCarthy/4954ab92ca44c19eb4316d9d683efd50 to your computer and use it in GitHub Desktop.
Mica support in .NET MAUI
// ************************************************************************ //
// 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);
}
}
}
@FHWWC
Copy link

FHWWC commented Dec 29, 2023

That's good! I'm trying now...

@LanceMcCarthy
Copy link
Author

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

@FHWWC
Copy link

FHWWC commented Dec 30, 2023

It seems cannot put wasdk api on maui...

@LanceMcCarthy
Copy link
Author

LanceMcCarthy commented Dec 30, 2023 via email

@FHWWC
Copy link

FHWWC commented Dec 30, 2023

Oh sorry, I'm forgot to update the nuget package, now works fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment