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.