XAML UI в Space Station 14

SS14_#2 // Полный гайд по UI разработке // Время чтения: 25 мин

Для кого ориентирован данный гайд?

Допустим, вы уже прочитали все доступные документации по программированию Space Station 14, но всё ещё не понимаете, как именно что-то должно работать. Этот гайд для новичков, которые хотят научиться создавать пользовательские интерфейсы (UI) для SS14.

Вводная

Представьте ситуацию: вы прочитали ВСЕ документации по SS14. Все.
Вы знаете, что такое Entity, Component, System... Но когда дело доходит до создания окошка с кнопочкой - вы не понимаете что это такое.

Спокойно! Этот гайд - поможет базово ознакомиться с UI в разработке SS14. Посмотрим как можно сделать: окна, кнопки, которые реально работают, и интерфейсы, от которых игроки не будут в ужасе закрывать игру ахаха.

Архитектура Content & Engine

SS14 есть слои в сборках. Вот краткое пояснение:

RobustToolbox (Engine) - "Фундамент"

Это база. Тут живут скучные, но важные вещи:

  • 1 - Математика и физика (да, без них никуда, как бы нам этого ни хотелось)
  • 2 - Рендер графики (чтобы вы видели свои творения, а не чёрный экран)
  • 3 - Сетевое взаимодействие (чтобы игроки могли друг друга бить кувалдами)
  • 4 - Система UI (включая XAML, XML, CSS)

Content (Контент)

В сборке вы увидите папки Content - это ваша песочница:

  • Content.Client - тут живут ваши окна, кнопочки и прочий UI. Ваш второй дом.
  • Content.Server - тут живёт логика сервера. Он решает, кто жил, а кто умер.
  • Content.Shared - общий код. Швейцарский нож: и клиент, и сервер могут им пользоваться.

Важный инсайт: 99% времени вы будете ковыряться в Content. Это нормально.

Система UI: XAML + Code-behind

UI в SS14 использует XAML - язык разметки на основе XML для описания структуры интерфейсов.
XAML позволяет описывать деревья контролов в декларативном стиле.
Каждое изменение требует перекомпиляции - горячая перезагрузка не поддерживается.

Что такое XAML?

XAML (eXtensible Application Markup Language) - это язык разметки, но который выглядит как XML и... (ладно, это и есть XML)

С его помощью вы описываете:

  • - Кнопки (чтобы игроки могли на них тыкать)
  • - Текстовые поля (чтобы игроки могли писать там глупости ехех)
  • - Панели (чтобы всё это не развалилось)
  • - Списки (потому что все любят списки)
  • - И вообще всё, что можно увидеть глазами

Что такое Code-behind?

Code-behind - это ваш C# класс, который дружит с XAML файлом. Он:

  • - Слушает, что делает пользователь (нажал кнопку? Получи реакцию!)
  • - Обновляет данные в UI (чтобы игроки видели актуальную информацию)
  • - Общается с сервером (потому что без сервера мы никто)

Структура UI файла

Каждый UI компонент в SS14 состоит из двух файлов:

file structure

MyWindow.xaml # Разметка UI (структура, элементы)
MyWindow.xaml.cs # Логика UI (обработчики событий, связь с данными)

Пример простой структуры

MyWindow.xaml:

xml
<DefaultWindow xmlns="https://spacestation14.io"
            Title="Моё окно"
            MinSize="300 200">
    <BoxContainer Orientation="Vertical">
        <Label Text="Привет, мир!" />
        <Button Name="MyButton" Text="Нажми меня" />
    </BoxContainer>
</DefaultWindow>

MyWindow.xaml.cs:

csharp
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;

namespace Content.Client.MyModule.UI;

[GenerateTypedNameReferences]
public sealed partial class MyWindow : DefaultWindow
{
    public MyWindow()
    {
        RobustXamlLoader.Load(this);

        // Подписываемся на событие нажатия кнопки
        MyButton.OnPressed += _ =>
        {
            // Действие при нажатии
        };
    }
}

