OutWit.Common.MVVM.Blazor
2.0.2
dotnet add package OutWit.Common.MVVM.Blazor --version 2.0.2
NuGet\Install-Package OutWit.Common.MVVM.Blazor -Version 2.0.2
<PackageReference Include="OutWit.Common.MVVM.Blazor" Version="2.0.2" />
<PackageVersion Include="OutWit.Common.MVVM.Blazor" Version="2.0.2" />
<PackageReference Include="OutWit.Common.MVVM.Blazor" />
paket add OutWit.Common.MVVM.Blazor --version 2.0.2
#r "nuget: OutWit.Common.MVVM.Blazor, 2.0.2"
#:package OutWit.Common.MVVM.Blazor@2.0.2
#addin nuget:?package=OutWit.Common.MVVM.Blazor&version=2.0.2
#tool nuget:?package=OutWit.Common.MVVM.Blazor&version=2.0.2
OutWit.Common.MVVM.Blazor
Blazor-specific MVVM components and utilities for building Blazor applications with the MVVM pattern.
Features
- ViewModelBase: Base class combining
ComponentBasewithINotifyPropertyChanged - ViewModelBaseAsync: Extended base with async lifecycle support and error handling
- BlazorDispatcher:
IDispatcherimplementation for UI thread invocation - SafeObservableCollection: Collection with automatic
StateHasChangedcallbacks - Component Extensions: Helper methods for Blazor components
Installation
dotnet add package OutWit.Common.MVVM.Blazor
This automatically includes:
OutWit.Common.MVVM(base cross-platform package)OutWit.Common.Logging
Quick Start
ViewModelBase
The simplest way to create a Blazor component with MVVM support:
@inherits ViewModelBase
<h1>Counter: @Count</h1>
<button @onclick="IncrementCount" disabled="@Busy">
@(Busy ? "Loading..." : "Increment")
</button>
@code {
private int _count;
public int Count
{
get => _count;
set => SetProperty(ref _count, value);
}
private async Task IncrementCount()
{
await RunAsync(async () =>
{
await Task.Delay(500); // Simulate work
Count++;
});
}
}
ViewModelBase Features
public class MyViewModel : ViewModelBase
{
private string _name = "";
private int _count;
// Property with automatic change notification
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
public int Count
{
get => _count;
set => SetProperty(ref _count, value);
}
// Run async operation with automatic Busy state management
public async Task LoadDataAsync()
{
await RunAsync(async () =>
{
var data = await FetchDataAsync();
Name = data.Name;
});
// Busy is automatically set to true/false
// StateHasChanged is called automatically after completion
}
// Safe execution with error handling
public void SafeOperation()
{
// Returns default on exception
var result = Check(() => int.Parse("invalid"), defaultValue: 0);
// Returns false on exception
var success = Check(() => RiskyOperation());
// Swallows exception
Check(() => MayThrow());
}
// React to property changes
protected override void OnPropertyChanged(string? propertyName)
{
if (propertyName == nameof(Name))
{
// Handle name change
}
}
}
ViewModelBaseAsync
Extended base class with async initialization and error handling:
@inherits ViewModelBaseAsync
@if (HasError)
{
<div class="alert alert-danger">@Error</div>
}
else if (Busy)
{
<div>Loading...</div>
}
else
{
<div>@Data</div>
}
@code {
public string? Data { get; private set; }
// Called during OnInitializedAsync - errors are caught automatically
protected override async Task InitializeAsync()
{
Data = await LoadDataAsync();
}
// Optional: Handle initialization errors
protected override void OnInitializationError(Exception ex)
{
// Log error, show notification, etc.
}
}
SafeObservableCollection
Collection that automatically notifies UI when modified:
@inherits ViewModelBase
<ul>
@foreach (var item in Items)
{
<li>@item</li>
}
</ul>
@code {
// Collection with automatic StateHasChanged callback
public SafeObservableCollection<string> Items { get; private set; } = null!;
protected override void OnInitialized()
{
// Pass StateHasChanged action to automatically refresh UI on changes
Items = new SafeObservableCollection<string>(() => StateHasChanged());
Items.Add("Item 1");
Items.Add("Item 2");
}
private void AddItem(string item)
{
Items.Add(item); // UI automatically updates
}
}
Component Extensions
using OutWit.Common.MVVM.Blazor.Utils;
// Force UI update
component.ForceUpdate();
// Run with busy state
await component.RunWithBusyAsync(
() => _busy,
v => _busy = v,
async () => await DoWorkAsync()
);
ViewModelBase API
| Member | Type | Description |
|---|---|---|
Busy |
bool |
Indicates if async operation is running |
PropertyChanged |
event |
Fired when property value changes |
SetProperty<T> |
method |
Sets property and raises PropertyChanged |
RaisePropertyChanged |
method |
Manually raises PropertyChanged |
RunAsync |
method |
Runs async operation with Busy management |
Check<T> |
method |
Executes with error handling |
InvokeOnUIAsync |
method |
Invokes on UI thread |
ViewModelBaseAsync API
Includes all ViewModelBase members plus:
| Member | Type | Description |
|---|---|---|
Error |
string? |
Error message from initialization |
HasError |
bool |
Indicates if there's an error |
InitializeAsync |
method |
Override for async initialization |
OnInitializationError |
method |
Called when initialization fails |
DisposeAsync |
method |
Async dispose support |
Design Considerations
Why No Source Generator?
Unlike WPF and Avalonia, Blazor doesn't have a DependencyProperty or StyledProperty equivalent. Blazor uses:
[Parameter]attribute for component parameters (built-in)[CascadingParameter]for cascading values (built-in)- Standard C# properties with
INotifyPropertyChanged
The existing Blazor infrastructure handles these patterns well, so a source generator isn't necessary.
Differences from WPF/Avalonia
| Feature | WPF/Avalonia | Blazor |
|---|---|---|
| Property System | DependencyProperty/StyledProperty | Standard C# + INotifyPropertyChanged |
| UI Thread | Dispatcher | SynchronizationContext + InvokeAsync |
| Visual Tree | Yes | Render Tree (different model) |
| Data Binding | XAML Binding | Razor @bind |
Related Packages
OutWit.Common.MVVM- Cross-platform base classesOutWit.Common.MVVM.WPF- WPF-specific implementationOutWit.Common.MVVM.Avalonia- Avalonia-specific implementation
License
Licensed under the Apache License, Version 2.0. See LICENSE.
Attribution (optional)
If you use OutWit.Common.MVVM.Blazor in a product, a mention is appreciated (but not required), for example: "Powered by OutWit.Common.MVVM.Blazor (https://ratner.io/)".
Trademark / Project name
"OutWit" and the OutWit logo are used to identify the official project by Dmitry Ratner.
You may:
- refer to the project name in a factual way (e.g., "built with OutWit.Common.MVVM.Blazor");
- use the name to indicate compatibility (e.g., "OutWit.Common.MVVM.Blazor-compatible").
You may not:
- use "OutWit.Common.MVVM.Blazor" as the name of a fork or a derived product in a way that implies it is the official project;
- use the OutWit.Common.MVVM.Blazor logo to promote forks or derived products without permission.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. 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 is compatible. 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. |
-
net10.0
- Microsoft.AspNetCore.Components.Web (>= 10.0.2)
- OutWit.Common.Logging (>= 1.2.3)
- OutWit.Common.MVVM (>= 2.0.2)
-
net6.0
- Microsoft.AspNetCore.Components.Web (>= 6.0.36)
- OutWit.Common.Logging (>= 1.2.3)
- OutWit.Common.MVVM (>= 2.0.2)
-
net7.0
- Microsoft.AspNetCore.Components.Web (>= 7.0.20)
- OutWit.Common.Logging (>= 1.2.3)
- OutWit.Common.MVVM (>= 2.0.2)
-
net8.0
- Microsoft.AspNetCore.Components.Web (>= 8.0.22)
- OutWit.Common.Logging (>= 1.2.3)
- OutWit.Common.MVVM (>= 2.0.2)
-
net9.0
- Microsoft.AspNetCore.Components.Web (>= 9.0.11)
- OutWit.Common.Logging (>= 1.2.3)
- OutWit.Common.MVVM (>= 2.0.2)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on OutWit.Common.MVVM.Blazor:
| Package | Downloads |
|---|---|
|
OutWit.Web.Framework
WitDocs Framework: Blazor WebAssembly framework for creating static websites with markdown content, SEO optimization, skeleton loading, and automatic content generation. Part of https://witdocs.io |
|
|
OutWit.Common.Blazor.Logging
Blazor WebAssembly log viewer components built on MudBlazor. Provides a toolbar, filter tree, table, and detail panel for querying and displaying NewRelic logs. |
GitHub repositories
This package is not used by any popular GitHub repositories.