Nabs.Launchpad.Ui.Shell 10.0.210

Prefix Reserved
dotnet add package Nabs.Launchpad.Ui.Shell --version 10.0.210
                    
NuGet\Install-Package Nabs.Launchpad.Ui.Shell -Version 10.0.210
                    
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="Nabs.Launchpad.Ui.Shell" Version="10.0.210" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Nabs.Launchpad.Ui.Shell" Version="10.0.210" />
                    
Directory.Packages.props
<PackageReference Include="Nabs.Launchpad.Ui.Shell" />
                    
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 Nabs.Launchpad.Ui.Shell --version 10.0.210
                    
#r "nuget: Nabs.Launchpad.Ui.Shell, 10.0.210"
                    
#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.
#:package Nabs.Launchpad.Ui.Shell@10.0.210
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Nabs.Launchpad.Ui.Shell&version=10.0.210
                    
Install as a Cake Addin
#tool nuget:?package=Nabs.Launchpad.Ui.Shell&version=10.0.210
                    
Install as a Cake Tool

Nabs Launchpad UI Shell Library

The Nabs Launchpad UI Shell library provides core UI services and messaging infrastructure for Blazor applications. This library implements global loading state management using the MVVM messaging pattern, enabling coordinated UI feedback across components without tight coupling.

Key Features

  • Global Loading State Management: Centralized control of loading indicators across the application
  • MVVM Messaging: Built on CommunityToolkit.Mvvm's WeakReferenceMessenger for memory-safe communication
  • Simple API: Static methods for easy integration from any component or service
  • Decoupled Architecture: Components subscribe to loading state changes without direct references
  • Thread-Safe: Safe for use across multiple threads and components
  • Memory Efficient: Uses weak references to prevent memory leaks

Core Components

LoadingService

Static service providing global loading state control with three simple methods: Start(), Stop(), and Spin(bool).

LoadingStateMessage

Message class carrying loading state changes through the MVVM messenger infrastructure.

Usage Examples

Basic Loading State Control

// In a service or code-behind
public async Task LoadDataAsync()
{
    // Start loading indicator
    LoadingService.Start();
    
    try
    {
        await FetchDataFromApiAsync();
        // Process data...
    }
    finally
    {
        // Always stop loading indicator
        LoadingService.Stop();
    }
}

Using Spin for Toggle

public async Task ProcessItemsAsync()
{
    LoadingService.Spin(true);  // Start loading
    
    try
    {
        foreach (var item in items)
        {
            await ProcessAsync(item);
        }
    }
    finally
    {
        LoadingService.Spin(false);  // Stop loading
    }
}

Subscribing to Loading State Changes

@page "/dashboard"
@implements IDisposable
@inject IMessenger Messenger

@if (isLoading)
{
    <div class="loading-overlay">
        <div class="spinner-border" role="status">
            <span class="visually-hidden">Loading...</span>
        </div>
    </div>
}

<div class="content">
    
</div>

@code {
    private bool isLoading = false;

    protected override void OnInitialized()
    {
        // Subscribe to loading state changes
        Messenger.Register<LoadingStateMessage>(this, (recipient, message) =>
        {
            isLoading = message.Value;
            InvokeAsync(StateHasChanged);
        });
    }

    public void Dispose()
    {
        // Clean up subscription
        Messenger.UnregisterAll(this);
    }
}

Global Progress Component

@* GlobalProgress.razor *@
@implements IRecipient<LoadingStateMessage>
@implements IDisposable
@inject IMessenger Messenger

@if (IsLoading)
{
    <div class="global-progress-overlay">
        <div class="progress-spinner">
            <span class="spinner-border spinner-border-lg"></span>
            <p>Loading...</p>
        </div>
    </div>
}

@code {
    private bool IsLoading { get; set; }

    protected override void OnInitialized()
    {
        Messenger.RegisterAll(this);
    }

    public void Receive(LoadingStateMessage message)
    {
        IsLoading = message.Value;
        InvokeAsync(StateHasChanged);
    }

    public void Dispose()
    {
        Messenger.UnregisterAll(this);
    }
}

Integration with ViewModels

public class UserManagementViewModel : BaseItemViewModel<UserDto>
{
    private readonly IUserService _userService;

    public UserManagementViewModel(IUserService userService, IStringLocalizer localizer)
        : base(localizer)
    {
        _userService = userService;
    }

    public async Task LoadUsersAsync()
    {
        LoadingService.Start();
        
        try
        {
            var result = await _userService.GetAllUsersAsync();
            
            if (result.IsSuccess)
            {
                // Process users
            }
        }
        finally
        {
            LoadingService.Stop();
        }
    }
}

Nested Loading Scenarios

public class DataSyncService
{
    public async Task SyncAllDataAsync()
    {
        LoadingService.Start();
        
        try
        {
            // Sync users - inner loading states are ignored
            await SyncUsersAsync();
            
            // Sync orders
            await SyncOrdersAsync();
            
            // Sync products
            await SyncProductsAsync();
        }
        finally
        {
            LoadingService.Stop();
        }
    }

    private async Task SyncUsersAsync()
    {
        // This won't interfere with outer loading state
        LoadingService.Start();
        try
        {
            await Task.Delay(1000);
        }
        finally
        {
            LoadingService.Stop();
        }
    }
}

Loading State in HTTP Interceptor

public class LoadingHttpMessageHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        LoadingService.Start();
        
        try
        {
            return await base.SendAsync(request, cancellationToken);
        }
        finally
        {
            LoadingService.Stop();
        }
    }
}

// Register in Program.cs
builder.Services.AddTransient<LoadingHttpMessageHandler>();
builder.Services.AddHttpClient("api")
    .AddHttpMessageHandler<LoadingHttpMessageHandler>();

API Reference

LoadingService

Static service for controlling global loading state.

Methods
Start
public static void Start()

Starts the global loading indicator by sending a loading state message with value true.

Usage:

LoadingService.Start();
Stop
public static void Stop()

Stops the global loading indicator by sending a loading state message with value false.

Usage:

LoadingService.Stop();
Spin
public static void Spin(bool value)

Sets the loading state to the specified value.

Parameters:

  • value: true to start loading, false to stop

Usage:

LoadingService.Spin(isLoading);

LoadingStateMessage

Message class for communicating loading state changes.

Constructor
public LoadingStateMessage(bool isLoading)

Creates a new loading state message.

Parameters:

  • isLoading: The loading state value
Properties
Value
public bool Value { get; }

Gets the loading state value (inherited from ValueChangedMessage<bool>).

Architecture and Design Patterns

MVVM Messaging Pattern

The library uses CommunityToolkit.Mvvm's WeakReferenceMessenger to implement a publish-subscribe pattern:

  1. Publisher: LoadingService publishes LoadingStateMessage instances
  2. Subscribers: Components register to receive messages
  3. Weak References: Prevents memory leaks from forgotten subscriptions

Singleton Service Pattern

LoadingService is implemented as a static class providing a singleton-like behavior without requiring dependency injection registration.

Message Broadcasting

When a loading state changes, all registered subscribers receive the message simultaneously, enabling coordinated UI updates.

Best Practices

1. Always Use Try-Finally

// Good
LoadingService.Start();
try
{
    await DoWorkAsync();
}
finally
{
    LoadingService.Stop();
}

// Bad - loading might not stop on exception
LoadingService.Start();
await DoWorkAsync();
LoadingService.Stop();

2. Dispose Subscriptions

// Implement IDisposable in components
public void Dispose()
{
    Messenger.UnregisterAll(this);
}

3. Use InvokeAsync for State Changes

Messenger.Register<LoadingStateMessage>(this, (r, m) =>
{
    isLoading = m.Value;
    InvokeAsync(StateHasChanged);  // Required for Blazor components
});

4. Centralize Loading Indicators

Create a single global progress component rather than multiple loading indicators throughout the application.

5. Avoid Nesting Issues

Be careful with nested operations that both control loading state. Consider using a counter-based approach for complex scenarios.

6. Consider User Experience

  • Don't show loading for operations < 300ms
  • Provide cancellation options for long operations
  • Show progress details when possible

Integration with Launchpad Components

With Core.ViewModels