Базовые элементы управления (Controls)

1. Контейнеры (Containers)

Контейнеры используются для группировки и расположения элементов.

BoxContainer - универсальный контейнер

xml
<!-- Вертикальное расположение (элементы сверху вниз) -->
<BoxContainer Orientation="Vertical">
    <Label Text="Первый" />
    <Label Text="Второй" />
    <Label Text="Третий" />
</BoxContainer>

<!-- Горизонтальное расположение (элементы слева направо) -->
<BoxContainer Orientation="Horizontal">
    <Label Text="Левый" />
    <Label Text="Правый" />
</BoxContainer>

Важные свойства BoxContainer:

  • Orientation="Vertical" - вертикальное расположение
  • Orientation="Horizontal" - горизонтальное расположение
  • SeparationOverride="10" - отступ между элементами в пикселях
  • Margin="10 5" - внешний отступ (горизонтальный вертикальный)

ScrollContainer - контейнер с прокруткой

xml
<ScrollContainer VerticalExpand="True" HScrollEnabled="False">
    <BoxContainer Name="ContentList" Orientation="Vertical">
        <!-- Много элементов, которые можно прокручивать -->
    </BoxContainer>
</ScrollContainer>

GridContainer - сетка

xml
<GridContainer Columns="3">
    <Label Text="1,1" />
    <Label Text="1,2" />
    <Label Text="1,3" />
    <Label Text="2,1" />
    <Label Text="2,2" />
    <Label Text="2,3" />
</GridContainer>

PanelContainer - контейнер с фоном

xml
<PanelContainer StyleClasses="BackgroundPanel">
    <Label Text="Текст на фоне" />
</PanelContainer>

2. Текстовые элементы

Label - простая надпись

xml
<!-- Простой текст -->
<Label Text="Привет, мир!" />

<!-- С локализацией -->
<Label Text="{Loc 'my-window-title'}" />

<!-- С выравниванием -->
<Label Text="По центру" HorizontalAlignment="Center" />

<!-- Со стилем -->
<Label Text="Заголовок" StyleClasses="LabelHeading" />

<!-- С ограничением ширины и переносом текста -->
<Label Text="Длинный текст..." SetWidth="200" />

Часто используемые стили Label:

  • LabelHeading - заголовок
  • LabelSubText - подзаголовок (мелкий текст)
  • LabelBig - большой текст
  • LabelKeyText - текст для клавиш

RichTextLabel - текст с форматированием

xml
<RichTextLabel Name="Description" SetWidth="300" />

В C# коде можно установить форматированный текст:

csharp
Description.SetMessage(FormattedMessage.FromMarkup
("Текст с [color=red]красным[/color] словом"));

3. Кнопки

Button - обычная кнопка

xml
<!-- Простая кнопка -->
<Button Text="Нажать" />

<!-- Кнопка с Name для доступа в коде -->
<Button Name="ConfirmButton" Text="Подтвердить" />

<!-- Кнопка-переключатель -->
<Button Name="ToggleLight" Text="Свет" ToggleMode="True" />

<!-- Кнопка с иконкой -->
<Button Name="IconButton">
    <TextureRect TexturePath="/Textures/Interface/Icons/plus.png" />
</Button>

<!-- Кнопка во всю ширину -->
<Button Name="WideButton" Text="Широкая" HorizontalExpand="True" />

Стили кнопок:

  • ButtonSquare - квадратная кнопка
  • OpenLeft - открыта слева (для группы кнопок)
  • OpenRight - открыта справа
  • OpenBoth - открыта с обеих сторон
  • ButtonCaution - красная кнопка (опасность)
xml
<Button Text="Опасно" StyleClasses="ButtonCaution" />

TextureButton - кнопка с текстурой

xml
<TextureButton Name="CloseButton"
               StyleClasses="windowCloseButton"
               VerticalAlignment="Center" />

CheckBox - флажок

xml
<CheckBox Name="EnableSound" Text="Включить звук" />

