ImageLoader
The Markdown control resolves image URLs through a MarkdownImageLoader. Set Markdown.ImageLoader on the control and every image in its document uses it.
This control is available as part of Avalonia Pro or higher.
Default behavior
No loader is set by default, so images are not loaded until you supply one. The MarkdownImageLoader base class resolves the http://, https:// and file:// schemes, and returns an IImage on success or null on failure. For most common uses, you assign the loader directly and write no code:
<Markdown Text="">
<Markdown.ImageLoader>
<MarkdownImageLoader />
</Markdown.ImageLoader>
</Markdown>
Subclass the loader if you need a scheme, image format, authentication or caching strategy the base class does not cover.
Example: loading SVG images
Required packages
To use the custom image loader example below, you need to install the following NuGet package:
dotnet add package Avalonia.Svg.Skia
Implementation
Below is an example of a custom image loader that supports SVG images:
using Avalonia.Controls;
using Avalonia.Media.Imaging;
using Avalonia.Svg.Skia;
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
public class CustomImageLoader : MarkdownImageLoader
{
public override async Task<IImage?> LoadImageAsync(string url)
{
IImage? image = null;
if (Uri.TryCreate(url, UriKind.Absolute, out var uri))
{
Stream? stream = null;
if (uri.Scheme == "http" || uri.Scheme == "https")
{
stream = await DownloadImage(uri);
}
else if (uri.Scheme == "file" && File.Exists(uri.LocalPath))
{
stream = File.OpenRead(uri.LocalPath);
}
if (stream is null)
{
return null;
}
using (stream)
{
if (IsSvgFile(stream))
{
var svg = new SvgImage
{
Source = SvgSource.LoadFromStream(stream)
};
image = svg;
}
else
{
image = new Bitmap(stream);
}
}
}
return image;
}
private static async Task<Stream> DownloadImage(Uri url)
{
using var client = new HttpClient();
using var response = await client.GetAsync(url).ConfigureAwait(false);
using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0;
return memoryStream;
}
private static bool IsSvgFile(Stream stream)
{
if (stream == null || stream.Length == 0)
return false;
try
{
const int bufferSize = 512;
byte[] buffer = new byte[Math.Min(bufferSize, stream.Length)];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
string header = Encoding.UTF8.GetString(buffer, 0, bytesRead);
return header.Contains("<svg", StringComparison.OrdinalIgnoreCase);
}
catch
{
return false;
}
finally
{
stream.Position = 0;
}
}
}
Usage
Markdown.ImageLoader is an attached property. Set it on the control itself.
XAML
<Window xmlns="https://github.com/avaloniaui"
xmlns:local="using:MarkdownSample">
<Markdown Text="">
<Markdown.ImageLoader>
<local:CustomImageLoader />
</Markdown.ImageLoader>
</Markdown>
</Window>
To share one loader across several controls, declare it as a resource and point each control at it:
<Window.Resources>
<local:CustomImageLoader x:Key="ImageLoader" />
</Window.Resources>
<StackPanel>
<Markdown Markdown.ImageLoader="{StaticResource ImageLoader}" Text="{Binding First}" />
<Markdown Markdown.ImageLoader="{StaticResource ImageLoader}" Text="{Binding Second}" />
</StackPanel>
Code
var loader = new CustomImageLoader();
// Every image in this control's document uses it
markdown.ImageLoader = loader;
// Or through the static accessor, which takes any StyledElement
Markdown.SetImageLoader(markdown, loader);
To resolve one image differently from the rest, set MarkdownImage.ImageLoader on that element. A value set on one image wins over the value supplied by the control.
Image loading is deferred until both the URL (set automatically from the Markdown source) and a loader are available, and assigning a loader later re-resolves images already in the document. This decouples the document model from image resolution.
When to use
You should implement a custom MarkdownImageLoader whenever the default image resolution does not meet your needs. For example, you might need to render SVG images, load images from a remote server that requires authentication, or apply a caching strategy to avoid repeated downloads. A custom loader gives you full control over how image URIs are resolved and what image types your Markdown control can display.