Skip to content

Releases: Barbatos-Labs/Barbatos.Wpf

v2.3.1

Choose a tag to compare

@StHung StHung released this 02 Aug 15:29

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.AppId are the real members from now on.
  • AppGuid still exists, still returns the identical value, and is marked [Obsolete]. It will
    be removed in a future release.
  • The Barbatos.Wpf.ApplicationModel.AppInfo.AppGuid assembly metadata key is still honoured as
    a fallback for the new ...AppInfo.AppId key.

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, so C6BB… and {C6BB…} both work.
  • A non-GUID AppId now resolves. An app identified as Contoso.MyApp and installed under
    ...\Uninstall\Contoso.MyApp previously always returned null. 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 _is1 to its
own AppId when naming the uninstall key (AppId=MyProgram...\Uninstall\MyProgram_is1),
and there is no directive to change or remove that suffix — only CreateUninstallRegKey=no,
which removes the entry altogether. Verified against the [Setup]: AppId and
[Setup]: CreateUninstallRegKey topics shipped in ISetup.chm 6.4.3.

Keep the suffix in UninstallRegistryKey, not in AppIdAppId is 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 an AppInfo.AppId assembly 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 UninstallRegistryKey line, 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 and RegisterHotKey global hotkeys.
  • MCP and PushNotifications modules upgraded.
  • Ejectable store interceptors and StoreInterceptorBuilder.
  • Barbatos.Wpf.Samples.Shared for cross-sample component reuse.
  • The barbatos-wpf-app template reworked into a module picker.

v2.3.0 AquariusRouter + AquariusValee (New)

Choose a tag to compare

@StHung StHung released this 01 Aug 16:39

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 - RouteRecord with nested Children, Name, Redirect, Alias, and
    per-record Meta.
  • 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 any Button/Hyperlink/element, with live
    IsActive/IsExactActive state you can style against.
  • {aqr:RouteTo} - builds a RouteLocationRaw straight 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 /
    ViewModels assign the DataContext without 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, and TextResolver lets 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.AquariusValee
  • Field<T> / Form - reactive validation state, with FieldMeta/FormMeta exposing
    Touched / Dirty / Pending / Valid (all computed on read, never cached).
  • System.ComponentModel.DataAnnotations is 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
      IValidatableObject resolve at all.
    • Ad-hoc mode - Form.DefineField(...) per field, no model class.
  • INotifyDataErrorInfo-native - errors reach WPF through the mechanism WPF already has, so
    the built-in validation adorner works with no extra plumbing (and Valee.Form suppresses it at
    container level, where it would otherwise light up the whole panel).
  • Three cross-field mechanisms - [Compare]-style attributes via schema mode,
    IValidatableObject for model-level rules, and Field<T>.DependsOn for hand-wired
    dependencies. PropertyRangeAttribute + IDependsOnProperties let an attribute declare its own
    dependencies so FromModel wires the re-validation up for you.
  • Async rules - AsyncValidationRule<T> with cancellation, surfaced through Meta.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 made PasswordBox.Password bindable), Valee.Mask for positional input
    masks, and Valee.Numeric + Valee.Minimum/Maximum for filtered numeric entry that clamps on
    blur rather than mid-keystroke. Plus FieldTemplateSelector (control-per-field from [UIHint]/
    [DataType]) and SafeMessageConverter (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
    RegularExpressionAttribute that can actually take RegexOptions), and PropertyRange.

