简体   繁体   中英

Changing Theme in Windows 10 UWP App Programmatically

I was able to change theme using this.RequestedTheme = ElementTheme.Dark;But what I need is the whole application level, since this one only change the theme of the current page to dark.

Whenever I try this App.Current.RequestedTheme = ApplicationTheme.Dark; I always get this error

An exception of type 'System.NotSupportedException' occurred in UWPApp.exe but was not handled in user code

Is there such a way that I can change the whole application theme from Light to Dark or vice versa?

I'm using VS2015

Updated answer with what I finally decided on.

I used a settings class that holds all of the apps settings including what theme to use. Since the theme can only be set when it starts we need to make sure to set it them. This is the code I used:

In the App.xaml.cs file:

public App()
{
    //Load settings
    AppSettings.LoadSettings();
    this.RequestedTheme = AppSettings.SelectedTheme;

    this.InitializeComponent();
}

In the App.xaml file make sure to remove this property:

    RequestedTheme="Light"

If its not removed it always default to light with no way to change it.

This way the user can choose the theme, it gets stored and used when the app starts. Just make sure to load it and apply it in the app initialization phase.

I found another solution that worked pretty well for me. If the app has a root frame that loads the pages (which is the case by default), I could set the requested theme of that root frame to the desired value and the theme of the app got changed without restarting . The code looks like this:

// Set theme for window root.
if (Window.Current.Content is FrameworkElement frameworkElement)
{
   frameworkElement.RequestedTheme = theme;
}

I got that snippet from the Windows Template Studio GitHub repository here , so this seems to be kind of the best way to do this.

Application's RequestedTheme can only be updated in constructor.However (as you discovered), Page's RequestedTheme can be updated anytime in runtime.

This is really annoying I know and there's not much information about this situation other than this MSDN page:

https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.application.requestedtheme?f=255&MSPPError=-2147217396

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). If you give the user an option to pick a theme that's part of app UI, you must save the setting in the app data and apply it when the app is restarted.

There should be workarounds but they'll be very dumb.Microsoft doing this in their own official apps like Groove though...

My solution is doing theme updating on the Element level, not the Application level.

  1. Create your own BasePage (inherited from Page)
  2. Keep your settings under a static class and create an Event for keep tracking the model changes.
  3. In your BasePage's constructor listen to this event and apply changes when there's.

I'm not able to provide code example right now (since i'm very busy today), but this is by far the most efficient way IMHO.

I found axnull 's answer most helpful, because it allows setting the theme while the app is running. After more than a good afternoon's work, I was able to set the app's theme on the fly and keep it in memory for next startup, giving the user control through a ToggleButton .

First, I made a settings class with a Theme property that automatically stores the current setting:

AppSettings.cs

class AppSettings
{
   public const ElementTheme DEFAULTTHEME = ElementTheme.Light;
   public const ElementTheme NONDEFLTHEME = ElementTheme.Dark;

   const string KEY_THEME = "appColourMode";
   static ApplicationDataContainer LOCALSETTINGS = ApplicationData.Current.LocalSettings;

   /// <summary>
   /// Gets or sets the current app colour setting from memory (light or dark mode).
   /// </summary>
   public static ElementTheme Theme {
      get {
         // Never set: default theme
         if (LOCALSETTINGS.Values[KEY_THEME] == null)
         {
            LOCALSETTINGS.Values[KEY_THEME] = (int)DEFAULTTHEME;
            return DEFAULTTHEME;
         }
         // Previously set to default theme
         else if ((int)LOCALSETTINGS.Values[KEY_THEME] == (int)DEFAULTTHEME)
            return DEFAULTTHEME;
         // Previously set to non-default theme
         else
            return NONDEFLTHEME;
      }
      set {
         // Error check
         if (value == ElementTheme.Default)
            throw new System.Exception("Only set the theme to light or dark mode!");
         // Never set
         else if (LOCALSETTINGS.Values[KEY_THEME] == null)
            LOCALSETTINGS.Values[KEY_THEME] = (int)value;
         // No change
         else if ((int)value == (int)LOCALSETTINGS.Values[KEY_THEME])
            return;
         // Change
         else
            LOCALSETTINGS.Values[KEY_THEME] = (int)value;
      }
   }
}

Then, in the page constructor, added the following code:

MainPage.xaml.cs

  public MainPage()
  {
     this.InitializeComponent();

     // Set theme for window root
     FrameworkElement root = (FrameworkElement)Window.Current.Content;
     root.RequestedTheme = AppSettings.Theme;
     SetThemeToggle(AppSettings.Theme);
  }

This sets the theme according to the previous selection in app memory, and sets the toggle to match.

The following method gets called when the page loads:

MainPage.xaml.cs

  /// <summary>
  /// Set the theme toggle to the correct position (off for the default theme, and on for the non-default).
  /// </summary>
  private void SetThemeToggle(ElementTheme theme)
  {
     if (theme == AppSettings.DEFAULTTHEME)
        tglAppTheme.IsOn = false;
     else
        tglAppTheme.IsOn = true;
  }

And this handles the toggle's being switched:

MainPage.xaml.cs

  /// <summary>
  /// Switch the app's theme between light mode and dark mode, and save that setting.
  /// </summary>
  private void ToggleSwitch_Toggled(object sender, RoutedEventArgs e)
  {
     FrameworkElement window = (FrameworkElement)Window.Current.Content;

     if (((ToggleSwitch)sender).IsOn)
     {
        AppSettings.Theme = AppSettings.NONDEFLTHEME;
        window.RequestedTheme = AppSettings.NONDEFLTHEME;
     }
     else
     {
        AppSettings.Theme = AppSettings.DEFAULTTHEME;
        window.RequestedTheme = AppSettings.DEFAULTTHEME;
     }
  }

All the above code being created for the following ToggleButton switch:

MainPage.xaml

<ToggleSwitch Name="tglAppTheme"
              Header="Theme"
              OffContent="Light"
              OnContent="Dark"
              IsOn="False"
              Toggled="ToggleSwitch_Toggled" />

This setup is simple enough, and can hopefully save someone the grunt work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM