Releases: Barbatos-Labs/Barbatos.Wpf
Release list
v2.3.1
v2.3.1
A small release, but it renames one public property and changes when
AppInfo.InstallDate/InstallLocation resolve. Nothing moves your users' stored data.
Deprecated: AppInfo.AppGuid is now AppInfo.AppId
The old name promised a GUID that the value never had to be. Inno Setup's own AppId defaults
to the application name, and since this identifier doubles as the storage folder name
(%LocalAppData%\{Company}\{AppId}\), a readable string is usually the better choice.
IAppInfo.AppId/AppInfo.AppIdare the real members from now on.AppGuidstill exists, still returns the identical value, and is marked[Obsolete]. It will
be removed in a future release.- The
Barbatos.Wpf.ApplicationModel.AppInfo.AppGuidassembly metadata key is still honoured as
a fallback for the new...AppInfo.AppIdkey.
Nothing moves. Preferences, SecureStorage and FileSystem.AppDataDirectory resolve to
exactly the same folder as before, whether you rename or not.
One real break: if you implement IAppInfo yourself (a custom implementation, a test
double), rename your property to AppId. Everyone who only calls it gets a warning, not an
error.
- <AssemblyMetadata Include="Barbatos.Wpf.ApplicationModel.AppInfo.AppGuid" Value="..." />
+ <AssemblyMetadata Include="Barbatos.Wpf.ApplicationModel.AppInfo.AppId" Value="..." />Changed: the uninstall registry lookup no longer guesses
AppInfo.InstallDate and AppInfo.InstallLocation read ...\Uninstall\{AppId}. That lookup
used to append a hardcoded _is1 suffix speculatively, and bailed out entirely unless the
identifier parsed as a GUID. Both are gone:
- The subkey name is used verbatim. A GUID-shaped one is still normalised to the braced
{...}form the registry convention uses, soC6BB…and{C6BB…}both work. - A non-GUID
AppIdnow resolves. An app identified asContoso.MyAppand installed under
...\Uninstall\Contoso.MyApppreviously always returnednull. It works now.
New: UninstallRegistryKey, for installers that register under another name
Installers do not all name their uninstall key after the identifier your app knows itself by.
Point the lookup at the real name instead of bending AppId to match it:
<AssemblyMetadata Include="Barbatos.Wpf.ApplicationModel.AppInfo.AppId" Value="Contoso.MyApp" />
<AssemblyMetadata Include="Barbatos.Wpf.ApplicationModel.AppInfo.UninstallRegistryKey" Value="Contoso.MyApp_is1" />It defaults to AppId, so apps whose installer already matches need nothing.
If you ship with Inno Setup, you need this line. Inno Setup always appends
_is1to its
ownAppIdwhen naming the uninstall key (AppId=MyProgram→...\Uninstall\MyProgram_is1),
and there is no directive to change or remove that suffix — onlyCreateUninstallRegKey=no,
which removes the entry altogether. Verified against the[Setup]: AppIdand
[Setup]: CreateUninstallRegKeytopics shipped inISetup.chm6.4.3.Keep the suffix in
UninstallRegistryKey, not inAppId—AppIdis also your storage folder
name, and it can never be changed after shipping without stranding existing user data.
MSI/WiX needs no override: its uninstall key is the product code.
Fixed: sample apps reported the library's version
Every sample set <Version>1.0.0</Version> but inherited <AssemblyVersion> from the repo's
Directory.Build.props, and the .NET SDK derives FileVersion from AssemblyVersion rather
than from Version. So each sample built as AssemblyVersion/FileVersion 2.3.1 against
InformationalVersion 1.0.0 — File Explorer, an installer or a crash reporter would report the
library's version for a sample app, while only NuGet and AppInfo.VersionString saw 1.0.0.
This affected the samples in this repo, not the shipped packages. It is worth knowing about for
your own apps, because the same trap applies to any project that sets only <Version>:
<Version>1.0.0</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>(AssemblyVersion and FileVersion take numeric parts only — for 1.1.0-beta.1, set both to
the bare 1.1.0.)
Template: barbatos-wpf-app now generates a shippable identity
The starter template had no app identity or version configuration at all. It now generates:
- The three-property version block above, wired to one
<Version>. Product/Company/Copyright, and anAppInfo.AppIdassembly metadata line defaulting
to your project name — deliberately a readable string rather than a GUID, to show that it can
be one.- A commented-out
UninstallRegistryKeyline, ready for when you add an installer.
Package references move to 2.3.1. Change <Company> before you ship: together with AppId it
decides where user data lives.
Docs
src/Barbatos.Wpf.Core/README.md and API-REFERENCE.md are updated throughout, and the Inno
Setup guidance now cites its source. templates/README.md gains an App identity and version
section.
Coming from v2.1.0?
v2.2.0 and v2.3.0 were never tagged, so this release is the first tag since v2.1.0. In
between, the following landed — see the commit history for detail:
Barbatos.Wpf.AquariusRouter— a Vue Router port for WPF, with its own sample.Barbatos.Wpf.AquariusValee— vee-validate-style form validation over DataAnnotations.Barbatos.Wpf.Core: InputSystem — in-app key bindings andRegisterHotKeyglobal hotkeys.- MCP and PushNotifications modules upgraded.
- Ejectable store interceptors and
StoreInterceptorBuilder. Barbatos.Wpf.Samples.Sharedfor cross-sample component reuse.- The
barbatos-wpf-apptemplate reworked into a module picker.
v2.3.0 AquariusRouter + AquariusValee (New)
v2.3.0
The family grows from two packages to four. Barbatos.Wpf.AquariusRouter brings Vue Router's
navigation model to WPF, Barbatos.Wpf.AquariusValee brings vee-validate's form-validation
model, and Barbatos.Wpf.Core picks up a full input/shortcut system, an agent layer on top of its
MCP module, and a push-notification pipeline that no longer shows you the same toast twice.
Everything that landed in the un-released 2.2.0 line ships here too.
✨ New package: Barbatos.Wpf.AquariusRouter
A Vue Router-style navigation system - for WPF. Built directly on Barbatos.Wpf.Aquarius, the
same way vue-router is built on vue.
dotnet add package Barbatos.Wpf.AquariusRouter- Route table -
RouteRecordwith nestedChildren,Name,Redirect,Alias, and
per-recordMeta. - Path matching - required, optional, repeatable (
+/*) and custom-regex segments, ranked so
a more specific route wins. RouterView- the outlet. Named outlets and nested outlets both supported.RouterLink- attached property on anyButton/Hyperlink/element, with live
IsActive/IsExactActivestate you can style against.{aqr:RouteTo}- builds aRouteLocationRawstraight from XAML, so a nav bar can point at a
route by name ({aqr:RouteTo Name=user, Params='id=42'}) instead of hardcoding where that
route currently lives.- Pairing a View with its ViewModel from the route table -
RouteRecord.ViewModel/
ViewModelsassign theDataContextwithout either type knowing about the other. This is the
option for when the View ships in one component library and the ViewModel in another, each under
its own change control, and you can't edit either. - Navigation guards - global (
Router.BeforeEach/BeforeResolve/AfterEach), per-route
(RouteRecord.BeforeEnter), and in-component hooks a ViewModel simply implements
(IOnBeforeRouteEnter/IOnBeforeRouteUpdate/IOnBeforeRouteLeave/IOnRouteEnter/
IOnRouteUpdate), with a documented pipeline order across all three. - Navigation failures - a cancelled or redirected navigation returns a
NavigationFailure
rather than throwing. - In-app search -
RouteSearchEntry+RouteSearchIndex: opt-in, per-route search entries so a
user can type their way straight to a deeply-nested feature instead of walking the menu. Several
entries may point at one route, so the same feature stays findable under whichever phrasing gets
typed, andTextResolverlets the titles be resource keys in a localized app. This has no Vue
Router counterpart - it's new surface. AquariusRouter deliberately ships the query engine only;
the result list stays ordinary WPF so it can look like the rest of your app. RouteLocationRaw.Parse- turns a"/users/42?tab=billing#top"string into the structured
target.
✨ New package: Barbatos.Wpf.AquariusValee
A vee-validate-style form-validation system - for WPF. Also built directly on
Barbatos.Wpf.Aquarius.
dotnet add package Barbatos.Wpf.AquariusValeeField<T>/Form- reactive validation state, withFieldMeta/FormMetaexposing
Touched/Dirty/Pending/Valid(all computed on read, never cached).System.ComponentModel.DataAnnotationsis the schema mechanism - no new rule vocabulary to
learn. Two ways to build a form:- Schema mode -
Form.FromModel<T>(instance)reflects a plain model class's attributes into
fields and validates against the live instance, which is what makes[Compare]and
IValidatableObjectresolve at all. - Ad-hoc mode -
Form.DefineField(...)per field, no model class.
- Schema mode -
INotifyDataErrorInfo-native - errors reach WPF through the mechanism WPF already has, so
the built-in validation adorner works with no extra plumbing (andValee.Formsuppresses it at
container level, where it would otherwise light up the whole panel).- Three cross-field mechanisms -
[Compare]-style attributes via schema mode,
IValidatableObjectfor model-level rules, andField<T>.DependsOnfor hand-wired
dependencies.PropertyRangeAttribute+IDependsOnPropertieslet an attribute declare its own
dependencies soFromModelwires the re-validation up for you. - Async rules -
AsyncValidationRule<T>with cancellation, surfaced throughMeta.Pending;
a superseded run is cancelled rather than allowed to land late. - XAML integration -
Valee.Form,Valee.Field,Valee.For,Valee.PasswordField(because
WPF deliberately never madePasswordBox.Passwordbindable),Valee.Maskfor positional input
masks, andValee.Numeric+Valee.Minimum/Maximumfor filtered numeric entry that clamps on
blur rather than mid-keystroke. PlusFieldTemplateSelector(control-per-field from[UIHint]/
[DataType]) andSafeMessageConverter(a localized message that fails to resolve falls back to
the literal text instead of rendering as nothing). - The rules the BCL genuinely lacks -
Alpha,AlphaNumeric,AlphaDash,AlphaSpaces,
OneOf,NotOneOf,Digits,NumericString,IntegerString,Pattern(a
RegularExpressionAttributethat can actually takeRegexOptions), andPropertyRange.
🚀 Barbatos.Wpf.Core
New module: InputSystem
Keyboard shortcuts, conceptually ported from Unity's Input System rather than from WPF's raw
InputBinding: an InputActionMap holds InputActions, an action holds bindings, and the app
subscribes to the action - so rebinding a key never touches the code that reacts to it.
- Three binding kinds under one action -
KeyBinding(in-app, needs focus),
GlobalKeyBinding(OS-level, viaRegisterHotKey, fires even when the app isn't focused), and
ChordBinding(multi-step, Visual-Studio-styleCtrl+K, Ctrl+C, with a per-step timeout). - Interactions -
Tap,Hold,MultiTap,Press, so one key can mean different things
depending on how it's pressed. - Runtime rebinding - change a binding while the app runs; global hotkeys additionally need
IInputSystemService.RefreshBindings()to re-register with the OS. - Configuration - binds the
Barbatos:InputSystemsection, and picks up live edits through
IOptionsMonitor.
builder.ConfigureInputSystem(options =>
{
var shortcuts = new InputActionMap("AppShortcuts");
shortcuts.AddAction("QuickEntry").AddGlobalBinding(Key.Space, ModifierKeys.Control | ModifierKeys.Alt);
options.ActionMaps.Add(shortcuts);
});MCP: the Microsoft Agent Framework layer
The MCP module previously gave you one-shot request/response calls. It now also gives you an
agent - registered alongside IAiChatService, not replacing it, because a "summarize this"
call and a conversational assistant genuinely want different things.
IAiAgentFactorybuilds anAIAgent(Microsoft's own type, not a wrapper) over the same
bring-your-own-key provider and the same connected MCP servers, with logging/OpenTelemetry
middleware attached.- Conversation memory - an
AgentSessioncarries history across turns. IAiSessionStore/FileAiSessionStorepersist that conversation across app restarts.- Tool approval - under
AiToolApprovalMode.Always(the default) a run pauses and hands the
pending tool call back to the app, so a dialog can ask the end user before anything executes.
SeeAgentApprovalExtensions. AiAgentOptions-Name,Description,Instructions,ToolApproval,IncludeMcpTools,
EnableLogging,EnableOpenTelemetry, bindable fromBarbatos:Mcp:Agent.ConfigureMcptakes a third optionalconfigureAgentcallback.
BYOK is unchanged and non-negotiable: no API key is ever baked into the app, and the publisher
never pays for the end user's usage.
Push notifications: deliveries you can trust
IPushNotification.ReceiptId- the server's own id for a notification.IPushNotificationTransport.AcknowledgeAsync- sent automatically for every notification
that carries aReceiptId, so a server with an offline queue knows it can stop re-sending.PushNotificationOptions.DeduplicationHistorySize(default256) - a server that re-sends
until acknowledged will occasionally deliver a duplicate when an acknowledgement is lost to a
dropped connection. This is the window in which that stays invisible to the user. Deliberately
per-process and never persisted: the only notification a restart can bring back is one the app
died before acknowledging, and showing that twice beats losing it.0disables de-duplication.- SignalR transport -
AppKey+AppKeyHeaderName(sent both as a header and as
access_token, since which one reaches the server depends on the negotiated transport), a
free-formTagsdictionary sent with the handshake so a server can target a subset of devices,
and a configurableAcknowledgeMethodName(set it tonullfor a server that doesn't track
delivery).
Both new interface members are default interface implementations - existing payload types and
custom transports keep compiling untouched.
🐛 Barbatos.Wpf.Aquarius
- Lifecycle:
OnCreated/OnBeforeMountare now tracked perDataContext, not by a one-shot
flag. An element'sDataContextis very often replaced betweenInitializedandLoaded-
by a parent'sDataContext="{Binding ...}"at the usage site, or by a router pairing a View
with a ViewModel from its route table. With a bare flag, those early hooks were spent on the
ViewModel that got discarded, and the one actually mounted receivedOnMountedhaving never
seenOnCreated/OnBeforeMount- a half-initialized ViewModel. Remount still resets the pass
entirely, so every ...
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.
# v2.1.0: Permissions API and Live Notification Availability
v2.1.0: Permissions API and Live Notification Availability
🚀 Two ports from .NET MAUI land in this release: a full Permissions API for checking and
requesting runtime permissions, and live notification-availability detection so apps can tell
when Windows is silently blocking their toasts — plus a scenario fix for Error-severity
notifications.
⚠️ Breaking Changes
- Minimum target Windows version raised.
Barbatos.Wpf.Core'sTargetFrameworkschanged
from unversionednet10.0-windows;net9.0-windows;net8.0-windowsto
net10.0-windows10.0.17763.0;net9.0-windows10.0.17763.0;net8.0-windows10.0.17763.0(Windows
10, version 1809+). This is required to light up the WinRT
Windows.UI.Notifications.NotificationSettingcontract backing the new
INotificationService.Availability. Consuming projects must targetwindows10.0.17763.0or
higher — most apps already do; if yours targets a barenetX.0-windowsTFM, bump it to
include the Windows version.
✨ What's New
Permissions— ported MAUI'sPermissionsAPI 1:1: the same generic, DI-free static
surface (Permissions.CheckStatusAsync<T>()/Permissions.RequestAsync<T>(),
ShouldShowRationale<T>()), noIPermissionsinterface to register, exactly like MAUI
itself. Every permission type MAUI ships is present as a nested type —Battery,
Bluetooth,CalendarRead/CalendarWrite,Camera,ContactsRead/ContactsWrite,
Flashlight,LaunchApp,LocationWhenInUse/LocationAlways,Maps,Media,
Microphone,NearbyWifiDevices,NetworkState,Phone,Photos/PhotosAddOnly,
PostNotifications,Reminders,Sensors,Sms,Speech,StorageRead/StorageWrite,
Vibrate. On this platform:- Most permissions report
PermissionStatus.Granted— an unpackaged WPF app has no
AppxManifest.xmlcapabilities to check, and .NET MAUI's own Windows implementation
already just returnsGrantedfor these same permissions. ContactsRead,ContactsWrite,LocationWhenInUse,LocationAlways,Microphone, and
SensorsthrowFeatureNotSupportedExceptioninstead of silently reportingGranted—
MAUI backs these six with real WinRT device-access contracts (ContactManager,
Geolocator,DeviceAccessInformation,MediaCapture) that need WinRT projections and,
for some APIs, MSIX packaging, the same machineryContactsandGeolocationalready opt
out of.- Extensible the same way MAUI is: subclass
Permissions.BasePlatformPermissionto back a
permission with a real check (e.g. the Windows privacy consent registry).
- Most permissions report
INotificationService.Availability(NotificationAvailability) — reads live (never
cached) whether Windows currently allows the app to display notifications, and why:
Enabled,DisabledForApplication,DisabledForUser,DisabledByGroupPolicy,
DisabledByManifest. Needed because Windows silently drops a blocked toast instead of
raising an error, soShow(...)alone can never tell you it didn't go through.INotificationService.OpenSystemSettings()— deep-links to the Windows notifications
settings page (ms-settings:notifications) so the user can act on whatAvailability
reported. The sample's "Notifications" row demonstrates the full pattern: the description
turns into a warning and an "Open notification settings" button appears whenever
Availability != Enabled, refreshed on window activation.
🛠️ Fixes
NotificationContent.Severity = Errornow sets the toast'sAlarmscenario (stays on
screen until dismissed, in addition to bypassing Focus Assist) instead of just marking it
Urgent, so error notifications no longer disappear before they're seen.
📦 Updating via NuGet
```
dotnet add package Barbatos.Wpf.Core --version 2.1.0
```
If your app targets a bare net8.0-windows / net9.0-windows / net10.0-windows TFM, add
the Windows version to keep resolving this package: net8.0-windows10.0.17763.0 (or higher).
v2.0.0
v2.0.0: Renamed to Barbatos.Wpf.Core, and a Whole New Essentials Layer
🚀 Barbatos.Wpf.Hosting is now Barbatos.Wpf.Core. Alongside the rename, this release adds a
full MAUI-style Essentials layer (AppInfo, Preferences, SecureStorage, Connectivity,
DeviceIdentity, and a dozen more) plus three new desktop features (SingleInstance,
IDialogService, SplashScreen) on top of the hosting model you already know.
⚠️ Breaking Changes
- NuGet package ID renamed:
Barbatos.Wpf.Hosting→Barbatos.Wpf.Core. This is an install
change only —RootNamespacewas alreadyBarbatos.Wpf, so every existingusingstatement
for the hosting APIs (WpfApp,WpfAppBuilder,WpfApplication, lifecycle events,
dispatching, ...) keeps compiling unchanged. Update your project file / install command to the
new package id (see below) and you're done. GlobalHotkeysfeature removed (IGlobalHotkeyService,ConfigureGlobalHotkeys,
HotkeyGesture). If you were registering global hotkeys, that surface is gone in this release.
✨ What's New
- A full Essentials layer, ported MAUI-style (
I{Api}interface +{Api}.Current/.Default
static facade), covering:AppInfo/PublisherInfo— app and publisher metadata: name, version,AppGuid, theme,
packaging model, plusInstallDate/InstallLocationread back from the Windows
Programs-and-Features uninstall registry entry once your installer has run.DeviceInfo/DeviceDisplay— device identity and live display metrics.Connectivity— current network access and connection profiles, with change notifications.FileSystem,Preferences,SecureStorage— app data/cache paths, key-value settings, and
DPAPI-encrypted secret storage.VersionTracking— first-launch-ever / first-launch-for-this-version/build detection.AppActions— taskbar Jump List actions.Launcher— open URIs and files through the shell, the Win32 desktop counterpart of MAUI's
WinRTLauncher.Email,Contacts— compose email via Simple MAPI, pick a contact.Geolocation— device location.DeviceIdentity— a reinstall-persistentInstanceIdplus a salted hardware fingerprint,
built for license enforcement without hardware IDs or third-party IP lookups.
SingleInstance— prevents duplicate launches of the same app, keyed byAppGuid.
Enabled by default as soon asConfigureSingleInstance()is called, unlike every other
optional feature in this library — matches what nearly every modern desktop app already does.IDialogService— centralizes showing and tracking child windows: reliable owner
assignment (no more dialogs losing their owner or picking up an unrelated foreground app),
gracefulcloseOthers/CloseAll()that still respects each dialog's ownClosingveto, and
duplicate-open prevention forShow()so a rapid double-click can't open the same dialog
twice.SplashScreen— aWpfApplicationhook that shows a splash window beforeCreateWpfApp()
runs, so it actually covers slow startup work. Use the built-inSplashWindow(app name/logo,
tagline, clickable sponsor logos, clickable related-product links) via
GetSplashScreenOptions(), or supply your ownWindowentirely viaCreateSplashScreen().
SplashScreenOptions.MinimumDisplayDuration(default 1.5s) keeps it visible long enough to
avoid a flash on fast machines without ever delaying a slow one further.
🛠 Fixes
WpfHostEnvironment.EnvironmentNameno longer returns a hardcoded value — it now reflects the
actual configured hosting environment.
📦 Updating via NuGet
Barbatos.Wpf.Hosting is no longer published; install Barbatos.Wpf.Core instead:
dotnet remove package Barbatos.Wpf.Hosting
dotnet add package Barbatos.Wpf.Core --version 2.0.0