Make your Avalonia app look like a ’native’ Windows 11 style app supporting the ‘Mica’ background and accent color border. Sample app included!
Make your Avalonia app look like a ’native’ Windows 11 style app supporting the ‘Mica’ background and accent color border. Sample app included!
Avalonia is a great open source, cross-platform, UI framework for .net which allows you to run your app almost everywhere. Windows, macOS, Linux, Mobile, web browser, and more. If you only target Windows, or even when you target multiple OSes, you may want your app to look like a native Windows 11 app on Windows. Thanks to the powerful styling system, you can make this happen. In this blog post, I will focus on Windows and go through the steps needed to make your app look like this:
The sample application can be found on my github repository.
Let’s start with the ViewModel. Chances are that you want your user to change the appearance and preferences for your app, so it makes sense to handle these settings in a view model.
The sample app, allows you to change the following:
The Window
/ TopLevel
class has a property called TransparencyLevelHint
, which we bind to the view model’s TransparencyLevelHint
property.
If we want to disable transparency completely, we just set the property to an empty list, otherwise we simply set a new list which has one entry: WindowTransparencyLevel.Mica
.
The accent border color is controlled using a dynamic resource. All we do in our view model, when the user switches the accent color on or off, is setting the dynamic resource with the name UIWindowBorderColorActive
to the system’s accent color when enabled or to Colors.Transparent
when disabled:
partial void OnBorderEnabledChanged(bool value)
{
Application.Current!.Resources["UIWindowBorderColorActive"] = value
? App.MainWindow.PlatformSettings?.GetColorValues().AccentColor1 ?? Colors.Transparent
: Colors.Transparent;
}
App.MainWindow.PlatformSettings.ColorValuesChanged
which should be subscribed to in order to update the color values.To make Mica apps look great, you need to tweak a couple of control styles. You may want controls to not always have solid colors. You probably want controls to be translucent, so that the Mica background can shine through.
For this I decided to create theme dependent color resources with various levels of transparency and some color tinting. You can further tweak those colors and adapt the transparency to your liking.
In Windows, the Mica transparency is only showed on active/focused windows. That’s why I have two color resources UIWindowBackgroundColorInactive
(without any transparency) and UIWindowBackgroundColorActive
with some transparency.
UI
and depending on the color theme, I have different colors and transparencies applied on various elements (borders, backgrounds, etc.).After defining all the colors, I can refer to them in my Brush
resources as needed using a DynamicResource
binding to the color value. This way, my brush will automatically use the correct color value depending on the selected theme.
Now, let’s wire up everything to make it work.
All we need to do is to select the correct border (with name PART_RootWindowBorder
) and set the BorderBrush
accordingly.
<Style Selector=":is(Window) Border#PART_RootWindowBorder">
<Setter Property="Transitions">
<Transitions>
<BrushTransition Property="BorderBrush" Duration="0:0:0.2" />
</Transitions>
</Setter>
<Setter Property="BorderBrush" Value="{DynamicResource UIWindowBorderColorInactive}" />
</Style>
<Style Selector=":is(Window)[IsActive=True][WindowState=Normal] Border#PART_RootWindowBorder">
<Setter Property="BorderBrush" Value="{DynamicResource UIWindowBorderColorActive}" />
</Style>
Note that our selector for the “Active” border brush also takes the IsActive
and WindowState
property into account. Our style here applies the UIWindowBorderColorActive
dynamic resource which we set in our view model.
<Style Selector=":is(Window)">
<Setter Property="Background" Value="{DynamicResource UIWindowBackgroundBrushInactive}" />
</Style>
<Style Selector=":is(Window)[ActualTransparencyLevel=Mica][IsActive=True]">
<Setter Property="Background" Value="{DynamicResource UIWindowBackgroundBrushActive}" />
</Style>
Similar to the accent border, we have two style selector where the selector for the “Active” background brush also takes the IsActive
and the ActualTransparencyLevel
property into account.
As mentioned before, you may also want to tweak control styles to use transparency levels instead of solid color brushes.
In my sample app, I created two style classes to demonstrate the effect:
Border.setting
represents a typical settings border where we use the background and border brushes we defined in the App.axaml
.Button.setting
tweaks the style for the button to use translucent background and border brushes defined in the App.axaml
.To use the styles, simply add Classes="setting"
to your border or button.
In case you are wondering about the UIGlobal
class and this style:
<Style Selector=":is(Window)">
<Setter Property="(avaloniaMica:UIGlobal.TransparencyEnabled)"
Value="{DynamicResource TransparencyEnabled}" />
</Style>
Check out this blog post. In the view model there’s also this line in the OnTransparencyEnabledChanged
method:
Application.Current!.Resources[nameof(UIGlobal.TransparencyEnabled)] = value;
This can be helpful if you need a style selector to only apply styles when transparency is enabled.
As you can see, implementing and using Mica background takes some effort to make it work correctly. The approach shown above has worked quite well for me and I’m happy with the result. Once the infrastructure is set up, most of the work has to be done around tweaking the control styles. Depending on the Avalonia Theme you are using, this can take some time.
As always, feedback is welcome. Happy coding…
Here’s what you might have missed.