# Blazor Extension for the MVVM CommunityToolkit
This is an expansion of the blazor-mvvm repo by Kelly Adams that implements full MVVM support via the CommunityToolkit.Mvvm. Minor changes were made to prevent cross-thread exceptions, added extra base class types, Mvvm-style navigation, and converted into a usable library.
The library packages the support into a resuable library and includes a new MvvmNavigationManager
class and the MvvmNavLink
component for Mvvm-style navigation, no more hard-coded paths.
Library supports the following hosting models:
There are two additional sample projects in seperate GitHub repositories:
Add the Nuget package to your project
Enable MvvmNavigation support in your Program.cs
file
2-1. Blazor Server App:
builder.Services.AddMvvmNavigation(options =>
{
options.HostingModel = BlazorHostingModel.Server;
});
2-2. Blazor WebAssembly App:
builder.Services.AddMvvmNavigation();
2-3. Blazor WebApp:
builder.Services.AddMvvmNavigation(options =>
{
options.HostingModel = BlazorHostingModel.WebApp;
});
ViewModel
inheriting the ViewModelBase
classpublic partial class FetchDataViewModel : ViewModelBase
{
[ObservableProperty]
private ObservableCollection<WeatherForecast> _weatherForecasts = new();
public override async Task Loaded()
=> WeatherForecasts = new ObservableCollection<WeatherForecast>(Get());
private static readonly string[] Summaries =
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public IEnumerable<WeatherForecast> Get()
=> Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
ViewModel
in your Program.cs
filebuilder.Services.AddTransient<FetchDataViewModel>();
MvvmComponentBase<TViewModel>
component@page "/fetchdata"
@inherits MvvmComponentBase<FetchDataViewModel>
<PageTitle>Weather forecast</PageTitle>
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from the server.</p>
@if (!ViewModel.WeatherForecasts.Any())
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in ViewModel.WeatherForecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
NavMenu.razor
to use MvvmNavLink
for Navigation by ViewModel<div class="nav-item px-3">
<MvvmNavLink class="nav-link" TViewModel=FetchDataViewModel>
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</MvvmNavLink>
</div>
Now run the app.
Navigating by ViewModel using the MvvmNavigationManager
from code, inject the class into your page or ViewModel, then use the NavigateTo
method
mvvmNavigationManager.NavigateTo<FetchDataViewModel>();
The NavigateTo
method works the same as the standard Blazor NavigationManager
and also support passing of a Relative URL &/or QueryString.
If you are into abstraction, then you can also navigate by interface
mvvmNavigationManager.NavigateTo<ITestNavigationViewModel>();
The same principle worhs with the MvvmNavLink
component
<div class="nav-item px-3">
<MvvmNavLink class="nav-link"
TViewModel=ITestNavigationViewModel
Match="NavLinkMatch.All">
<span class="oi oi-calculator" aria-hidden="true"></span>Test
</MvvmNavLink>
</div>
<div class="nav-item px-3">
<MvvmNavLink class="nav-link"
TViewModel=ITestNavigationViewModel
RelativeUri="this is a MvvmNavLink test"
Match="NavLinkMatch.All">
<span class="oi oi-calculator" aria-hidden="true"></span>Test + Params
</MvvmNavLink>
</div>
<div class="nav-item px-3">
<MvvmNavLink class="nav-link"
TViewModel=ITestNavigationViewModel
RelativeUri="?test=this%20is%20a%20MvvmNavLink%20querystring%20test"
Match="NavLinkMatch.All">
<span class="oi oi-calculator" aria-hidden="true"></span>Test + QueryString
</MvvmNavLink>
</div>
<div class="nav-item px-3">
<MvvmNavLink class="nav-link"
TViewModel=ITestNavigationViewModel
RelativeUri="this is a MvvmNvLink test/?test=this%20is%20a%20MvvmNavLink%20querystring%20test"
Match="NavLinkMatch.All">
<span class="oi oi-calculator" aria-hidden="true"></span>Test + Both
</MvvmNavLink>
</div>
There are two parts:
ViewModelBase
MvvmComponentBase
The MvvmComponentBase
handles wiring up the ViewModel
to the component.
EditForm Validation and Messaging are also supported. See the sample code for examples of how to use.
No more magic strings! Strongly-typed navigation is now possible. If the page URI changes, there is no more need to go hunting through your source code to make changes. It is auto-magically resolved at runtime for you!
When the MvvmNavigationManager
is initialized by the IOC container as a Singleton, the class will examine all assemblies and internally caches all ViewModels (classes and interfaces) and the page it is associated with. Then when it comes time to navigate, a quick lookup is done and the Blazor NavigationManager
is then used to navigate to the correct page. IF any Relative Uri &/or QueryString was passed in via the NavigateTo
method call, that is passed too.
Note: The MvvmNavigationManager
class is not a total replacement for the Blazor NavigationManager
class, only support for MVVM is implemented.
The MvvmNavLink
component is based on the blazor Navlink
component and has an extra TViewModel
and RelativeUri
properties. Internally, uses the MvvmNavigationManager
to do the navigation.
RecipientViewModelBase
ValidatorViewModelBase
MvvmNavigationManager
cache generation for improved debugging experienceMvvmLayoutComponentBase
to support MVVM in the MainLayout.razorMvvmLayoutComponentBase
usageBlazor Server App
support(Auto) Blazor WebApp
supportBlazor Server App
support(Auto) Blazor WebApp
with new hosting model configuration supportIf you find this library useful, then please consider buying me a coffee ☕.