Framework
jbe2277/waf avatar
jbe2277/waf

WAF and a small XAML framework for layered MVVM applications

Win Application Framework (WAF) is a lightweight Framework that helps you to create well structured XAML Applications.

730 stars135 forksC#MIT

At a glance

What is it?
The Win Application Framework is a lightweight C# library set that helps structure XAML applications on MAUI, WPF and WinUI, offering Layered Architecture, MVVM and DataModel-MVVM patterns in two NuGet packages plus test helpers. The judgement is that it is a useful set of small building blocks rather than a framework that dictates your application.
Who is it for?
WAF fits a Windows developer who has written the same view model base class and the same file dialog service in three applications and would rather reference a package than copy them again, and who wants layering and MVVM to be the path of least resistance without adopting a full application framework. It is the wrong tool if you want a project template, a dependency injection container, or a navigation stack, since it deliberately stays out of those areas.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 25 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A successor that kept the old package name alive

The Win Application Framework, abbreviated WAF, describes itself as a lightweight framework that helps you create well-structured XAML applications for MAUI, WPF and WinUI, and the readme heads it as the successor of the WPF Application Framework.

The renaming history is still visible in the packaging table, and it matters if you are migrating. The `System.Waf.Wpf` package is marked as the successor of `waf`, and `System.Waf.UnitTesting.Wpf` as the successor of `waf.testing`. The `System.Waf.Core` and `System.Waf.UnitTesting.Core` packages have no predecessor listed, because they are new: Core is the platform independent layer, and it carries the support for all .NET based applications, while Wpf is the Windows specific extension.

The platform split is explicit. System.Waf.Core targets .NET 6.0 and .NET Standard 2.0 and supports all .NET based applications, and System.Waf.Wpf targets .NET 6.0 and .NET Framework 4.7.2 for Windows Presentation Foundation. So the same framework can serve a MAUI application on Android, Windows and iOS through the Core package alone, and a WPF application adds the Wpf package for the pieces that only make sense on the desktop.

The word lightweight in that description is doing real work. The whole surface is a set of base classes, helpers and services listed in the readme, with a wiki page for each architectural pattern rather than a code generator or a project template. There is no scaffolding tool, no convention that forces a folder layout on you, and no runtime that owns your application. You reference the packages and you use the pieces you want.

On installation, the readme gives no command at all. It lists the packages and links each one to its NuGet page, so the practical step is to add System.Waf.Core, and System.Waf.Wpf if you are on WPF, to your project, and to pick the unit testing packages to match. A changelog is kept at `src/System.Waf/Changelog.md`, which is where you look before taking a new major version.

Three patterns, and the wiki is where the detail lives

WAF supports three architectural patterns, and the readme links each to a wiki page rather than explaining them in place.

Layered Architecture is the first, and it is the one the sample applications all use alongside MVVM. The Model-View-ViewModel pattern is the second, and the DataModel-View-ViewModel pattern is the third, which is the variant where the view model talks to a separate data model rather than to the domain entities directly.

The division of labour in the code follows the pattern names. In the Foundation group of System.Waf.Core, `Model` is a base class implementing `INotifyPropertyChanged` and `ValidatableModel` is the base class for a model that supports validation by implementing `INotifyDataErrorInfo`. `ObservableList` extends `ObservableCollection` with support for `INotifyCollectionChanging` and `INotifyCollectionItemChanged`, and `ObservableListViewCore` provides change notifications for sorting and filtering, which is the piece that lets a list view keep its ordering and its filter in step with edits to the underlying collection.

The Applications group holds the pieces that model an actual application. `ApplicationInfo` provides information about the running application, `RecentFileList` is a most recently used file list, and `(Async)DelegateCommand` is an implementation of `ICommand` that delegates Execute and CanExecute.

The Presentation group has one entry in Core, `SettingsService`, which loads and saves user settings as an XML file. That is a small, specific piece of functionality, and it is worth noticing how little of a framework this is. There is no dependency injection container here, no navigation stack, no logging abstraction, and no serialisation configuration. The framework helps with the patterns; it does not run the application.

The pieces that solve the annoying problems

Several of the entries in the feature list exist because the equivalent hand written code is a recurring source of bugs, and they are the most persuasive part of the case for using the package.

`WeakEvent` supports listening to events via a weak reference, which the readme says can prevent memory leaks. In a WPF or MAUI application where a view model subscribes to a long lived service, the classic leak is the view model being kept alive by the event handler. A weak event pattern fixes it once, in a base class, instead of in every subscription site.

`ThrottledAction` throttles multiple method calls to improve the responsiveness of an application, which is the standard fix for a search box or a slider that fires an event on every keystroke or every tick.

