xaml-csharp-development-skill-for-avalonia

Binding Value, Notification, and Instanced Binding Semantics

Table of Contents

  1. Scope and APIs
  2. Typed Binding Value States (BindingValue<T>)
  3. Construction and Implicit Conversions (BindingValue<T>)
  4. Optional<T> as a Missing-Value Primitive
  5. Untyped Binding Error Channel (BindingNotification)
  6. Typed/Untyped Bridging
  7. Binding Chain Diagnostics (BindingChainException)
  8. BindingOperations.DoNothingType and No-Update Semantics
  9. InstancedBinding for Dynamic Pipelines
  10. Binding Expression Control and Diagnostics
  11. IndexerDescriptor and Property Indexer Binding
  12. Practical Patterns
  13. AOT and Trimming Notes
  14. Troubleshooting

Scope and APIs

Primary APIs:

Compatibility-sensitive APIs in this space:

Reference source files:

Typed Binding Value States (BindingValue<T>)

BindingValue<T> is the typed transport used in binding pipelines. It carries both value and state.

Core states (BindingValueType):

Useful members:

Design implication:

Construction and Implicit Conversions (BindingValue<T>)

Construction and conversion APIs:

Practical semantics:

Example:

BindingValue<int> fromCtor = new BindingValue<int>(42);
BindingValue<int> fromImplicitValue = 42;

Optional<int> missing = default;
BindingValue<int> fromOptional = missing; // maps to unset semantics

Optional<T> as a Missing-Value Primitive

Optional<T> is a small two-state value (HasValue / missing) used by property/binding internals.

Useful members:

Use:

Creation forms:

Interop helper:

Untyped Binding Error Channel (BindingNotification)

BindingNotification is the untyped carrier for:

Useful APIs:

Use it when integrating untyped binding edges, custom adapters, or diagnostics surfaces.

Typed/Untyped Bridging

Bridge APIs:

Special-value mapping is stable:

Example:

BindingValue<string> typed = BindingValue<string>.BindingError(
    new InvalidOperationException("Missing source"),
    fallbackValue: "(fallback)");

object? untyped = typed.ToUntyped();
BindingValue<string> roundTrip = BindingValue<string>.FromUntyped(untyped);

Binding Chain Diagnostics (BindingChainException)

BindingChainException is the dedicated exception type for binding chain parse/evaluation failures.

Constructor forms:

Diagnostic members:

Use:

Example:

catch (BindingChainException ex)
{
    logger.LogWarning("Binding chain failed: {Message} @ {ErrorPoint}", ex.Message, ex.ExpressionErrorPoint);
}

BindingOperations.DoNothingType and No-Update Semantics

BindingOperations.DoNothing is a singleton marker whose runtime type is BindingOperations.DoNothingType.

Meaning:

Boundaries:

InstancedBinding for Dynamic Pipelines

InstancedBinding represents a binding already initiated for a concrete target context.

Factory helpers:

Key fields:

Practical guidance:

Binding Expression Control and Diagnostics

For a bound property, BindingOperations.GetBindingExpressionBase(...) gives access to active expression control.

Useful controls:

Example:

var expr = BindingOperations.GetBindingExpressionBase(textBox, TextBox.TextProperty);
expr?.UpdateSource();

If you use UpdateSourceTrigger=Explicit, this call is required to push value back.

IndexerDescriptor and Property Indexer Binding

Advanced property-indexer binding path uses:

Example:

var descriptor = !TextBox.TextProperty;
textBox[descriptor.WithMode(BindingMode.TwoWay)] = new ReflectionBinding(nameof(ViewModel.Query));

Use this only when dynamic property selection is required. For normal authoring, regular Bind(...) or XAML bindings are clearer.

Practical Patterns

1) Consume typed binding stream with error channel

using Avalonia;
using Avalonia.Data;

IDisposable sub = textBox
    .GetBindingObservable(TextBox.TextProperty)
    .Subscribe(v =>
    {
        if (v.HasError)
        {
            viewModel.ValidationMessage = v.Error?.Message;
            return;
        }

        if (v.Type == BindingValueType.Value)
            viewModel.LastText = v.GetValueOrDefault() ?? string.Empty;
    });

2) Preserve current value intentionally

When a custom converter/adapter returns BindingOperations.DoNothing, target value is retained.

Use this for partial-update workflows where “no change” is a valid output state.

3) Explicit source push in form commit

This gives deterministic source updates and avoids per-keystroke writes.

AOT and Trimming Notes

Advanced conversion paths can use runtime conversion logic (for example BindingValue<T>.FromUntyped(...)).

Guidance:

Troubleshooting

  1. Value appears lost after adapter step
    • Verify whether result was UnsetValue (fallback/default path) vs DoNothing (retain current target value).
  2. Error exists but target shows fallback text
    • This is expected when using BindingError/DataValidationError with fallback values.
  3. UpdateSource() seems ineffective
    • Check binding mode and trigger; explicit update only applies to TwoWay / OneWayToSource scenarios.
  4. Dynamic binding behaves inconsistently across targets
    • Confirm InstancedBinding.Mode, Priority, and target property metadata default binding mode.
  5. Indexer-descriptor syntax is hard to follow
    • Prefer explicit Bind(...) or XAML binding unless dynamic property selection is a hard requirement.