Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Support setting/changing Application.Current.RequestedTheme after app constructor (rather than throw exception) #4474

Open
Tracked by #10490
billhenn opened this issue Mar 10, 2021 · 13 comments
Labels
area-Styling feature proposal New feature proposal needs-winui-3 Indicates that feature can only be done in WinUI 3.0 or beyond. (needs winui 3) team-Controls Issue for the Controls team

Comments

@billhenn
Copy link

Describe the bug
Setting Application.Current.RequestedTheme at run-time to alter the app's light/dark theme throws this exception:

System.NotSupportedException
  HResult=0x80131515
  Message=Specified method is not supported.
  Source=WinRT.Runtime
  StackTrace:
   at WinRT.ExceptionHelpers.ThrowExceptionForHR(Int32 hr)
   at ABI.Microsoft.UI.Xaml.IApplication.global::Microsoft.UI.Xaml.IApplication.set_RequestedTheme(ApplicationTheme value)
   at Microsoft.UI.Xaml.Application.set_RequestedTheme(ApplicationTheme value)
   at TestApp.RootWindow.OnThemeMenuFlyoutItemClick(Object sender, RoutedEventArgs e) in S:\Code\TestApp\Views\RootWindow.xaml.cs:line 71
   at ABI.Microsoft.UI.Xaml.RoutedEventHandler.<>c__DisplayClass10_0.<Do_Abi_Invoke>b__0(RoutedEventHandler invoke)
   at WinRT.ComWrappersSupport.MarshalDelegateInvoke[T](IntPtr thisPtr, Action`1 invoke)
   at ABI.Microsoft.UI.Xaml.RoutedEventHandler.Do_Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr e)

Setting the RequestedTheme property on the root Grid in my Window succeeds. But I want to change it app-wide, not only on this window.

Steps to reproduce the bug

Use this code to reproduce:

Application.Current.RequestedTheme = ApplicationTheme.Dark;

Expected behavior

No exception and the app theme should toggle to the selected theme.

Version Info

NuGet package version:
[Microsoft.WinUI 3.0.0-preview4.210210.4]

Windows app type:

UWP Win32
Yes
Windows 10 version Saw the problem?
Insider Build (xxxxx)
October 2020 Update (19042)
May 2020 Update (19041) Yes
November 2019 Update (18363)
May 2019 Update (18362)
October 2018 Update (17763)
April 2018 Update (17134)
Fall Creators Update (16299)
Creators Update (15063)
Device form factor Saw the problem?
Desktop Yes
Xbox
Surface Hub
IoT
@ghost ghost added the needs-triage Issue needs to be triaged by the area owners label Mar 10, 2021
@ranjeshj
Copy link
Contributor

@llongley @codendone as FYI

@codendone
Copy link
Contributor

This is currently by design. It is a little hidden down in the Remarks section of the documentation:

The theme can only be set when the app is started, not while it's running. Attempting to set RequestedTheme while the app is running throws an exception (NotSupportedException for Microsoft .NET code). ...

You can change specific theme values at run-time after Application.RequestedTheme is applied, if you use the FrameworkElement.RequestedTheme property and sets values on specific elements in the UI.

The error message could certainly be better -- a problem we have for all errors right now. This error will be much easier to understand when the code is finally open source, since you'd then be able to see this code comment at the source of the exception:

    // RequestedTheme cannot be set after app.xaml has been loaded.

@billhenn
Copy link
Author

@codendone Thanks, I apologize I didn't see that remark before. The more descriptive error message would certainly help prevent confusion on this, and as you said, error messages really need to be more detailed across the board.

I would really ask that you improve this to support app-wide theme changes without having to restart the app. All popular modern apps support instant live theme changes between variations of light and dark themes without having to restart, and it's kind of an expected app capability at this point. Not having support for that in a "modern" UI platform is very disappointing. Thanks for listening!

@codendone codendone added feature proposal New feature proposal needs-winui-3 Indicates that feature can only be done in WinUI 3.0 or beyond. (needs winui 3) and removed product-winui3 WinUI 3 issues labels Mar 12, 2021
@codendone
Copy link
Contributor

I believe this restriction exists simply because way back in Windows 8 when the RequestedTheme support was implemented there weren't available resources to also support dynamic changes. Adding that support could be done in the future, so I'm marking this as a future feature proposal.

@codendone codendone changed the title Setting Application.Current.RequestedTheme throws exception Proposal: Support setting/changing Application.Current.RequestedTheme after app constructor (rather than throw exception) Mar 12, 2021
@billhenn
Copy link
Author

Thank you @codendone, much appreciated.

@jtorjo
Copy link

jtorjo commented Jul 5, 2022

Guys, can you please please please add just a tad of description in the exception, when you throw it?
I wanted to bang my head against the wall on this one
(Until i figured out i should set that in the constructor). The (insanely generic) exception is beyond annoying.

@ajsuydam
Copy link

ajsuydam commented Jul 1, 2024

A potentially dumb question: if runtime switching isn't supported, how does task manager do it? Or notepad? Both support runtime switching, and they're both using WinUI 3.

@MartyIX
Copy link
Contributor

MartyIX commented Aug 29, 2024

The proposal: 💯


This is a sort of workaround for the missing feature. One can call SetTheme("Light", window.Content) to enforce theme changes for all ComboBoxes (simplification). However, it modifies only visible ComboBoxes as VisualTreeHelper (doc) is used.

Not sure how to change all ComboBoxes in the window (being visible in the UI tree or not).

public static void SetTheme(string appTheme, Microsoft.UI.Xaml.DependencyObject obj)
{
    Microsoft.UI.Xaml.ElementTheme elementTheme = appTheme switch
    {
        "Light" => Microsoft.UI.Xaml.ElementTheme.Light,
        "Dark" => Microsoft.UI.Xaml.ElementTheme.Dark,
        "System" => Microsoft.UI.Xaml.ElementTheme.Default,
        _ => throw new NotSupportedException($"Invalid application theme value '{appTheme}'."),
    };

    foreach (Microsoft.UI.Xaml.Controls.ComboBox comboBox in FindDescendants<Microsoft.UI.Xaml.Controls.ComboBox>(obj))
    {
        comboBox.RequestedTheme = elementTheme;
    }
}

public static IEnumerable<T> FindDescendants<T>(Microsoft.UI.Xaml.DependencyObject dobj)
    where T : Microsoft.UI.Xaml.DependencyObject
{
    int count = Microsoft.UI.Xaml.Media.VisualTreeHelper.GetChildrenCount(dobj);
    for (int i = 0; i < count; i++)
    {
        Microsoft.UI.Xaml.DependencyObject element = Microsoft.UI.Xaml.Media.VisualTreeHelper.GetChild(dobj, i);
        if (element is T t)
            yield return t;

        foreach (T descendant in FindDescendants<T>(element))
            yield return descendant;
    }
}

@MartyIX
Copy link
Contributor

MartyIX commented Sep 2, 2024

This is a more complete code one can use: dotnet/maui#21042 (comment)

@Lightczx
Copy link

Lightczx commented Nov 26, 2024

place this line of code inside your App's OnLaunched method to set m_isRequestedThemeSettable to true

*(bool*)(((IWinRTObject)this).NativeObject.As<IUnknownVftbl>(IApplicationIID).ThisPtr + 0x118U) = true;

IApplicationIID is defined like below.

private static ref readonly Guid IApplicationIID
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    get => ref MemoryMarshal.AsRef<Guid>([231, 244, 168, 6, 70, 17, 175, 85, 130, 13, 235, 213, 86, 67, 176, 33]);
}

(Tested and works in WASDK 1.6.3)


Additional Context:
microsoft/WindowsAppSDK#4710 (comment)

@ghost1372
Copy link
Contributor

place this line of code inside your App's OnLaunched method to set m_isRequestedThemeSettable to true

(bool)(((IWinRTObject)this).NativeObject.As(IApplicationIID).ThisPtr + 0x118U) = true;
(Tested and works in WASDK 1.6.3)

Additional Context: microsoft/WindowsAppSDK#4710 (comment)

what is IApplicationIID ?
Image

@Lightczx
Copy link

what is IApplicationIID ?

@ghost1372 My bad, you can find through looking the code decompiled by VS.

There is a Make___objRef_global__Microsoft_UI_Xaml_IApplication method in Application

Image

And finally get this

Image

@ghost1372
Copy link
Contributor

ghost1372 commented Nov 27, 2024

what is IApplicationIID ?

@ghost1372 My bad, you can find through looking the code decompiled by VS.

There is a Make___objRef_global__Microsoft_UI_Xaml_IApplication method in Application

Image

And finally get this

Image

thank you now its working fine

complete code:

unsafe
{
    *(bool*)(((IWinRTObject)this).NativeObject.As<IUnknownVftbl>(IID).ThisPtr + 0x118U) = true;
}

public static ref readonly Guid IID
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    get
    {
        return ref Unsafe.As<byte, Guid>(ref MemoryMarshal.GetReference((ReadOnlySpan<byte>)new byte[16]
        {
            231, 244, 168, 6, 70, 17, 175, 85, 130, 13,
            235, 213, 86, 67, 176, 33
        }));
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-Styling feature proposal New feature proposal needs-winui-3 Indicates that feature can only be done in WinUI 3.0 or beyond. (needs winui 3) team-Controls Issue for the Controls team
Projects
None yet
Development

No branches or pull requests

10 participants