`Cache` provides support for caching a value, and `SynchronizingList` represents a collection that synchronizes all its items with the items of a specified original collection, which is what you need when a detail view edits a row in a list.

`DispatcherHelper` is an implementation of DoEvents, and the Wpf package also brings `ResourceHelper` for managing resources in WPF and `ValidationHelper` for data validation tracking, plus `DataErrorInfoSupport` for working with the legacy `IDataErrorInfo` interface alongside the newer `INotifyDataErrorInfo` that Core already uses.

The Wpf-specific `ViewModel` class extends `ViewModelCore` and automatically initializes the DataContext with the Dispatcher, which is the small piece of platform glue that a shared Core cannot provide.

The converters in the Wpf package are ordinary WPF converters, but they are collected here so that they are available without hunting: `BoolToVisibilityConverter`, `InvertBooleanConverter`, `NullToVisibilityConverter`, `StringFormatConverter` and `ValidationErrorsConverter`, which converts a ValidationError collection to a multi-line string error message.

The services that keep dialogs out of your view models

Two services in the Wpf package address a specific design problem in XAML applications, and the reason they live in the framework rather than in an application is that they are annoying to write correctly.

`FileDialogService` shows an open or save file dialog box, and `MessageService` shows messages via the MessageBox. The point is not convenience. It is that a view model which calls `MessageBox.Show` directly has a hard dependency on a WPF presentation type, which makes it untestable and unusable from a non UI thread, and it locks the application into one way of asking the user something.

Routing those calls through a service interface means the view model depends on an abstraction, the test can substitute a double, and a different implementation could show the same prompt in a different way. For a framework whose whole purpose is structural discipline, this is exactly the kind of concern it should be handling.

The unit testing packages extend the same idea. `System.Waf.UnitTesting.Core` provides `AssertHelper`, with assertion helper methods for expected exceptions and for the `CanExecuteChanged` and `PropertyChanged` events, which are the two events an MVVM test most often needs to assert on. It also provides `UnitTestSynchronizationContext`, a synchronization context for unit tests that simulates the behaviour of a UI synchronization context, so a test that raises a property change and pumps the dispatcher does not deadlock.

`System.Waf.UnitTesting.Wpf` is the WPF counterpart for unit testing WPF applications.

This is the part of the framework where the value is hardest to see from a feature list, because the value is in what the tests are able to assert, not in a feature you can point at.

Three sample applications, and the recommendation to read them first

The readme's answer to how to get started is to look at the samples rather than at a tutorial, and the samples are substantial enough to justify that.

Waf NewsReader is a MAUI and .NET 10 application, an RSS and ATOM news feed reader for Android, Windows and iOS. Its listed architecture is Layering and MVVM with async patterns, and it syncs feeds with multiple devices via MS Graph and OneDrive, uses OAuth authentication, has a responsive UI with a navigation pane, includes validation on the add feed view, is localized in English and German, and has UI testing with Appium on Android, iOS and Windows executed automatically through GitHub Actions.

Waf Writer is a WPF and .NET 10 word processing application, a basic one, with a ribbon and tabbed MDI, an animated transition between pages, a most recently used file list, the message service and open/save dialog service, print preview and a print dialog, English and German localisation, and UI testing with FlaUI executed through GitHub Actions.

The third sample, Waf Book Library, is a WPF and .NET 10 application, and its description is cut off in the readme as it stands.

The choice of what to build is instructive. A news reader that has to sync across devices through a cloud API with OAuth is a much harder application than a toy, and it forces the async patterns and the layering to be real rather than decorative. A word processor with a ribbon and MDI exercises the WPF specific pieces, the MRU list and the dialog services.

Both mention UI test automation wired into GitHub Actions, which is a stronger signal about how the project is worked on than any badge would be.

Where a lightweight framework is the wrong choice

There are three situations where reaching for WAF costs more than it saves.

The first is when you want a project template and a convention you do not fight. WAF is a set of libraries, not a scaffolding tool. There is no `dotnet new` template here, no generated folder structure, no dependency injection container wired for you, and no navigation or page stack. You get a view model base class and some helpers; the layering still has to be designed. If your goal is to have the architecture decided for you, this is not that tool, and a project template plus a DI container from the platform would serve better.

The second is if you are starting a MAUI application today. The target frameworks are .NET 6.0 and .NET Standard 2.0 for Core, while the sample applications are built on .NET 10. Those are two different generations, and a package targeting .NET Standard 2.0 running on .NET 10 works but leaves you on the compatibility path rather than the modern one, and the readme does not describe what the framework does or does not do on the newer runtime.