🚀 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, via RegisterHotKey, fires even when the app isn't focused), and
    ChordBinding (multi-step, Visual-Studio-style Ctrl+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:InputSystem section, 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.

  • IAiAgentFactory builds an AIAgent (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 AgentSession carries history across turns.
  • IAiSessionStore / FileAiSessionStore persist 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.
    See AgentApprovalExtensions.
  • AiAgentOptions - Name, Description, Instructions, ToolApproval, IncludeMcpTools,
    EnableLogging, EnableOpenTelemetry, bindable from Barbatos:Mcp:Agent.
  • ConfigureMcp takes a third optional configureAgent callback.

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 a ReceiptId, so a server with an offline queue knows it can stop re-sending.
  • PushNotificationOptions.DeduplicationHistorySize (default 256) - 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. 0 disables 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-form Tags dictionary sent with the handshake so a server can target a subset of devices,
    and a configurable AcknowledgeMethodName (set it to null for 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/OnBeforeMount are now tracked per DataContext, not by a one-shot
    flag.
    An element's DataContext is very often replaced between Initialized and Loaded -
    by a parent's DataContext="{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 received OnMounted having never
    seen OnCreated/OnBeforeMount - a half-initialized ViewModel. Remount still resets the pass
    entirely, so every ...
Read more

v2.2.0: Barbatos.Wpf.Aquarius, Apsu State Management, and MCP AI Chat

Choose a tag to compare

@StHung StHung released this 25 Jul 06:10

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 by Schedule (PeriodicSchedule);
    IPeriodicServiceScheduler.UpdateInterval(name, TimeSpan) is replaced by
    UpdateSchedule(name, PeriodicSchedule); PeriodicServiceStatus.Interval is replaced by
    .Schedule, joined by new NextRunTime/IsCompleted properties. PeriodicSchedule carries
    a Frequency (Once/Hourly/Daily/Weekly/Monthly/Custom) plus TimeOfDay,
    DaysOfWeek, DayOfMonth, and StartTime/EndTime - a Daily/Weekly/Monthly schedule
    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 any IWpfPeriodicService implementation
    and UpdateInterval call 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's ObservableObject rather 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 *Async twin.
  • 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,
    #ElementName identifiers) via MultiBinding, so If.Condition/Directives.Show can take
    something like a + b >= c or status == "Active" directly instead of only a plain bound
    bool.
  • Teleport/TeleportHost - move content to a different part of the tree (or a different
    Window entirely) 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's ControlTemplate, chosen at the use site rather than pre-declared as one
    DependencyProperty per name.
  • Transition/TransitionGroup, Provide/Inject, and Suspense (an explicit
    IsPending + Fallback loading-state control) round out the set.
  • An installable dotnet new aq-view item 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:

  • StoreBase stores registered via AddStore<TStore>(), with [Action]-attributed
    methods and Getter<T> computed values.
  • Action interceptors - IStoreActionInterceptor, composable as global (every store) or
    local (one store type) chains, now with an out InterceptorHandle overload 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, the axios.interceptors.request.eject(id)
    counterpart.
  • IStorePlugin and IStoreRegistry for cross-cutting store setup and runtime
    discovery.
  • Implemented with Castle.Core-based dynamic proxying under the hood (a new dependency pulled
    in by Barbatos.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/McpServerDescriptor to register and track MCP servers (stdio or
    HTTP transport) and their connection status.
  • IAiChatService/IAiChatClientFactory for the actual chat calls, with built-in
    Anthropic and OpenAI provider factories. Provider is a free-form string rather than an
    enum, and AiProviderOptions.Providers is an optional catalog you can switch between at
    runtime via SelectProvider(key).
  • IAiApiKeyProvider, backed by SecureStorage by 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:

  • IPushNotificationTransport keeps the delivery mechanism swappable; the bundled
    SignalRPushNotificationTransport is 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 bundled PushNotification (Title/Body/ImageUrl/Action).
  • A notification's Action (Url/Setting/Route/None) auto-dispatches for Url/
    Setting; Route raises RouteRequested for 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.Dashboard

Both 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
    ObjectDisposedException instead 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.0

If you implement IWpfPeriodicService or call IPeriodicServiceScheduler.UpdateInterval
directly, see Breaking Changes above before upgrading.

# v2.1.0: Permissions API and Live Notification Availability

Choose a tag to compare

@StHung StHung released this 20 Jul 10:39

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's TargetFrameworks changed
    from unversioned net10.0-windows;net9.0-windows;net8.0-windows to
    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.NotificationSetting contract backing the new
    INotificationService.Availability. Consuming projects must target windows10.0.17763.0 or
    higher — most apps already do; if yours targets a bare netX.0-windows TFM, bump it to
    include the Windows version.

✨ What's New

  • Permissions — ported MAUI's Permissions API 1:1: the same generic, DI-free static
    surface (Permissions.CheckStatusAsync<T>() / Permissions.RequestAsync<T>(),
    ShouldShowRationale<T>()), no IPermissions interface 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.xml capabilities to check, and .NET MAUI's own Windows implementation
      already just returns Granted for these same permissions.
    • ContactsRead, ContactsWrite, LocationWhenInUse, LocationAlways, Microphone, and
      Sensors throw FeatureNotSupportedException instead of silently reporting Granted
      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 machinery Contacts and Geolocation already opt
      out of.
    • Extensible the same way MAUI is: subclass Permissions.BasePlatformPermission to back a
      permission with a real check (e.g. the Windows privacy consent registry).
  • 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, so Show(...) 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 what Availability
    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 = Error now sets the toast's Alarm scenario (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

Choose a tag to compare

@StHung StHung released this 19 Jul 19:43

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.HostingBarbatos.Wpf.Core. This is an install
    change only — RootNamespace was already Barbatos.Wpf, so every existing using statement
    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.
  • GlobalHotkeys feature 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, plus InstallDate/InstallLocation read 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
      WinRT Launcher.
    • Email, Contacts — compose email via Simple MAPI, pick a contact.
    • Geolocation — device location.
    • DeviceIdentity — a reinstall-persistent InstanceId plus 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 by AppGuid.
    Enabled by default as soon as ConfigureSingleInstance() 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),
    graceful closeOthers/CloseAll() that still respects each dialog's own Closing veto, and
    duplicate-open prevention for Show() so a rapid double-click can't open the same dialog
    twice.
  • SplashScreen — a WpfApplication hook that shows a splash window before CreateWpfApp()
    runs, so it actually covers slow startup work. Use the built-in SplashWindow (app name/logo,
    tagline, clickable sponsor logos, clickable related-product links) via
    GetSplashScreenOptions(), or supply your own Window entirely via CreateSplashScreen().
    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.EnvironmentName no 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

v1.0.0

Choose a tag to compare

@StHung StHung released this 18 Jul 15:28

Barbatos.Wpf.Hosting