binding-mewui-data

SKILL.md

ObservableValue

Reactive value container:

// Create
var name = new ObservableValue<string>("Initial");
var count = new ObservableValue<int>(0);
var enabled = new ObservableValue<bool>(true);

// Read/Write
string current = name.Value;
name.Value = "New";  // Triggers Changed event

// Subscribe via Changed event
name.Changed += () => Console.WriteLine($"Changed to: {name.Value}");

// With coercion (value constraint)
var percent = new ObservableValue<double>(50, coerce: v => Math.Clamp(v, 0, 100));
percent.Value = 150;  // Becomes 100

Fluent Binding

var vm = new MyViewModel();

new StackPanel().Children(
    // One-way (source → UI)
    new Label().BindText(vm.Message),

    // Two-way (source ↔ UI)
    new TextBox().BindText(vm.Name),
    new CheckBox().BindIsChecked(vm.IsEnabled),
    new Slider().BindValue(vm.Volume),

    // With converter
    new Label().BindText(vm.Count, c => $"Count: {c}"),

    // Common bindings
    new Button().BindIsEnabled(vm.CanSubmit).BindIsVisible(vm.ShowButton)
)

ViewModel Pattern

public class PersonViewModel
{
    public ObservableValue<string> FirstName { get; } = new("");
    public ObservableValue<string> LastName { get; } = new("");
    public ObservableValue<string> FullName { get; } = new("");
    public ObservableValue<bool> IsValid { get; } = new(false);

    public PersonViewModel()
    {
        FirstName.Changed += Update;
        LastName.Changed += Update;
    }

    private void Update()
    {
        FullName.Value = $"{FirstName.Value} {LastName.Value}".Trim();
        IsValid.Value = FirstName.Value.Length > 0 && LastName.Value.Length > 0;
    }
}

ValueBinding (Low-level)

// Note: subscribe/unsubscribe use lambda pattern, not method group
var binding = new ValueBinding<string>(
    get: () => source.Value,
    set: v => source.Value = v,  // null for one-way
    subscribe: h => source.Changed += h,
    unsubscribe: h => source.Changed -= h,
    onSourceChanged: () => control.Text = source.Value
);

Preventing Binding Loops

private bool _suppressBindingSet;

public string Text
{
    get => _text;
    set {
        if (_text != value) {
            _text = value;
            if (!_suppressBindingSet) _binding?.Set(value);
            InvalidateMeasure();
        }
    }
}

private void SetTextFromSource(string value)
{
    _suppressBindingSet = true;
    try { Text = value; }
    finally { _suppressBindingSet = false; }
}

Advanced patterns: See viewmodel-patterns.md

Weekly Installs
3
GitHub Stars
16
First Seen
14 days ago
Installed on
opencode3
gemini-cli3
codebuddy3
github-copilot3
codex3
kimi-cli3