Goffo.MVVM 1.1.0

dotnet add package Goffo.MVVM --version 1.1.0
                    
NuGet\Install-Package Goffo.MVVM -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Goffo.MVVM" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Goffo.MVVM" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Goffo.MVVM" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Goffo.MVVM --version 1.1.0
                    
#r "nuget: Goffo.MVVM, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=Goffo.MVVM&version=1.1.0
                    
Install Goffo.MVVM as a Cake Addin
#tool nuget:?package=Goffo.MVVM&version=1.1.0
                    
Install Goffo.MVVM as a Cake Tool

READ ME

MVVM

MVVM Library for startup

Version Updates

<u>1.1.0</u>

  1. Added methods to AbstractViewModel for StartLoading and StopLoading and added property LoadingIsComplete

Includes

  1. Abstract View Model with Logging
  2. Abstract Command
  3. Abstract Async Command
  4. Navigation for View Models

Contents

  1. Dependency Injection Examples
  2. How to use Abstract View Model
  3. How to use Abstract Command
  4. How to use Abstract Async Command
  5. How to use Navigation
  6. View Models with Interfaces

Dependency Injection

  1. Add using Goffo.MVVM.Extensions
  2. Use extension method RegisterGoffoMVVM

Example: WPF


using Goffo.MVVM.Library.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Windows;

namespace Goffo.MVVM.WPF;

public partial class App : Application
{
    private readonly IHost _host;

    public App()
    {
        _host = Host.CreateDefaultBuilder()
            .ConfigureLogging((context, logger) =>
            {
                logger.ClearProviders();
                logger.AddDebug();
                logger.AddConsole();
            })
            .ConfigureServices((context, services) =>
            {
                services.RegisterGoffoMVVM();
                services.RegisterMVVMLibrary();
                services.AddSingleton<MainWindow>(provider =>
                {
                    MainWindow window = new()
                    {
                        DataContext = provider.CreateScope().ServiceProvider.GetRequiredService<MainViewModel>()
                    };

                    return window;
                });
            })
            .Build();
    }

    protected override async void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        _host.Services.GetRequiredService<MainWindow>().Show();
        await _host.StartAsync();
    }

    protected override async void OnExit(ExitEventArgs e)
    {
        base.OnExit(e);
        await _host.StopAsync();
    }
}


Example: Blazor

  1. Server

using Goffo.MVVM.Blazor.Components;
using Goffo.MVVM.Extensions;
using Goffo.MVVM.Library.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents()
    .AddInteractiveWebAssemblyComponents();

builder.Services.RegisterGoffoMVVM();
....

  1. Client

using Goffo.MVVM.Extensions;
using Goffo.MVVM.Library.Extensions;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.RegisterGoffoMVVM();
await builder.Build().RunAsync();

How to use Abstract View Model


using Goffo.MVVM.Base.ViewModels;
using Goffo.MVVM.Navigation;
using Microsoft.Extensions.Logging;

namespace Goffo.MVVM.Library.Examples;
public class ExampleViewModel : AbstractViewModel<ExampleViewModel>
{
    public ExampleViewModel(ILogger<ExampleViewModel> logger, NavigationCommand navigationCommand) : base(logger, navigationCommand)
    {
    }

    public void DoSomething()
    {
        // Do something
    }

    public async Task DoSomethingAsync()
    {
        // Do something async
        await Task.Delay(1000);
    }
}

How to use Abstract Command


using Goffo.MVVM.Base.Commands;

namespace Goffo.MVVM.Library.Examples;
public class ExampleCommand : AbstractCommand<ExampleViewModel>
{
    public ExampleCommand(ExampleViewModel viewModel) : base(viewModel)
    {
    }

    public override void Execute(object? parameter = null)
    {
        // Command Logic goes here

        base.ViewModel.DoSomething();
    }
}

How to use Abstract Async Command


using Goffo.MVVM.Base.Commands;

namespace Goffo.MVVM.Library.Examples;
public class ExampleAsyncCommand : AbstractAsyncCommand<ExampleViewModel>
{
    public ExampleAsyncCommand(ExampleViewModel viewModel) : base(viewModel)
    {
    }

    public override async Task ExecuteAsync(object? parameter = null)
    {
        try
        {
            // Command Logic goes here

            await base.ViewModel.DoSomethingAsync();
            OnCompleted();
        }
        catch (Exception ex)
        {
            OnException(ex);
        }
    }

    protected override void OnCompleted()
    {
        // Do something after the command has completed
        base.OnCompleted();
    }

    protected override void OnException(Exception exception)
    {
        // Do something when an exception occurs
        base.OnException(exception);
    }
}

How to use Navigation

Create a ViewModel to be the Navigator

  1. Inherit from INavigator
  2. Inject the NavigationCommand if not using Abstract View Model
  3. Subscribe to the Navigate EventHandler
  4. Inject NavigationViewModelFactory to use in OnNavigate Method to navigate to different view models

