A Markdown preview control for AvaloniaUI — renders Markdown with math (KaTeX), syntax highlighting (highlight.js), diagrams (Mermaid / PlantUML), and video embeds.
🌏 中文文档
dotnet add package AvalonMarkdownEverything you need lives on two types:
MarkdownView— show a Markdown document and react to the user (links, scroll, headings).MarkdownThemeViewModel— style how that document looks.
Add the control to a window and feed it Markdown content.
<Window xmlns="https://github.com/avaloniaui"
xmlns:md="clr-namespace:AvalonMarkdown.Views;assembly=AvalonMarkdown">
<md:MarkdownView x:Name="Preview" />
</Window>Bind the Text property (this is the recommended way — it updates automatically):
<md:MarkdownView Text="{Binding MarkdownContent}" />Or render explicitly after the control is ready:
Preview.OnReady += async (_, _) =>
{
await Preview.RenderMarkdownAsync("# Hello World\n\n**Bold** *Italic*");
};When content is empty you can show a hint with Placeholder (blank by default):
<md:MarkdownView Text="{Binding MarkdownContent}" Placeholder="Type something…" />Every hyperlink click fires LinkClicked. Set Handled = true to take over navigation yourself; otherwise the link opens in the system browser.
Preview.LinkClicked += (_, e) =>
{
if (e.Url.StartsWith("app://"))
{
OpenInternalPage(e.Url); // your in-app routing
e.Handled = true; // suppress the default browser launch
}
};Markdown headings are linkable from within the same page, so [See §2](#2-text-formatting)-style links just work.
To build a clickable table of contents in your own UI:
var headings = await Preview.GetHeadingsAsync(); // [{ Text, Level }, …]
var ok = await Preview.NavigateToHeadingAsync("Chapter 2"); // scroll to a headingReport/restore the vertical read position — handy for editor ⇄ preview split views:
double? progress = await Preview.GetScrollProgressAsync(); // 0–100
await Preview.ScrollToProgressAsync(50);Content updates auto-scroll to the newest content, and pause while you operate the scrollbar. Behaviour can be tuned with ApplyConfigAsync:
await Preview.ApplyConfigAsync("setPreviewConfig({ autoScrollOnUpdate: true, autoScrollCooldown: 2000 })");ErrorOccurred and ConsoleMessage events let you surface or log any rendering/script message.
| Member | Type | What it does |
|---|---|---|
Text |
string? |
Markdown content (two-way bindable) |
Placeholder |
string? |
Hint shown when content is empty |
OnReady |
event |
Fires when the preview is ready to render |
LinkClicked |
event |
Fires on hyperlink clicks; Handled = true takes over navigation |
ErrorOccurred |
event |
Fires on recoverable internal errors |
ConsoleMessage |
event |
Fires for each WebView console message (log/warn/error) |
RenderMarkdownAsync(text) |
Task |
Renders Markdown content |
GetHeadingsAsync() |
Task<IReadOnlyList<MarkdownHeading>> |
Lists all headings (Text, Level) |
NavigateToHeadingAsync(heading) |
Task<bool> |
Scrolls to the heading; returns whether it was found |
GetScrollProgressAsync() |
Task<double?> |
Current vertical progress (0–100) |
ScrollToProgressAsync(percent) |
Task |
Scrolls to a vertical progress position (0–100) |
ApplyConfigAsync(expression) |
Task |
Applies preview behaviour configuration |
ApplyCustomCssAsync(css) |
Task |
Applies a full custom stylesheet |
RestartPreviewAsync() |
Task |
Recreates the preview page |
Configures how the document looks: colors, typography, code highlighting, and diagram styling. Every property carries a [Description] (hover it in IntelliSense to see what it controls).
MarkdownThemeView is a ready-made editor panel for the properties below. Point it at your preview; any change is applied immediately.
<md:MarkdownView x:Name="Preview" />
<md:MarkdownThemeView Target="{Binding #Preview}" />The panel is collapsed by default — click its header to expand it.
Create a MarkdownThemeViewModel, change the properties you care about, and let it push changes to one or more previews:
var theme = new MarkdownThemeViewModel
{
BgR = 30, BgG = 30, BgB = 30, // page background
TextR = 212, TextG = 212, TextB = 212, // body text
HljsKeyword = "#569cd6",
MermaidContainerPadding = 8,
};
theme.RegisterRenderer(myPreview);
theme.StartAutoApply(); // any further property change auto-appliesPrefer a one-shot apply instead?
await myPreview.ApplyCustomCssAsync(theme.GenerateCss());The same styling applies whatever light/dark theme your app currently uses.
| Group | Covers |
|---|---|
| Core colors | Page background, text, links, headings, inline code, borders |
| Surface colors | Secondary background (e.g. code headers), secondary text, inline-code background, code-block background, table header |
| Typography | Code font size; code-block / pre corner radius |
| Code highlighting | Color of each syntax token (keyword, string, number, comment, …) |
| Diagrams | Mermaid & PlantUML container background / padding / margin / radius; PlantUML dark invert strength |
Colors — each surface is three int channels R / G / B (0–255); each XxxHex is a read-only #RRGGBB preview.
| Surface | Channel properties | Preview |
|---|---|---|
| Background | BgR BgG BgB |
BgHex |
| Body text | TextR TextG TextB |
TextHex |
| Link | LinkR LinkG LinkB |
LinkHex |
| Heading | HeadingR HeadingG HeadingB |
HeadingHex |
| Inline code text | CodeR CodeG CodeB |
CodeHex |
| Border | BorderR BorderG BorderB |
BorderHex |
| Secondary background | BgSecR BgSecG BgSecB |
BgSecHex |
| Secondary text | TextSecR TextSecG TextSecB |
TextSecHex |
| Inline-code background | CodeBgR CodeBgG CodeBgB |
CodeBgHex |
| Code block background | PreBgR PreBgG PreBgB |
PreBgHex |
| Table header background | TableBgR TableBgG TableBgB |
TableBgHex |
Typography & layout (double / int, in px unless noted)
| Property | Meaning |
|---|---|
CodeFontSize |
Code font size |
BorderRadius |
Code-block / pre corner radius |
MermaidBgR MermaidBgG MermaidBgB |
Mermaid container background (0–255) |
MermaidContainerPadding · MermaidContainerMargin · MermaidBorderRadius |
Mermaid container padding / margin / radius |
PumlBgR PumlBgG PumlBgB |
PlantUML container background (0–255) |
PumlContainerPadding · PumlContainerMargin · PumlBorderRadius |
PlantUML container padding / margin / radius |
PumlDarkInvert |
PlantUML dark invert strength (0–1) |
Code highlighting — highlight.js token colors (string, e.g. "#569cd6"):
HljsKeyword, HljsLiteral, HljsSymbol, HljsName, HljsBuiltIn, HljsType, HljsClass, HljsNumber, HljsString, HljsMetaString, HljsTitle, HljsTitleClass, HljsTitleClassInherited, HljsParams, HljsVariable, HljsTemplateVariable, HljsComment, HljsQuote, HljsAttr, HljsAttribute, HljsMeta, HljsTag, HljsSelectorAttr, HljsSelectorClass, HljsSelectorId, HljsSelectorPseudo, HljsSelectorTag, HljsBullet, HljsSection, HljsLink, HljsRegexp, HljsTemplateTag, HljsDoctag, HljsBackground, HljsForeground — plus diff markers HljsAdditionBg/HljsAdditionColor and HljsDeletionBg/HljsDeletionColor.
The built-in
MarkdownThemeViewpanel covers colors, typography, and diagrams; code-highlighting tokens are configured in code via theHljs*properties above (or bound in your own UI).
The repository contains runnable sample apps (AvalonMarkdown.Test.Desktop and Android/iOS/Browser variants) that exercise both levels.