ObservableModel 1.0.2

dotnet add package ObservableModel --version 1.0.2
                    
NuGet\Install-Package ObservableModel -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ObservableModel" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ObservableModel" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="ObservableModel" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ObservableModel --version 1.0.2
                    
#r "nuget: ObservableModel, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ObservableModel@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ObservableModel&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=ObservableModel&version=1.0.2
                    
Install as a Cake Tool

ObservableModel

A lightweight .NET library providing observable and trackable object models with support for INotifyPropertyChanged, reactive patterns with IObservable<T>, and comprehensive change tracking capabilities.

NuGet

Installation

Install via NuGet Package Manager:

dotnet add package ObservableModel

Features

  • Observable Objects: Automatic INotifyPropertyChanged implementation
  • Change Tracking: Track changes to properties with original value preservation
  • Reactive Extensions: Built-in support for IObservable<T> patterns
  • Observable Collections: Feature-rich observable lists with change notifications
  • Property Dependencies: Automatic dependent property notifications
  • Deferred Changes: Batch property change notifications
  • MVVM Support: Perfect for WPF, Avalonia, and other MVVM frameworks

Quick Start

Observable Objects

Create objects with automatic property change notifications:

public abstract class Person : ObservableObject
{
    [ObservableProperty]
    public virtual string Name { get; set; }
    
    [ObservableProperty]
    public virtual int Age { get; set; }
}

// Create an instance
var person = Observable<Person>.Create(x =>
{
    x.Name = "John Doe";
    x.Age = 30;
});

// Subscribe to property changes
person.PropertyChanged += (sender, e) =>
{
    Console.WriteLine($"Property {e.PropertyName} changed");
};

person.Name = "Jane Doe"; // Triggers PropertyChanged event

Observable Collections

var people = new ObservableList<Person>();

// Subscribe to collection changes
people.CollectionChanged += (sender, e) =>
{
    Console.WriteLine($"Collection changed: {e.Action}");
};

people.Add(Observable<Person>.Create(x => 
{
    x.Name = "Alice";
    x.Age = 25;
}));

// Sort with persistent sorting
people.SortBy(x => x.Age, persist: true);

// Aggregate values reactively
var averageAge = people.Aggregate(0.0, (sum, p) => sum + p.Age / people.Count);

Reactive Observables

Use LINQ-style operators with observables:

var person = Observable<Person>.Create();

// Create a reactive property
var nameObservable = person.Observe(x => x.Name)
    .DistinctUntilChanged()
    .Select(name => name.ToUpper())
    .ToProperty();

person.Name = "john"; // nameObservable.Value becomes "JOHN"

Change Tracking

Track changes to objects with original value preservation:

public abstract class Employee : Trackable
{
    [TrackableProperty]
    public virtual string Name { get; set; }
    
    [TrackableProperty]
    public virtual decimal Salary { get; set; }
}

var employee = Trackable<Employee>.Create(x =>
{
    x.Name = "John";
    x.Salary = 50000m;
});

employee.Salary = 55000m;

Console.WriteLine(employee.IsChanged); // True
Console.WriteLine(employee.GetOriginalValue<decimal>(nameof(Employee.Salary))); // 50000

// Revert changes
employee.RejectChanges();
Console.WriteLine(employee.Salary); // 50000

// Or accept changes
employee.Salary = 60000m;
employee.AcceptChanges();
Console.WriteLine(employee.IsChanged); // False

Trackable Collections

var team = new TrackableList<Employee>();

team.Reset(employees, initialize: true);
team[0].Salary = 65000m;

// Get changes
var changes = team.GetChanges();
foreach (var change in changes)
{
    Console.WriteLine($"{change.Type}: {change.Item}");
}

// Revert all changes
team.RejectChanges();

Property Dependencies

Automatically notify dependent properties:

public abstract class Person : ObservableObject
{
    [ObservableProperty]
    public virtual string FirstName { get; set; }
    
    [ObservableProperty]
    public virtual string LastName { get; set; }
    
    [ObservablePropertyDependency(nameof(FirstName), nameof(LastName))]
    public string FullName => $"{FirstName} {LastName}";
}

var person = Observable<Person>.Create();
person.PropertyChanged += (s, e) => Console.WriteLine(e.PropertyName);

person.FirstName = "John"; // Triggers: FirstName, FullName

Deferred Property Changes

Batch multiple property changes into a single notification:

var person = Observable<Person>.Create();

using (person.DeferPropertyChanges())
{
    person.FirstName = "John";
    person.LastName = "Doe";
    person.Age = 30;
    // No PropertyChanged events yet
}
// All PropertyChanged events fire here

Combining Observables

var firstName = new BehaviorSubject<string>("John");
var lastName = new BehaviorSubject<string>("Doe");

var fullName = Observable.CombineLatest(
    firstName, 
    lastName, 
    (first, last) => $"{first} {last}"
);

fullName.Subscribe(name => Console.WriteLine(name)); // "John Doe"
lastName.OnNext("Smith"); // "John Smith"

Advanced Features

Observable Aggregates

var numbers = new ObservableList<int> { 1, 2, 3, 4, 5 };
var sum = numbers.Aggregate(0, (total, n) => total + n);

sum.PropertyChanges.Subscribe(change => 
{
    Console.WriteLine($"Sum changed to: {sum.Value}");
});

numbers.Add(6); // Sum automatically updates to 21

Weak References

Prevent memory leaks with weak subscriptions:

observable.SubscribeWeak(observer);

Observable Operators

  • Select, Where, DistinctUntilChanged
  • Take, Skip
  • CombineLatest
  • ObserveOn (dispatcher support)
  • FirstAsync

Requirements

  • .NET 9.0 or later

License

This project is licensed under the MIT License.

Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

Repository

https://github.com/zlatanov/observable-model

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.2 176 12/6/2025
1.0.1 170 12/6/2025
1.0.0 174 12/6/2025