using Goffo.MVVM.Base.ViewModels;
using Goffo.MVVM.Navigation;
using Microsoft.Extensions.Logging;

namespace Goffo.MVVM.Library.ViewModels;

public class MainViewModel : AbstractViewModel<MainViewModel>, INavigator
{
    private readonly NavigationViewModelFactory _navigationViewModelFactory;
    public MainViewModel(ILogger<MainViewModel> logger, NavigationCommand navigationCommand, NavigationViewModelFactory navigationViewModelFactory) : base(logger, navigationCommand)
    {
        _navigationViewModelFactory = navigationViewModelFactory;
        base.NavigationCommand.Navigate += NavigationCommand_Navigate;
        base.NavigateToViewModel = _navigationViewModelFactory.GetNavigationViewModel<HomeViewModel>();
    }

    public override void Dispose()
    {
        base.NavigationCommand.Navigate -= NavigationCommand_Navigate;
        base.Dispose();
    }

    private void NavigationCommand_Navigate(object? sender, NavigationEventArgs e)
    {
        OnNavigate(e.NavigateToView);
    }
   

    public void OnNavigate(string? navigateToView)
    {   
        // Navigation Logic goes here

        switch (navigateToView)
        {
            case Views.Home:
                NavigateToViewModel = _navigationViewModelFactory.GetNavigationViewModel<HomeViewModel>();
                break;
            case Views.About:
                NavigateToViewModel = _navigationViewModelFactory.GetNavigationViewModel<AboutViewModel>();
                break;
            case Views.Contact:
                NavigateToViewModel = _navigationViewModelFactory.GetNavigationViewModel<ContactViewModel>();
                break;
            default:
                break;
        }

        base.OnPropertyChanged(nameof(NavigateToViewModel));
    }

}


Give View Models Ability to Navigate

  1. Inherit from the INavigationViewModel
  2. Set the NavigateToView name

using Goffo.MVVM.Base.ViewModels;
using Goffo.MVVM.Library.Commands;
using Goffo.MVVM.Navigation;
using Microsoft.Extensions.Logging;

namespace Goffo.MVVM.Library.ViewModels
{
    public class AboutViewModel : AbstractViewModel<AboutViewModel>, INavigationViewModel
    {
        public AboutViewModel(ILogger<AboutViewModel> logger, NavigationCommand navigationCommand) : base(logger, navigationCommand)
        {
            NavigateHomeCommand = new AboutCommandNavigateHome(this);
        }

        public AboutCommandNavigateHome NavigateHomeCommand { get; init; }

        // This is the View Name that will be used to navigate to this view
        public string NavigateToView => Views.About;

        public void NavigateHome()
        {
            NavigationCommand.Execute(Views.Home);
        }

    }
}

Example Command to use for Navigation

using Goffo.MVVM.Base.Commands;
using Goffo.MVVM.Library.ViewModels;

namespace Goffo.MVVM.Library.Commands;

public class AboutCommandNavigateHome : AbstractCommand<AboutViewModel>
{
    public AboutCommandNavigateHome(AboutViewModel viewModel) : base(viewModel)
    {
    }

    public override void Execute(object? parameter = null)
    {
        base.ViewModel.NavigateHome();
    }
}

MainWindow XAML


