v2.2.0: Barbatos.Wpf.Aquarius, Apsu State Management, and MCP AI Chat
v2.2.0: Barbatos.Wpf.Aquarius, Apsu State Management, and MCP AI Chat
🚀 The big one: Barbatos.Wpf.Aquarius ships as a brand-new package, bringing Vue's
Composition API and template-directive model to plain WPF - reactive Ref/Computed/Watch,
lifecycle hooks, v-model/v-show/v-if/v-on-style directives, Teleport, Transition,
Provide/Inject, Suspense, and named Slots. Alongside it, Barbatos.Wpf.Core gains its
own reactive store framework (Barbatos.Wpf.Apsu), a bring-your-own-key AI chat + MCP client
(Barbatos.Wpf.Mcp), realtime push notifications, a from-scratch Shell_NotifyIcon tray icon,
and installable dotnet new templates for both packages - plus a calendar-anchored redesign of
periodic services and a couple of stability fixes.
⚠️ Breaking Changes
- Periodic services now schedule on a calendar instead of a bare interval.
IWpfPeriodicService.Interval(TimeSpan) is replaced bySchedule(PeriodicSchedule);
IPeriodicServiceScheduler.UpdateInterval(name, TimeSpan)is replaced by
UpdateSchedule(name, PeriodicSchedule);PeriodicServiceStatus.Intervalis replaced by
.Schedule, joined by newNextRunTime/IsCompletedproperties.PeriodicSchedulecarries
aFrequency(Once/Hourly/Daily/Weekly/Monthly/Custom) plusTimeOfDay,
DaysOfWeek,DayOfMonth, andStartTime/EndTime- aDaily/Weekly/Monthlyschedule
now fires at a specific wall-clock time the way a calendar reminder or Task Scheduler trigger
would, instead of "every N minutes from whenever the app happened to start." The
configuration key also moved:Barbatos:PeriodicServices:Intervals:<Name>is now
Barbatos:PeriodicServices:Schedules:<Name>. Update anyIWpfPeriodicServiceimplementation
andUpdateIntervalcall site accordingly.
✨ What's New
🌊 Barbatos.Wpf.Aquarius (new package)
Reactive state and composition-style XAML directives for plain WPF, modeled on Vue's
Composition API - no dependency on Core, install standalone or alongside it:
- Reactivity -
Ref<T>/Computed<T>/Watch/NextTick, built directly on
CommunityToolkit.Mvvm'sObservableObjectrather than a competing system. - Lifecycle hooks - 11 hooks spanning a ViewModel's create/mount/update/unmount/activate/
deactivate/error-capture lifecycle (IOnBeforeCreate,IOnCreated,IOnBeforeMount,
IOnMounted,IOnBeforeUpdate,IOnUpdated,IOnBeforeUnmount,IOnUnmounted,
IOnActivated,IOnDeactivated,IOnErrorCaptured), 8 of them with an*Asynctwin. - Directives -
Directives.Model(v-model),Directives.Show(v-show),If/Else
(v-if/v-else/v-else-if, the latter expressed as nesting),Directives.Event(v-on),
Directives.Class/Directives.Style, and support for writing custom directives. Expr- a MarkupExtension that parses and reactively evaluates small XAML-embedded
expressions (comparison, arithmetic, logical short-circuit, ternary, enum-vs-string equality,
#ElementNameidentifiers) viaMultiBinding, soIf.Condition/Directives.Showcan take
something likea + b >= corstatus == "Active"directly instead of only a plain bound
bool.Teleport/TeleportHost- move content to a different part of the tree (or a different
Windowentirely) without losing its identity, state, or bindings; the README's new
"Dockable Panels" recipe builds a dock/float panel out of nothing but this.Slot/SlotHost/SlotContent/SlotProvided- free-form named content placeholders for
a custom control'sControlTemplate, chosen at the use site rather than pre-declared as one
DependencyPropertyper name.Transition/TransitionGroup,Provide/Inject, andSuspense(an explicit
IsPending+Fallbackloading-state control) round out the set.- An installable
dotnet new aq-viewitem template scaffolds a matching View/ViewModel pair.
🍱 State management - Barbatos.Wpf.Apsu (new, ships in Barbatos.Wpf.Core)
A small Pinia-like reactive store framework:
StoreBasestores registered viaAddStore<TStore>(), with[Action]-attributed
methods andGetter<T>computed values.- Action interceptors -
IStoreActionInterceptor, composable as global (every store) or
local (one store type) chains, now with anout InterceptorHandleoverload and
StoreInterceptorBuilder<TStore>fluent registration
(AddStore<T>(i => i.Add<X>().Add(...))) for declaring a store and its local interceptors in
one call. - Ejectable interceptors -
InterceptorHandle.Eject()removes a registered interceptor
from every chain it applies to at runtime, theaxios.interceptors.request.eject(id)
counterpart. IStorePluginandIStoreRegistryfor cross-cutting store setup and runtime
discovery.- Implemented with Castle.Core-based dynamic proxying under the hood (a new dependency pulled
in byBarbatos.Wpf.Core).
🤖 AI chat + MCP - Barbatos.Wpf.Mcp (new, ships in Barbatos.Wpf.Core)
A Model Context Protocol client and bring-your-own-key AI chat service - your app's own end
user supplies their own Anthropic/OpenAI/other API key, so you never pay for their usage:
IMcpServerRegistry/McpServerDescriptorto register and track MCP servers (stdio or
HTTP transport) and their connection status.IAiChatService/IAiChatClientFactoryfor the actual chat calls, with built-in
Anthropic and OpenAI provider factories.Provideris a free-form string rather than an
enum, andAiProviderOptions.Providersis an optional catalog you can switch between at
runtime viaSelectProvider(key).IAiApiKeyProvider, backed bySecureStorageby default, so end users' own keys are
never stored in plain text.
🔔 Realtime push notifications (new, ships in Barbatos.Wpf.Core)
IPushNotificationService listens for incoming notifications from your own push server and
displays each one through the existing INotificationService toast pipeline, falling back to
a small in-app window whenever a real toast isn't available:
IPushNotificationTransportkeeps the delivery mechanism swappable; the bundled
SignalRPushNotificationTransportis the default, but every SignalR-specific detail (hub
URL, method names) stays out of the transport-agnostic surface.- Deserialize into your own type via
IPushNotification/ConfigurePushNotifications<T>(), or
use the bundledPushNotification(Title/Body/ImageUrl/Action). - A notification's
Action(Url/Setting/Route/None) auto-dispatches forUrl/
Setting;RouteraisesRouteRequestedfor your own app to handle.
🖥️ Tray icon rewritten on raw Win32
ITrayIconPlatform's default implementation no longer goes through
System.Windows.Forms.NotifyIcon - it now talks to Shell_NotifyIcon directly through a
hidden message-only window, so the tray icon feature no longer pulls in a
System.Windows.Forms reference. Also new: ITrayIconService.ShowBalloonTip(title, text, icon) with a TrayIconBalloonIcon (None/Info/Warning/Error), which checks
SHQueryUserNotificationState first so balloons don't get silently discarded during a locked/
full-screen session.
📦 dotnet new templates
Installable project and item templates for both packages, listed in templates/README.md:
dotnet new install ./templates/Barbatos.Wpf.Aquarius/item-templates/aquarius-view
dotnet new install ./templates/Combined/project-templates/barbatos-wpf-app
dotnet new barbatos-wpf-app -n MyApp # Core + Aquarius + Barbatos.i18n starter
dotnet new aq-view -n Dashboard --namespace MyApp.Features.DashboardBoth also show up in Visual Studio 2022's and Rider's own New Project/New Item dialogs once
installed, in addition to the CLI.
🛠️ Fixes
- Disposing the app host on exit could reenter
WpfApplication's own lifecycle overrides
mid-dispose (e.g. via the tray icon's native window teardown) and throw
ObjectDisposedExceptioninstead of exiting cleanly. The host reference is now cleared
before it's disposed, so any reentrant call becomes a no-op. - Closing an owned dialog didn't always hand activation back to its owner - most noticeable
with an IDE debugger attached, which could steal the foreground instead. The owner is now
explicitly reactivated when its dialog closes.
📦 Updating via NuGet
dotnet add package Barbatos.Wpf.Core --version 2.2.0
dotnet add package Barbatos.Wpf.Aquarius --version 2.2.0If you implement IWpfPeriodicService or call IPeriodicServiceScheduler.UpdateInterval
directly, see Breaking Changes above before upgrading.