The third is if you are building something that is not a XAML application. The framework is explicitly about XAML applications on MAUI, WPF and WinUI. Its topic tags include Xamarin and UWP, which are the ancestors of those platforms, but the Wpf package is the only platform specific one shipped, so a WinUI 3 application would use Core and do without the dialog, message and resource helpers, which is where much of the WPF specific value lives.

There is also a smaller point about the .NET Framework 4.7.2 target of the Wpf package. It is there for legacy line-of-business applications still on the desktop framework, which is a real and large population of WPF apps, but it means the package has to support two quite different runtimes, and any behaviour difference between them is something you discover rather than something the framework hides.

Against writing the base classes yourself, the difference is the shared layer

The realistic alternative is to write the handful of base classes yourself, which is what most teams have already done at least once.

A view model base class that implements `INotifyPropertyChanged`, a validation base class, an observable list with change notification for sorting and filtering, a throttled action, a cache, a weak event, a file dialog abstraction and a message service abstraction. That is perhaps a few hundred lines in a project where you have written them, and a day of work in a project where you have not.

The difference in approach is where the code lives and who maintains it. WAF's implementation is shared across applications and updated in one place, and the weak event pattern in particular is the kind of thing that is easy to write and easy to get subtly wrong, where getting it wrong means a memory leak that only shows up under a long session.

What you give up is control of the shape. A framework's base class is a base class: if your view model needs to derive from something else, or if you want a different `INotifyPropertyChanged` implementation, or if you need the `PropertyChanged` event raised with a specific name, you are working around the framework rather than with it. The same is true of the services: the `MessageService` shows messages via the MessageBox, and if your application wants a different kind of prompt, you have to write your own.

The other difference is the versioning. WAF is at version 8.3.0, with 8.2.0 in January 2026 and 8.1.0 in March 2025. The major version has moved a long way, which tells you the API has changed shape more than once, and upgrading a base class across a major version can mean touching every view model in the solution. That is the cost of a shared library, and it is the cost that only shows up the first time you upgrade across a boundary.

The pragmatic answer is that WAF is worth it for a team that maintains several XAML applications and keeps rewriting the same base classes, and for a team that wants the layering to be the path of least resistance without committing to a heavyweight framework. For one application, written once, the base classes you write yourself will be exactly the ones you want.

Editorial conclusion

WAF fits a Windows developer who has written the same view model base class and the same file dialog service in three applications and would rather reference a package than copy them again, and who wants layering and MVVM to be the path of least resistance without adopting a full application framework. It is the wrong tool if you want a project template, a dependency injection container, or a navigation stack, since it deliberately stays out of those areas. Before adopting, verify three things: that the target framework in the package matches your project, since System.Waf.Core targets .NET 6.0 and .NET Standard 2.0 while System.Waf.Wpf targets .NET 6.0 and .NET Framework 4.7.2, which matters against the .NET 10 samples, and that the sample applications in `src/` match the shape of your own project, since the readme points you at them as the starting point. The code is MIT licensed, the current release is v8.3.0 from 2026-06-13, and the last push was on 2026-09-28.

Frequently asked questions

What is the Win Application Framework (WAF)?

It is a lightweight C# framework that helps you create well-structured XAML applications for MAUI, WPF and WinUI. It supports the Layered Architecture, Model-View-ViewModel and DataModel-View-ViewModel patterns through a set of base classes, helpers and services rather than a code generator.

Which NuGet packages does WAF ship?

System.Waf.Core for all .NET based applications, System.Waf.Wpf for WPF applications, System.Waf.UnitTesting.Core for unit testing of all .NET based applications, and System.Waf.UnitTesting.Wpf for unit testing of WPF applications. The Wpf packages are marked as the successors of the older `waf` and `waf.testing` packages.

What target frameworks does WAF support?

System.Waf.Core targets .NET 6.0 and .NET Standard 2.0, and System.Waf.Wpf targets .NET 6.0 and .NET Framework 4.7.2. The sample applications in the repository are built on .NET 10.

Does WAF provide an MVVM base class and services for dialogs?

Yes. Core provides `Model` implementing `INotifyPropertyChanged` and `ViewModelCore`, and the Wpf package extends that with a `ViewModel` that initialises the DataContext with the Dispatcher. It also provides `FileDialogService` for open and save dialogs and `MessageService` for messages via the MessageBox.

How do I start with WAF?

The readme points you at the realistic sample applications rather than a tutorial, and at the project wiki for further guidance. The samples are Waf NewsReader for MAUI, Waf Writer for WPF, and Waf Book Library for WPF, all on .NET 10 and all under `src/`.

Official sources

  1. Issues
  2. jbe2277/waf on GitHub
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jbe2277-waf.svg)](https://hysenlabs.com/projects/jbe2277-waf)

Was this useful?