<Window x:Class="Goffo.MVVM.WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Goffo.MVVM.WPF"
        xmlns:views="clr-namespace:Goffo.MVVM.WPF.Views"
        xmlns:library="clr-namespace:Goffo.MVVM.Library;assembly=Goffo.MVVM.Library"
        xmlns:viewmodels="clr-namespace:Goffo.MVVM.Library.ViewModels;assembly=Goffo.MVVM.Library"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <Style x:Key="NoCircleRadioButton" TargetType="RadioButton">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="RadioButton">
                        <Border Background="Transparent">
                            <ContentPresenter />
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Border Grid.Row="1" Grid.Column="1" Background="Black" HorizontalAlignment="Stretch">
            <StackPanel Background="Black" Orientation="Horizontal" Margin="10" HorizontalAlignment="Stretch">
                <TextBlock FontSize="36" FontWeight="Bold" Foreground="Red" Margin="0 0 20 0">MY APP</TextBlock>
                <RadioButton Content="Home" GroupName="navGroup" Style="{StaticResource NoCircleRadioButton}" Command="{Binding NavigationCommand}" CommandParameter="{x:Static library:Views.Home}" Cursor="Hand" Margin="10 7 0 0" Foreground="Red" FontSize="24" FontWeight="SemiBold" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"></RadioButton>
            </StackPanel>
        </Border>

        <Border Grid.Row="1" Grid.Column="2" Background="Black" HorizontalAlignment="Right">
            <StackPanel Background="Black" Orientation="Horizontal" Margin="10" HorizontalAlignment="Stretch">
                <RadioButton Content="About" GroupName="navGroup" Style="{StaticResource NoCircleRadioButton}" Command="{Binding NavigationCommand}" CommandParameter="{x:Static library:Views.About}" Cursor="Hand" Margin="10 7 10 0" Foreground="Red" FontSize="24" FontWeight="SemiBold" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"></RadioButton>
                <RadioButton Content="Contact" GroupName="navGroup" Style="{StaticResource NoCircleRadioButton}" Command="{Binding NavigationCommand}" CommandParameter="{x:Static library:Views.Contact}" Cursor="Hand" Margin="10 7 10 0" Foreground="Red" HorizontalAlignment="Left" FontSize="24" FontWeight="SemiBold" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"></RadioButton>
            </StackPanel>
        </Border>

        <ContentControl Grid.Row="2" Grid.RowSpan="2" Grid.Column="0" Grid.ColumnSpan="3" Content="{Binding NavigateToViewModel}">
            <ContentControl.Resources>
                <DataTemplate DataType="{x:Type viewmodels:HomeViewModel}">
                    <views:Home></views:Home>
                </DataTemplate>
                <DataTemplate DataType="{x:Type viewmodels:AboutViewModel}">
                    <views:About></views:About>
                </DataTemplate>
                <DataTemplate DataType="{x:Type viewmodels:ContactViewModel}">
                    <views:Contact></views:Contact>
                </DataTemplate>
            </ContentControl.Resources>
        </ContentControl>
        
        
    </Grid>
</Window>

  1. Bind to your Command

<UserControl x:Class="Goffo.MVVM.WPF.Views.About"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Goffo.MVVM.WPF.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <TextBlock Margin="20" HorizontalAlignment="Center" VerticalAlignment="Top" FontSize="36" FontWeight="Bold" Foreground="LightGreen">THIS IS THE ABOUT PAGE</TextBlock>
        <Button Width="200" Height="50" FontWeight="Bold" FontSize="16" Padding="5" Command="{Binding NavigateHomeCommand}">Go To Home Page</Button>
    </Grid>
</UserControl>

  1. Need to make NavMenu (or your own menu) Interactive
  2. Inject your Navigator View Model
  3. Subscribe to the OnPropertyChanged EventHandler
  4. Use NavigationManager to navigate when Property changing is the NavigateToViewModel

@using Goffo.MVVM.Library
@using Goffo.MVVM.Navigation
@rendermode InteractiveAuto
@implements IDisposable
@inject ILogger<NavMenu> _logger
@inject MainViewModel _viewModel
@inject NavigationManager _navigationManager

<div class="top-row ps-3 navbar navbar-dark">
    <div class="container-fluid">
        <a class="navbar-brand" href="">Goffo.MVVM.Blazor</a>
    </div>
</div>

<input type="checkbox" title="Navigation menu" class="navbar-toggler" />

<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
    <nav class="nav flex-column">
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="weather">
                <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
            </NavLink>
        </div>
    </nav>
</div>

@code
{
    protected override void OnInitialized()
    {
        base.OnInitialized();
        _viewModel.PropertyChanged += ViewModel_PropertyChanged!;
    }

    public void Dispose()
    {
        _viewModel.PropertyChanged -= ViewModel_PropertyChanged!;
    }

    private void ViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs args)
    {
        if (args.PropertyName == nameof(_viewModel.NavigateToViewModel))
        {
            if (_viewModel.NavigateToViewModel is not null)
            {
                if (_viewModel.NavigateToViewModel.NavigateToView == Views.Home)
                {
                    _navigationManager.NavigateTo("");
                }
                else
                {
                    _navigationManager.NavigateTo(_viewModel.NavigateToViewModel.NavigateToView);
                }
            }
            else
            {
                _logger.LogWarning("Navigate To View Model was NULL");
            }
        }
    }
}

  1. Create a method that calls your command that will navigate

@page "/about"
@rendermode InteractiveAuto
@inject AboutViewModel _viewModel

<PageTitle>About</PageTitle>

<h1>About</h1>

<a style="color: blue; text-decoration: underline; cursor: pointer;" @onclick="OnNavigateHome">Click To Go Home</a>

@code 
{
    private void OnNavigateHome()
    {
        _viewModel.NavigateHomeCommand.Execute();
    }
}

View Models with Interfaces

  1. Implement the IViewModel interface on your View Model interfaces

namespace Goffo.MVVM.Base.ViewModels;
public interface IViewModel : INotifyPropertyChanged, IDisposable, IAsyncDisposable
{
}

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.0 117 3/26/2025
1.0.2 148 3/22/2025
1.0.1 64 3/21/2025
1.0.0 121 3/21/2025

Made all Validation Messages on AbstractBaseViewModel to be full properties and virtual