Cross-platform base components for the MVVM pattern. Includes ViewModelBase, RelayCommand, DelegateCommand, SortedCollection, and Table data models. Platform-specific implementations are available in OutWit.Common.MVVM.WPF and OutWit.Common.MVVM.Avalonia packages.
$ dotnet add package OutWit.Common.MVVMCross-platform MVVM library providing base components for building modern .NET applications with WPF, Avalonia, and Blazor.
INotifyPropertyChanged supportRelayCommand: Simple command with manual CanExecuteChanged raisingDelegateCommand<T>: Generic typed commandSortedCollection<TKey, TValue>: Sorted collection with change notificationsObservableSortedCollection<TKey, TValue>: Sorted collection observing item property changesSafeObservableCollection<T>: Thread-safe observable collectionTableView, TableViewPage, TableViewRow, etc.)IDispatcher for cross-platform thread marshallingdotnet add package OutWit.Common.MVVM
For platform-specific features:
# WPF
dotnet add package OutWit.Common.MVVM.WPF
# Avalonia
dotnet add package OutWit.Common.MVVM.Avalonia
# Blazor
dotnet add package OutWit.Common.MVVM.Blazor
public class MyViewModel : ViewModelBase<IApplicationViewModel>
{
private string m_name = "";
public MyViewModel(IApplicationViewModel appVm) : base(appVm)
{
}
public string Name
{
get => m_name;
set
{
m_name = value;
OnPropertyChanged();
}
}
}
public class MyViewModel : ViewModelBase<IApplicationViewModel>
{
public RelayCommand SaveCommand { get; }
public MyViewModel(IApplicationViewModel appVm) : base(appVm)
{
SaveCommand = new RelayCommand(
execute: _ => Save(),
canExecute: _ => CanSave());
}
private void Save() { /* Save logic */ }
private bool CanSave() => !string.IsNullOrEmpty(Name);
private void OnNameChanged()
{
SaveCommand.RaiseCanExecuteChanged();
}
}
public DelegateCommand<string> SearchCommand { get; }
SearchCommand = new DelegateCommand<string>(
execute: searchText => PerformSearch(searchText),
canExecute: searchText => !string.IsNullOrEmpty(searchText));Thread-safe sorted collection with change notifications:
var items = new SortedCollection<int, Item>(x => x.Id);
items.ItemsAdded += (s, added) => Console.WriteLine($"Added {added.Count} items");
items.ItemsRemoved += (s, removed) => Console.WriteLine($"Removed {removed.Count} items");
items.Add(new Item { Id = 1, Name = "First" });Observes property changes in collection items:
var items = new ObservableSortedCollection<int, Item>(x => x.Id);
// Listen for item property changes
items.CollectionContentChanged += (sender, e) =>
{
var item = sender as Item;
Console.WriteLine($"Property {e.PropertyName} changed on item {item?.Id}");
};
items.Add(new Item { Id = 1, Name = "First" });
items[1].Name = "Updated"; // Triggers CollectionContentChanged// Thread-safe collection that marshals notifications to UI thread
var items = new SafeObservableCollection<Item>(dispatcher);
// Safe to call from background thread
await Task.Run(() =>
{
foreach (var item in loadedItems)
{
items.Add(item); // UI updates happen on correct thread
}
});| Package | Platform | Features |
|---|---|---|
OutWit.Common.MVVM.WPF | WPF | DependencyProperty source generator, WPF commands, visual tree utilities |
OutWit.Common.MVVM.Avalonia | Avalonia | StyledProperty/DirectProperty source generator, Avalonia utilities |
OutWit.Common.MVVM.Blazor | Blazor | ViewModelBase for ComponentBase, async lifecycle support |
See the Migration Guide for detailed instructions.
Key changes:
BindableAttribute is obsolete (use StyledPropertyAttribute)SortedCollectionEx renamed to ObservableSortedCollectionOutWit.Common.MVVM.WPF - WPF-specific implementationOutWit.Common.MVVM.Avalonia - Avalonia-specific implementationOutWit.Common.MVVM.Blazor - Blazor-specific implementationLicensed under the Apache License, Version 2.0. See LICENSE.
If you use OutWit.Common.MVVM in a product, a mention is appreciated (but not required), for example: "Powered by OutWit.Common.MVVM (https://ratner.io/)".
"OutWit" and the OutWit logo are used to identify the official project by Dmitry Ratner.
You may:
You may not: