binding-mewui-data
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
More from christian289/dotnet-with-claudecode
converting-html-css-to-wpf-xaml
Converts HTML/CSS to WPF CustomControl XAML with correct patterns and common pitfall solutions. Use when transforming web designs to WPF, converting CSS animations to Storyboards, implementing CSS border-radius clipping, CSS pseudo-elements (::before/::after), or CSS transforms in XAML.
56publishing-wpf-apps
Guides WPF application publishing and installer options. Use when user mentions publish, deploy, release, packaging, or installer to help choose deployment method and installer technology.
14using-avalonia-collectionview
Provides CollectionView alternatives for AvaloniaUI using DataGridCollectionView and ReactiveUI. Use when filtering, sorting, or grouping collections in AvaloniaUI applications.
9designing-avalonia-customcontrol-architecture
Defines the basic solution structure for AvaloniaUI Desktop Applications using CustomControl. Use when creating new AvaloniaUI projects or designing stand-alone control styles with ControlTheme.
9using-xaml-property-element-syntax
Converts long inline XAML bindings to Property Element Syntax for better readability. Use when XAML binding expressions become too long or complex.
8managing-styles-resourcedictionary
Manages WPF Style definitions and ResourceDictionary patterns including BasedOn inheritance and resource merging. Use when building theme systems, organizing resources, or choosing between StaticResource and DynamicResource.
8