AI Core: Reactivity
AI-first map of PropertyBinder, Rx/ReactiveUI, DynamicData, reactive collections, and object-level bindings used by EyeAuras-style code.
Reactivity Discovery Map
This map covers the shared reactivity layer used by application code, Blazor/WPF views, long-running services, and script mini-apps.
Use it when code needs derived properties, live collections, change tracking, UI updates from state, or subscriptions which must be disposed together with the owning object.
User Intents
- Recompute one property when other properties change.
- Keep UI state synchronized with service/model state.
- Subscribe to events or value changes without leaking handlers.
- Maintain a live table/list/tree from changing data.
- Filter, sort, transform, or group a live collection.
- Bind values conditionally, including null/fallback cases.
- Move reactive updates onto a UI scheduler/dispatcher.
- Decide between
PropertyBinder, Rx subscriptions, and DynamicData.
Concept Model
DisposableReactiveObjectis the usual base for objects that need lifetime management and/or property notifications. Put subscriptions and owned resources intoAnchors.- Rx (
IObservable<T>) is the stream layer. Use it for events, timers, async state, throttling, switching, combining, and one-off subscriptions. - ReactiveUI provides property observation helpers such as
WhenAnyValue,WhenAnyProperty,ReactiveCommand, andObservableAsPropertyHelper. PropertyBinderis for declarative object-level bindings. It is commonly declared as a staticBinder<TContext>and attached once from the instance constructor.- DynamicData is the preferred layer for mutable live collections.
SourceList<T>andSourceCache<T, TKey>emit change sets throughConnect(). SourceCache<T, TKey>is usually better when items have stable IDs.SourceList<T>is better for ordered/list-like data without a key.SourceListEx<T>andSourceCacheEx<T, TKey>are PoeShared wrappers that own the DynamicData source and expose materialized read-only collections.- UI code usually consumes materialized
IReadOnlyObservableCollection<T>produced byBindToCollection(...)or equivalent helpers. - UI-owned properties must be updated on the correct scheduler/dispatcher. Use scheduler-aware binder helpers or
ObserveOn(...)where needed.
Key APIs
PropertyBinder.Binder<TContext>- owns declarative binding rules for a context type.Binder.Attach(instance)- activates all binder rules for a concrete object; dispose it with the object lifetime.Binder.Bind(...)- binds an expression to a target property.Binder.BindIf(...).ElseIf(...).Else(...)- conditional binding chain.To(...)- sets the target property or calls an assignment callback.WithDependency(...)- adds extra dependency signals which should trigger recalculation.PropagateNullValues(...)- controls null propagation behavior in property paths.DoNotRunOnAttach()- prevents initial binding evaluation during attach.DoNotOverride()/OverrideKey(...)- controls how binding rules override each other.OnScheduler(...)- schedules binder assignment on a selected scheduler.WhenAnyValue(...)- observes one or more property values.WhenAnyProperty(...)- observes property change notifications by property name/expression.ReactiveCommand- command abstraction with observable execution state.ObservableAsPropertyHelper<T>- stores a computed observable value as a property.SourceList<T>- mutable live list.SourceCache<T, TKey>- mutable live keyed collection.Connect()- opens a DynamicData change-set stream.Preview()- observes changes before they are applied.Edit(...)- batch-mutates a DynamicData source.Watch(key)- observes one item in aSourceCache.Lookup(key)/GetOrDefault(...)- reads keyed cache values.CountChanged- observes live collection count.BindToCollection(out collection)- materializes a change set into a read-only observable collection.ToObservableChangeSet()- adapts an observable sequence or collection into DynamicData change sets.SourceListEx<T>/SourceCacheEx<T, TKey>- PoeShared lifetime-aware wrappers around DynamicData sources.BindableReactiveObject/ReactiveBinding- property-path binding infrastructure for dynamic source/target binding cases.
PropertyBinder Pattern
Use Binder<T> for stable object-level derived state. Do not create binders inside render loops, polling loops, or per-frame code.
private sealed class CharacterState : DisposableReactiveObject
{
private static readonly Binder<CharacterState> Binder = new();
static CharacterState()
{
Binder.Bind(x => $"{x.Name} ({x.Level})")
.To(x => x.DisplayName);
Binder.BindIf(x => x.Target != null, x => x.Target.Name)
.Else(x => "<no target>")
.To(x => x.TargetName);
}
public CharacterState()
{
Binder.Attach(this).AddTo(Anchors);
}
public string Name { get; set; } = "";
public int Level { get; set; }
public TargetInfo? Target { get; set; }
public string DisplayName { get; private set; } = "";
public string TargetName { get; private set; } = "";
}
Rx Subscription Pattern
Use Rx for streams and side effects. Always dispose subscriptions with the owner.
this.WhenAnyValue(x => x.IsRunning)
.DistinctUntilChanged()
.Subscribe(isRunning => Log.Info($"Running: {isRunning}"), Log.HandleException)
.AddTo(Anchors);
Common operators:
Select- map a value.Where- filter events.DistinctUntilChanged- suppress duplicate values.CombineLatest- combine multiple latest values.Switch- use the latest inner observable and unsubscribe previous one.Throttle/Sample- reduce noisy event streams.ObserveOn- move downstream notifications to a scheduler.
DynamicData Pattern
Use DynamicData when a collection changes over time. Prefer change-set pipelines over manual list diffing.
private readonly SourceCache<ItemVm, string> items = new(x => x.Id);
public IReadOnlyObservableCollection<ItemVm> Items { get; }
public InventoryVm()
{
items.Connect()
.Filter(x => x.IsVisible)
.BindToCollection(out var visibleItems)
.Subscribe()
.AddTo(Anchors);
Items = visibleItems;
items.AddTo(Anchors);
}
Use Edit(...) for batch changes:
items.Edit(update =>
{
update.Clear();
update.AddOrUpdate(newItems);
});
When To Use What
- Use
PropertyBinderfor long-lived derived properties and object state projections. - Use
WhenAnyValue/ Rx for event streams, timers, subscriptions, debounce/throttle logic, async switching, and side effects. - Use
DynamicDatafor live collections, especially tables, trees, caches, and continuously refreshed data. - Use plain properties for simple local state that does not need notification or derived updates.
- Use
ReactiveCommandwhen UI actions need observable execution state, errors, or can-execute logic. - Use
SourceCache<T, TKey>for entity lists, process lists, windows, aura objects, files, or anything with stable IDs. - Use
SourceList<T>when order itself is the model and there is no natural key.
Avoid
- Do not leave Rx subscriptions undisposed. Add them to
Anchors. - Do not mutate UI-bound properties from arbitrary background threads.
- Do not put
Binder.Attach(...)outside the object lifetime owner. - Do not create
Binder<T>instances repeatedly for the same type. - Do not manually diff
List<T>when DynamicData can emit change sets. - Do not rely on plain auto-properties if consumers expect property change notifications.
- Do not create circular reactive bindings unless guarded very carefully.
- Do not hide long-running work inside property getters or binding expressions.
Search Synonyms
- reactivity, reactive state, property changed, INPC, notify property changed
- PropertyBinder, binder, derived property, computed property, conditional binding
- Rx, System.Reactive, observable, subscription, scheduler, ObserveOn
- ReactiveUI, WhenAnyValue, ReactiveCommand, ObservableAsPropertyHelper
- DynamicData, SourceList, SourceCache, change set, live collection
- BindToCollection, observable collection, filtered table, sorted list
- AddTo Anchors, lifetime, Dispose, DisposableReactiveObject
Research Anchors
Binder<TContext>PropertyRuleBuilderBindBindIfElseIfElseToWithDependencyPropagateNullValuesOnSchedulerWhenAnyValueWhenAnyPropertyReactiveCommandObservableAsPropertyHelperSourceList<T>SourceCache<T, TKey>ConnectPreviewEditWatchLookupCountChangedBindToCollectionToObservableChangeSetSourceListEx<T>SourceCacheEx<T, TKey>BindableReactiveObjectReactiveBindingReactiveTrackerListReactiveSection