ConvMVVM3 (Convergence MVVM3) is a modern, cross-platform MVVM framework that combines the best features from Prism and Community Toolkit while adding unique innovations.
ConvMVVM3 exposes a single XAML XML namespace for a clean, consistent XAML experience:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:convMVVM3="https://github.com/gellston/ConvMVVM3"
convMVVM3:ViewModelLocator.AutoWireViewModel="True">
</Window>xmlns:convMVVM3="https://github.com/gellston/ConvMVVM3"β ConvMVVM3 XAML features (Regions, ViewModelLocator, Behaviors/Interactivity, etc.)convMVVM3:ViewModelLocator.AutoWireViewModel="True"β ViewModel auto-wiring (Prism-like)
Microsoft-compatible UI thread dispatcher for multiple platforms:
// Setup - Available for WPF, MAUI, Avalonia, WinUI
services.AddWPFUIDispatcher(); // WPF
// Usage - Cross-platform compatible
await dispatcher.InvokeAsync(() => Title = "Updated");Lightweight service container with addon system - No external DI required:
// Built-in container - Zero external dependencies
services.AddSingleton<IMyService, MyService>();Thread-safe messaging with automatic cleanup:
// Automatic weak references - Platform-agnostic
WeakReferenceMessenger.Default.Send<Message>(this, newData);Built-in region management for multiple frameworks:
<!-- Works on WPF, MAUI, Avalonia, WinUI -->
<ContentControl convMVVM3:RegionPlugin.RegionName="MainContent" />Automatic property and command generation with enhanced features:
[ObservableProperty] private string title; // Generates Title property
[RelayCommand] private void Save(); // Generates SaveCommand
[AsyncRelayCommand] private async Task Load(); // Generates LoadCommandConvMVVM3.Core/
βββ ObservableObject # Base class with INotifyPropertyChanged
βββ ObservableRecipient # Message recipient base class
βββ UIDispatcher # Cross-platform dispatcher interface
βββ WeakReferenceMessenger # Memory-safe messaging system
βββ Commands/
β βββ RelayCommand # Synchronous command implementation
β βββ AsyncRelayCommand # Async command with cancellation
βββ DependencyInjection/ # Built-in DI container
ConvMVVM3.WPF/ # WPF-specific implementation
βββ Regions/ # Region system (Prism-inspired)
βββ WPFUIDispatcher # WPF-specific dispatcher
βββ Interactivity/ # Behaviors / triggers / actions
βββ WeakEventManager # Memory-efficient event handling
ConvMVVM3.SourceGenerator/
βββ ObservableProperty generation # Auto property implementation
βββ RelayCommand generation # Auto command creation
βββ Dependency tracking # Smart property notification
βββ Compile-time validation # Early error detection
ConvMVVM3.Core/ # Platform-agnostic MVVM library
ConvMVVM3.SourceGenerator/ # Cross-platform source generator
ConvMVVM3.WPF/ # WPF-specific interactivity + regions
ConvMVVM3.Host/ # DI host implementation
ConvMVVM3.WPF.Tests/ # WPF unit tests
ConvMVVM3.Tests/ # Core unit tests
dotnet add package ConvMVVM3// Works on ALL supported platforms
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private string title = "Hello ConvMVVM3!";
[RelayCommand]
private void ShowMessage() => MessageBox.Show(Title);
[AsyncRelayCommand]
private async Task LoadDataAsync()
{
Title = "Loading...";
await Task.Delay(1000);
Title = "Data Loaded!";
}
}<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:convMVVM3="https://github.com/gellston/ConvMVVM3"
convMVVM3:ViewModelLocator.AutoWireViewModel="True">
<!-- View content -->
</Window>// Setup varies by platform
services.AddWPFUIDispatcher(); // WPF
// Usage is identical across platforms
public partial class MyViewModel : ObservableObject
{
private readonly IUIDispatcher _dispatcher;
public MyViewModel(IUIDispatcher dispatcher)
{
_dispatcher = dispatcher;
}
[AsyncRelayCommand]
private async Task UpdateFromBackgroundAsync()
{
var data = await Task.Run(() => GetHeavyData());
// Thread-safe UI update (cross-platform)
await _dispatcher.InvokeAsync(() => Title = data);
}
}<!-- Works on WPF, MAUI, Avalonia, WinUI -->
<Grid>
<ContentControl convMVVM3:RegionPlugin.RegionName="MainContent" />
</Grid>// Platform-agnostic navigation
_regionManager.RequestNavigate("MainContent", typeof(HomeViewModel));// Sender (all platforms)
WeakReferenceMessenger.Default.Send<DataUpdatedMessage>(this, newData);
// Receiver (auto-cleanup, all platforms)
public partial class MyViewModel : ObservableObject, IRecipient<DataUpdatedMessage>
{
public void Receive(DataUpdatedMessage message)
{
Title = message.Data;
}
}<Button Content="Click Me">
<convMVVM3:Interaction.Triggers>
<convMVVM3:EventTrigger EventName="Click">
<convMVVM3:InvokeCommandAction Command="{Binding SaveCommand}" />
</convMVVM3:EventTrigger>
</convMVVM3:Interaction.Triggers>
</Button>This section includes a few practical helpers used frequently in Prism-style MVVM apps:
ConvMVVM3.Core.Mvvm.ThreadingConvMVVM3.Core.Mvvm.CollectionsConvMVVM3.Host.DependencyInjection
Use a scope to ensure IsBusy is always restored (even if an exception occurs).
Recommended for simple βsingle operationβ flows.
using ConvMVVM3.Core.Mvvm.Threading;
// In your ViewModel
private bool _isBusy;
public bool IsBusy
{
get => _isBusy;
set { _isBusy = value; /* RaisePropertyChanged */ }
}
public async Task RefreshAsync()
{
using (BusyScope.Enter(v => IsBusy = v))
{
await LoadAsync();
}
}Recommended when operations can overlap (nested calls, multiple async commands).
using ConvMVVM3.Core.Mvvm.Threading;
private int _busyCount;
private bool _isBusy;
public bool IsBusy
{
get => _isBusy;
set { _isBusy = value; /* RaisePropertyChanged */ }
}
public async Task RefreshAsync()
{
using (BusyCounterScope.Enter(
getCount: () => _busyCount,
setCount: v => _busyCount = v,
setBusy: v => IsBusy = v))
{
await LoadAsync();
}
}Prevents re-entrancy (double-click save, multiple async executions).
Combine it with BusyScope/BusyCounterScope for a great UX.
using ConvMVVM3.Core.Mvvm.Threading;
private readonly OperationGuard _saveGuard = new OperationGuard();
private int _busyCount;
public Task SaveAsync()
{
return _saveGuard.RunAsync(async () =>
{
using (BusyCounterScope.Enter(
() => _busyCount,
v => _busyCount = v,
v => IsBusy = v))
{
await SaveAsyncCore();
}
});
}Use AddRange/ReplaceRange to avoid thousands of per-item collection change notifications.
using ConvMVVM3.Core.Mvvm.Collections;
public ObservableRangeCollection<ItemViewModel> Items { get; } = new();
public async Task LoadItemsAsync()
{
var items = await LoadFromServerAsync();
// One reset notification instead of N adds
Items.ReplaceRange(items);
}ActivatorUtilities helps create objects by mixing:
- DI-resolved services from
IServiceResolver - runtime arguments you provide (e.g., IDs, parameters, view references)
using ConvMVVM3.Core.DependencyInjection.Abstractions;
using ConvMVVM3.Host.DependencyInjection;
// Example types
public sealed class UserDetailsViewModel
{
public UserDetailsViewModel(IUserService service, int userId)
{
// service comes from DI, userId comes from runtime args
}
}
public static class Example
{
public static void CreateWithRuntimeArgs(IServiceResolver resolver)
{
// 1) CreateInstance: DI + runtime args
var vm = (UserDetailsViewModel)ActivatorUtilities.CreateInstance(
resolver,
typeof(UserDetailsViewModel),
123);
// 2) GetServiceOrCreateInstance: prefer registered instance, otherwise create
var shell = ActivatorUtilities.GetServiceOrCreateInstance<MainShellViewModel>(resolver);
// 3) CreateFactory: preselect ctor + argument mapping for speed
var factory = ActivatorUtilities.CreateFactory(
typeof(UserDetailsViewModel),
new[] { typeof(int) });
var vmFast = (UserDetailsViewModel)factory(resolver, new object[] { 456 });
}
}ConvMVVM3 supports a Prism-style Bootstrapper flow for registering modules, building the shell, and initializing regions/services.
public class AppBootStrapper : Bootstrapper
{
protected override void ConfigureRegion(IRegionManager regionManager)
{
}
protected override Window CreateShell(IServiceContainer provider)
{
return (Window)provider.GetService("MainWindowView");
}
protected override void OnInitialized(IServiceContainer provider)
{
}
protected override void RegisterModules()
{
this.RegisterModule<AModule>();
this.RegisterModule<BModule>();
this.RegisterModule<CModule>();
this.RegisterModule<MainModule>();
}
protected override void RegisterServices(IServiceRegistry container)
{
}
}| Feature | WPF | MAUI | Avalonia | WinUI | UNO |
|---|---|---|---|---|---|
| Core MVVM | β | β | β | β | β |
| UIDispatcher | β | π | π | π | π |
| Source Generator | β | β | β | β | β |
| Region System | β | π | π | π | π |
| Built-in DI | β | β | β | β | β |
| Weak Messenger | β | β | β | β | β |
| WPF Interactivity | β | β | β | β | β |
| Platform Interactivity | β | π | π | π | π |
β Available | π Planned | β Not applicable
MIT License - see LICENSE file
ConvMVVM3: Modern, cross-platform MVVM framework that bridges the gap between Prism's power and Community Toolkit's simplicity while adding unique cross-platform innovations.
Future Roadmap: MAUI, Avalonia, WinUI, and UNO Platform support coming soon!