4. Поля ввода

LineEdit - однострочное поле ввода

xml
<!-- Простое поле -->
<LineEdit Name="NameInput" />

<!-- С плейсхолдером -->
<LineEdit Name="SearchBar" PlaceHolder="Поиск..." />

<!-- С начальным текстом -->
<LineEdit Name="NameEdit" Text="Имя" />

<!-- Во всю ширину -->
<LineEdit Name="WideInput" HorizontalExpand="True" />

SpinBox - ввод чисел

xml
<SpinBox Name="AmountInput" Value="1" MinWidth="100" />

FloatSpinBox - ввод дробных чисел

xml
<FloatSpinBox Name="RatioInput" Value="1.0" MinWidth="80" />

5. Выбор из списка

OptionButton - выпадающий список

xml
<OptionButton Name="CategorySelector" HorizontalExpand="True" />

В C# коде:

csharp
// Добавление вариантов
CategorySelector.AddItem("Вариант 1", 1);
CategorySelector.AddItem("Вариант 2", 2);

// Получение выбранного
var selectedId = CategorySelector.SelectedId;

ItemList - список элементов

xml
<ScrollContainer VerticalExpand="True">
    <ItemList Name="ItemsList" VerticalExpand="True" />
</ScrollContainer>

6. Прогресс и индикаторы

ProgressBar - полоса прогресса

xml
<ProgressBar Name="HealthBar"
             MinValue="0"
             MaxValue="100"
             Value="75"
             HorizontalExpand="True" />

7. Изображения

TextureRect - изображение

xml
<TextureRect TexturePath="/Textures/Interface/Icons/star.png"
             Stretch="KeepAspectCentered"
             SetSize="32 32" />

SpriteView - просмотр спрайта сущности

xml
<SpriteView Name="EntityPreview"
            OverrideDirection="South"
            Scale="2 2"
            SetSize="64 64" />

Окна (Windows)

DefaultWindow - базовое окно

xml
<DefaultWindow xmlns="https://spacestation14.io"
            Title="{Loc 'my-window-title'}"
            MinSize="400 300"
            SetSize="400 300"
            Resizable="True">
    <BoxContainer Orientation="Vertical" Margin="10">
        <!-- Содержимое окна -->
    </BoxContainer>
</DefaultWindow>

FancyWindow - улучшенное окно

FancyWindow имеет дополнительные возможности:

  • Кнопка справки
  • Лучший заголовок
  • Возможность прикрепить гайдбук
xml
<controls:FancyWindow xmlns="https://spacestation14.io"
                     xmlns:controls=
                     "clr-namespace:Content.Client.UserInterface.Controls"
                     Title="{Loc 'my-fancy-window-title'}"
                     MinSize="500 400"
                     Resizable="True">
    <BoxContainer Orientation="Vertical" Margin="10">
        <!-- Содержимое окна -->
    </BoxContainer>
</controls:FancyWindow>

Свойства элементов

Размеры

xml
<!-- Минимальный размер -->
<Label MinSize="100 30" />

<!-- Точный размер -->
<Label SetSize="200 50" />

<!-- Максимальный размер -->
<Label MaxSize="300 100" />

Расширение

Важно: HorizontalExpand и VerticalExpand работают только в контейнерах BoxContainer, SplitContainer и GridContainer. В других контейнерах эти свойства игнорируются.

xml
<!-- Расширяться по горизонтали -->
<Button HorizontalExpand="True" />

<!-- Расширяться по вертикали -->
<BoxContainer VerticalExpand="True" />

<!-- Расширяться в обе стороны -->
<PanelContainer HorizontalExpand="True" VerticalExpand="True" />

Пропорциональное распределение места:

Если несколько элементов имеют Expand="True", пространство распределяется пропорционально их SizeFlagsStretchRatio:

xml
<!-- Первый элемент получит в 2 раза больше места -->
<Button VerticalExpand="True" SizeFlagsStretchRatio="2" Text="Большой" />
<Button VerticalExpand="True" SizeFlagsStretchRatio="1" Text="Маленький" />

