25. Design-time tooling and the XAML Previewer

Goal

Why this matters

Prerequisites

1. Previewer pipeline and transport

IDE hosts spawn a preview process that loads your view or resource dictionary over the remote protocol. DesignWindowLoader spins up RemoteDesignerEntryPoint, which compiles your project with the design configuration, loads the control, then streams rendered frames back to the IDE through Avalonia.Remote.Protocol.DesignMessages.

Key components:

Because the previewer compiles your project, build errors surface exactly as in dotnet build. Keep AvaloniaResource items and generated code in sync or the previewer will refuse to load.

2. Mock data with Design.DataContext

Provide lightweight POCOs or design view models for preview without touching production services.

Sample POCO:

namespace MyApp.Design;

public sealed class SamplePerson
{
    public string Name { get; set; } = "Ada Lovelace";
    public string Email { get; set; } = "ada@example.com";
    public int Age { get; set; } = 37;
}

Usage in XAML:

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:design="clr-namespace:Avalonia.Controls;assembly=Avalonia.Controls"
             xmlns:samples="clr-namespace:MyApp.Design" x:Class="MyApp.Views.ProfileView">
  <design:Design.DataContext>
    <samples:SamplePerson/>
  </design:Design.DataContext>

  <StackPanel Spacing="12" Margin="16">
    <TextBlock Classes="h1" Text="{Binding Name}"/>
    <TextBlock Text="{Binding Email}"/>
    <TextBlock Text="Age: {Binding Age}"/>
  </StackPanel>
</UserControl>

At runtime the transformer removes Design.DataContext; real view models take over. For complex forms, expose design view models with stub services but avoid heavy logic. When you need multiple sample contexts, expose them as static properties on a design-time provider class and bind with {x:Static}.

Design.IsDesignMode checks

Guard expensive operations:

if (Design.IsDesignMode)
    return; // skip service setup, timers, network

Place guards in view constructors, OnApplyTemplate, or view model initialization.

3. Design.Width/Height & DesignStyle

Set design canvas size:

<StackPanel design:Design.Width="320"
            design:Design.Height="480"
            design:Design.DesignStyle="{StaticResource DesignOutlineStyle}">

</StackPanel>

DesignStyle can add dashed borders or backgrounds for preview only (define style in resources).

Example design style:

<Style x:Key="DesignOutlineStyle">
  <Setter Property="Border.BorderThickness" Value="1"/>
  <Setter Property="Border.BorderBrush" Value="#808080"/>
</Style>

4. Preview resource dictionaries with Design.PreviewWith

Previewing a dictionary or style requires a host control:

<ResourceDictionary xmlns="https://github.com/avaloniaui"
                    xmlns:design="clr-namespace:Avalonia.Controls;assembly=Avalonia.Controls"
                    xmlns:views="clr-namespace:MyApp.Views">
  <design:Design.PreviewWith>
    <Border Padding="16" Background="#1f2937">
      <StackPanel Spacing="8">
        <views:Badge Content="1" Classes="success"/>
        <views:Badge Content="Warning" Classes="warning"/>
      </StackPanel>
    </Border>
  </design:Design.PreviewWith>


</ResourceDictionary>

PreviewWith ensures the previewer renders the host when you open the dictionary alone.

5. Inspect previewer logs and compilation errors

6. Extend design-time services

RemoteDesignerEntryPoint registers services in a tiny IoC container separate from your production DI. Override or extend them by wiring a helper that only executes when Design.IsDesignMode is true:

using Avalonia;
using Avalonia.Controls;

public static class DesignTimeServices
{
    public static void Register()
    {
        if (!Design.IsDesignMode)
            return;

        AvaloniaLocator.CurrentMutable
            .Bind<INavigationService>()
            .ToConstant(new FakeNavigationService());
    }
}

Call DesignTimeServices.Register(); inside BuildAvaloniaApp().AfterSetup(...) so the previewer receives the fake services without altering production setup. Use this pattern to swap HTTP clients, repositories, or configuration with in-memory fakes while keeping runtime untouched.

7. IDE-specific tips

Visual Studio

Rider

VS Code

General

8. Troubleshooting & best practices

Issue Fix
Previewer blank/crashes Guard code with Design.IsDesignMode; simplify layout; ensure no blocking calls in constructor
Design-only styles appear at runtime Design.* stripped at runtime; if they leak, inspect generated .g.cs to confirm transformer ran
Resource dictionary preview fails Add Design.PreviewWith; ensure resources compiled (check AvaloniaResource includes)
Sample data not showing Confirm namespace mapping correct, sample object constructs without exceptions, and preview log shows DataContext attachment
Slow preview Remove animations/effects temporarily; large data sets or virtualization can slow preview host
Transport errors (SocketException) Restart previewer. Firewalls can block the loopback port used by Avalonia.Remote.Protocol

9. Automation

10. Practice exercises

  1. Add Design.DataContext to a complex form, providing realistic sample data (names, email, totals). Ensure preview shows formatted values.
  2. Set Design.Width/Height to 360x720 for a mobile view; use Design.DesignStyle to highlight layout boundaries.
  3. Create a resource dictionary for badges; use Design.PreviewWith to render multiple badge variants side-by-side.
  4. Open the previewer diagnostics window, reproduce a binding failure, and note how DesignMessages trace the failing binding path.
  5. Guard service initialization with if (Design.IsDesignMode) and confirm preview load improves.
  6. Bonus: implement a design-only service override and register it from BuildAvaloniaApp().AfterSetup(...).

Look under the hood (source bookmarks)

Check yourself

What's next