From 66fa3f1d146bcbfe8f8152e80fbd72b8fef6ec46 Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Mon, 10 Nov 2025 23:11:53 +0000 Subject: [PATCH 1/2] Add RxAppBuilder docs and update .NET SDK to 10 preview Introduced a new RxAppBuilder documentation page and linked it in the handbook index and table of contents. Updated the GitHub Actions workflow to use .NET 8, 9, and 10 preview with caching. Changed global.json to target .NET SDK 10.0.100-rc.2.25502.107 with prerelease enabled. Modernized array initialization in Build.cs. Clarified legacy and recommended approaches in the view-models boilerplate code documentation. --- .github/workflows/main.yml | 20 ++- build/Build.cs | 2 +- global.json | 8 +- reactiveui/docs/handbook/index.md | 1 + reactiveui/docs/handbook/rxappbuilder.md | 128 ++++++++++++++++++ reactiveui/docs/handbook/toc.yml | 2 + .../handbook/view-models/boilerplate-code.md | 4 +- 7 files changed, 154 insertions(+), 11 deletions(-) create mode 100644 reactiveui/docs/handbook/rxappbuilder.md diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2db0b0511..2977910bc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,13 +10,21 @@ jobs: build: runs-on: windows-latest steps: - - name: 'Setup dotnet 6 / 7 / 8' - uses: actions/setup-dotnet@v5 + - name: Setup .NET (With cache) + uses: actions/setup-dotnet@v5.0.0 with: - dotnet-version: | - 6.0.x - 7.0.x - 8.0.x + dotnet-version: | + 8.0.x + 9.0.x + 10.0.x + dotnet-quality: 'preview' + cache: true + cache-dependency-path: | + **/Directory.Packages.props + **/*.sln + **/*.csproj + **/global.json + **/nuget.config - name: 'Setup Java JDK 11' uses: actions/setup-java@v5.0.0 diff --git a/build/Build.cs b/build/Build.cs index 46e6adaef..c5270352d 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -26,7 +26,7 @@ class Build : NukeBuild private static readonly string splat = nameof(splat); private static readonly string DynamicData = nameof(DynamicData); private static readonly string reactivemarbles = nameof(reactivemarbles); - private static readonly string[] RxUIProjects = new string[] { reactiveui, akavache, fusillade, punchclock, splat, "ReactiveUI.Validation" }; + private static readonly string[] RxUIProjects = [reactiveui, akavache, fusillade, punchclock, splat, "ReactiveUI.Validation"]; private AbsolutePath RxUIAPIDirectory => RootDirectory / reactiveui / "api" / reactiveui; private AbsolutePath RxMAPIDirectory => RootDirectory / reactiveui / "api" / reactivemarbles; diff --git a/global.json b/global.json index 5cfa01d9c..33b94ff50 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,7 @@ { - "msbuild-sdks": { - "MSBuild.Sdk.Extras": "3.0.44" - } + "sdk": { + "version": "10.0.100-rc.2.25502.107", + "rollForward": "latestFeature", + "allowPrerelease": true + } } diff --git a/reactiveui/docs/handbook/index.md b/reactiveui/docs/handbook/index.md index dd02306b3..5b830a6a6 100644 --- a/reactiveui/docs/handbook/index.md +++ b/reactiveui/docs/handbook/index.md @@ -6,6 +6,7 @@ Order: 15 Welcome to the ReactiveUI Handbook. This section provides comprehensive documentation for using ReactiveUI with .NET 8 and the latest best practices. Explore the topics below to learn about core concepts, advanced features, platform-specific guidance, and related projects. ## Core Topics +- [RxAppBuilder](rxappbuilder.md) - [Commands](commands/index.md) - [Data Binding](data-binding/index.md) - [View Models](view-models/index.md) diff --git a/reactiveui/docs/handbook/rxappbuilder.md b/reactiveui/docs/handbook/rxappbuilder.md new file mode 100644 index 000000000..91224f0f8 --- /dev/null +++ b/reactiveui/docs/handbook/rxappbuilder.md @@ -0,0 +1,128 @@ +# RxAppBuilder in ReactiveUI - introduced in V21.0.1 + +The `RxAppBuilder` class in ReactiveUI is responsible for initializing and configuring the ReactiveUI framework within your application. +It provides a fluent API to set up various components and services that ReactiveUI relies on. +It extends the capabilities of the Splat Builder and Splat AppLocator to register platform-specific services, views, view models, and other dependencies required for ReactiveUI to function correctly. + +## Key Features of RxAppBuilder +- **Fluent Configuration**: The `RxAppBuilder` class allows you to chain configuration methods to set up your application in a readable and maintainable way. +- **Service Registration**: You can register services and dependencies that your application will use, such as view models, views, and other services. +- **Platform Integration**: It provides methods to integrate ReactiveUI with different platforms, ensuring that the framework works seamlessly across various environments. +- **Customization**: You can customize the behavior of ReactiveUI by configuring options and settings that suit your application's needs. +- **Initialization**: The `RxAppBuilder` class includes methods to initialize the ReactiveUI framework, ensuring that all necessary components are set up before your application starts. + +## Example Usage +```csharp +var rxuiInstance = RxAppBuilder.CreateReactiveUIBuilder() + .WithWpf() // Register WPF platform services + .WithViewsFromAssembly(typeof(App).Assembly) // Register views and view models + .WithRegistration(locator => + { + // Register IScreen as a singleton so all resolutions share the same Router + locator.RegisterLazySingleton(static () => new AppBootstrapper()); + }) + .BuildApp(); + +var mainUIThreadScheduler = rxuiInstance.MainThreadScheduler; +var taskpoolScheduler = rxuiInstance.TaskpoolScheduler; +``` + +## Platform Support +The `RxAppBuilder` class supports various platforms, including WPF, Xamarin.Forms, and others. You can use platform-specific methods to ensure that ReactiveUI is properly configured for the target environment. + +ReactiveUI.AndroidX - `WithAndroidX()` +ReactiveUI.WPF - `WithWpf()` +ReactiveUI.Maui - `WithMaui()` +ReactiveUI.Blazor - `WithBlazor()` +ReactiveUI.WinForms - `WithWinForms()` +ReactiveUI.WinUI - `WithWinUI()` + +## Registering Views and ViewModels +You can register views and view models from specific assemblies using the `WithViewsFromAssembly` method. This allows ReactiveUI to automatically discover and register your views and view models. +```csharp +.WithViewsFromAssembly(typeof(App).Assembly) +``` +You can also register individual views and view models using the `WithView` and `WithViewModel` methods. +```csharp +.RegisterView() +.RegisterSingletonView() +.RegisterViewModel() +.RegisterSingletonViewModel() +``` + +## Custom Service Registration +You can register custom services and dependencies using the `WithPlatformModule` method. This allows you to integrate your own platform-specific services into the ReactiveUI framework. Where T is a class that implements IWantsToRegisterStuff. +```csharp +.WithPlatformModule() +``` +## Configure Schedulers +You can configure the main thread and task pool schedulers used by ReactiveUI using the `WithMainThreadScheduler` and `WithTaskpoolScheduler` methods. +```csharp +.WithMainThreadScheduler(DispatcherScheduler.Current) +.WithTaskpoolScheduler(TaskPoolScheduler.Default) +``` + +The default scheduler extensions register the RxApp.MainThreadScheduler and RxApp.TaskpoolScheduler to use the appropriate schedulers for the platform. +This can be overridden by calling the above methods with `setRxApp = false` to prevent setting the RxApp static properties. + +```csharp +.WithMainThreadScheduler(DispatcherScheduler.Current, setRxApp: false) +.WithTaskpoolScheduler(TaskPoolScheduler.Default, setRxApp: false) +``` + +Each platform module also registers the appropriate default schedulers for that platform. +Custom schedulers can be provided as needed. +Each platform can be custom configured as needed. +By default the platform registers the appropriate `Schedulers` for that platform, the relevant `PlatformServices`, and the relevant `PlatformModule`. + +## Registration Options +The `RxAppBuilder` class provides various options for registering views, view models, and services. You can choose to register them as singletons or transient instances based on your application's requirements. + +One option is to use an immediate registration approach, where services are registered directly within the builder configuration. +```csharp +.WithRegistration(locator => +{ + // Register IScreen as a singleton so all resolutions share the same Router, this is applied immediately + locator.RegisterLazySingleton(static () => new AppBootstrapper()); +}) +``` +Another option is to use a deferred registration approach, where services are registered as the builder is built. +```csharp +.WithRegistrationOnBuild(locator => +{ + // Register IScreen as a singleton so all resolutions share the same Router, this is applied after BuildApp is called + locator.RegisterLazySingleton(static () => new AppBootstrapper()); +}) +``` + +## Using Splat Modules +You can also use Splat modules to register services and dependencies. This allows you to leverage existing Splat modules or create your own for better organization. +```csharp +.WithSplatModule(new MyCustomSplatModule()) +.WithSplatModule() +``` + +## Building the App +Once you have configured the `RxAppBuilder`, you can build the ReactiveUI application instance using the `BuildApp` method. This finalizes the configuration and prepares the application for use. +```csharp +var rxuiInstance = rxAppBuilder.BuildApp(); +var mainUIThreadScheduler = rxuiInstance.MainThreadScheduler; +var taskpoolScheduler = rxuiInstance.TaskpoolScheduler; +``` + +## WithInstance usage +This allows you to get instances from the underlying Splat service locator. +```csharp +rxuiInstance.WithInstance(screen => +{ // Use the registered IScreen instance for other services that depend on it. + screen.Router.Navigate.Execute(new MainViewModel(screen)); +}) +``` + +Up to 16 registered instances from Splat.AppLocator can be provided in a single WithInstance call. +```csharp +rxuiInstance.WithInstance((screen, dataService, logger) => +{ + // Use the registered instances for other services that depend on them. +}) +``` diff --git a/reactiveui/docs/handbook/toc.yml b/reactiveui/docs/handbook/toc.yml index c04d5f6d2..ecfe816bf 100644 --- a/reactiveui/docs/handbook/toc.yml +++ b/reactiveui/docs/handbook/toc.yml @@ -1,5 +1,7 @@ - name: Handbook href: index.md +- name: RxAppBuilder + href: rxappbuilder.md - name: Collections href: collections.md - name: Commands diff --git a/reactiveui/docs/handbook/view-models/boilerplate-code.md b/reactiveui/docs/handbook/view-models/boilerplate-code.md index f2eeef8cd..a29e93b53 100644 --- a/reactiveui/docs/handbook/view-models/boilerplate-code.md +++ b/reactiveui/docs/handbook/view-models/boilerplate-code.md @@ -1,5 +1,6 @@ # Source Generators and Fody, the easy way to create properties in ReactiveUI +## Legacy Note If you are tired of writing boilerplate code for property change notifications, you can try one of the following: - [PropertyChanged.Fody](https://github.com/Fody/PropertyChanged) or - [ReactiveUI.Fody](https://www.nuget.org/packages/ReactiveUI.Fody). @@ -7,6 +8,7 @@ If you are tired of writing boilerplate code for property change notifications, These two libraries are both based on [Fody](https://github.com/Fody) - an extensible tool for weaving .NET assemblies, and they'll inject `INotifyPropertyChanged` code into decorated properties at compile time for you. +## Recommend for new projects - [ReactiveUI.SourceGenerators](https://www.nuget.org/packages/ReactiveUI.SourceGenerators/) This library is a Source Generator that generates properties and commands for you. It is a new way to generate properties and commands for ReactiveUI taking decorated fields and methods and generating the properties and ReactiveCommands for you. @@ -499,7 +501,7 @@ using ReactiveUI.SourceGenerators.WinForms; public partial class MyCustomViewModelControlHost; ``` -# Using ReactiveUI.Fody +# Using ReactiveUI.Fody - Legacy With [ReactiveUI.Fody](https://www.nuget.org/packages/ReactiveUI.Fody/), you don't have to write boilerplate code for getters and setters of read-write properties — the package will do it automagically for you at compile time. All you have to do is annotate the property with the `[Reactive]` attribute, as shown below. From b1ad7a0d7f1d9461c71d125ea9c37b817fae758c Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Mon, 10 Nov 2025 23:27:08 +0000 Subject: [PATCH 2/2] Update main.yml --- .github/workflows/main.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2977910bc..4cc362262 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,13 +18,6 @@ jobs: 9.0.x 10.0.x dotnet-quality: 'preview' - cache: true - cache-dependency-path: | - **/Directory.Packages.props - **/*.sln - **/*.csproj - **/global.json - **/nuget.config - name: 'Setup Java JDK 11' uses: actions/setup-java@v5.0.0