Выравнивание

HorizontalAlignment и VerticalAlignment определяют, как элемент располагается внутри выделенного ему пространства. По умолчанию оба значения Stretch (растягивание).

xml
<!-- Горизонтальное выравнивание -->
<Label HorizontalAlignment="Center" />  <!-- Центр -->
<Label HorizontalAlignment="Left" />    <!-- Влево -->
<Label HorizontalAlignment="Right" />   <!-- Вправо -->
<Label HorizontalAlignment="Stretch" /> <!-- Растянуть (по умолчанию) -->

<!-- Вертикальное выравнивание -->
<Label VerticalAlignment="Center" />    <!-- Центр -->
<Label VerticalAlignment="Top" />       <!-- Вверх -->
<Label VerticalAlignment="Bottom" />    <!-- Вниз -->
<Label VerticalAlignment="Stretch" />   <!-- Растянуть (по умолчанию) -->

Важно понимать разницу:

  • Alignment — как элемент ведёт себя внутри уже выделенного пространства
  • Expand — сколько пространства родительский контейнер выделит элементу

Отступы

xml
<!-- Внешний отступ (все стороны одинаковые) -->
<BoxContainer Margin="10" />

<!-- Внешний отступ (горизонтальный вертикальный) -->
<BoxContainer Margin="10 5" />

<!-- Внешний отступ (левый верхний правый нижний) -->
<BoxContainer Margin="5 10 5 10" />

Name и доступ к элементам

Атрибут Name

Атрибут Name позволяет обращаться к элементу из C# кода:

xml
<Button Name="ConfirmButton" Text="Подтвердить" />
<Label Name="StatusLabel" Text="Статус" />
<LineEdit Name="NameInput" PlaceHolder="Введите имя" />

Атрибут Access

Access определяет уровень доступа к элементу:

xml
<!-- Публичный доступ (можно использовать из других классов) -->
<Button Name="PublicButton" Access="Public" />

<!-- Внутренний доступ (только внутри сборки) -->
<Button Name="InternalButton" Access="Internal" />

<!-- Приватный доступ (только в этом классе) -->
<Button Name="PrivateButton" Access="Private" />

Связь XAML и C# кода

GenerateTypedNameReferences

Атрибут [GenerateTypedNameReferences] автоматически генерирует ссылки на элементы с Name:

csharp
[GenerateTypedNameReferences]
public sealed partial class MyWindow : DefaultWindow
{
    public MyWindow()
    {
        RobustXamlLoader.Load(this);

        // ConfirmButton автоматически доступен!
        ConfirmButton.Text = "Нажми меня";
        ConfirmButton.OnPressed += OnConfirmPressed;
    }

    private void OnConfirmPressed(BaseButton.ButtonEventArgs args)
    {
        // Действие при нажатии
    }
}

Внедрение зависимостей

Для IoC сервисов и других контроллеров используйте [Dependency]:

csharp
[GenerateTypedNameReferences]
public sealed partial class MyWindow : DefaultWindow
{
    [Dependency] private readonly IEntityManager _entityManager = default!;
    [Dependency] private readonly IPrototypeManager _prototype = default!;

    public MyWindow()
    {
        RobustXamlLoader.Load(this);
        IoCManager.InjectDependencies(this);

        // Теперь можно использовать _entityManager и _prototype
    }
}

Важно: Для entity systems в UI контроллерах используйте [UISystemDependency] вместо [Dependency].

События и обработчики

События кнопок

csharp
// Одинарное нажатие
MyButton.OnPressed += _ => { /* действие */ };

// Нажатие и отпускание
MyButton.OnMouseDown += _ => { /* нажата */ };
MyButton.OnMouseUp += _ => { /* отпущена */ };

// Режим переключателя (ToggleMode="True")
MyToggleButton.OnToggled += args =>
{
    if (args.Pressed)
    {
        // Включено
    }
    else
    {
        // Выключено
    }
};

