Editor Support
Role
The editor layer determines how an asset is presented and edited in GameStudio. Three tiers exist with increasing complexity. Choose the lowest tier that meets your needs — Tier 1 requires no editor code at all.
Tier 1: Automatic Property Grid (No Code Required)
Every asset automatically gets a property grid in GameStudio at no cost:
- Quantum introspects the asset class and builds a node graph from its
[DataContract]/[DataMember]-annotated properties. - The property grid renders each property with an appropriate template based on its type.
[DataMember(N)]controls display order.[Display("Label", "Category")]customises the label and groups properties under a collapsible category header.[DataMemberIgnore]hides a property from the grid entirely.- Collection items are shown as expandable sub-lists with inline add/remove controls.
This tier is active automatically for every asset that has no custom AssetViewModel. No additional classes, attributes, or registrations are needed.
Decision: choose Tier 1 when the asset's properties can be expressed as plain data members with standard types (primitives, references to other assets, known collection types). Most new engine assets start here.
Tier 2: Custom AssetViewModel<T>
Decision: choose Tier 2 when you need custom commands in the property grid, computed display properties, cross-property validation, or custom drag-and-drop handling for the asset in the asset browser — but you don't need a dedicated editor panel or window.
Implementation
Create a class that inherits AssetViewModel<TAsset> and decorate it with [AssetViewModel<TAsset>]. The framework discovers and instantiates it automatically when the asset is selected in GameStudio.
using Stride.Core.Assets.Editor.Annotations;
using Stride.Core.Assets.Editor.ViewModel;
namespace Stride.Assets.Presentation.ViewModel;
[AssetViewModel<%%AssetName%%Asset>]
public class %%AssetName%%ViewModel : AssetViewModel<%%AssetName%%Asset>
{
public %%AssetName%%ViewModel(AssetViewModelConstructionParameters parameters)
: base(parameters)
{
}
// Override or extend as needed.
// Access the typed asset via: Asset (returns %%AssetName%%Asset)
}
What can be overridden or extended:
- Add computed properties (read-only, no Quantum backing node required).
- Override
UpdateAssetFromSource(protected internal virtual Task UpdateAssetFromSource(Logger logger)) as a general refresh/sync hook: the editor calls it whenever the asset needs to be re-synchronized with external state (e.g. a source file change, a manual reload request, or another editor-triggered refresh). - Attach commands by creating
AnonymousCommandinstances and surfacing them as properties; command buttons can then be bound from XAML templates. - Access editor services through the inherited
ServiceProvider.
Keep the ViewModel thin — business logic belongs in the asset class itself, not here.
Assembly Placement
Stride.Assets.Presentation (sources/editor/Stride.Assets.Presentation/). This assembly is editor-only and must not be referenced by runtime or compiler assemblies.
Tier 3: Full Custom Editor
Decision: choose Tier 3 when your asset requires a dedicated editing environment beyond a property grid — a canvas, a node graph, a timeline, a sprite editor, etc. Examples:
SpriteSheetEditorViewModel,GraphicsCompositorEditorViewModel,SceneEditorViewModel.
ViewModel
Create a class inheriting AssetEditorViewModel, decorated with [AssetEditorViewModel<TViewModel>] where TViewModel is your Tier 2 ViewModel (or AssetViewModel<TAsset> directly if you have no Tier 2 class).
using Stride.Core.Assets.Editor.Annotations;
using Stride.Core.Assets.Editor.ViewModel;
namespace Stride.Assets.Presentation.AssetEditors.%%AssetName%%Editor.ViewModels;
[AssetEditorViewModel<%%AssetName%%ViewModel>]
public class %%AssetName%%EditorViewModel : AssetEditorViewModel
{
public %%AssetName%%EditorViewModel(%%AssetName%%ViewModel asset)
: base(asset)
{
}
/// <inheritdoc/>
public override async Task<bool> Initialize()
{
// Perform async initialisation (load preview data, set up subscriptions, etc.)
// Return false to abort opening the editor.
return true;
}
/// <inheritdoc/>
public override bool PreviewClose(bool? save)
{
// Return false to cancel closing (e.g. prompt the user to save unsaved changes).
return true;
}
}
Key members inherited from AssetEditorViewModel:
Asset— theAssetViewModelpassed to the constructor (cast it to your typed subclass as needed).UndoRedoService— the undo/redo stack; use it for all mutating operations so that Ctrl+Z/Ctrl+Y work correctly.ServiceProvider— access editor services such asIEditorDialogService.Session— the currentSessionViewModel, providing access to the full asset database.
View (XAML)
Create a WPF UserControl (partial class) that implements IEditorView and is decorated with [AssetEditorView<TEditorViewModel>]. The view is the control shown inside the editor tab when the asset is opened.
using Stride.Core.Assets.Editor.Annotations;
using Stride.Core.Assets.Editor.Services;
using Stride.Core.Assets.Editor.ViewModel;
namespace Stride.Assets.Presentation.AssetEditors.%%AssetName%%Editor.Views;
[AssetEditorView<%%AssetName%%EditorViewModel>]
public partial class %%AssetName%%EditorView : IEditorView
{
private readonly TaskCompletionSource editorInitializationNotifier = new();
public %%AssetName%%EditorView()
{
InitializeComponent();
}
/// <inheritdoc/>
public Task EditorInitialization => editorInitializationNotifier.Task;
/// <inheritdoc/>
public async Task<bool> InitializeEditor(IAssetEditorViewModel editor)
{
var result = await editor.Initialize();
if (!result)
editor.Destroy();
editorInitializationNotifier.SetResult();
return result;
}
}
Matching XAML stub (%%AssetName%%EditorView.xaml):
<UserControl x:Class="Stride.Assets.Presentation.AssetEditors.%%AssetName%%Editor.Views.%%AssetName%%EditorView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Editor UI goes here -->
</UserControl>
Assembly Placement
Both the ViewModel and View live in Stride.Assets.Presentation. Place the ViewModel under AssetEditors/%%AssetName%%Editor/ViewModels/ and the View under AssetEditors/%%AssetName%%Editor/Views/, following the convention used by SpriteEditor, GraphicsCompositorEditor, SceneEditor, etc.
How the Editor Opens
When a user double-clicks an asset whose AssetViewModel type has a registered [AssetEditorViewModel<T>] attribute, GameStudio's AssetEditorsManager finds the editor ViewModel type, instantiates it, then finds the matching [AssetEditorView<T>] view, creates it, and calls InitializeEditor. No additional registration code is needed beyond the attributes.