gum-runtime-binding

Reference guide for Gum's runtime data binding system — BindingContext, SetBinding on both GraphicalUiElement visuals and FrameworkElement Forms controls, binding types (string, Binding object, lambda), and how the two systems differ.

447 stars

Best use case

gum-runtime-binding is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Reference guide for Gum's runtime data binding system — BindingContext, SetBinding on both GraphicalUiElement visuals and FrameworkElement Forms controls, binding types (string, Binding object, lambda), and how the two systems differ.

Teams using gum-runtime-binding should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/gum-runtime-binding/SKILL.md --create-dirs "https://raw.githubusercontent.com/vchelaru/Gum/main/.claude/skills/gum-runtime-binding/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/gum-runtime-binding/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How gum-runtime-binding Compares

Feature / Agentgum-runtime-bindingStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Reference guide for Gum's runtime data binding system — BindingContext, SetBinding on both GraphicalUiElement visuals and FrameworkElement Forms controls, binding types (string, Binding object, lambda), and how the two systems differ.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Gum Runtime Binding

## Two Binding Systems

**GraphicalUiElement** (`GumRuntime/GraphicalUiElement.Binding.cs`) — basic binding available on all visuals.
**FrameworkElement** (`MonoGameGum/Forms/Controls/`) — richer binding on Forms controls, built on top of the GUE system.

`FrameworkElement.BindingContext` delegates to its `Visual.BindingContext` — they share one context.

## BindingContext

Set on any `GraphicalUiElement` or `FrameworkElement`. Cascades automatically to all descendants unless overridden:

```csharp
root.BindingContext = viewModel;  // all children inherit it
child.BindingContext = other;     // explicit overrides inherited
```

Subscribes to `INotifyPropertyChanged` and updates bound UI properties on change.

## GraphicalUiElement Binding (Visuals)

Simple string-only binding. No converters, no modes, no path traversal:

```csharp
element.SetBinding("X", nameof(vm.Position));           // basic
element.SetBinding("Text", nameof(vm.Name), "{0:N0}");  // with format string
```

`PushValueToViewModel()` is called from property setters to write back to the VM (always two-way implicitly).

## FrameworkElement Binding (Forms)

Three binding styles, all richer than the GUE version.

### 1. String-based

```csharp
textBox.SetBinding(nameof(TextBox.Text), nameof(vm.Name));
```

Shorthand — wraps the string in a default `Binding` object internally.

### 2. Explicit Binding object

```csharp
var binding = new Binding(nameof(vm.IsEnabled))
{
    Mode = BindingMode.OneWay,
    Converter = new BoolToVisibilityConverter(),
    FallbackValue = false
};
checkBox.SetBinding(nameof(CheckBox.IsChecked), binding);
```

`Binding` properties: `Path`, `Mode` (OneWay/TwoWay/OneWayToSource), `UpdateSourceTrigger` (Default/PropertyChanged/LostFocus), `Converter`, `ConverterParameter`, `StringFormat`, `FallbackValue`, `TargetNullValue`.

### 3. Lambda / expression tree

```csharp
// Typed (preferred — compiler-checked, extracts "Child.Text" path):
textBox.SetBinding<MyVm>(nameof(TextBox.Text), vm => vm.Child.Text);

// Parameterless closure:
textBox.SetBinding(nameof(TextBox.Text), () => vm.Child.Text);
```

Extension methods in `FrameworkElementExt.cs`. `BinderHelpers.ExtractPath()` walks the expression tree to produce a dotted path string, then creates a `Binding` normally. Nested paths (e.g. `vm => vm.A.B.C`) are fully supported via `PropertyPathObserver`.

## Index-Based Binding (Forms only)

Paths support integer indexer access via `[N]` syntax. Works in string paths, Binding objects, and lambdas:

```csharp
// String path
textBox.SetBinding(nameof(TextBox.Text), new Binding("Items[0].Text"));

// Lambda
textBox.SetBinding<MyVm>(nameof(TextBox.Text), vm => vm.Items[0].Text);

// Nested: index in the middle of a path
textBox.SetBinding(nameof(TextBox.Text), new Binding("Child.Items[1].Text"));
```

All binding features work with indexed paths: modes, converters, StringFormat, FallbackValue, LostFocus trigger.

**Collection change notification:** `PropertyPathObserver` subscribes to `INotifyCollectionChanged` on collections in indexed path segments. When items are added, removed, replaced, or cleared, the binding re-evaluates. Out-of-bounds indexes resolve to null (triggering `FallbackValue` if set). Currently reacts to ALL collection changes regardless of whether the specific bound index is affected — this is intentionally broad for correctness; a future optimization could filter by index relevance.

**Limitations:** Dictionary/string key indexing is not supported.

**Implementation:** `BinderHelpers.ParseSegments()` splits paths into `PathSegment` structs (name + optional int index). `BuildGetter`/`BuildSetter` emit indexer calls via `Expression.MakeIndex` or `Expression.ArrayIndex`. `ExtractPath` handles `MethodCallExpression` (`get_Item`) and `IndexExpression` nodes from lambdas. `PropertyPathObserver` uses `GetIndexedValue()` after property resolution for indexed segments.

## Feature Comparison