События LineEdit

csharp
// При изменении текста
MyInput.OnTextChanged += args =>
{
    var text = args.Text;
};

// При нажатии Enter
MyInput.OnTextEntered += args =>
{
    var text = args.Text;
};

// При получении/потере фокуса
MyInput.OnFocusEnter += _ => { /* фокус получен */ };
MyInput.OnFocusExit += _ => { /* фокус потерян */ };

События CheckBox

csharp
MyCheckBox.OnToggled += args =>
{
    var isChecked = args.Pressed;
};

Локализация в UI

Использование {Loc}

xml
<!-- Текст с локализацией -->
<Label Text="{Loc 'my-window-title'}" />

<!-- Кнопка с локализацией -->
<Button Text="{Loc 'my-button-text'}" />

<!-- ToolTip с локализацией -->
<Button Text="?" ToolTip="{Loc 'help-tooltip'}" />

Файлы локализации

Создайте FTL файл в /Resources/Locale/en-US/my-module.ftl:

ftl
my-window-title = Моё окно
my-button-text = Нажать
help-tooltip = Это подсказка

Полный пример: Окно настроек

SettingsWindow.xaml

xml
<DefaultWindow xmlns="https://spacestation14.io"
            Title="{Loc 'settings-window-title'}"
            MinSize="400 350"
            Resizable="False">
    <BoxContainer Orientation="Vertical" Margin="10" SeparationOverride="8">

        <!-- Заголовок -->
        <Label Text="{Loc 'settings-general'}" StyleClasses="LabelHeading" />

        <!-- Настройка звука -->
        <BoxContainer Orientation="Horizontal">
            <Label Text="{Loc 'settings-sound-volume'}" MinWidth="120" />
            <ProgressBar Name="VolumeBar"
                        MinValue="0" MaxValue="100" Value="50"
                        HorizontalExpand="True" />
        </BoxContainer>

        <!-- Чекбоксы -->
        <CheckBox Name="EnableMusicCheckBox" Text="{Loc 'settings-enable-music'}"/>
        <CheckBox Name="EnableSfxCheckBox" Text="{Loc 'settings-enable-sfx'}"
         Pressed="True" />

        <!-- Разделитель -->
        <PanelContainer StyleClasses="LowDivider" />

        <!-- Поле ввода имени -->
        <BoxContainer Orientation="Horizontal">
            <Label Text="{Loc 'settings-player-name'}" MinWidth="120" />
            <LineEdit Name="PlayerNameInput" HorizontalExpand="True"
             PlaceHolder="{Loc 'settings-name-placeholder'}" />
        </BoxContainer>

        <!-- Выбор темы -->
        <BoxContainer Orientation="Horizontal">
            <Label Text="{Loc 'settings-theme'}" MinWidth="120" />
            <OptionButton Name="ThemeSelector" HorizontalExpand="True" />
        </BoxContainer>

        <!-- Разделитель -->
        <PanelContainer StyleClasses="LowDivider" />

        <!-- Кнопки -->
        <BoxContainer Orientation="Horizontal" HorizontalAlignment="Right">
            <Button Name="ResetButton" Text="{Loc 'settings-reset'}"
             StyleClasses="ButtonSquare" />
            <Control MinWidth="10" />
            <Button Name="SaveButton" Text="{Loc 'settings-save'}"
             StyleClasses="ButtonSquare" />
        </BoxContainer>

    </BoxContainer>
</DefaultWindow>

SettingsWindow.xaml.cs

csharp
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;

namespace Content.Client.MyModule.UI;

[GenerateTypedNameReferences]
public sealed partial class SettingsWindow : DefaultWindow
{
    public SettingsWindow()
    {
        RobustXamlLoader.Load(this);

        // Инициализация выпадающего списка
        ThemeSelector.AddItem("Тёмная", 0);
        ThemeSelector.AddItem("Светлая", 1);
        ThemeSelector.AddItem("Системная", 2);
        ThemeSelector.SelectId(0);

        // Подписка на события
        SaveButton.OnPressed += OnSavePressed;
        ResetButton.OnPressed += OnResetPressed;
    }

