How To: Avalonia UI Overlay

Any Avalonia control placed in the same layout container as MonoGameControl is composited on top of the game surface. Avalonia handles the compositing automatically — no extra render pass, no blitting.

Reference files: Overlay/OverlayWindow.axaml · Overlay/OverlayViewModel.cs · Advanced/MainWindow.axaml


Basic pattern: Panel as root

Use Panel as the root container. MonoGameControl fills the panel first; any additional controls are layered on top in z-order:

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mg="clr-namespace:Thunder.MonoGame.Avalonia.Controls;assembly=MonoGame.Framework"
        x:Class="MyApp.MainWindow">
  <Panel>

    <!-- Game surface — fills the panel -->
    <mg:MonoGameControl x:Name="GameHost" />

    <!-- Avalonia overlay — composited on top -->
    <Button HorizontalAlignment="Right"
            VerticalAlignment="Bottom"
            Margin="16"
            Content="Pause" />

  </Panel>
</Window>

The Overlay sample (Overlay/OverlayWindow.axaml) uses exactly this structure with a data-bound pause button.


MVVM pattern

The ViewModel owns the game instance and exposes commands. XAML binds both Game and the overlay controls declaratively — no code-behind logic is required:

<Panel>
  <mg:MonoGameControl Game="{Binding Game}" />

  <Button HorizontalAlignment="Right"
          VerticalAlignment="Bottom"
          Margin="16"
          Command="{Binding TogglePauseCommand}"
          Content="{Binding PauseButtonText}" />
</Panel>
// ViewModel
public partial class MainViewModel : ObservableObject
{
    public MyGame Game { get; } = new MyGame();

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(PauseButtonText))]
    private bool _isPaused;

    public string PauseButtonText => _isPaused ? "Resume" : "Pause";

    [RelayCommand]
    private void TogglePause() => IsPaused = !IsPaused;

    partial void OnIsPausedChanged(bool value) => Game.IsPaused = value;
}

See Overlay/OverlayViewModel.cs for the complete working example.


Advanced pattern: draggable overlay

The Advanced sample uses a Canvas over the MonoGameControl. Controls on a Canvas are positioned via Canvas.Left / Canvas.Top, which the code-behind updates during pointer drag events:

<Panel>
  <mg:MonoGameControl x:Name="Host" Game="{Binding Game}" />

  <Canvas x:Name="OverlayCanvas"
          HorizontalAlignment="Stretch"
          VerticalAlignment="Stretch">

    <StackPanel x:Name="OverlayPanel">
      <Border x:Name="DragHandle" Cursor="SizeAll" ...>
        <TextBlock Text="≡  Drag me" />
      </Border>
      <Button Command="{Binding TogglePauseCommand}" ... />
    </StackPanel>

  </Canvas>
</Panel>

See Advanced/MainWindow.axaml for the full draggable overlay with snap-to-corner buttons, bouncer controls, and multi-screen toggles.


Compositing note

Avalonia composites the overlay and the game surface on the same frame. There is no separate overlay pass or additional latency — the overlay controls appear at the same frame rate as the game.

latest ▼