public class MyViewModel : BaseItemViewModel<MyDto>
{
    protected override async Task LoadDataAsync()
    {
        LoadingService.Start();
        try
        {
            // Load data
        }
        finally
        {
            LoadingService.Stop();
        }
    }
}

With Ui.Shell.Blazor.Sf

The Syncfusion UI Shell library includes a pre-built GlobalProgress component that subscribes to loading state messages.

With Core.Portal

Portal applications can use the loading service in middleware or during authentication.

Memory Management

Weak References

The messenger uses weak references, which means:

  • Subscribers don't prevent garbage collection
  • No memory leaks from forgotten unregistrations
  • Components are still cleaned up properly
public void Dispose()
{
    // Explicitly unregister for cleaner resource management
    Messenger.UnregisterAll(this);
}

Thread Safety

The loading service is thread-safe:

  • Can be called from any thread
  • Multiple simultaneous calls are safe
  • Message delivery is thread-safe
// Safe to call from multiple threads
await Task.WhenAll(
    Task.Run(() => Operation1()),
    Task.Run(() => Operation2()),
    Task.Run(() => Operation3())
);

async Task Operation1()
{
    LoadingService.Start();
    try { /* work */ }
    finally { LoadingService.Stop(); }
}

Testing

Unit Testing with Loading Service

[Fact]
public async Task Service_ShowsLoadingState()
{
    // Arrange
    var messenger = WeakReferenceMessenger.Default;
    var loadingStates = new List<bool>();
    
    messenger.Register<LoadingStateMessage>(this, (r, m) =>
    {
        loadingStates.Add(m.Value);
    });

    var service = new MyService();

    // Act
    await service.ProcessAsync();

    // Assert
    loadingStates.Should().Contain(true);  // Started loading
    loadingStates.Should().Contain(false); // Stopped loading
    
    // Cleanup
    messenger.UnregisterAll(this);
}

Testing Components with Loading State

[Fact]
public void Component_RespondsToLoadingState()
{
    // Arrange
    using var ctx = new TestContext();
    var component = ctx.RenderComponent<MyComponent>();
    
    // Act - Send loading message
    WeakReferenceMessenger.Default.Send(new LoadingStateMessage(true));
    
    // Assert
    component.Find(".loading-indicator").Should().NotBeNull();
    
    // Act - Stop loading
    WeakReferenceMessenger.Default.Send(new LoadingStateMessage(false));
    
    // Assert
    component.FindAll(".loading-indicator").Should().BeEmpty();
}

Advanced Scenarios

Loading with Progress Percentage

// Define custom message
public class LoadingProgressMessage : ValueChangedMessage<(bool IsLoading, int Percentage)>
{
    public LoadingProgressMessage(bool isLoading, int percentage)
        : base((isLoading, percentage)) { }
}

// Service with progress
public static class ProgressLoadingService
{
    public static void ReportProgress(int percentage)
    {
        WeakReferenceMessenger.Default.Send(
            new LoadingProgressMessage(true, percentage));
    }
}

Multiple Loading Contexts

// Different loading contexts
public enum LoadingContext
{
    Global,
    Grid,
    Form
}

public class ContextualLoadingMessage : ValueChangedMessage<(LoadingContext Context, bool IsLoading)>
{
    public ContextualLoadingMessage(LoadingContext context, bool isLoading)
        : base((context, isLoading)) { }
}

Troubleshooting

Loading Indicator Doesn't Appear

Issue: Loading state changes but UI doesn't update

Solutions:

  • Ensure you're using InvokeAsync(StateHasChanged) in the message handler
  • Verify the component is registered as a message recipient
  • Check that the global progress component is included in your layout

Loading Never Stops

Issue: Loading indicator stays visible

Solutions:

  • Ensure Stop() is called in a finally block
  • Check for exceptions that prevent finally execution
  • Verify no other code is calling Start() without matching Stop()

Memory Leaks

Issue: Components not being garbage collected

Solutions:

  • Implement IDisposable and call Messenger.UnregisterAll(this)
  • Don't capture this in lambda expressions that outlive the component
  • Use weak reference patterns for long-lived subscriptions