    private void OnSavePressed(BaseButton.ButtonEventArgs args)
    {
        // Сохранение настроек
        var playerName = PlayerNameInput.Text;
        var enableMusic = EnableMusicCheckBox.Pressed;
        var enableSfx = EnableSfxCheckBox.Pressed;
        var volume = VolumeBar.Value;
        var theme = ThemeSelector.SelectedId;

        // ... логика сохранения ...

        Close();
    }

    private void OnResetPressed(BaseButton.ButtonEventArgs args)
    {
        // Сброс к значениям по умолчанию
        PlayerNameInput.Text = "";
        EnableMusicCheckBox.Pressed = false;
        EnableSfxCheckBox.Pressed = true;
        VolumeBar.Value = 50;
        ThemeSelector.SelectId(0);
    }
}

Связь с сервером (Network Messages)

Для создания интерактивного UI, которое взаимодействует с сервером, нужно:

1. Создать сетевые сообщения

Shared/MyWindowMessages.cs:

csharp
using Robust.Shared.Serialization;

namespace Content.Shared.MyModule;

[Serializable, NetSerializable]
public sealed class MyWindowRequestMessage : EntityEventArgs
{
    public string Data { get; }

    public MyWindowRequestMessage(string data)
    {
        Data = data;
    }
}

[Serializable, NetSerializable]
public sealed class MyWindowResponseMessage : EntityEventArgs
{
    public string Result { get; }

    public MyWindowResponseMessage(string result)
    {
        Result = result;
    }
}

2. Создать UI контроллер на клиенте

Client/MyWindowUIController.cs:

UI контроллеры отвечают за создание, обновление и удаление контролов. Entity systems не должны делать это напрямую.

csharp
using Content.Shared.MyModule;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controllers;

namespace Content.Client.MyModule.UI;

// IOnSystemChanged позволяет реагировать на загрузку/выгрузку системы
public sealed class MyWindowUIController : UIController, IOnSystemChanged<MyWindowSystem>
{
    // Для IoC сервисов и других контроллеров используйте [Dependency]
    [Dependency] private readonly IEntityManager _entityManager = default!;
    
    // Для entity systems используйте [UISystemDependency]
    [UISystemDependency] private readonly MyWindowSystem? _system = default!;

    private MyWindow? _window;

    public void OpenWindow()
    {
        if (_window == null)
        {
            _window = new MyWindow();
            _window.OnClose += () => _window = null;

            // Подписываемся на событие кнопки
            _window.ConfirmButton.OnPressed += OnConfirmPressed;
        }

        _window.Open();
    }

    private void OnConfirmPressed(BaseButton.ButtonEventArgs args)
    {
        if (_window == null) return;

        var data = _window.DataInput.Text;

        // Отправляем сообщение на сервер
        RaiseNetworkEvent(new MyWindowRequestMessage(data));
    }

    public void UpdateResponse(string result)
    {
        _window?.ResultLabel.Text = result;
    }

    // Вызывается при загрузке системы
    public void OnSystemLoaded(MyWindowSystem system)
    {
        // Подписываемся на события системы
    }

    // Вызывается при выгрузке системы
    public void OnSystemUnloaded(MyWindowSystem system)
    {
        // Отписываемся от событий
    }
}

Доступные интерфейсы для UI контроллеров:

  • IOnSystemLoaded<T> - вызывается при загрузке системы
  • IOnSystemUnloaded<T> - вызывается при выгрузке системы
  • IOnStateEntered<T> - вызывается при входе в состояние (например, GameplayState)
  • IOnStateExited<T> - вызывается при выходе из состояния

3. Создать систему на сервере

Server/MyWindowSystem.cs:

csharp
using Content.Shared.MyModule;
using Robust.Server.GameObjects;

namespace Content.Server.MyModule;

