View Model
Overview
ViewModel is the base abstract class for all view models in the Asv.Avalonia framework. It implements the IViewModel interface and provides core features for property notification, resource management, hierarchical structure and routed events.
Key Features:
Property Change Notifications: implements
INotifyPropertyChangedandINotifyPropertyChanging.Resource Management: a
CancellationTokenand disposable resource collections, with a thread-safe disposal pattern ensuring resources are released exactly once.Hierarchical Structure: creates a tree of view models with parent-child relationships, providing the structure and data required for navigation.
Routed Events: propagates events through the hierarchy using different routing strategies.
Every view model inherits from ViewModel, or from ViewModel<TExtensionIfc> when it must be open to extensions.
Core Components
NavId
The Id property (of type NavId) identifies the view model instance. It's essential for:
Identifying: Distinguishing ViewModels within the application.
Navigation: Providing the type id and parameters for routing via
Navigateand the helper methods inRoutableMixin.
You supply custom arguments through the constructor — ViewModel(string typeId, NavArgs args = default) builds the NavId from the type id and the arguments. Id is immutable after construction.
NavId does not enforce uniqueness by itself. IDs must be unique within their runtime context; this requirement is validated at runtime.
Property Notification
IViewModel inherits from INotifyPropertyChanged and INotifyPropertyChanging.
To update properties, use the SetField method. It updates the backing field and automatically raises the PropertyChanging and PropertyChanged events only when the value has actually changed, preventing unnecessary UI updates.
Disposable Resource Collections
ViewModel provides two protected collections for resources that must be disposed together with the view model:
DisposableBagis an add-only value type with lower allocation overhead. Register resources with.AddTo(ref DisposableBag). Prefer it when resources are added from a single thread and do not need to be removed individually.Disposableis a thread-safeCompositeDisposablethat supports removing individual resources. Register resources with.DisposeItWith(Disposable). Prefer it when resources can be registered concurrently or must be removed before the view model is disposed.
Both collections automatically dispose all remaining registered resources when the view model is disposed.
After the view model is disposed, Disposable returns a shared, already-disposed container: registering a resource on it disposes that resource immediately instead of leaking it, so late registrations from racing background work are safe.
CancellationToken
The DisposeCancel property provides a CancellationToken that is automatically triggered when the ViewModel starts its disposal process.
Thread-Safety: The cancellation and disposal of the source are handled in a thread-safe manner.
Primary Use Case: Passing the token to asynchronous tasks, background loops, or observable subscriptions to ensure they stop immediately when the ViewModel is destroyed.
Disposal Pattern
ViewModel implements a thread-safe disposal mechanism:
The public
Dispose()method handles thread safety and prevents multiple disposal calls.Derived classes should override the
Dispose(bool disposing)method to perform their own cleanup (e.g. unsubscribing from events or stopping timers), and callbase.Dispose(disposing).
The base implementation tears down in a specific order:
Detach:
Parentis set tonulland thePropertyChanging/PropertyChangedhandlers are cleared.Cancel: The
CancellationTokenis canceled, notifying all linked tasks to stop.Dispose Source: The
CancellationTokenSourceitself is disposed.Dispose Resources: The
Disposablecontainer is disposed, which in turn disposes of all registered objects.Dispose Bag: The
DisposableBagis disposed.
Hierarchical Structure and Navigation Support
The Parent property holds a reference to the parent IViewModel in the navigation hierarchy. This creates a tree structure where:
Every view model (except the root) has exactly one parent.
A view model can have multiple children.
The hierarchy forms the basis for navigation and event propagation.
Parent is read-only — assign it with the SetParent method. When adding child view models to a collection, use the SetRoutableParent extension methods to manage parent references automatically.
The GetChildren method returns an enumerable of all direct child view models. It has a default implementation returning an empty collection, so you only need to override it when your view model has children:
The Navigate method is used to find child view models by their ID. The default implementation searches immediate children and returns the first match, or returns itself if no match is found.
Routed Events
Routed events allow communication between view models in the hierarchy. Instead of direct dependencies, view models can raise events that propagate through the hierarchy based on a routing strategy.
Raising Events
Use the Rise method to raise an asynchronous routed event:
Or with the helper method:
The event object must extend AsyncRoutedEvent from the Asv.Modeling library, which requires a source and a routing strategy.
Handling Events
To handle routed events, you can create an InternalCatchEvent method:
And catch events via the Events property in the constructor:
There is also a typed overload that filters by event type for you:
Routing Strategies
The event propagation is controlled by the RoutingStrategy enum:
Bubble: The event starts at the source and propagates upward through parents to the root. Each parent view model can handle it or pass it further up.
Tunnel: The event starts at the view model it is raised on and propagates downward through its children. Raise it on the root (see Finding the Root) to reach the whole tree. Useful for distributing state changes or notifications from the top level.
Direct: The event is only handled at the source and does not propagate through the hierarchy.
Helper Methods (RoutableMixin)
The RoutableMixin class provides useful extension methods for working with IViewModel view models. For example:
Setting Parent for Collections
Automatically set the parent for all items in a collection:
The methods handle both existing items and items added/removed later. Removed items have their parent reset to null. The overloads for collections implementing INotifyCollectionChanged additionally dispose removed items.
Navigating by Path
Navigate through the hierarchy using a complete path:
Finding the Root
Get the topmost parent in the hierarchy:
Common Use Cases
Managing Child Elements
A common pattern is to have a parent view model that contains child view models. These child elements are automatically part of the hierarchy and can be navigated to individually. For example:
The constructor and unrelated members are omitted. The constructor must register _items with SetRoutableParent(this) to maintain parent references.
Propagating Events Through the Hierarchy
Use routed events to communicate between view models without creating direct dependencies. For example, a child view model can notify its parent when something needs attention:
The parent handles the event and removes the item from its collection. This way, the child doesn't need a direct reference to the parent — it just raises an event and the parent decides what to do.
API
IViewModel
Defines a base contract for all view models in the application. It declares no members of its own — it composes IDisposable, INotifyPropertyChanging, INotifyPropertyChanged and the Asv.Modeling ISupportUndo<IViewModel>, ISupportParentChange<IViewModel>, ISupportRootTracking<IViewModel, IShell> and ISupportLayout interfaces, which supply identity, hierarchy, routed events, undo and layout support.
ViewModel: IViewModel
Represents the base implementation of a view model that provides property change notifications and a proper undo and disposal mechanism. This class is designed to be inherited by other view models.
ViewModel constructor
Parameter | Type | Description |
|---|---|---|
|
| The type identifier the |
|
| Optional navigation arguments. Defaults to |
Property | Type | Description |
|---|---|---|
|
| Identifies the ViewModel within its runtime context. |
|
| Gets or sets an arbitrary user-defined object associated with the view model. |
|
| Gets the parent |
|
| Gets an observable that emits the new parent every time |
|
| Gets the controller for routed events within the |
|
| Gets the controller that tracks the |
|
| Gets the undo controller of the view model, used to register undoable values via |
|
| Gets the layout controller of the view model, used to register values that are saved and restored via |
|
| Gets a value indicating whether the view model has been disposed. |
|
| Protected. A cancellation token linked to the disposal state of the view model. If the view model is disposed, the token is set to |
|
| Protected. A thread-safe collection for resources registered via |
|
| Protected. An add-only collection for resources registered via |
Event | Type | Description |
|---|---|---|
|
| Occurs when a property value changes. Implements |
|
| Occurs when a property value is about to change. |
Method | Return Type | Description |
|---|---|---|
|
| Releases resources used by the view model. Ensures that the disposal operation is only performed once. |
|
| Protected virtual. Releases managed resources when disposing. Derived classes override it to add their own cleanup. |
|
| Sets the parent and raises |
|
| Virtual. Returns the direct child view models. Returns an empty collection by default. |
|
| Virtual. Finds a direct child by its identifier; returns itself if no child matches. |
|
| Override. Returns a string representation of the view model in the form |
|
| Protected. Sets the field to the specified value and raises the change events if the value has changed. |
ViewModel.Dispose(bool disposing)
Parameter | Type | Description |
|---|---|---|
|
|
|
ViewModel.SetField<T>
Parameter | Type | Description |
|---|---|---|
|
| The backing field reference. |
|
| The new value to set. |
|
| The name of the property that changed. Automatically set by the caller if not provided. |