Dependencies

  • CommunityToolkit.Mvvm: For MVVM messaging infrastructure and weak reference messenger
  • Nabs.Launchpad.Core.Context: For portal and user context integration
  • Nabs.Launchpad.Ui.Shell.Blazor.Sf: Syncfusion-based UI components with built-in loading indicators
  • Nabs.Launchpad.Core.ViewModels: ViewModel base classes that can integrate with loading service
  • Nabs.Launchpad.Core.Portal: Portal infrastructure that may use loading states

Target Framework

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

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Nabs.Launchpad.Ui.Shell:

Package Downloads
Nabs.Launchpad.Core.ViewModels

Package Description

Nabs.Launchpad.Core.Portal

Package Description

Nabs.Launchpad.Ui.Shell.Blazor.Sf

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.210 0 12/30/2025
10.0.209 47 12/30/2025
10.0.208 55 12/30/2025
10.0.207 70 12/29/2025
10.0.206 72 12/29/2025
10.0.205 165 12/24/2025
10.0.204 188 12/21/2025
10.0.203 282 12/18/2025
10.0.202 278 12/17/2025
10.0.200 287 12/17/2025
10.0.199 442 12/10/2025
10.0.197 183 12/5/2025
10.0.196 682 12/3/2025
10.0.195 687 12/3/2025
10.0.194 686 12/3/2025
10.0.193 691 12/2/2025
10.0.192 190 11/28/2025
10.0.190 202 11/27/2025
10.0.189 181 11/23/2025
10.0.187 179 11/23/2025
10.0.186 163 11/23/2025
10.0.184 417 11/20/2025
10.0.181-rc3 295 11/11/2025
10.0.180 299 11/11/2025
10.0.179-rc2 294 11/11/2025
10.0.178-rc2 240 11/10/2025
10.0.177-rc2 239 11/10/2025
10.0.176-rc2 190 11/6/2025
10.0.175-rc2 198 11/6/2025
10.0.174-rc2 193 11/5/2025
10.0.173-rc2 302 11/3/2025 10.0.173-rc2 is deprecated because it is no longer maintained.
10.0.172-rc2 226 11/2/2025 10.0.172-rc2 is deprecated because it is no longer maintained.
10.0.170-rc2 210 11/1/2025 10.0.170-rc2 is deprecated because it is no longer maintained.
10.0.169-rc2 214 11/1/2025 10.0.169-rc2 is deprecated because it is no longer maintained.
10.0.168-rc2 206 10/31/2025 10.0.168-rc2 is deprecated because it is no longer maintained.
10.0.166-rc2 215 10/31/2025 10.0.166-rc2 is deprecated because it is no longer maintained.
10.0.164-rc2 275 10/28/2025 10.0.164-rc2 is deprecated because it is no longer maintained.
10.0.162-rc2 271 10/24/2025 10.0.162-rc2 is deprecated because it is no longer maintained.
10.0.161 276 10/24/2025 10.0.161 is deprecated because it is no longer maintained.
9.0.151 234 10/17/2025 9.0.151 is deprecated because it is no longer maintained.
9.0.150 308 9/10/2025 9.0.150 is deprecated because it is no longer maintained.
9.0.146 251 8/15/2025 9.0.146 is deprecated because it is no longer maintained.
9.0.145 295 8/11/2025 9.0.145 is deprecated because it is no longer maintained.
9.0.144 309 8/8/2025 9.0.144 is deprecated because it is no longer maintained.
9.0.137 273 7/29/2025 9.0.137 is deprecated because it is no longer maintained.
9.0.136 267 7/29/2025 9.0.136 is deprecated because it is no longer maintained.
9.0.135 276 7/28/2025 9.0.135 is deprecated because it is no longer maintained.
9.0.134 308 7/9/2025 9.0.134 is deprecated because it is no longer maintained.
9.0.133 317 7/9/2025 9.0.133 is deprecated because it is no longer maintained.
9.0.132 302 7/9/2025 9.0.132 is deprecated because it is no longer maintained.
9.0.131 331 7/9/2025 9.0.131 is deprecated because it is no longer maintained.
9.0.130 306 7/7/2025 9.0.130 is deprecated because it is no longer maintained.