public sealed class MyWindowSystem : EntitySystem
{
    public override void Initialize()
    {
        SubscribeNetworkEvent(OnRequest);
    }

    private void OnRequest(MyWindowRequestMessage msg, EntitySessionEventArgs args)
    {
        var sender = args.SenderSession;

        // Обрабатываем запрос
        var result = $"Обработано: {msg.Data}";

        // Отправляем ответ клиенту
        RaiseNetworkEvent(new MyWindowResponseMessage(result),
        sender.ConnectedClient);
    }
}

Стилизация (StyleClasses)

Применение стилей

xml
<!-- Кнопка с классом стиля -->
<Button Text="Опасно" StyleClasses="ButtonCaution" />

<!-- Несколько классов стилей -->
<Label Text="Заголовок" StyleClasses="LabelHeading LabelBig" />

Часто используемые стили

Элемент Стиль Описание
ButtonButtonSquareКвадратная кнопка
ButtonButtonCautionКрасная кнопка опасности
ButtonOpenLeftОткрыта слева
ButtonOpenRightОткрыта справа
ButtonOpenBothОткрыта с обеих сторон
LabelLabelHeadingЗаголовок
LabelLabelSubTextМелкий текст
LabelLabelBigБольшой текст
PanelBackgroundPanelФон панели
PanelLowDividerТонкий разделитель
PanelHighDividerТолстый разделитель

Практические советы

1. Отступы и размеры

xml
<!-- Используйте Margin для отступов -->
<BoxContainer Margin="10">
    <!-- Содержимое с отступами 10px -->
</BoxContainer>

<!-- Используйте MinSize для минимальных размеров -->
<Button MinSize="100 30" Text="Кнопка" />

<!-- Используйте HorizontalExpand/VerticalExpand для заполнения -->
<ScrollContainer VerticalExpand="True" HorizontalExpand="True">
    <!-- Заполняет всё доступное пространство -->
</ScrollContainer>

2. Динамическое добавление элементов

csharp
// Добавление элемента в контейнер
var button = new Button { Text = "Динамическая кнопка" };
MyContainer.AddChild(button);

// Очистка контейнера
MyContainer.DisposeAllChildren();

3. Показ/скрытие элементов

csharp
// Скрыть элемент
MyElement.Visible = false;

// Показать элементы
MyElement.Visible = true;

4. Отладка UI

Используйте интегрированные инструменты отладки:

  • devwindow - открывает окно разработчика с вкладкой UI для просмотра структуры интерфейса
  • /uitest [название_UI_окна] - команда для отладки UI
    Пример: uitest Slider - откроет тестовые окна для проверки UI
  • ~ (тильда/ё) - открывает консоль клиента

Частые ошибки и решения

1. Элемент не найден в коде

Проблема: MyButton не существует в текущем контексте.

Решение: Убедитесь, что:

  • У элемента есть атрибут Name="MyButton"
  • Класс имеет атрибут [GenerateTypedNameReferences]
  • Вызов RobustXamlLoader.Load(this) есть в конструкторе

2. XAML не загружается

Проблема: Исключение при загрузке XAML.

Решение: Проверьте:

  • Правильность XML синтаксиса
  • Все xmlns ссылки корректны
  • Все типы элементов существуют

3. Окно не открывается

Проблема: Окно создаётся, но не отображается.

Решение: Убедитесь, что вызываете Open():

csharp
var window = new MyWindow();
window.Open();  // Важно!

4. События не работают

Проблема: Обработчик события не вызывается.

Решение: Проверьте, что подписка на событие происходит после RobustXamlLoader.Load(this):

csharp
public MyWindow()
{
    RobustXamlLoader.Load(this);  // Сначала загрузка
    MyButton.OnPressed += OnPressed;  // Потом подписка
}

Дополнительные ресурсы

note
Примечание: Официальная документация SS14 может содержать устаревшую информацию.
Всегда проверяйте актуальность информации в исходном коде проекта.

Автор гайда

Автор: Кейси

Discord: nimf0374