diff --git a/components/Helpers/src/CommunityToolkit.WinUI.Helpers.csproj b/components/Helpers/src/CommunityToolkit.WinUI.Helpers.csproj
index c586beef..76f50f8f 100644
--- a/components/Helpers/src/CommunityToolkit.WinUI.Helpers.csproj
+++ b/components/Helpers/src/CommunityToolkit.WinUI.Helpers.csproj
@@ -11,7 +11,7 @@
-
+
@@ -20,6 +20,13 @@
\
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
diff --git a/components/Helpers/src/MultiTarget.props b/components/Helpers/src/MultiTarget.props
index b11c1942..c780abff 100644
--- a/components/Helpers/src/MultiTarget.props
+++ b/components/Helpers/src/MultiTarget.props
@@ -6,4 +6,4 @@
-->
uwp;wasdk;wpf;wasm;linuxgtk;macos;ios;android;
-
\ No newline at end of file
+
diff --git a/components/Helpers/src/NativeMethods.json b/components/Helpers/src/NativeMethods.json
new file mode 100644
index 00000000..be639985
--- /dev/null
+++ b/components/Helpers/src/NativeMethods.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "https://aka.ms/CsWin32.schema.json",
+ "allowMarshaling": false,
+ "comInterop": {
+ "preserveSigMethods": [
+ "*"
+ ]
+ }
+}
diff --git a/components/Helpers/src/NativeMethods.txt b/components/Helpers/src/NativeMethods.txt
new file mode 100644
index 00000000..3da417f6
--- /dev/null
+++ b/components/Helpers/src/NativeMethods.txt
@@ -0,0 +1,4 @@
+RegisterClassEx
+DefWindowProc
+CreateWindowEx
+WM_SETTINGCHANGE
diff --git a/components/Helpers/src/ThemeListener.cs b/components/Helpers/src/ThemeListener.cs
index ecd04b13..aaacbf56 100644
--- a/components/Helpers/src/ThemeListener.cs
+++ b/components/Helpers/src/ThemeListener.cs
@@ -4,6 +4,8 @@
using Windows.Foundation.Metadata;
using Windows.UI.ViewManagement;
+using CommunityToolkit.WinUI.HelpersRns;
+
#if !WINAPPSDK
using Windows.System;
@@ -83,6 +85,15 @@ public ThemeListener(DispatcherQueue? dispatcherQueue = null)
Window.Current.CoreWindow.Activated += CoreWindow_Activated;
}
+#if WINAPPSDK
+ ThemeListenerHelperWindow.Instance.ThemeChanged += this.Instance_ThemeChanged;
+#endif
+ }
+
+ private void Instance_ThemeChanged(ApplicationTheme theme)
+ {
+ CurrentTheme = theme;
+ ThemeChanged?.Invoke(this);
}
private async void Accessible_HighContrastChanged(AccessibilitySettings sender, object args)
diff --git a/components/Helpers/src/ThemeListenerHelperWindow.cs b/components/Helpers/src/ThemeListenerHelperWindow.cs
new file mode 100644
index 00000000..e4a7944c
--- /dev/null
+++ b/components/Helpers/src/ThemeListenerHelperWindow.cs
@@ -0,0 +1,79 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Win32;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Windows.Win32;
+using Windows.Win32.Foundation;
+using Windows.Win32.UI.WindowsAndMessaging;
+
+namespace CommunityToolkit.WinUI.HelpersRns;
+
+#if WINAPPSDK
+internal class ThemeListenerHelperWindow
+{
+ public delegate void ThemeChangedHandler(ApplicationTheme theme);
+
+ public static ThemeListenerHelperWindow Instance = new();
+
+ public event ThemeChangedHandler? ThemeChanged;
+
+ private static string s_className = "ThemeListenerHelperWindow";
+
+ private HWND m_hwnd;
+
+ private ThemeListenerHelperWindow()
+ {
+ s_className = "ThemeListenerHelperWindow";
+ RegisterClass();
+ m_hwnd = CreateWindow();
+ }
+
+ private static unsafe void RegisterClass()
+ {
+ WNDCLASSEXW wcx = default;
+ wcx.cbSize = (uint)sizeof(WNDCLASSEXW);
+ fixed (char* pClassName = s_className)
+ {
+ wcx.lpszClassName = pClassName;
+ wcx.lpfnWndProc = &WndProc;
+ if (PInvoke.RegisterClassEx(in wcx) is 0)
+ {
+ Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
+ }
+ }
+ }
+
+ private static unsafe HWND CreateWindow()
+ {
+ HWND hwnd = PInvoke.CreateWindowEx(0, s_className, string.Empty, 0, 0, 0, 0, 0, default, null, null, null);
+ if (hwnd == HWND.Null)
+ {
+ Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
+ }
+ return hwnd;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
+ private static LRESULT WndProc(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam)
+ {
+ if (msg is PInvoke.WM_SETTINGCHANGE)
+ {
+ // REG_DWORD
+ object? value = Registry.GetValue(
+ @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
+ "AppsUseLightTheme",
+ true);
+
+ if (value is int dword)
+ {
+ Instance.ThemeChanged?.Invoke(dword is 1 ? ApplicationTheme.Light : ApplicationTheme.Dark);
+ }
+ }
+
+ return PInvoke.DefWindowProc(hWnd, msg, wParam, lParam);
+ }
+}
+#endif