Build your first UI in XAML
This route renders the same first interface through Forma's build-time XAML compiler. It uses an
x:Class root, typed one-way and two-way bindings, namescope lookup, and a button event. Release
builds contain injected view construction and binding code, not source XAML or a runtime reader.
Run the compiled view
# MonoGame
dotnet run --project samples/Forma.QuickStart.MonoGame/Forma.QuickStart.MonoGame.csproj \
--configuration Release -p:FormaRuntime=MonoGame -- --xaml
# FNA
dotnet run --project samples/Forma.QuickStart.FNA/Forma.QuickStart.FNA.csproj \
--configuration Release -p:FormaRuntime=FNA -- --xaml
The source checkout references Forma.Xaml.Build as a private build project. After the first NuGet
preview is indexed, package consumers will pair Forma.Xaml.Build.MonoGame or
Forma.Xaml.Build.FNA with the matching core package and version.
Define the view
Docfx stages this block from samples/Forma.QuickStart/FirstView.xaml, the file compiled by both
runtime hosts:
<BoxContainer
xmlns="https://forma.dev/xaml"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Forma.QuickStart"
x:Class="Forma.QuickStart.FirstView"
x:DataType="local:FirstViewModel"
x:Name="QuickStartRoot"
Separation="12">
<Label Text="Your first Forma XAML UI" />
<LineEdit
x:Name="NameInput"
Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
PlaceholderText="Your name"
CustomMinimumSize="320,44" />
<Button
x:Name="GreetButton"
Text="Greet"
CustomMinimumSize="120,44" />
<Label Text="{Binding Greeting}" />
</BoxContainer>
x:DataType makes every binding compile against FirstViewModel. The Name field updates on each
text change through a two-way binding. Greeting is one-way and refreshes when the view model raises
PropertyChanged.
Load and wire the class
The code-behind and view model are also compiled directly by both hosts:
// SPDX-License-Identifier: MIT
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Forma.Xaml;
namespace Forma.QuickStart;
public sealed class FirstView : BoxContainer
{
public FirstView() : this(new FirstViewModel()) { }
public FirstView(FirstViewModel viewModel) : base(Orientation.Vertical)
{
DataContext = viewModel;
FormaXamlLoader.Load(this);
var scope = NameScope.GetNameScope(this)
?? throw new InvalidOperationException("FirstView did not create a namescope.");
scope.Find<Button>("GreetButton").Pressed += (_, _) => viewModel.Greet();
}
}
public sealed class FirstViewModel : INotifyPropertyChanged
{
private string _name = "Player";
private string _greeting = "Ready.";
public event PropertyChangedEventHandler? PropertyChanged;
public string Name
{
get => _name;
set => Set(ref _name, value);
}
public string Greeting
{
get => _greeting;
private set => Set(ref _greeting, value);
}
public void Greet() => Greeting = $"Hello, {Name.Trim()}!";
private void Set<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return;
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
FirstView derives from non-sealed BoxContainer; its constructor chooses vertical orientation
before FormaXamlLoader.Load(this) populates it. XAML cannot set Orientation because that property
is constructor-owned. The generated namescope resolves GreetButton, and the event calls the typed
view model.
Debug hot reload
Debug builds reference Forma.Xaml.HotReload and copy development XAML beside the executable. Run:
dotnet run --project samples/Forma.QuickStart.MonoGame/Forma.QuickStart.MonoGame.csproj \
--configuration Debug -p:FormaRuntime=MonoGame -- --xaml
Use the FNA host and FormaRuntime=FNA for its peer. While the process runs, edit
samples/Forma.QuickStart/FirstView.xaml; the registered service recompiles and replaces the root at
a frame boundary. Diagnostics are written by the service/compiler instead of being deferred to
Release.
Hot reload is a development aid, not the production loading model. The clean-cache gate separately
builds and starts Debug, then verifies Release output excludes Forma.Xaml.HotReload.dll,
Forma.Xaml.Compiler.dll, XamlX.dll, and XamlX.IL.Cecil.dll:
FORMA_RUNTIME=MonoGame bash scripts/check-quick-start.sh
FORMA_RUNTIME=FNA bash scripts/check-quick-start.sh
For namespaces, resources, templates, selectors, binding modes, diagnostics, command-line tooling, and compatibility rules, continue with the Forma XAML language contract.