Pamba.WinUI 0.3.0

dotnet add package Pamba.WinUI --version 0.3.0
                    
NuGet\Install-Package Pamba.WinUI -Version 0.3.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="Pamba.WinUI" Version="0.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Pamba.WinUI" Version="0.3.0" />
                    
Directory.Packages.props
<PackageReference Include="Pamba.WinUI" />
                    
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 Pamba.WinUI --version 0.3.0
                    
#r "nuget: Pamba.WinUI, 0.3.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.
#:package Pamba.WinUI@0.3.0
                    
#: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=Pamba.WinUI&version=0.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Pamba.WinUI&version=0.3.0
                    
Install as a Cake Tool

Pamba.WinUI

WinUI 3 integration for the Pamba MVU runtime.

dotnet add package Pamba.WinUI

Requires .NET 10 / C# 14, Pamba, and Microsoft.WindowsAppSDK. Minimum platform: Windows 11 22H2.

Starting the runtime

WinUIMvuRuntime builds and starts the MVU loop. The builder enforces the correct construction order at compile time. Projection is optional - call .Start() directly after .WithSubscriptionStarter() if you have no UI projection.

var projection = new AppProjection(mainWindow);

MvuRuntime<AppState, Msg, Cmd, Sub> runtime = WinUIMvuRuntime
    .Create(program, mainWindow.DispatcherQueue)
    .WithCommandExecutor(commandExecutor.Execute)
    .WithSubscriptionStarter(subscriptionStarter.Start)
    .WithProjection(projection)
    .Start();

State projection

StateProjectionBase<TState> maps state changes to UI updates. Subclass it and register segments in the constructor. Each segment receives a selector that identifies a slice of state, and an action that updates the UI for that slice. Only segments whose selected value has changed are called on each transition.

public sealed class AppProjection : StateProjectionBase<AppState>
{
    public AppProjection(MainWindow window)
    {
        Segment(s => s.Auth, auth => ProjectAuth(window, auth));
        Segment(s => s.CurrentModule, mod => ProjectNavigation(window, mod));
        Segment(s => s.Items, items => ProjectItems(window, items));
    }
}

All segments run against the initial state once at startup via ProjectInitial.

The selector return type must implement IEquatable<TSegment>. C# records satisfy this automatically.

Transition-aware segments

When a segment needs the previous value to determine transition behaviour (e.g. animation direction), use the three-parameter overload:

Segment(
    s => s.CurrentModule,
    mod => SetModuleWithoutAnimation(mod),        // initial projection
    (old, @new) => AnimateModuleSwitch(old, @new));  // transition projection

projectInitial fires once at startup. projectTransition fires on each state change that alters the selected value, receiving both old and new values.

Timer Subscriptions

Pre-built helpers for timer-based subscriptions. Use them inside your SubscriptionStarter delegate to handle specific subscription types:

IDisposable StartSubscription(Sub subscription, Dispatch<Msg> dispatch) =>
    subscription switch
    {
      Sub.RefreshTimer t => TimerSubscription.Start(
          interval: t.Interval,
          createMessage: () => new Msg.RefreshTick(),
          dispatch: dispatch,
          dispatcherQueue: _dispatcherQueue),

      Sub.SearchDebounce d => DelayedSubscription.Start(
          delay: d.Delay,
          createMessage: () => new Msg.DebounceComplete(),
          dispatch: dispatch,
          dispatcherQueue: _dispatcherQueue),

      _ => throw new InvalidOperationException($"Unknown subscription: {subscription}")
    };

Both return IDisposable. The runtime manages their lifecycle via subscription diffing - you do not need to dispose them manually.

Property Changed Subscription

Bridges INotifyPropertyChanged events into the MVU loop. Use this to subscribe to external observable state such as Lugha's LocaleHost or system theme providers.

