Agent Skills › vchelaru/Gum › gum-runtime-binding

gum-runtime-binding

GitHub

说明 Gum 运行时数据绑定机制,涵盖 GraphicalUiElement 基础绑定与 FrameworkElement 增强绑定,包括上下文继承、模式、转换器及索引路径支持。

.claude/skills/gum-runtime-binding/SKILL.md vchelaru/Gum

Trigger Scenarios

询问 Gum 框架的数据绑定用法 排查 UI 属性未更新问题 比较两种绑定系统的差异

Install

npx skills add vchelaru/Gum --skill gum-runtime-binding -g -y
More Options

Non-standard path

npx skills add https://github.com/vchelaru/Gum/tree/main/.claude/skills/gum-runtime-binding -g -y

Use without installing

npx skills use vchelaru/Gum@gum-runtime-binding

指定 Agent (Claude Code)

npx skills add vchelaru/Gum --skill gum-runtime-binding -a claude-code -g -y

安装 repo 全部 skill

npx skills add vchelaru/Gum --all -g -y

预览 repo 内 skill

npx skills add vchelaru/Gum --list

SKILL.md

Frontmatter
{
    "name": "gum-runtime-binding",
    "description": "Gum runtime data binding — BindingContext, SetBinding on GraphicalUiElement visuals and FrameworkElement Forms controls, binding types (string, Binding, lambda), differences between the two systems."
}

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:

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:

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

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

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

2. Explicit Binding object

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

// 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:

// 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.

UpdateSourceTrigger.Default is project-configurable. Binding.DefaultUpdateTriggers lets a project register a default trigger per control type + property, resolved in NpcBindingExpression.HookUpdateSource before falling back to PropertyChanged. See Default Update Triggers.

Version History

  • c93866f Current 2026-08-20 09:20

Same Skill Collection

.claude/skills/gum-cross-platform-unification/SKILL.md
.claude/skills/gum-issue-creation/SKILL.md
.claude/skills/gum-localization/SKILL.md
.claude/skills/gum-monthly-release/SKILL.md
.claude/skills/gum-runtime-syntax-version/SKILL.md
.claude/skills/gum-tool-selection/SKILL.md
.claude/skills/refactoring-direction/SKILL.md
.claude/skills/tdd/SKILL.md

Metadata

Files
0
Version
7fc2261
Hash
cc569676
Indexed
2026-08-20 09:20

ホーム - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-09-29 14:16
浙ICP备14020137号-1