Overview
Flowery.Uno provides comprehensive theme switching capabilities through DaisyThemeManager and specialized controls. This guide covers advanced patterns for theme persistence, programmatic management, and custom workflows.
For basic theme switching, see the Theming Guide . This guide focuses on advanced patterns and edge cases.
DaisyThemeController Modes
DaisyThemeController is a flexible toggle control that supports multiple presentation modes for theme switching.
Mode: Toggle
A simple toggle switch between two themes.
< daisy:DaisyThemeController
Mode = "Toggle"
UncheckedTheme = "Light"
CheckedTheme = "Dark" />
Mode: Checkbox
Checkbox-style presentation.
< daisy:DaisyThemeController
Mode = "Checkbox"
UncheckedTheme = "Corporate"
CheckedTheme = "Synthwave" />
Mode: Swap
Animated sun/moon swap indicator.
< daisy:DaisyThemeController
Mode = "Swap"
UncheckedTheme = "Light"
CheckedTheme = "Dark" />
The Swap mode provides a delightful animated transition between light (sun) and dark (moon) themes.
Mode: ToggleWithText
Toggle with custom text labels.
< daisy:DaisyThemeController
Mode = "ToggleWithText"
UncheckedTheme = "Light"
CheckedTheme = "Synthwave"
UncheckedLabel = "Light"
CheckedLabel = "Synthwave" />
Mode: ToggleWithIcons
Toggle with sun/moon icons.
< daisy:DaisyThemeController
Mode = "ToggleWithIcons"
UncheckedTheme = "Light"
CheckedTheme = "Dark" />
Theme Persistence
Persisting user theme preferences requires applying the saved theme before UI construction.
Application Startup Pattern
Load saved theme
Retrieve the theme preference from settings/storage.
Apply theme first
Call DaisyThemeManager.ApplyTheme() before creating the main window.
Create UI
Instantiate and activate the main window. Controls will sync to the current theme.
protected override void OnLaunched ( LaunchActivatedEventArgs args )
{
// 1. Load saved theme preference
var savedTheme = LoadThemeFromSettings () ?? "Dark" ;
// 2. Apply theme BEFORE creating UI
DaisyThemeManager . ApplyTheme ( savedTheme );
// 3. Now create the main window
MainWindow = new MainWindow ();
MainWindow . Activate ();
}
private string ? LoadThemeFromSettings ()
{
try
{
var settings = ApplicationData . Current . LocalSettings ;
return settings . Values [ "AppTheme" ] as string ;
}
catch
{
return null ;
}
}
Saving Theme Changes
Subscribe to ThemeChanged to persist user selections:
public MainWindow ()
{
InitializeComponent ();
DaisyThemeManager . ThemeChanged += OnThemeChanged ;
}
private void OnThemeChanged ( object ? sender , string themeName )
{
SaveThemeToSettings ( themeName );
}
private void SaveThemeToSettings ( string themeName )
{
try
{
var settings = ApplicationData . Current . LocalSettings ;
settings . Values [ "AppTheme" ] = themeName ;
}
catch ( Exception ex )
{
// Log error
}
}
Using StateStorageProvider
For consistency with Flowery.Uno’s persistence layer:
using Flowery . Services ;
private const string ThemeKey = "app.theme" ;
private string ? LoadThemeFromSettings ()
{
return StateStorageProvider . Read < string >( ThemeKey );
}
private void SaveThemeToSettings ( string themeName )
{
StateStorageProvider . Write ( ThemeKey , themeName );
}
Programmatic Theme Management
Apply Theme
DaisyThemeManager . ApplyTheme ( "Synthwave" );
Get Current Theme
var currentTheme = DaisyThemeManager . CurrentThemeName ;
Console . WriteLine ( $"Current theme: { currentTheme } " );
Check if Theme is Dark
if ( DaisyThemeManager . IsDarkTheme ( "Dracula" ))
{
// Adjust UI for dark theme
}
// Check current theme
if ( DaisyThemeManager . IsDarkTheme ( DaisyThemeManager . CurrentThemeName ))
{
StatusBar . Foreground = Brushes . White ;
}
List Available Themes
foreach ( var theme in DaisyThemeManager . AvailableThemes )
{
Console . WriteLine ( $" { theme . Name } (Dark: { theme . IsDark } )" );
}
ThemeChanged Event
DaisyThemeManager . ThemeChanged += ( sender , themeName ) =>
{
Console . WriteLine ( $"Theme changed to: { themeName } " );
UpdateCustomUI ();
};
Custom Theme Workflows
Theme Picker with Preview
Show all themes in a grid with live preview:
< Grid >
< Grid.RowDefinitions >
< RowDefinition Height = "*" />
< RowDefinition Height = "Auto" />
</ Grid.RowDefinitions >
<!-- Preview Area -->
< Border Grid.Row = "0"
Background = "{ThemeResource DaisyBase100Brush}"
Padding = "24" >
< StackPanel Spacing = "12" >
< TextBlock Text = "Theme Preview"
Foreground = "{ThemeResource DaisyBaseContentBrush}"
Style = "{StaticResource TitleTextBlockStyle}" />
< daisy:DaisyButton Content = "Primary Button" Variant = "Primary" />
< daisy:DaisyButton Content = "Secondary Button" Variant = "Secondary" />
< daisy:DaisyCard Title = "Sample Card" >
< TextBlock Text = "Card content with current theme." />
</ daisy:DaisyCard >
</ StackPanel >
</ Border >
<!-- Theme Selector -->
< daisy:DaisyThemeDropdown
Grid.Row = "1"
MinWidth = "220"
Margin = "12" />
</ Grid >
Conditional Theme Logic
public void ApplyAppropriateTheme ()
{
var hour = DateTime . Now . Hour ;
// Auto dark mode at night
if ( hour >= 20 || hour < 6 )
{
DaisyThemeManager . ApplyTheme ( "Dark" );
}
else
{
DaisyThemeManager . ApplyTheme ( "Light" );
}
}
Theme Rotation
public void CycleTheme ()
{
var themes = DaisyThemeManager . AvailableThemes . ToList ();
var current = DaisyThemeManager . CurrentThemeName ;
var currentIndex = themes . FindIndex ( t => t . Name == current );
var nextIndex = ( currentIndex + 1 ) % themes . Count ;
DaisyThemeManager . ApplyTheme ( themes [ nextIndex ]. Name );
}
Random Theme
public void ApplyRandomTheme ()
{
var themes = DaisyThemeManager . AvailableThemes . ToList ();
var random = new Random ();
var theme = themes [ random . Next ( themes . Count )];
DaisyThemeManager . ApplyTheme ( theme . Name );
}
Advanced Patterns
Per-Window Theme Override
Apply different themes to different windows:
public class ThemedWindow : Window
{
public string WindowTheme { get ; set ; } = "Light" ;
public ThemedWindow ()
{
InitializeComponent ();
Activated += OnActivated ;
DaisyThemeManager . ThemeChanged += OnGlobalThemeChanged ;
}
private void OnActivated ( object sender , WindowActivatedEventArgs args )
{
if ( args . WindowActivationState != WindowActivationState . Deactivated )
{
DaisyThemeManager . ApplyTheme ( WindowTheme );
}
}
private void OnGlobalThemeChanged ( object ? sender , string themeName )
{
// Restore window theme if different
if ( themeName != WindowTheme && IsActive )
{
DaisyThemeManager . ApplyTheme ( WindowTheme );
}
}
}
Per-window theming is complex and can lead to visual inconsistencies. Use sparingly.
Theme Transition Animation
Animate theme changes with opacity transitions:
private async Task ApplyThemeWithTransition ( string themeName )
{
// Fade out
var fadeOut = new DoubleAnimation
{
From = 1.0 ,
To = 0.0 ,
Duration = TimeSpan . FromMilliseconds ( 150 )
};
Storyboard . SetTarget ( fadeOut , RootGrid );
Storyboard . SetTargetProperty ( fadeOut , "Opacity" );
var storyboard = new Storyboard ();
storyboard . Children . Add ( fadeOut );
storyboard . Begin ();
await Task . Delay ( 150 );
// Apply theme
DaisyThemeManager . ApplyTheme ( themeName );
// Fade in
var fadeIn = new DoubleAnimation
{
From = 0.0 ,
To = 1.0 ,
Duration = TimeSpan . FromMilliseconds ( 150 )
};
Storyboard . SetTarget ( fadeIn , RootGrid );
Storyboard . SetTargetProperty ( fadeIn , "Opacity" );
var fadeInStoryboard = new Storyboard ();
fadeInStoryboard . Children . Add ( fadeIn );
fadeInStoryboard . Begin ();
}
Theme Analytics
Track theme usage:
public class ThemeAnalytics
{
private readonly Dictionary < string , int > _themeUsage = new ();
public ThemeAnalytics ()
{
DaisyThemeManager . ThemeChanged += OnThemeChanged ;
}
private void OnThemeChanged ( object ? sender , string themeName )
{
if ( ! _themeUsage . ContainsKey ( themeName ))
_themeUsage [ themeName ] = 0 ;
_themeUsage [ themeName ] ++ ;
SaveAnalytics ();
}
public string GetMostUsedTheme ()
{
return _themeUsage . OrderByDescending ( kvp => kvp . Value )
. FirstOrDefault (). Key ?? "Dark" ;
}
private void SaveAnalytics ()
{
StateStorageProvider . Write ( "theme.analytics" , _themeUsage );
}
}
Troubleshooting
Theme Doesn’t Update
Check ThemeResource usage
Ensure you’re using {ThemeResource} instead of {StaticResource} for palette brushes: <!-- ✅ DO -->
< Border Background = "{ThemeResource DaisyBase100Brush}" />
<!-- ❌ DON'T -->
< Border Background = "{StaticResource DaisyBase100Brush}" />
Listen to ThemeChanged in code-behind
Controls created in code-behind need manual updates: private void OnLoaded ( object sender , RoutedEventArgs e )
{
DaisyThemeManager . ThemeChanged += OnThemeChanged ;
ApplyTheme ();
}
private void OnUnloaded ( object sender , RoutedEventArgs e )
{
DaisyThemeManager . ThemeChanged -= OnThemeChanged ;
}
private void OnThemeChanged ( object ? sender , string themeName )
{
ApplyTheme ();
}
private void ApplyTheme ()
{
MyBorder . Background = DaisyResourceLookup . GetBrush ( "DaisyBase200Brush" );
}
Verify ThemeChanged event fires
Add a handler to confirm the event is firing: DaisyThemeManager . ThemeChanged += ( s , theme ) =>
{
Debug . WriteLine ( $"Theme changed to: { theme } " );
};
Controls Don’t Sync to Theme
Apply theme before UI construction
Ensure controls subscribe to ThemeChanged in Loaded and unsubscribe in Unloaded.
Theme Persistence Issues
Don’t save only on app shutdown - save immediately when theme changes: DaisyThemeManager . ThemeChanged += ( s , theme ) =>
{
SaveThemeToSettings ( theme );
};
Load before window creation
Load theme before new MainWindow() in OnLaunched.
Best Practices
Use {ThemeResource} for all palette brushes to ensure runtime updates.
Subscribe/unsubscribe properly
Always unsubscribe from ThemeChanged in Unloaded to prevent memory leaks.
Save theme preference immediately when changed, not on app shutdown.
Apply saved theme in OnLaunched before creating any UI.
Use controls like DaisyThemeController with Mode="Swap" for delightful feedback.
Always test your app in both light and dark themes to ensure contrast and readability.