Skip to content
NLNuvyntra Labs

Documentation

ViewModels

ObservableModel, ViewModel lifecycle, PageViewModel, AsyncState, and Outcome — the bindable unit of work.

ObservableModel

ObservableModel is the INPC / INPChanging base. SetProperty compares with EqualityComparer<T>.Default and exits without raising events when the value is unchanged. PropertyChangedEventArgs are cached by property name so a hot bind path does not allocate a new args object on every raise.

NotifyDependsOn raises a named set of dependents. Prefer that over PropertyChanged(null), which forces every binding to refresh. Hand-write the field and the property, or mark a partial class with [Notify] / [NotifyAlso] from the SourceGenerators package.

public sealed class CounterViewModel : ViewModel
{
    private int _count;

    public int Count
    {
        get => _count;
        set
        {
            if (SetProperty(ref _count, value))
            {
                NotifyDependsOn(nameof(Count), nameof(Label));
            }
        }
    }

    public string Label => $"Count {Count}";
}

ViewModel lifecycle

ViewModel adds Status, IsBusy, ViewModelCancellationToken, InitializeAsync, OnAppearingAsync, OnDisappearingAsync, and ExecuteAsync. The token is created in the constructor and cancelled on Dispose. After dispose the token stays readable — IsCancellationRequested is true — so late continuations can still observe cancel.

Construct (DI)
  → Accept(args) / Accept(query)    IAcceptNavArgs / IAcceptNavQuery
  → InitializeAsync(token)          once
  → OnNavigatedToAsync(token)
  → OnAppearingAsync(token)
  → OnDisappearingAsync(token)
  → OnNavigatedFromAsync(token)
  → Dispose                         cancels ViewModelCancellationToken

PageViewModel implements INavigable and optionally holds INavigator and IDialogs. The page owns BindingContext. The ViewModel never holds Page. ViewModelLifecycleBehavior in the host package calls appear / disappear and unsubscribes on Unloaded.

AsyncState and Outcome

AsyncState<T> is the bindable UI status object: Status, Data, Error, Exception, Timestamp, plus IsLoading, IsRefreshing, IsEmpty, HasError, and IsSuccess. ViewModelStatus values are Idle, Loading, Refreshing, Saving, Success, Empty, Error, Offline, Unauthorized, and Cancelled. LoadAsync and RefreshAsync return Outcome<T>. The host package ships AsyncStateView to swap loading / empty / error / success templates.

Outcome / Outcome<T> is the library result type — success or failure with code, message, exception, validation, and metadata. The name is Outcome so it does not collide with FluentResults, LanguageExt, or an app-level Result<T>.

public AsyncState<IReadOnlyList<Product>> Products { get; } = new();

await Products.LoadAsync(token => catalog.ListAsync(token), ct);
// bind ItemsSource to Products.Data
// bind IsRefreshing to Products.IsRefreshing