From 01d47c97f59cbf2210a6d2d36cc0003f6586a5b5 Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Sun, 30 Nov 2025 03:16:38 +0000 Subject: [PATCH 1/4] Replace RxApp schedulers with RxSchedulers throughout codebase Refactors all usages of RxApp.MainThreadScheduler and RxApp.TaskpoolScheduler to use RxSchedulers.MainThreadScheduler and RxSchedulers.TaskpoolScheduler, respectively. Updates documentation comments, registration logic, and all relevant method calls to reference the new RxSchedulers static class. Also removes obsolete dispatcher extension methods from DispatcherObservable.cs and ensures scheduler usage is consistent across all platforms and components. --- src/ReactiveUI.Blazor/Registrations.cs | 4 +- .../FollowObservableStateBehavior.cs | 2 +- .../Platforms/net4/ObservableTrigger.cs | 2 +- .../uap/ObservableTriggerBehavior.cs | 2 +- src/ReactiveUI.Builder.WpfApp/App.xaml.cs | 4 +- .../ViewModels/ChatRoomViewModel.cs | 2 +- .../ViewModels/LobbyViewModel.cs | 4 +- .../Common/ViewModelViewHost.cs | 2 +- src/ReactiveUI.Maui/Registrations.cs | 4 +- src/ReactiveUI.Maui/RoutedViewHost.cs | 4 +- src/ReactiveUI.Winforms/Registrations.cs | 2 +- .../Common/ViewModelViewHost.cs | 2 +- src/ReactiveUI.Wpf/Registrations.cs | 4 +- .../Rx/Linq/DispatcherObservable.cs | 80 ------------------- src/ReactiveUI.sln | 2 +- src/ReactiveUI/Builder/ReactiveUIBuilder.cs | 4 +- src/ReactiveUI/Mixins/AutoPersistHelper.cs | 4 +- .../POCOObservableForProperty.cs | 2 +- .../android/PlatformRegistrations.cs | 4 +- .../Platforms/apple-common/ReactiveControl.cs | 2 +- .../apple-common/ReactiveImageView.cs | 2 +- .../Platforms/apple-common/ReactiveView.cs | 2 +- .../apple-common/ViewModelViewHost.cs | 6 +- .../Platforms/mac/AutoSuspendHelper.cs | 2 +- .../Platforms/mac/PlatformRegistrations.cs | 4 +- .../Platforms/net/PlatformRegistrations.cs | 4 +- .../netstandard2.0/PlatformRegistrations.cs | 4 +- .../Platforms/tizen/PlatformRegistrations.cs | 4 +- .../uikit-common/CommonReactiveSource.cs | 2 +- .../uikit-common/PlatformRegistrations.cs | 4 +- .../uikit-common/ReactiveCollectionView.cs | 2 +- .../uikit-common/ReactiveTableView.cs | 2 +- .../uikit-common/UICollectionViewAdapter.cs | 2 +- .../uikit-common/UITableViewAdapter.cs | 2 +- .../ReactiveCommand/ReactiveCommand.cs | 51 ++++++------ .../ReactiveCommand/ReactiveCommandBase.cs | 2 +- src/ReactiveUI/Routing/RoutingState.cs | 2 +- src/ReactiveUI/RxSchedulers.cs | 2 +- 38 files changed, 78 insertions(+), 157 deletions(-) diff --git a/src/ReactiveUI.Blazor/Registrations.cs b/src/ReactiveUI.Blazor/Registrations.cs index 4aed7989cc..cd843b6b7f 100644 --- a/src/ReactiveUI.Blazor/Registrations.cs +++ b/src/ReactiveUI.Blazor/Registrations.cs @@ -53,7 +53,7 @@ public void Register(Action, Type> registerFunction) PlatformEnlightenmentProvider.Current.EnableWasm(); } - RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; - RxApp.MainThreadScheduler = CurrentThreadScheduler.Instance; + RxSchedulers.TaskpoolScheduler = TaskPoolScheduler.Default; + RxSchedulers.MainThreadScheduler = CurrentThreadScheduler.Instance; } } diff --git a/src/ReactiveUI.Blend/FollowObservableStateBehavior.cs b/src/ReactiveUI.Blend/FollowObservableStateBehavior.cs index 73af00383c..c8201e4f9f 100644 --- a/src/ReactiveUI.Blend/FollowObservableStateBehavior.cs +++ b/src/ReactiveUI.Blend/FollowObservableStateBehavior.cs @@ -114,7 +114,7 @@ protected static void OnStateObservableChanged(DependencyObject? sender, Depende #pragma warning disable CA2208 // Instantiate argument exceptions correctly var newValue = (IObservable)e.NewValue ?? throw new ArgumentNullException(nameof(e.NewValue)); #pragma warning restore CA2208 // Instantiate argument exceptions correctly - item._watcher = newValue.ObserveOn(RxApp.MainThreadScheduler).Subscribe( + item._watcher = newValue.ObserveOn(RxSchedulers.MainThreadScheduler).Subscribe( x => { var target = item.TargetObject ?? item.AssociatedObject; diff --git a/src/ReactiveUI.Blend/Platforms/net4/ObservableTrigger.cs b/src/ReactiveUI.Blend/Platforms/net4/ObservableTrigger.cs index 76a293cd2f..4fb1dfe6c7 100644 --- a/src/ReactiveUI.Blend/Platforms/net4/ObservableTrigger.cs +++ b/src/ReactiveUI.Blend/Platforms/net4/ObservableTrigger.cs @@ -62,7 +62,7 @@ protected static void OnObservableChanged(DependencyObject sender, DependencyPro triggerItem._watcher = null; } - triggerItem._watcher = ((IObservable)e.NewValue).ObserveOn(RxApp.MainThreadScheduler).Subscribe( + triggerItem._watcher = ((IObservable)e.NewValue).ObserveOn(RxSchedulers.MainThreadScheduler).Subscribe( triggerItem.InvokeActions, _ => { diff --git a/src/ReactiveUI.Blend/Platforms/uap/ObservableTriggerBehavior.cs b/src/ReactiveUI.Blend/Platforms/uap/ObservableTriggerBehavior.cs index 10fa51b9f4..579efc5148 100644 --- a/src/ReactiveUI.Blend/Platforms/uap/ObservableTriggerBehavior.cs +++ b/src/ReactiveUI.Blend/Platforms/uap/ObservableTriggerBehavior.cs @@ -117,7 +117,7 @@ private static void OnObservableChanged(DependencyObject sender, DependencyPrope { var @this = (ObservableTriggerBehavior)sender; - @this._watcher.Disposable = ((IObservable)e.NewValue).ObserveOn(RxApp.MainThreadScheduler).Subscribe( + @this._watcher.Disposable = ((IObservable)e.NewValue).ObserveOn(RxSchedulers.MainThreadScheduler).Subscribe( x => Interaction.ExecuteActions(@this._resolvedSource, @this.Actions, x), ex => { diff --git a/src/ReactiveUI.Builder.WpfApp/App.xaml.cs b/src/ReactiveUI.Builder.WpfApp/App.xaml.cs index c8dca6a0b1..6c993f42bc 100644 --- a/src/ReactiveUI.Builder.WpfApp/App.xaml.cs +++ b/src/ReactiveUI.Builder.WpfApp/App.xaml.cs @@ -55,7 +55,7 @@ protected override void OnStartup(StartupEventArgs e) // Network service used to broadcast/receive messages across instances r.RegisterLazySingleton(static () => new Services.ChatNetworkService()); }) - .Build(); + .BuildApp(); // Setup Suspension RxApp.SuspensionHost.CreateNewAppState = static () => new ChatState(); @@ -74,7 +74,7 @@ protected override void OnStartup(StartupEventArgs e) // Load persisted state asynchronously and update UI when ready _ = _driver .LoadState() - .ObserveOn(RxApp.MainThreadScheduler) + .ObserveOn(RxSchedulers.MainThreadScheduler) .Subscribe( static stateObj => { diff --git a/src/ReactiveUI.Builder.WpfApp/ViewModels/ChatRoomViewModel.cs b/src/ReactiveUI.Builder.WpfApp/ViewModels/ChatRoomViewModel.cs index 539f2cd9ad..4b73883e0c 100644 --- a/src/ReactiveUI.Builder.WpfApp/ViewModels/ChatRoomViewModel.cs +++ b/src/ReactiveUI.Builder.WpfApp/ViewModels/ChatRoomViewModel.cs @@ -40,7 +40,7 @@ public ChatRoomViewModel(IScreen hostScreen, ChatRoom room, string user) MessageBus.Current.Listen(contract: room.Name) .Where(msg => msg.InstanceId != Services.AppInstance.Id) .Throttle(TimeSpan.FromMilliseconds(33)) - .ObserveOn(RxApp.MainThreadScheduler) + .ObserveOn(RxSchedulers.MainThreadScheduler) .Subscribe(msg => { _room.Messages.Add(new ChatMessage { Sender = msg.Sender, Text = msg.Text, Timestamp = msg.Timestamp }); diff --git a/src/ReactiveUI.Builder.WpfApp/ViewModels/LobbyViewModel.cs b/src/ReactiveUI.Builder.WpfApp/ViewModels/LobbyViewModel.cs index ba1bbef9d2..b35b0660ec 100644 --- a/src/ReactiveUI.Builder.WpfApp/ViewModels/LobbyViewModel.cs +++ b/src/ReactiveUI.Builder.WpfApp/ViewModels/LobbyViewModel.cs @@ -75,11 +75,11 @@ public LobbyViewModel(IScreen hostScreen) this.WhenAnyObservable(x => x.RoomsChanged) .StartWith(Unit.Default) .Select(_ => (IReadOnlyList)[.. GetState().Rooms]) - .ObserveOn(RxApp.MainThreadScheduler) + .ObserveOn(RxSchedulers.MainThreadScheduler) .ToProperty(this, nameof(Rooms), out _rooms); // Request a snapshot from peers shortly after activation - RxApp.MainThreadScheduler.Schedule(Unit.Default, TimeSpan.FromMilliseconds(500), (s, __) => + RxSchedulers.MainThreadScheduler.Schedule(Unit.Default, TimeSpan.FromMilliseconds(500), (s, __) => { var req = new Services.RoomEventMessage(Services.RoomEventKind.SyncRequest, string.Empty) { InstanceId = Services.AppInstance.Id }; Trace.WriteLine("[Lobby] Broadcasting SyncRequest"); diff --git a/src/ReactiveUI.Maui/Common/ViewModelViewHost.cs b/src/ReactiveUI.Maui/Common/ViewModelViewHost.cs index 23c718b6c0..1cefc19a28 100644 --- a/src/ReactiveUI.Maui/Common/ViewModelViewHost.cs +++ b/src/ReactiveUI.Maui/Common/ViewModelViewHost.cs @@ -86,7 +86,7 @@ public ViewModelViewHost() this.WhenActivated(d => { d(contractChanged - .ObserveOn(RxApp.MainThreadScheduler) + .ObserveOn(RxSchedulers.MainThreadScheduler) .Subscribe(x => _viewContract = x ?? string.Empty)); d(vmAndContract.DistinctUntilChanged().Subscribe(x => ResolveViewForViewModel(x.ViewModel, x.Contract))); diff --git a/src/ReactiveUI.Maui/Registrations.cs b/src/ReactiveUI.Maui/Registrations.cs index e672c3aa3b..085b68596c 100644 --- a/src/ReactiveUI.Maui/Registrations.cs +++ b/src/ReactiveUI.Maui/Registrations.cs @@ -43,8 +43,8 @@ public void Register(Action, Type> registerFunction) if (!ModeDetector.InUnitTestRunner()) { - RxApp.MainThreadScheduler = new WaitForDispatcherScheduler(static () => DispatcherQueueScheduler.Current); - RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; + RxSchedulers.MainThreadScheduler = new WaitForDispatcherScheduler(static () => DispatcherQueueScheduler.Current); + RxSchedulers.TaskpoolScheduler = TaskPoolScheduler.Default; } RxApp.SuppressViewCommandBindingMessage = true; diff --git a/src/ReactiveUI.Maui/RoutedViewHost.cs b/src/ReactiveUI.Maui/RoutedViewHost.cs index cb220a8fc4..b8ab9f47b8 100644 --- a/src/ReactiveUI.Maui/RoutedViewHost.cs +++ b/src/ReactiveUI.Maui/RoutedViewHost.cs @@ -81,7 +81,7 @@ public RoutedViewHost() Router? .Navigate .Where(_ => StacksAreDifferent()) - .ObserveOn(RxApp.MainThreadScheduler) + .ObserveOn(RxSchedulers.MainThreadScheduler) .SelectMany(_ => PagesForViewModel(Router.GetCurrentViewModel())) .SelectMany(async page => { @@ -245,7 +245,7 @@ protected virtual Page PageForViewModel(IRoutableViewModel vm) if (SetTitleOnNavigate) { - RxApp.MainThreadScheduler.Schedule(() => pg.Title = vm.UrlPathSegment); + RxSchedulers.MainThreadScheduler.Schedule(() => pg.Title = vm.UrlPathSegment); } return pg; diff --git a/src/ReactiveUI.Winforms/Registrations.cs b/src/ReactiveUI.Winforms/Registrations.cs index bc7b76d0dd..e57581b4ab 100644 --- a/src/ReactiveUI.Winforms/Registrations.cs +++ b/src/ReactiveUI.Winforms/Registrations.cs @@ -45,7 +45,7 @@ public void Register(Action, Type> registerFunction) if (!ModeDetector.InUnitTestRunner()) { WindowsFormsSynchronizationContext.AutoInstall = true; - RxApp.MainThreadScheduler = new WaitForDispatcherScheduler(static () => new SynchronizationContextScheduler(new WindowsFormsSynchronizationContext())); + RxSchedulers.MainThreadScheduler = new WaitForDispatcherScheduler(static () => new SynchronizationContextScheduler(new WindowsFormsSynchronizationContext())); } } } diff --git a/src/ReactiveUI.Wpf/Common/ViewModelViewHost.cs b/src/ReactiveUI.Wpf/Common/ViewModelViewHost.cs index 280d199bfe..e6430d4863 100644 --- a/src/ReactiveUI.Wpf/Common/ViewModelViewHost.cs +++ b/src/ReactiveUI.Wpf/Common/ViewModelViewHost.cs @@ -113,7 +113,7 @@ public ViewModelViewHost() this.WhenActivated(d => { d(contractChanged - .ObserveOn(RxApp.MainThreadScheduler) + .ObserveOn(RxSchedulers.MainThreadScheduler) .Subscribe(x => _viewContract = x ?? string.Empty)); d(vmAndContract.DistinctUntilChanged().Subscribe(x => ResolveViewForViewModel(x.ViewModel, x.Contract))); diff --git a/src/ReactiveUI.Wpf/Registrations.cs b/src/ReactiveUI.Wpf/Registrations.cs index 0e90f109f9..05309a8ef3 100644 --- a/src/ReactiveUI.Wpf/Registrations.cs +++ b/src/ReactiveUI.Wpf/Registrations.cs @@ -43,8 +43,8 @@ public void Register(Action, Type> registerFunction) if (!ModeDetector.InUnitTestRunner()) { // NB: On .NET Core, trying to touch DispatcherScheduler blows up :cry: - RxApp.MainThreadScheduler = new WaitForDispatcherScheduler(static () => DispatcherScheduler.Current); - RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; + RxSchedulers.MainThreadScheduler = new WaitForDispatcherScheduler(static () => DispatcherScheduler.Current); + RxSchedulers.TaskpoolScheduler = TaskPoolScheduler.Default; } RxApp.SuppressViewCommandBindingMessage = true; diff --git a/src/ReactiveUI.Wpf/Rx/Linq/DispatcherObservable.cs b/src/ReactiveUI.Wpf/Rx/Linq/DispatcherObservable.cs index 0364734bc4..2235b9b8b6 100644 --- a/src/ReactiveUI.Wpf/Rx/Linq/DispatcherObservable.cs +++ b/src/ReactiveUI.Wpf/Rx/Linq/DispatcherObservable.cs @@ -133,43 +133,6 @@ public static IObservable ObserveOn(this IObservable return ObserveOn_(source, dispatcherObject.Dispatcher, priority); } - - /// - /// Wraps the source sequence in order to run its observer callbacks on the dispatcher associated with the current thread. - /// - /// The type of the elements in the source sequence. - /// Source sequence. - /// The source sequence whose observations happen on the current thread's dispatcher. - /// is null. - [Obsolete("Use ObserveOn(RxApp.MainThreadScheduler)", false)] - public static IObservable ObserveOnDispatcher(this IObservable source) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - return ObserveOn_(source, DispatcherScheduler.Current.Dispatcher); - } - - /// - /// Wraps the source sequence in order to run its observer callbacks on the dispatcher associated with the current thread. - /// - /// The type of the elements in the source sequence. - /// Source sequence. - /// Priority to schedule work items at. - /// The source sequence whose observations happen on the current thread's dispatcher. - /// is null. - public static IObservable ObserveOnDispatcher(this IObservable source, DispatcherPriority priority) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - return ObserveOn_(source, DispatcherScheduler.Current.Dispatcher, priority); - } - private static IObservable ObserveOn_(IObservable source, Dispatcher dispatcher, DispatcherPriority priority) { return Synchronization.ObserveOn(source, new DispatcherSynchronizationContext(dispatcher, priority)); @@ -323,49 +286,6 @@ public static IObservable SubscribeOn(this IObservable - /// Wraps the source sequence in order to run its subscription and unsubscription logic on the dispatcher associated with the current thread. - /// - /// The type of the elements in the source sequence. - /// Source sequence. - /// The source sequence whose subscriptions and unsubscriptions happen on the current thread's dispatcher. - /// is null. - /// - /// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the dispatcher associated with the current thread. - /// In order to invoke observer callbacks on the dispatcher associated with the current thread, e.g. to render results in a control, use . - /// - public static IObservable SubscribeOnDispatcher(this IObservable source) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - return SubscribeOn_(source, DispatcherScheduler.Current.Dispatcher); - } - - /// - /// Wraps the source sequence in order to run its subscription and unsubscription logic on the dispatcher associated with the current thread. - /// - /// The type of the elements in the source sequence. - /// Source sequence. - /// Priority to schedule work items at. - /// The source sequence whose observations happen on the current thread's dispatcher. - /// is null. - /// - /// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the dispatcher associated with the current thread. - /// In order to invoke observer callbacks on the dispatcher associated with the current thread, e.g. to render results in a control, use . - /// - public static IObservable SubscribeOnDispatcher(this IObservable source, DispatcherPriority priority) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - return SubscribeOn_(source, DispatcherScheduler.Current.Dispatcher, priority); - } - private static IObservable SubscribeOn_(IObservable source, Dispatcher dispatcher, DispatcherPriority priority) { return Synchronization.SubscribeOn(source, new DispatcherSynchronizationContext(dispatcher, priority)); diff --git a/src/ReactiveUI.sln b/src/ReactiveUI.sln index 62fafd4f06..a3f8342d3d 100644 --- a/src/ReactiveUI.sln +++ b/src/ReactiveUI.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 -VisualStudioVersion = 18.0.11018.127 d18.0 +VisualStudioVersion = 18.0.11018.127 MinimumVisualStudioVersion = 16.0.31613.86 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BD9762CF-E104-481C-96A6-26E624B86283}" ProjectSection(SolutionItems) = preProject diff --git a/src/ReactiveUI/Builder/ReactiveUIBuilder.cs b/src/ReactiveUI/Builder/ReactiveUIBuilder.cs index a51cd995df..25c395beeb 100644 --- a/src/ReactiveUI/Builder/ReactiveUIBuilder.cs +++ b/src/ReactiveUI/Builder/ReactiveUIBuilder.cs @@ -926,12 +926,12 @@ private void ConfigureSchedulers() => { if (MainThreadScheduler != null && _setRxAppMainScheduler) { - RxApp.MainThreadScheduler = MainThreadScheduler; + RxSchedulers.MainThreadScheduler = MainThreadScheduler; } if (TaskpoolScheduler != null && _setRxAppTaskPoolScheduler) { - RxApp.TaskpoolScheduler = TaskpoolScheduler; + RxSchedulers.TaskpoolScheduler = TaskpoolScheduler; } }); } diff --git a/src/ReactiveUI/Mixins/AutoPersistHelper.cs b/src/ReactiveUI/Mixins/AutoPersistHelper.cs index c1aee213cd..926d257798 100644 --- a/src/ReactiveUI/Mixins/AutoPersistHelper.cs +++ b/src/ReactiveUI/Mixins/AutoPersistHelper.cs @@ -91,14 +91,14 @@ public static IDisposable AutoPersist(this T @this, Func x.PropertyName is not null && persistableProperties.ContainsKey(x.PropertyName)).Select(_ => Unit.Default).Merge(manualSaveSignal.Select(_ => Unit.Default)); var autoSaver = saveHint - .Throttle(interval.Value, RxApp.TaskpoolScheduler) + .Throttle(interval.Value, RxSchedulers.TaskpoolScheduler) .SelectMany(_ => doPersist(@this)) .Publish(); // NB: This rigamarole is to prevent the initialization of a class // from triggering a save var ret = new SingleAssignmentDisposable(); - RxApp.MainThreadScheduler.Schedule(() => + RxSchedulers.MainThreadScheduler.Schedule(() => { if (ret.IsDisposed) { diff --git a/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs b/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs index 098acbdcd1..0ce1e98a4f 100644 --- a/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs +++ b/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs @@ -39,7 +39,7 @@ public class POCOObservableForProperty : ICreatesObservableForProperty _hasWarned[(type, propertyName)] = true; } - return Observable.Return(new ObservedChange(sender, expression, default), RxApp.MainThreadScheduler ?? CurrentThreadScheduler.Instance) + return Observable.Return(new ObservedChange(sender, expression, default), RxSchedulers.MainThreadScheduler ?? CurrentThreadScheduler.Instance) .Concat(Observable>.Never); } } diff --git a/src/ReactiveUI/Platforms/android/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/android/PlatformRegistrations.cs index 470e6390de..ccdd60554f 100644 --- a/src/ReactiveUI/Platforms/android/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/android/PlatformRegistrations.cs @@ -27,8 +27,8 @@ public void Register(Action, Type> registerFunction) // TODO: Creat if (!ModeDetector.InUnitTestRunner()) { - RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; - RxApp.MainThreadScheduler = HandlerScheduler.MainThreadScheduler; + RxSchedulers.TaskpoolScheduler = TaskPoolScheduler.Default; + RxSchedulers.MainThreadScheduler = HandlerScheduler.MainThreadScheduler; } registerFunction(static () => new BundleSuspensionDriver(), typeof(ISuspensionDriver)); diff --git a/src/ReactiveUI/Platforms/apple-common/ReactiveControl.cs b/src/ReactiveUI/Platforms/apple-common/ReactiveControl.cs index 71c93a7206..bfe6b0d7d6 100644 --- a/src/ReactiveUI/Platforms/apple-common/ReactiveControl.cs +++ b/src/ReactiveUI/Platforms/apple-common/ReactiveControl.cs @@ -121,7 +121,7 @@ public override void ViewWillMoveToSuperview(NSView? newsuper) /// void ICanForceManualActivation.Activate(bool activate) => - RxApp.MainThreadScheduler.Schedule(() => + RxSchedulers.MainThreadScheduler.Schedule(() => (activate ? _activated : _deactivated).OnNext(Unit.Default)); /// diff --git a/src/ReactiveUI/Platforms/apple-common/ReactiveImageView.cs b/src/ReactiveUI/Platforms/apple-common/ReactiveImageView.cs index 6de5ba75d9..10f87b61c8 100644 --- a/src/ReactiveUI/Platforms/apple-common/ReactiveImageView.cs +++ b/src/ReactiveUI/Platforms/apple-common/ReactiveImageView.cs @@ -146,7 +146,7 @@ public override void ViewWillMoveToSuperview(NSView? newsuper) /// void ICanForceManualActivation.Activate(bool activate) => - RxApp.MainThreadScheduler.Schedule(() => + RxSchedulers.MainThreadScheduler.Schedule(() => (activate ? _activated : _deactivated).OnNext(Unit.Default)); /// diff --git a/src/ReactiveUI/Platforms/apple-common/ReactiveView.cs b/src/ReactiveUI/Platforms/apple-common/ReactiveView.cs index b4550130fd..7b5296b316 100644 --- a/src/ReactiveUI/Platforms/apple-common/ReactiveView.cs +++ b/src/ReactiveUI/Platforms/apple-common/ReactiveView.cs @@ -128,7 +128,7 @@ public override void ViewWillMoveToSuperview(NSView? newsuper) /// void ICanForceManualActivation.Activate(bool activate) => - RxApp.MainThreadScheduler.Schedule(() => + RxSchedulers.MainThreadScheduler.Schedule(() => (activate ? _activated : _deactivated).OnNext(Unit.Default)); /// diff --git a/src/ReactiveUI/Platforms/apple-common/ViewModelViewHost.cs b/src/ReactiveUI/Platforms/apple-common/ViewModelViewHost.cs index 603bbfd2b8..45d3e5d71c 100644 --- a/src/ReactiveUI/Platforms/apple-common/ViewModelViewHost.cs +++ b/src/ReactiveUI/Platforms/apple-common/ViewModelViewHost.cs @@ -37,7 +37,7 @@ public ViewModelViewHost() _currentView = new SerialDisposable(); _viewContract = this .WhenAnyObservable(static x => x.ViewContractObservable) - .ToProperty(this, static x => x.ViewContract, initialValue: null, scheduler: RxApp.MainThreadScheduler); + .ToProperty(this, static x => x.ViewContract, initialValue: null, scheduler: RxSchedulers.MainThreadScheduler); Initialize(); } @@ -181,7 +181,7 @@ private void Initialize() .Select(x => x.DefaultContent); viewChange - .ObserveOn(RxApp.MainThreadScheduler) + .ObserveOn(RxSchedulers.MainThreadScheduler) .Subscribe( x => { @@ -215,7 +215,7 @@ private void Initialize() }); defaultViewChange - .ObserveOn(RxApp.MainThreadScheduler) + .ObserveOn(RxSchedulers.MainThreadScheduler) .Subscribe(x => Adopt(this, x)); } } diff --git a/src/ReactiveUI/Platforms/mac/AutoSuspendHelper.cs b/src/ReactiveUI/Platforms/mac/AutoSuspendHelper.cs index c8e1e8394a..902b8b621b 100644 --- a/src/ReactiveUI/Platforms/mac/AutoSuspendHelper.cs +++ b/src/ReactiveUI/Platforms/mac/AutoSuspendHelper.cs @@ -63,7 +63,7 @@ public AutoSuspendHelper(NSApplicationDelegate appDelegate) /// The termination reply from the application. public NSApplicationTerminateReply ApplicationShouldTerminate(NSApplication sender) { - RxApp.MainThreadScheduler.Schedule(() => + RxSchedulers.MainThreadScheduler.Schedule(() => _shouldPersistState.OnNext(Disposable.Create(() => sender.ReplyToApplicationShouldTerminate(true)))); diff --git a/src/ReactiveUI/Platforms/mac/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/mac/PlatformRegistrations.cs index 1c4f0c78c0..674914d354 100644 --- a/src/ReactiveUI/Platforms/mac/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/mac/PlatformRegistrations.cs @@ -31,8 +31,8 @@ public void Register(Action, Type> registerFunction) if (!ModeDetector.InUnitTestRunner()) { - RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; - RxApp.MainThreadScheduler = new WaitForDispatcherScheduler(static () => new NSRunloopScheduler()); + RxSchedulers.TaskpoolScheduler = TaskPoolScheduler.Default; + RxSchedulers.MainThreadScheduler = new WaitForDispatcherScheduler(static () => new NSRunloopScheduler()); } registerFunction(static () => new AppSupportJsonSuspensionDriver(), typeof(ISuspensionDriver)); diff --git a/src/ReactiveUI/Platforms/net/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/net/PlatformRegistrations.cs index cee5e7073b..5ff746c91d 100644 --- a/src/ReactiveUI/Platforms/net/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/net/PlatformRegistrations.cs @@ -27,8 +27,8 @@ public void Register(Action, Type> registerFunction) if (!ModeDetector.InUnitTestRunner()) { - RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; - RxApp.MainThreadScheduler = DefaultScheduler.Instance; + RxSchedulers.TaskpoolScheduler = TaskPoolScheduler.Default; + RxSchedulers.MainThreadScheduler = DefaultScheduler.Instance; } } } diff --git a/src/ReactiveUI/Platforms/netstandard2.0/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/netstandard2.0/PlatformRegistrations.cs index 1fc217703a..aad0f8d6a6 100644 --- a/src/ReactiveUI/Platforms/netstandard2.0/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/netstandard2.0/PlatformRegistrations.cs @@ -22,8 +22,8 @@ public void Register(Action, Type> registerFunction) if (!ModeDetector.InUnitTestRunner()) { - RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; - RxApp.MainThreadScheduler = DefaultScheduler.Instance; + RxSchedulers.TaskpoolScheduler = TaskPoolScheduler.Default; + RxSchedulers.MainThreadScheduler = DefaultScheduler.Instance; } } } diff --git a/src/ReactiveUI/Platforms/tizen/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/tizen/PlatformRegistrations.cs index ecd6b52194..a4cd42f3f9 100644 --- a/src/ReactiveUI/Platforms/tizen/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/tizen/PlatformRegistrations.cs @@ -25,8 +25,8 @@ public void Register(Action, Type> registerFunction) if (!ModeDetector.InUnitTestRunner()) { - RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; - RxApp.MainThreadScheduler = EcoreMainloopScheduler.MainThreadScheduler; + RxSchedulers.TaskpoolScheduler = TaskPoolScheduler.Default; + RxSchedulers.MainThreadScheduler = EcoreMainloopScheduler.MainThreadScheduler; } } } diff --git a/src/ReactiveUI/Platforms/uikit-common/CommonReactiveSource.cs b/src/ReactiveUI/Platforms/uikit-common/CommonReactiveSource.cs index 657a4195ab..a3f763242a 100644 --- a/src/ReactiveUI/Platforms/uikit-common/CommonReactiveSource.cs +++ b/src/ReactiveUI/Platforms/uikit-common/CommonReactiveSource.cs @@ -303,7 +303,7 @@ private void SubscribeToSectionInfoChanges(int sectionInfoId, IReadOnlyList { ApplyPendingChanges(sectionInfoId); diff --git a/src/ReactiveUI/Platforms/uikit-common/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/uikit-common/PlatformRegistrations.cs index be7dd2647b..948556c2bd 100644 --- a/src/ReactiveUI/Platforms/uikit-common/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/uikit-common/PlatformRegistrations.cs @@ -28,8 +28,8 @@ public void Register(Action, Type> registerFunction) if (!ModeDetector.InUnitTestRunner()) { - RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; - RxApp.MainThreadScheduler = new WaitForDispatcherScheduler(static () => new NSRunloopScheduler()); + RxSchedulers.TaskpoolScheduler = TaskPoolScheduler.Default; + RxSchedulers.MainThreadScheduler = new WaitForDispatcherScheduler(static () => new NSRunloopScheduler()); } registerFunction(static () => new AppSupportJsonSuspensionDriver(), typeof(ISuspensionDriver)); diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionView.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionView.cs index 05d13b12c6..2f630131e0 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionView.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionView.cs @@ -114,7 +114,7 @@ public override void RemoveFromSuperview() /// void ICanForceManualActivation.Activate(bool activate) => - RxApp.MainThreadScheduler.Schedule(() => + RxSchedulers.MainThreadScheduler.Schedule(() => (activate ? _activated : _deactivated).OnNext(Unit.Default)); /// diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableView.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableView.cs index 2ffdd7ddb0..0b4a20d8e3 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableView.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableView.cs @@ -117,7 +117,7 @@ public override void WillMoveToSuperview(UIView? newsuper) /// void ICanForceManualActivation.Activate(bool activate) => - RxApp.MainThreadScheduler.Schedule(() => + RxSchedulers.MainThreadScheduler.Schedule(() => (activate ? _activated : _deactivated).OnNext(Unit.Default)); /// diff --git a/src/ReactiveUI/Platforms/uikit-common/UICollectionViewAdapter.cs b/src/ReactiveUI/Platforms/uikit-common/UICollectionViewAdapter.cs index 2226b46325..d20a73f7ae 100644 --- a/src/ReactiveUI/Platforms/uikit-common/UICollectionViewAdapter.cs +++ b/src/ReactiveUI/Platforms/uikit-common/UICollectionViewAdapter.cs @@ -45,7 +45,7 @@ public void ReloadData() // since ReloadData() queues the appropriate messages on the UI thread, we know we're done reloading // when this subsequent message is processed (with one caveat - see FinishReloadData for details) - RxApp.MainThreadScheduler.Schedule(FinishReloadData); + RxSchedulers.MainThreadScheduler.Schedule(FinishReloadData); } // UICollectionView no longer has these methods so these are no-ops diff --git a/src/ReactiveUI/Platforms/uikit-common/UITableViewAdapter.cs b/src/ReactiveUI/Platforms/uikit-common/UITableViewAdapter.cs index f048be611e..cf52973f8d 100644 --- a/src/ReactiveUI/Platforms/uikit-common/UITableViewAdapter.cs +++ b/src/ReactiveUI/Platforms/uikit-common/UITableViewAdapter.cs @@ -55,7 +55,7 @@ public void ReloadData() // since ReloadData() queues the appropriate messages on the UI thread, we know we're done reloading // when this subsequent message is processed (with one caveat - see FinishReloadData for details) - RxApp.MainThreadScheduler.Schedule(FinishReloadData); + RxSchedulers.MainThreadScheduler.Schedule(FinishReloadData); } public void BeginUpdates() => _view.BeginUpdates(); diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs index b6b7b08665..4e81776dac 100644 --- a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs @@ -66,7 +66,7 @@ public static class ReactiveCommand /// /// The action to execute whenever the command is executed. /// An optional observable that dictates the availability of the command for execution. - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// The ReactiveCommand instance. /// @@ -101,7 +101,7 @@ public static ReactiveCommand Create( /// The action to execute whenever the command is executed. /// An optional observable that dictates the availability of the command for execution. /// The background scheduler. - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// The ReactiveCommand instance. /// @@ -118,7 +118,7 @@ public static ReactiveCommand CreateRunInBackground( { execute.ArgumentNullExceptionThrowIfNull(nameof(execute)); - return CreateFromObservable(() => Observable.Start(execute, backgroundScheduler ?? RxApp.TaskpoolScheduler), canExecute, outputScheduler); + return CreateFromObservable(() => Observable.Start(execute, backgroundScheduler ?? RxSchedulers.TaskpoolScheduler), canExecute, outputScheduler); } /// @@ -128,7 +128,7 @@ public static ReactiveCommand CreateRunInBackground( /// The type of value returned by command executions. /// The function to execute whenever the command is executed. /// An optional observable that dictates the availability of the command for execution. - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// The ReactiveCommand instance. /// @@ -164,7 +164,7 @@ public static ReactiveCommand Create( /// The function to execute whenever the command is executed. /// An optional observable that dictates the availability of the command for execution. /// The background scheduler. - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// The ReactiveCommand instance. /// @@ -181,7 +181,7 @@ public static ReactiveCommand CreateRunInBackground( { execute.ArgumentNullExceptionThrowIfNull(nameof(execute)); - return CreateFromObservable(() => Observable.Start(execute, backgroundScheduler ?? RxApp.TaskpoolScheduler), canExecute, outputScheduler); + return CreateFromObservable(() => Observable.Start(execute, backgroundScheduler ?? RxSchedulers.TaskpoolScheduler), canExecute, outputScheduler); } /// @@ -190,7 +190,7 @@ public static ReactiveCommand CreateRunInBackground( /// The type of the parameter passed through to command execution. /// The action to execute whenever the command is executed. /// An optional observable that dictates the availability of the command for execution. - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// The ReactiveCommand instance. /// @@ -226,7 +226,7 @@ public static ReactiveCommand Create( /// The action to execute whenever the command is executed. /// An optional observable that dictates the availability of the command for execution. /// The background scheduler. - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// The ReactiveCommand instance. /// @@ -243,7 +243,7 @@ public static ReactiveCommand CreateRunInBackground( { execute.ArgumentNullExceptionThrowIfNull(nameof(execute)); - return CreateFromObservable(p => Observable.Start(() => execute(p), backgroundScheduler ?? RxApp.TaskpoolScheduler), canExecute, outputScheduler); + return CreateFromObservable(p => Observable.Start(() => execute(p), backgroundScheduler ?? RxSchedulers.TaskpoolScheduler), canExecute, outputScheduler); } /// @@ -254,7 +254,7 @@ public static ReactiveCommand CreateRunInBackground( /// The type of value returned by command executions. /// The function to execute whenever the command is executed. /// An optional observable that dictates the availability of the command for execution. - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// The ReactiveCommand instance. /// @@ -291,7 +291,7 @@ public static ReactiveCommand Create( /// The function to execute whenever the command is executed. /// An optional observable that dictates the availability of the command for execution. /// The background scheduler. - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// The ReactiveCommand instance. /// @@ -308,7 +308,7 @@ public static ReactiveCommand CreateRunInBackground(p => Observable.Start(() => execute(p), backgroundScheduler ?? RxApp.TaskpoolScheduler), canExecute, outputScheduler); + return CreateFromObservable(p => Observable.Start(() => execute(p), backgroundScheduler ?? RxSchedulers.TaskpoolScheduler), canExecute, outputScheduler); } /// @@ -322,7 +322,7 @@ public static ReactiveCommand CreateRunInBackground /// - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// /// The CombinedReactiveCommand instance. @@ -357,7 +357,7 @@ public static CombinedReactiveCommand CreateCombined /// - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// /// The ReactiveCommand instance. @@ -392,7 +392,7 @@ public static ReactiveCommand CreateFromObservable( /// An optional observable that dictates the availability of the command for execution. /// /// - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// /// The ReactiveCommand instance. @@ -430,7 +430,7 @@ public static ReactiveCommand CreateFromObservable /// - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// /// The ReactiveCommand instance. @@ -462,7 +462,7 @@ public static ReactiveCommand CreateFromTask( /// An optional observable that dictates the availability of the command for execution. /// /// - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// /// The ReactiveCommand instance. @@ -494,7 +494,7 @@ public static ReactiveCommand CreateFromTask( /// An optional observable that dictates the availability of the command for execution. /// /// - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// /// The ReactiveCommand instance. @@ -523,7 +523,7 @@ public static ReactiveCommand CreateFromTask( /// An optional observable that dictates the availability of the command for execution. /// /// - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// /// The ReactiveCommand instance. @@ -552,7 +552,7 @@ public static ReactiveCommand CreateFromTask( /// An optional observable that dictates the availability of the command for execution. /// /// - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// /// The ReactiveCommand instance. @@ -590,7 +590,7 @@ public static ReactiveCommand CreateFromTask( /// An optional observable that dictates the availability of the command for execution. /// /// - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// /// The ReactiveCommand instance. @@ -628,7 +628,7 @@ public static ReactiveCommand CreateFromTask( /// An optional observable that dictates the availability of the command for execution. /// /// - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// /// The ReactiveCommand instance. @@ -663,7 +663,7 @@ public static ReactiveCommand CreateFromTask( /// An optional observable that dictates the availability of the command for execution. /// /// - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// /// The ReactiveCommand instance. @@ -695,7 +695,7 @@ public static ReactiveCommand CreateFromTask( /// The type of the command's result. /// Provides an observable representing the command's asynchronous execution logic. /// An optional observable that dictates the availability of the command for execution. - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// The ReactiveCommand instance. /// @@ -727,7 +727,7 @@ internal static ReactiveCommand CreateFromObservableCancellable /// An optional observable that dictates the availability of the command for execution. /// /// - /// An optional scheduler that is used to surface events. Defaults to RxApp.MainThreadScheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// /// The ReactiveCommand instance. @@ -847,6 +847,7 @@ protected internal ReactiveCommand( .Select(x => x.Result); _canExecuteSubscription = _canExecute + .ObserveOn(_outputScheduler) .Subscribe(OnCanExecuteChanged); } diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs index 70ad9888db..e25a80de7e 100644 --- a/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs @@ -140,7 +140,7 @@ public void Dispose() protected void OnCanExecuteChanged(bool newValue) { _canExecuteValue = newValue; - _canExecuteChanged?.Invoke(this, EventArgs.Empty); + RxSchedulers.MainThreadScheduler.Schedule(() => _canExecuteChanged?.Invoke(this, EventArgs.Empty)); } /// diff --git a/src/ReactiveUI/Routing/RoutingState.cs b/src/ReactiveUI/Routing/RoutingState.cs index e17c0cbbb9..5cf6cb720d 100644 --- a/src/ReactiveUI/Routing/RoutingState.cs +++ b/src/ReactiveUI/Routing/RoutingState.cs @@ -40,7 +40,7 @@ public class RoutingState : ReactiveObject public RoutingState(IScheduler? scheduler = null) #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. { - _scheduler = scheduler ?? RxApp.MainThreadScheduler; + _scheduler = scheduler ?? RxSchedulers.MainThreadScheduler; NavigationStack = []; SetupRx(); } diff --git a/src/ReactiveUI/RxSchedulers.cs b/src/ReactiveUI/RxSchedulers.cs index 5162e59407..ac08d4105f 100644 --- a/src/ReactiveUI/RxSchedulers.cs +++ b/src/ReactiveUI/RxSchedulers.cs @@ -32,7 +32,7 @@ public static class RxSchedulers /// /// /// This is a simplified version that doesn't include unit test detection. - /// For full functionality including unit test support, use RxApp.MainThreadScheduler. + /// For full functionality including unit test support, use RxSchedulers.MainThreadScheduler. /// public static IScheduler MainThreadScheduler { From aff624dd4c0bee175e47d1a761a3393df13a0d47 Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Sun, 30 Nov 2025 03:31:17 +0000 Subject: [PATCH 2/4] Remove scheduler usage from CanExecuteChanged handling Eliminated the use of ObserveOn and explicit scheduling on the main thread when raising CanExecuteChanged events. This simplifies event invocation and avoids unnecessary scheduler dependencies. --- src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs | 1 - src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs index 4e81776dac..de4336b3fc 100644 --- a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs @@ -847,7 +847,6 @@ protected internal ReactiveCommand( .Select(x => x.Result); _canExecuteSubscription = _canExecute - .ObserveOn(_outputScheduler) .Subscribe(OnCanExecuteChanged); } diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs index e25a80de7e..70ad9888db 100644 --- a/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs @@ -140,7 +140,7 @@ public void Dispose() protected void OnCanExecuteChanged(bool newValue) { _canExecuteValue = newValue; - RxSchedulers.MainThreadScheduler.Schedule(() => _canExecuteChanged?.Invoke(this, EventArgs.Empty)); + _canExecuteChanged?.Invoke(this, EventArgs.Empty); } /// From 1a7a078b190e7ef6ffc1764d0045a4ef72faaa43 Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Sun, 30 Nov 2025 15:02:52 +0000 Subject: [PATCH 3/4] Fix execution info notification in Execute method Replaces _synchronizedExecutionInfo with _executionInfo when notifying the start of execution in the Execute method. Ensures correct subject is used for execution state updates. --- src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs index de4336b3fc..cf532b5923 100644 --- a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs @@ -912,7 +912,7 @@ public override IObservable Execute(TParam parameter) return Observable.Defer( () => { - _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateBegin()); + _executionInfo.OnNext(ExecutionInfo.CreateBegin()); return Observable<(IObservable, Action)>.Empty; }) .Concat(_execute(parameter)) From eff881138fe34911cab7f2caa1e6ebfceff32778 Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Sun, 30 Nov 2025 18:01:17 +0000 Subject: [PATCH 4/4] Replace RxSchedulers.MainThreadScheduler with RxApp.MainThreadScheduler Updated CombinedReactiveCommand and ReactiveCommand to use RxApp.MainThreadScheduler instead of RxSchedulers.MainThreadScheduler for output scheduling. This change ensures consistency during tests and aligns with the preferred scheduler usage in ReactiveUI tests. --- src/ReactiveUI/ReactiveCommand/CombinedReactiveCommand.cs | 2 +- src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ReactiveUI/ReactiveCommand/CombinedReactiveCommand.cs b/src/ReactiveUI/ReactiveCommand/CombinedReactiveCommand.cs index 75c37da46b..881a61f474 100644 --- a/src/ReactiveUI/ReactiveCommand/CombinedReactiveCommand.cs +++ b/src/ReactiveUI/ReactiveCommand/CombinedReactiveCommand.cs @@ -56,7 +56,7 @@ protected internal CombinedReactiveCommand( { childCommands.ArgumentNullExceptionThrowIfNull(nameof(childCommands)); - _outputScheduler = outputScheduler ?? RxSchedulers.MainThreadScheduler; + _outputScheduler = outputScheduler ?? RxApp.MainThreadScheduler; var childCommandsArray = childCommands.ToArray(); diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs index cf532b5923..bd950c9a72 100644 --- a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs @@ -44,6 +44,7 @@ namespace ReactiveUI; /// /// Creating asynchronous reactive commands: /// +/// /// ( @@ -812,7 +813,7 @@ protected internal ReactiveCommand( IScheduler? outputScheduler) { _execute = execute ?? throw new ArgumentNullException(nameof(execute)); - _outputScheduler = outputScheduler ?? RxSchedulers.MainThreadScheduler; + _outputScheduler = outputScheduler ?? RxApp.MainThreadScheduler; _exceptions = new ScheduledSubject(_outputScheduler, RxApp.DefaultExceptionHandler); _executionInfo = new Subject(); _synchronizedExecutionInfo = Subject.Synchronize(_executionInfo, _outputScheduler); @@ -882,7 +883,7 @@ protected internal ReactiveCommand( }); }, canExecute, - outputScheduler) + outputScheduler ?? RxApp.MainThreadScheduler) { }