Sub.LocaleChanged s => PropertyChangedSubscription.Start(
    source: localeHost,
    propertyName: "Current",
    createMessage: () => new Msg.LocaleChanged(localeHost.Current.Culture.Name),
    dispatch: dispatch,
    dispatcherQueue: _dispatcherQueue),

The dispatcherQueue parameter ensures createMessage runs on the UI thread regardless of which thread raised the property change event.

Command Debouncer

Wraps a CommandExecutor to debounce high-frequency commands. Each invocation cancels the previous pending execution.

var debounced = new CommandDebouncer<Cmd, Msg>(
    delay: TimeSpan.FromMilliseconds(300),
    inner: actualExecutor,
    dispatcherQueue: dispatcherQueue,
    onError: (cmd, ex) => new Msg.CommandFailed(cmd, ex.Message));

// Pass debounced.Execute as the command executor

Call FlushAsync during graceful shutdown to execute any pending debounced command before disposing. Without this, the last debounced command is lost on disposal.

await debounced.FlushAsync();
debounced.Dispose();

Localisation with Lugha

Lugha is a typed localisation library for .NET 10 with compile-time-enforced text contracts and CLDR pluralisation. The active locale lives in MVU state; a projection segment keeps the Lugha LocaleHost in sync so that XAML text bindings update automatically on locale switch.

Add the locale to your state type:

public sealed record AppState
{
    public required IAppLocale Locale { get; init; }
    // ...
}

Handle locale switching in Update. Resolve is total — returns the registry's default locale when no match is found:

Msg.LocaleSwitched m =>
    (state with { Locale = registry.Resolve(m.LanguageTag) }, []),

Register a segment in your projection that calls SetLocale when the locale changes:

public sealed class AppProjection : StateProjectionBase<AppState>
{
    public AppProjection(MainWindow window, WinUILocaleHost<IAppLocale> localeHost)
    {
        Segment(s => s.Locale, locale => localeHost.SetLocale(locale));
        Segment(s => s.Auth, auth => ProjectAuth(window, auth));
        // ...
    }
}

For runtime locale changes originating externally (e.g. system language change), use PropertyChangedSubscription to bridge LocaleHost.PropertyChanged into the MVU loop.

Wire everything at startup:

LocaleRegistry<IAppLocale> registry = LocaleRegistry<IAppLocale>
    .Create(new EnGbLocale(), new ArSaLocale())
    .Match(ok => ok, err => throw new InvalidOperationException($"Duplicate: {err.Tag}"));

WinUILocaleHost<IAppLocale> localeHost =
    LocaleHostFactory.Create(new EnGbLocale(), mainWindow.DispatcherQueue);

var projection = new AppProjection(mainWindow, localeHost);

_runtime = WinUIMvuRuntime
    .Create(program, mainWindow.DispatcherQueue)
    .WithCommandExecutor(executor)
    .WithSubscriptionStarter(starter)
    .WithProjection(projection)
    .Start();

Bind text and RTL flow direction in XAML:

<Grid FlowDirection="{x:Bind localeHost.FlowDirection, Mode=OneWay}">
  <TextBlock Text="{x:Bind localeHost.Current.Navigation.Dashboard, Mode=OneWay}" />
  <TextBlock Text="{x:Bind localeHost.Current.Connection.Connected(ViewModel.Host), Mode=OneWay}" />
</Grid>

For system language synchronisation (persistent PrimaryLanguageOverride), call SystemLanguageSync.TryApply inside the locale segment action. See the Lugha.WinUI documentation for details.

  • Pamba - Framework-agnostic core: contracts, dispatch loop, command/subscription infrastructure.
  • Pamba.Testing - Test utilities: MvuTestRunner.UpdateAndValidate and MvuScenario for multi-step flow testing.

Licence

Apache License 2.0

Product Compatible and additional computed target framework versions.
.NET net10.0-windows10.0.26100 is compatible. 
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
0.3.0 111 3/21/2026
0.2.0 108 3/20/2026
0.1.0 102 3/19/2026