| Feature | GraphicalUiElement | FrameworkElement |
|---|---|---|
| String binding | ✓ | ✓ |
| Explicit Binding object | ✗ | ✓ |
| Lambda binding | ✗ | ✓ |
| Nested paths (`A.B.C`) | ✗ | ✓ |
| Index paths (`Items[0].Text`) | ✗ | ✓ |
| Binding modes | Implicit TwoWay | Configurable |
| Converters | ✗ | ✓ |
| FallbackValue / TargetNullValue | ✗ | ✓ |
| UpdateSourceTrigger | Always PropertyChanged | Configurable |

## Key Files

| File | Purpose |
|---|---|
| `GumRuntime/GraphicalUiElement.Binding.cs` | GUE binding — BindingContext, SetBinding, PushValueToViewModel |
| `MonoGameGum/Forms/Data/Binding.cs` | Binding config class + BindingMode + UpdateSourceTrigger + IValueConverter |
| `MonoGameGum/Forms/Data/NpcBindingExpression.cs` | Forms binding engine — UpdateTarget, UpdateSource |
| `MonoGameGum/Forms/Data/PropertyPathObserver.cs` | Watches dotted paths, re-hooks on intermediate changes, weak listeners |
| `MonoGameGum/Forms/Data/BinderHelpers.cs` | Lambda path extraction, compiled getter/setter delegates |
| `MonoGameGum/Forms/Controls/FrameworkElementExt.cs` | Lambda SetBinding extension methods |
| `GumRuntime/BindableGue.cs` | Deprecated alias for GraphicalUiElement — do not use |

## Non-Obvious Behaviors

**BindableGue is deprecated.** `GraphicalUiElement` now owns all binding logic. `BindableGue` exists only as a legacy alias.

**Weak listeners in PropertyPathObserver.** Forms binding uses weak references to avoid memory leaks on deep paths. GUE binding does not — callers should unsubscribe when disposing.

**Lambda extracts path at call time, not at update time.** `vm => vm.Child.Text` becomes the static path `"Child.Text"`. If `Child` is replaced, `PropertyPathObserver` re-hooks the listener chain automatically.

**ListBox `Items` is bindable.** `listBox.SetBinding(nameof(ListBox.Items), nameof(vm.Items))` works and keeps the list in sync with an `ObservableCollection` on the VM.

Related Skills

gum-runtime-variable-references

447
from vchelaru/Gum

Reference guide for runtime variable reference propagation and the optional Gum.Expressions NuGet. Load this when working on ApplyAllVariableReferences, GumExpressionService, runtime styling/theming, or the GumExpressions project.

gum-runtime-fonts

447
from vchelaru/Gum

Reference guide for Gum's runtime font loading system in MonoGame/KNI — the three font loading paths (custom font, font-property cache lookup, in-memory generation), the lookup cascade, FontCache naming, and common gotchas. Load when working on TextRuntime font properties, BitmapFont loading, CustomSetPropertyOnRenderable.UpdateToFontValues, IInMemoryFontCreator, or font-related documentation.

validate-code-changes

447
from vchelaru/Gum

Validate all code changes on the current branch. Spawns QA and refactoring agents in parallel to review for correctness, edge cases, code quality, and pattern adherence. Use when ready to review branch changes before merging.

skills-writer

447
from vchelaru/Gum

Creates and updates skill files (.claude/skills/*/SKILL.md) by reading source code and condensing knowledge into concise reference guides. Use when asked to create a new skill, update an existing skill, or document a subsystem for Claude Code agent context.

gum-variable-deep-dive

447
from vchelaru/Gum

Deep dive into the full variable lifecycle — from VariableSave on ElementSave through runtime application on GraphicalUiElement and Forms controls. Load this when working on styling, theming, RefreshStyles, or when you need to understand how variable values flow from save data to live visuals.

gum-unit-tests

447
from vchelaru/Gum

Reference guide for writing unit tests in the Gum repository. Load this when writing or modifying tests in Gum.ProjectServices.Tests, Gum.Cli.Tests, or any other Gum test project.

gum-tool-viewmodels

447
from vchelaru/Gum

Reference guide for Gum tool ViewModel conventions. Load this when working on ViewModels, XAML views, data binding, DependsOn, or visibility properties in the Gum tool.

gum-tool-variable-references

447
from vchelaru/Gum

Reference guide for Gum's variable reference system — Excel-like cross-instance/cross-element variable binding using Roslyn-parsed assignment syntax. Load this when working on VariableReferenceLogic, EvaluatedSyntax, ApplyVariableReferences, VariableChangedThroughReference, or the VariableReferences VariableListSave.

gum-tool-variable-grid

447
from vchelaru/Gum

Reference guide for Gum's Variables tab and DataUiGrid system. Load this when working on the Variables tab, DataUiGrid control, MemberCategory, InstanceMember, category population, property grid refresh, or category expansion state persistence.

gum-tool-undo

447
from vchelaru/Gum

Reference guide for Gum's undo/redo system. Load this when working on undo/redo behavior, the History tab, UndoManager, UndoPlugin, UndoSnapshot, or stale reference issues after undo.

gum-tool-selection

447
from vchelaru/Gum

Reference guide for Gum's editor selection system. Load this when working on click/drag selection, the rectangle/marquee selector, input handlers (move, resize, rotate, polygon points), the IsActive flag, locked instance behavior, SelectionManager coordination, or the selection event cascade (plugin events, forced default state, tree view sync).

gum-tool-save-classes

447
from vchelaru/Gum

Reference guide for Gum's save/load data model. Load this when working with GumProjectSave, ScreenSave, ComponentSave, StandardElementSave, ElementSave, StateSave, VariableSave, InstanceSave, BehaviorSave, or any serialization/deserialization of Gum project files.