gbastecki.BlazorMvvm
1.0.2
See the version list below for details.
dotnet add package gbastecki.BlazorMvvm --version 1.0.2
NuGet\Install-Package gbastecki.BlazorMvvm -Version 1.0.2
<PackageReference Include="gbastecki.BlazorMvvm" Version="1.0.2" />
<PackageVersion Include="gbastecki.BlazorMvvm" Version="1.0.2" />
<PackageReference Include="gbastecki.BlazorMvvm" />
paket add gbastecki.BlazorMvvm --version 1.0.2
#r "nuget: gbastecki.BlazorMvvm, 1.0.2"
#:package gbastecki.BlazorMvvm@1.0.2
#addin nuget:?package=gbastecki.BlazorMvvm&version=1.0.2
#tool nuget:?package=gbastecki.BlazorMvvm&version=1.0.2
BlazorMvvm
Use the MVVM pattern for Blazor with a simple and lightweight library.
Quick start
This library is distributed via NuGet.
Check Live Demo.
Usage
BlazorMvvm provides a lightweight set of base classes and components to implement the Model-View-ViewModel (MVVM) pattern in Blazor applications.
This guide outlines the core components and their usage.
ViewModel
Viewmodels encapsulate the application's presentation logic and state. In BlazorMvvm, your viewmodels must inherit from the BlazorViewModel base class.
This base class implements IBlazorViewModel, which is essential for notifying the UI when a property's value has changed.
Example: HomeViewModel.cs
using BlazorMvvm;
namespace YourNamespace;
public class HomeViewModel : BlazorViewModel
{
// --- Option 1: Manual Property Notification ---
private int _counter;
public int Counter
{
get => _counter;
set
{
// Manual equality check
if (_counter == value) return;
_counter = value;
// Manually raise the OnPropertyChanged event
// This will trigger the UI to refresh
base.OnPropertyChanged();
}
}
// --- Option 2: Using the Set<T> Helper ---
private int _counter2;
public int Counter2
{
get => _counter2;
// The Set() helper method simplifies this pattern:
// 1. It performs an equality check.
// 2. If the value is new, it updates the backing field.
// 3. It raises the OnPropertyChanged event.
set => Set(ref _counter2, value);
}
}
ComponentBase
To bind a view (Blazor Component) to a viewmodel, your components should inherit from BlazorMvvmComponentBase<T>, where T is the type of your viewmodel.
The final step is to connect your viewmodel instance to the component by calling SetDataContext() in the component's OnInitialized lifecycle method. This subscribes the component to the viewmodel's PropertyChanged events, automatically triggering StateHasChanged() to re-render the component when a property is updated.
Example: Home.razor
@using BlazorMvvm
@inherits BlazorMvvmComponentBase<HomeViewModel>
Example: Home.razor.cs
using BlazorMvvm;
namespace YourNamespace;
public partial class Home : BlazorMvvmComponentBase<HomeViewModel>
{
// Instantiate the ViewModel
HomeViewModel ViewModel = new();
protected override void OnInitialized()
{
// Set the DataContext to link the View and ViewModel.
// This is the essential step for enabling data binding.
SetDataContext(ViewModel);
base.OnInitialized();
}
}
ObservableComponent
By default, when a viewmodel property changes, the entire component bound via SetDataContext is re-rendered. For complex UIs, this can be inefficient.
The ObservableComponent allows you to define fine-grained "observable" fragments within your component. These fragments can be bound to a viewmodel and will only re-render when specific properties change, isolating the UI update.
Usage
Full Update: Pass a
ViewModelinstance. TheObservableComponent's child content will re-render for any property change on that viewmodel.Selective Update: Pass a
ViewModeland aPropertyNamesarray. The child content will only re-render when one of the specified properties raises itsOnPropertyChangedevent.
Example: Home.razor
@using BlazorMvvm
@inherits BlazorMvvmComponentBase<HomeViewModel>
<ObservableComponent ViewModel="ObservablePartViewModel">
<div>ObservableComponent current counter: @ObservablePartViewModel.Counter</div>
</ObservableComponent>
<ObservableComponent ViewModel="SharedObservableViewModel" PropertyNames="[nameof(SharedObservableViewModel.Counter1)]">
<div>SharedObservableViewModel current counter 1: @SharedObservableViewModel.Counter1</div>
</ObservableComponent>
<ObservableComponent ViewModel="SharedObservableViewModel" PropertyNames="[nameof(SharedObservableViewModel.Counter2), nameof(SharedObservableViewModel.Counter3)]">
<div>SharedObservableViewModel current counter 2: @SharedObservableViewModel.Counter2</div>
<div>SharedObservableViewModel current counter 3: @SharedObservableViewModel.Counter3</div>
</ObservableComponent>
Example: Home.razor.cs
using BlazorMvvm;
using Microsoft.AspNetCore.Components;
namespace YourNamespace;
public class ObservablePartViewModel : BlazorViewModel { /* ... */ }
public class SharedObservableViewModel : BlazorViewModel { /* ... */ }
public partial class Home : BlazorMvvmComponentBase<HomeViewModel>
{
// Main ViewModel for the component
HomeViewModel ViewModel = new();
// ViewModels for the observable fragments
ObservablePartViewModel ObservablePartViewModel = new();
SharedObservableViewModel SharedObservableViewModel = new();
protected override void OnInitialized()
{
// The main viewmodel is set as the primary DataContext
SetDataContext(ViewModel);
base.OnInitialized();
}
}
Commands
BlazorMvvm provides Command implementations that allow you to bind UI actions (like @onclick) to methods on your viewmodel, while also managing execution state (e.g., disabling a button while an async task is running).
Available Implementations
Parameterless:
BlazorCommand(Action execute, Func<bool>? canExecute = null)BlazorAsyncCommand(Func<Task> execute, Func<Task<bool>>? canExecute = null, bool allowConcurrentExecutions = false)
Generic (Type-Safe Parameter):
BlazorRelayCommand<T>(Action<T> execute, Func<T, bool>? canExecute = null)BlazorAsyncRelayCommand<T>(Func<T, Task> execute, Func<T, Task<bool>>? canExecute = null, bool allowConcurrentExecutions = false)
Object-based Parameter:
BlazorRelayCommand(Action<object[]?> execute, Func<object[]?, bool>? canExecute = null)BlazorAsyncRelayCommand(Func<object[]?, Task> execute, Func<object[]?, Task<bool>>? canExecute = null, bool allowConcurrentExecutions = false)
Key Features
canExecute: An optional delegate that determines if the command is allowed to run.IsExecuting(Async only): Aboolproperty that istruewhile theexecutetask is running.allowConcurrentExecutions(Async only): Iffalse(the default), prevents the command from executing if itIsExecuting.OnIsExecutingChanged(Async only): An event raised whenIsExecutingchanges. You must subscribe to this and callOnPropertyChanged()to notify the UI to update.
Example: ButtonExampleViewModel.cs
This example demonstrates an async command that disables a button for 5 seconds. It implements IDisposable to safely unsubscribe from the event handler.
using BlazorMvvm;
using System;
using System.Threading.Tasks;
namespace YourNamespace;
public class ButtonExampleViewModel : BlazorViewModel, IDisposable
{
public IBlazorAsyncCommand DisableButtonCommand { get; }
public ButtonExampleViewModel()
{
// Initialize the command, passing the method to execute
DisableButtonCommand = new BlazorAsyncCommand(DisableButton);
// Subscribe to the event to update the UI
DisableButtonCommand.OnIsExecutingChanged += DisableButtonCommand_OnIsExecutingChanged;
}
private async Task DisableButton()
{
// Simulate a long-running operation
await Task.Delay(5000);
}
private void DisableButtonCommand_OnIsExecutingChanged(bool isExecuting)
{
// Notify the UI that the command's state has changed
// This allows the button's 'disabled' attribute to update
base.OnPropertyChanged(nameof(DisableButtonCommand));
}
// Implement IDisposable to clean up event subscriptions
public void Dispose()
{
DisableButtonCommand.OnIsExecutingChanged -= DisableButtonCommand_OnIsExecutingChanged;
}
}
Example: .razor Component
This component hosts the ButtonExampleViewModel inside an ObservableComponent to ensure the button state updates correctly.
@using BlazorMvvm
@inherits BlazorMvvmComponentBase<HomeViewModel>
<ObservableComponent ViewModel="ButtonExampleViewModel">
<button
@onclick="ButtonExampleViewModel.DisableButtonCommand.Execute"
disabled="@ButtonExampleViewModel.DisableButtonCommand.IsExecuting">
Disable button for 5 seconds
</button>
</ObservableComponent>
@code {
ButtonExampleViewModel ButtonExampleViewModel = new();
protected override void OnDispose()
{
ButtonExampleViewModel.Dispose();
base.OnDispose();
}
}
| 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.0 && < 11.0.0)
-
net6.0
- Microsoft.AspNetCore.Components.Web (>= 6.0.0 && < 7.0.0)
-
net7.0
- Microsoft.AspNetCore.Components.Web (>= 7.0.0 && < 8.0.0)
-
net8.0
- Microsoft.AspNetCore.Components.Web (>= 8.0.0 && < 9.0.0)
-
net9.0
- Microsoft.AspNetCore.Components.Web (>= 9.0.0 && < 10.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.