Skip to main content

ImageLoader

The Markdown control resolves image URLs through a MarkdownImageLoader. Set Markdown.ImageLoader on the control and every image in its document uses it.

info

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="![Logo](https://example.com/logo.png)">
<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="![SVG Image](https://example.com/image.svg)">
<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.

See also​