Table of Contents

Your first screen

This page builds a small main menu: a title and two buttons, described in markup and connected to your game's code through data binding. It assumes you've finished Getting started with Stride or with MonoGame.

1. Describe the menu in markup

Add a file named Menu.xml to your game project:

<!-- Data context: an object with Title, PlayCommand and QuitCommand properties. -->
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Vertical">
  <TextBlock FontSize="32" Margin="0,0,0,24" HorizontalAlignment="Center" Text="{Binding Path=Title}" />
  <Button Padding="24,8" Margin="0,0,0,8" Command="{Binding Path=PlayCommand}">Play</Button>
  <Button Padding="24,8" Command="{Binding Path=QuitCommand}">Quit</Button>
</StackPanel>

Set its Copy to Output Directory property to "Copy if newer", or add this to your project file:

<ItemGroup>
  <None Update="Menu.xml" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

The markup is plain XML: each element is an IcyUI control, and each attribute sets a property. The three {Binding Path=...} values aren't fixed text: they read Title, PlayCommand and QuitCommand from the menu's data context, an object you supply in code.

2. Load it and give it data

Replace your startup script with this one (and pick MenuStartup in Add component):

using System.Windows.Input;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Icy.Stride.Configuration;
using Stride.Engine;

/// <summary>
/// The menu's data: what the markup's {Binding} expressions read. ObservableObject raises PropertyChanged, so the
/// screen updates when Title changes.
/// </summary>
public sealed class MenuViewModel : ObservableObject
{
    private string title = "My Game";

    public string Title
    {
        get => title;
        set => SetProperty(ref title, value);
    }

    public ICommand PlayCommand { get; } = new RelayCommand(() => { /* Start the game. */ });

    public ICommand QuitCommand { get; } = new RelayCommand(() => { /* Exit. */ });
}

/// <summary>Replaces IcyUIStartup: loads Menu.xml (copied to the output folder) onto the main canvas.</summary>
public class MenuStartup : StartupScript
{
    public override void Start() =>
        Game.UseIcyUI().MainCanvas.LoadMarkup("Menu.xml", new MenuViewModel());
}

LoadMarkup(string, object?) loads the markup file from the game's output folder, sets the view model as the menu's DataContext so every {Binding} inside reads from it, and adds the menu to the canvas. Clicking a button, or activating it with the keyboard or a gamepad, runs its command.

What you built

A menu whose layout lives in markup and whose behaviour lives in C#. Change the markup to restyle or rearrange the menu without touching code. Because MenuViewModel derives from ObservableObject (from CommunityToolkit.Mvvm, which IcyUI already depends on), setting its Title while the game runs updates the title on screen. A view model that doesn't raise PropertyChanged is read only once, when the binding is created.

Next