This is the fourth article in the recently started series titled Developer Tips, which offers concise hints to enhance productivity. All the articles in this series can be accessed from here.
Basically, theming refers to managing a consistent look and feel across the app. An app can be themed in a variety of ways (MS Office and Visual Studio offer various themes). For mobile apps, the most significant themes are the Light and Dark modes, both of which are inherently supported by the OS. So, the app should have the capability to adapt to mode changes to the least.
Consulting the previous article on Styles is recommended as a foundation for Theming.
Excerpt from the preceding article:
Styles can be referenced through StaticResource or DynamicResource. The key difference is that the former is applied just once, while the lattter maintains a reference and adjusts to changes in themes.
Note: When handling Styles that are not responsive to theme changes, it is advisable to reference them as StaticResource to enhance the app’s performance.
In essence, a theme is an assortment of related styles. To accommodate various themes, the corresponding set of styles must be defined within each theme.
Here’s a sample that defines 4 themes based on the Black and Teal color palette.
A ResourceDictionary serves as a logical container for styles that are associated with a theme.
Theme1.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp1.Theme1">
<Color x:Key="Primary">#0F0F0F</Color>
<Style TargetType="Shell" ApplyToDerivedTypes="True">
<Setter Property="Shell.BackgroundColor" Value="{StaticResource Primary}" />
</Style>
</ResourceDictionary>
Theme2.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp1.Theme2">
<Color x:Key="Primary">#232D3F</Color>
<Style TargetType="Shell" ApplyToDerivedTypes="True">
<Setter Property="Shell.BackgroundColor" Value="{StaticResource Primary}" />
</Style>
</ResourceDictionary>
Theme3.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp1.Theme3">
<Color x:Key="Primary">#005B41</Color>
<Style TargetType="Shell" ApplyToDerivedTypes="True">
<Setter Property="Shell.BackgroundColor" Value="{StaticResource Primary}" />
</Style>
</ResourceDictionary>
Theme4.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp1.Theme4">
<Color x:Key="Primary">#008170</Color>
<Style TargetType="Shell" ApplyToDerivedTypes="True">
<Setter Property="Shell.BackgroundColor" Value="{StaticResource Primary}" />
</Style>
</ResourceDictionary>
A working sample has now been published in the .NET MAUI Samples repository on GitHub. Check out the project in the src\NET_8\ThemedApp folder. Upon launching the app, change the value of the Theme dropdown for the Shell Navigation Bar to alter its background dynamically.
.NET MAUI Application object instance defines the following to manage theming.
- UserAppTheme (read & write)
- PlatformAppTheme (read-only)
- RequestedTheme (read-only)
- RequestedThemeChanged (event)
To apply the theme change, the UserAppTheme property, which is of type AppTheme, must be updated.
The PlatformAppTheme property returns the system-configured theme.
The RequestedTheme property correlates the UserAppTheme and PlatformAppTheme property values.
It should be noted that the app can have a theme on its own that is different from the system theme, or it can follow the system theme too.
For managing custom theme definitions apart from Light and Dark modes, a user-defined property is to be defined.
It is up to the user how to save and restore the last used theme value if it’s different from the system theme. Preferences might be a good place to store the value.
Subscribe to the RequestedThemeChanged event to react to theme changes. This is the appropriate place to update the set of styles corresponding to the requested theme. Essentially, manage the ResourceDictionary instances by discarding the old and incorporating the new. By referring to styles with the same key name, a theme change takes effect across the app.
Application.Current.RequestedThemeChanged += (s, e) =>
{
// Respond to the theme change
};
The AppThemeChangedEventArgs object, associated with the RequestedThemeChanged event, has a single property named RequestedTheme, of type AppTheme. This property can be examined to detect the requested theme.
.NET MAUI comes with built-in support for managing Light and Dark mode themes.
Changing a theme at runtime requires the use of XAML/C# styles definition, and is not possible using CSS.
In XAML, the AppThemeBinding extension is used to define values for Light and Dark themes by setting the respective properties.
This extension hides all the details under the hood, which would otherwise have to be managed manually in the theme change event.
AppThemeBinding effectively handles the values for future lookup during theme changes, thus referencing them as StaticResource is appropriate in this context.
<Grid BackgroundColor="{AppThemeBinding Dark={StaticResource BackgroundDark}, Light={StaticResource BackgroundLight}}">
</Grid>
This can be defined within a style as well.
<Style x:Key="ContentArea" TargetType="Grid">
<Setter Property="BackgroundColor" Value="{AppThemeBinding Dark={StaticResource BackgroundDark}, Light={StaticResource BackgroundLight}}" />
</Style>
The Grid’s definition can also be rewritten as (resource reuse):
<Grid Style="{StaticResource ContentArea}">
</Grid>
For C#, there are extension methods.
- SetAppTheme<T> – Generic method
- SetAppThemeColor
var lbl = new Label();
lbl.SetAppThemeColor(Label.TextColorProperty, Color.Black, Color.White);
The issue is that these extension methods are defined for types that inherit from BindableObject, which cannot be used in a Style definition (Setter).
The underlying type AppThemeBinding is not public. Have raised it as an issue highlighting its need here. You can add your comments too.
There’s theming support from CommunityToolkit as well:
MauiToolkit defines the following types:
- AppThemeObject<T> – Abstract type
- AppThemeObject
- AppThemeColor – Sealed type
The Markup toolkit includes extension methods that enable method call chaining.
- AppThemeBinding
- AppThemeColorBinding
- DynamicResource
- DynamicResources – Maps multiple resources in a single method call
var lbl = new Label()
.AppThemeColorBinding(Label.TextColorProperty, Color.Black, Color.White);
There is specialized support for types/methods based on Color, as this is the primary property targeted for theming. Each one has properties set for both Light and Dark modes.
Further reading:
If you would like to recognize the work, kindly consider sponsoring on the GitHub Sponsor page or Buy Me a Coffee page. Thanks in advance.
Happy coding. Stay connected as we continue to learn and share the experiences from this exciting journey of being a .NET developer.
One reply on “.NET MAUI – App Theming – Create Stylish Apps”
[…] which is closely associated with Styling and will be covered in a separate article. The article on App Theming is now […]