Goal
avares:// URIs, AssetLoader/IAssetLoader, and ResourceDictionary lookup so you can bundle assets cleanly.FontManagerOptions, and swap font families at runtime.ResourceDictionary updates, and diagnostics when a lookup fails.Why this matters
Prerequisites
App.axaml, views, and bind data (Ch. 3-9).avares:// URIs and project structureAssets live under your project (e.g., Assets/Images, Assets/Fonts). Include them as AvaloniaResource in the .csproj:
<ItemGroup>
<AvaloniaResource Include="Assets/**" />
</ItemGroup>
URI structure: avares://<AssemblyName>/<RelativePath>.
Example: avares://InputPlayground/Assets/Images/logo.png.
avares:// references the compiled resource stream (not the file system). Use it consistently even within the same assembly to avoid issues with resource lookups.
ResourceDictionary derives from ResourceProvider and implements IResourceProvider. When you request {StaticResource} or call TryGetResource, Avalonia walks this chain:
IResourceHost (control, style, or application).<Style.Resources>), control templates, and data templates.ThemeVariantScope, Application.Styles, Application.Resources).<ResourceDictionary.MergedDictionaries> or <ResourceInclude>).SystemResources).ResourceDictionary.cs and ResourceNode.cs coordinate this traversal. Use TryGetResource when retrieving values from code:
if (control.TryGetResource("AccentBrush", ThemeVariant.Dark, out var value) && value is IBrush brush)
{
control.Background = brush;
}
ThemeVariant lets you request a variant-specific value; pass ThemeVariant.Default to follow the same logic as {DynamicResource}.
Merge dictionaries to break assets into reusable packs:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="avares://AssetsDemo/Assets/Colors.axaml"/>
<ResourceInclude Source="avares://AssetsDemo/Assets/Icons.axaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Each merged dictionary is loaded lazily via IAssetLoader, so make sure the referenced file is marked as AvaloniaResource.
<Image Source="avares://AssetsDemo/Assets/Images/logo.png"
Stretch="Uniform" Width="160"/>
AssetLoaderusing Avalonia;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
var uri = new Uri("avares://AssetsDemo/Assets/Images/logo.png");
var assetLoader = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
await using var stream = assetLoader.Open(uri);
LogoImage.Source = new Bitmap(stream);
AssetLoader is a static helper over the same IAssetLoader service. Prefer the interface when unit testing or when you need to mock resource access. Both live in Avalonia.Platform.
Need to probe for optional assets? Use assetLoader.TryOpen(uri) or AssetLoader.Exists(uri) to avoid exceptions.
<ResourceDictionary xmlns="https://github.com/avaloniaui">
<Bitmap x:Key="LogoBitmap">avares://AssetsDemo/Assets/Images/logo.png</Bitmap>
</ResourceDictionary>
You can then StaticResource expose LogoBitmap. Bitmaps created this way are cached.
Image renders Avalonia.Media.Imaging.Bitmap. Decode streams once and keep the bitmap alive when the pixels are reused, instead of calling new Bitmap(stream) for every render. Performance tips:
Stretch to avoid unexpected distortions (Uniform, UniformToFill, Fill, None).RenderOptions.BitmapInterpolationMode for scaling quality:<Image Source="avares://AssetsDemo/Assets/Images/photo.jpg"
Width="240" Height="160"
RenderOptions.BitmapInterpolationMode="HighQuality"/>
Interpolation modes defined in RenderOptions.cs.
Decode oversized images to a target width/height to save memory:
await using var stream = assetLoader.Open(uri);
using var decoded = Bitmap.DecodeToWidth(stream, 512);
PhotoImage.Source = decoded;
Bitmap and decoder helpers live in Bitmap.cs. Avalonia picks the right codec (PNG, JPEG, WebP, BMP, GIF) using Skia; for unsupported formats supply a custom IBitmapDecoder.
ImageBrush paints surfaces:
<Ellipse Width="96" Height="96">
<Ellipse.Fill>
<ImageBrush Source="avares://AssetsDemo/Assets/Images/avatar.png"
Stretch="UniformToFill" AlignmentX="Center" AlignmentY="Center"/>
</Ellipse.Fill>
</Ellipse>
Tile backgrounds:
<Border Width="200" Height="120">
<Border.Background>
<ImageBrush Source="avares://AssetsDemo/Assets/Images/pattern.png"
TileMode="Tile"
Stretch="None"
Transform="{ScaleTransform 0.5,0.5}"/>
</Border.Background>
</Border>
ImageBrush documentation: ImageBrush.cs.
Vector art scales with DPI, can adapt to theme colors, and stays crisp.
<Path Data="M2 12 L9 19 L22 4"
Stroke="{DynamicResource AccentBrush}"
StrokeThickness="3"
StrokeLineCap="Round" StrokeLineJoin="Round"/>
Store geometry in resources for reuse:
<ResourceDictionary xmlns="https://github.com/avaloniaui">
<Geometry x:Key="IconCheck">M2 12 L9 19 L22 4</Geometry>
</ResourceDictionary>
Vector classes live under Avalonia.Media.
StreamGeometryContext for programmatic iconsGenerate vector shapes in code when you need to compose icons dynamically or reuse geometry logic:
var geometry = new StreamGeometry();
using (var ctx = geometry.Open())
{
ctx.BeginFigure(new Point(2, 12), isFilled: false);
ctx.LineTo(new Point(9, 19));
ctx.LineTo(new Point(22, 4));
ctx.EndFigure(isClosed: false);
}
IconPath.Data = geometry;
StreamGeometry and StreamGeometryContext live in StreamGeometryContext.cs. Remember to freeze geometry instances or share them via resources to reduce allocations.
Install the Avalonia.Svg.Skia package to render SVG assets natively:
<svg:SvgImage xmlns:svg="clr-namespace:Avalonia.Svg.Controls;assembly=Avalonia.Svg.Skia"
Source="avares://AssetsDemo/Assets/Images/logo.svg"
Stretch="Uniform" />
SVGs stay sharp at any DPI and can adapt colors if you parameterize them (e.g., replace fill attributes at build time). For simple icons, converting the path data into XAML keeps dependencies minimal.
Place fonts in Assets/Fonts. Register them in App.axaml via Global::Avalonia URI and specify the font face after #:
<Application.Resources>
<FontFamily x:Key="HeadingFont">avares://AssetsDemo/Assets/Fonts/Inter.ttf#Inter</FontFamily>
</Application.Resources>
Use the font in styles:
<Application.Styles>
<Style Selector="TextBlock.h1">
<Setter Property="FontFamily" Value="{StaticResource HeadingFont}"/>
<Setter Property="FontSize" Value="28"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
</Application.Styles>
Configure global font settings in AppBuilder:
AppBuilder.Configure<App>()
.UsePlatformDetect()
.With(new FontManagerOptions
{
DefaultFamilyName = "avares://AssetsDemo/Assets/Fonts/Inter.ttf#Inter",
FontFallbacks = new[] { new FontFallback { Family = "Segoe UI" }, new FontFallback { Family = "Roboto" } }
})
.StartWithClassicDesktopLifetime(args);
FontManagerOptions lives in FontManagerOptions.cs.
If fonts include multiple weights, specify them with FontWeight. If you ship multiple font files (Regular, Bold), ensure the #Family name is consistent.
You can inject fonts at runtime without restarting the app. Register an embedded collection and update resources:
using Avalonia.Media;
using Avalonia.Media.Fonts;
var baseUri = new Uri("avares://AssetsDemo/Assets/BrandFonts/");
var collection = new EmbeddedFontCollection(new Uri("fonts:brand"), baseUri);
FontManager.Current.AddFontCollection(collection);
Application.Current!.Resources["BodyFont"] = new FontFamily("fonts:brand#Brand Sans");
EmbeddedFontCollection pulls all font files under the provided URI using IAssetLoader. Removing the collection via FontManager.Current.RemoveFontCollection(new Uri("fonts:brand")) detaches it again.
Avalonia measures layout in DIPs (1 DIP = 1/96 inch). High DPI monitors scale automatically.
RenderOptions.BitmapInterpolationMode="None" for pixel art.RenderTargetBitmap or WriteableBitmap.RenderTargetBitmap and WriteableBitmap under Avalonia.Media.Imaging.
Bind brushes via DynamicResource so assets respond to theme changes. When a dictionary entry changes, ResourceDictionary.ResourcesChanged notifies every subscriber and controls update automatically:
<Application.Resources>
<SolidColorBrush x:Key="AvatarFallbackBrush" Color="#1F2937"/>
</Application.Resources>
<Ellipse Fill="{DynamicResource AvatarFallbackBrush}"/>
At runtime you can swap assets:
Application.Current!.Resources["AvatarFallbackBrush"] = new SolidColorBrush(Color.Parse("#3B82F6"));
To scope variants, wrap content in a ThemeVariantScope and supply dictionaries per variant:
<ThemeVariantScope RequestedThemeVariant="Dark">
<ThemeVariantScope.Resources>
<SolidColorBrush x:Key="AvatarFallbackBrush" Color="#E5E7EB"/>
</ThemeVariantScope.Resources>
<ContentPresenter Content="{Binding}"/>
</ThemeVariantScope>
ThemeVariantScope relies on IResourceHost to merge dictionaries in order (scope → parent scope → application). To inspect all merged resources in DevTools, open Resources and observe how RequestedThemeVariant switches dictionaries.
RenderOptions area) for "not found" messages.AssetLoader.Exists(uri) to verify at runtime:if (!AssetLoader.Exists(uri))
throw new FileNotFoundException($"Asset {uri} not found");
Application.Current.Resources.ResourcesChanged (or scope-specific hosts) to log when dictionaries update, especially when debugging DynamicResource refreshes.<Grid ColumnDefinitions="Auto,24,Auto" RowDefinitions="Auto,12,Auto">
<Image Width="160" Height="80" Stretch="Uniform"
Source="avares://AssetsDemo/Assets/Images/logo.png"/>
<Rectangle Grid.Column="1" Grid.RowSpan="3" Width="24"/>
<Ellipse Grid.Column="2" Width="96" Height="96">
<Ellipse.Fill>
<ImageBrush Source="avares://AssetsDemo/Assets/Images/avatar.png" Stretch="UniformToFill"/>
</Ellipse.Fill>
</Ellipse>
<Rectangle Grid.Row="1" Grid.ColumnSpan="3" Height="12"/>
<Canvas Grid.Row="2" Grid.Column="0" Width="28" Height="28">
<Path Data="M2 14 L10 22 L26 6"
Stroke="{DynamicResource AccentBrush}"
StrokeThickness="3" StrokeLineCap="Round" StrokeLineJoin="Round"/>
</Canvas>
<TextBlock Grid.Row="2" Grid.Column="2" Classes="h1" Text="Asset gallery"/>
</Grid>
Assets/Brand.axaml, include it with <ResourceInclude Source="avares://AssetsDemo/Assets/Brand.axaml"/>, and verify lookups succeed from a control in another assembly.SvgImage) but falls back to a PNG Bitmap on platforms where the SVG package is missing.Bitmap.DecodeToWidth and compare memory usage against eagerly loading the original stream.EmbeddedFontCollection at runtime and swap your typography resources by updating Application.Current.Resources["BodyFont"].ThemeVariantScope.RequestedThemeVariant at runtime and confirm DynamicResource-bound brushes and images update without recreating controls.ResourceProvider.cs, ResourceDictionary.csAssetLoader.cs, ResourceInclude.csBitmap.csStreamGeometryContext.cs, Path.csFontManager.cs, EmbeddedFontCollection.csThemeVariantScope.cs, ResourcesChangedHelper.csRenderOptions.cs{StaticResource} and {DynamicResource}?IAssetLoader instead of the static AssetLoader helper?StreamGeometry/SVG but falls back to a bitmap?Application.Current.Resources?What's next