Skip to content

Commit fec7219

Browse files
authored
feature: Add default WhenActivated calls to WPF base classes (#4277)
<!-- Please be sure to read the [Contribute](https://github.com/reactiveui/reactiveui#contribute) section of the README --> **What kind of change does this PR introduce?** <!-- Bug fix, feature, docs update, ... --> update **What is the current behavior?** <!-- You can also link to an open issue here. --> A IActivatableViewModel ViewModel would not trigger any events unless WhenActivated exists in the View **What is the new behavior?** <!-- If this is a feature change --> Introduced parameterless constructors in ReactivePage, ReactiveUserControl, and ReactiveWindow that call WhenActivated with a no-op. This ensures that IActivatableViewModel logic in the ViewModel is triggered when the view is activated, improving consistency and activation handling in WPF views. **What might this PR break?** If no WhenActivated has been created then the current code would not execute, from now on it would be triggered correctly. **Please check if the PR fulfills these requirements** - [ ] Tests for the changes have been added (for bug fixes / features) - [ ] Docs have been added / updated (for bug fixes / features) **Other information**:
1 parent 10ebe5d commit fec7219

File tree

14 files changed

+375
-321
lines changed

14 files changed

+375
-321
lines changed

src/ReactiveUI.Wpf/Common/ReactivePage.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,15 @@ protected ReactivePage(IntPtr handle)
160160
{
161161
}
162162
#endif
163+
#else
164+
/// <summary>
165+
/// Initializes a new instance of the <see cref="ReactivePage{TViewModel}"/> class.
166+
/// </summary>
167+
public ReactivePage() => this.WhenActivated(disposables =>
168+
{
169+
// No-op, but ensures that when the Page is activated,
170+
// any IActivatableViewModel logic in the ViewModel is also triggered.
171+
});
163172
#endif
164173

165174
/// <summary>

src/ReactiveUI.Wpf/Common/ReactiveUserControl.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,15 @@ protected ReactiveUserControl(IntPtr handle)
144144
{
145145
}
146146
#endif
147+
#else
148+
/// <summary>
149+
/// Initializes a new instance of the <see cref="ReactiveUserControl{TViewModel}"/> class.
150+
/// </summary>
151+
public ReactiveUserControl() => this.WhenActivated(disposables =>
152+
{
153+
// No-op, but ensures that when the Page is activated,
154+
// any IActivatableViewModel logic in the ViewModel is also triggered.
155+
});
147156
#endif
148157

149158
/// <summary>

src/ReactiveUI.Wpf/ReactiveWindow.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@ public class ReactiveWindow<TViewModel> :
5353
typeof(ReactiveWindow<TViewModel>),
5454
new PropertyMetadata(null));
5555

56+
/// <summary>
57+
/// Initializes a new instance of the <see cref="ReactiveWindow{TViewModel}"/> class.
58+
/// </summary>
59+
/// <remarks>When the window is activated, this constructor ensures that the ViewModel's activation logic
60+
/// is also triggered if the ViewModel implements IActivatableViewModel. This enables coordinated activation and
61+
/// deactivation of resources tied to the window and its ViewModel.</remarks>
62+
public ReactiveWindow() => this.WhenActivated(disposables =>
63+
{
64+
// No-op, but ensures that when the Page is activated,
65+
// any IActivatableViewModel logic in the ViewModel is also triggered.
66+
});
67+
5668
/// <summary>
5769
/// Gets the binding root view model.
5870
/// </summary>

src/tests/ReactiveUI.AOTTests/TestConfiguration.cs

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/tests/ReactiveUI.AOTTests/ViewLocatorAOTMappingTests.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,9 @@ private sealed class ViewB : IViewFor<VmB>
8585
public VmB? ViewModel { get; set; }
8686
}
8787

88-
private sealed class VmA : ReactiveObject
89-
{
90-
}
88+
private sealed class VmA : ReactiveObject;
9189

92-
private sealed class VmB : ReactiveObject
93-
{
94-
}
90+
private sealed class VmB : ReactiveObject;
9591

9692
private sealed class ViewA : IViewFor<VmA>
9793
{

src/tests/ReactiveUI.Blazor.Tests/ReactiveComponentBaseTests.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,5 @@ protected override void OnAfterRender(bool firstRender)
143143
/// <summary>
144144
/// A concrete implementation of ReactiveComponentBase for testing activatable ViewModels.
145145
/// </summary>
146-
public class TestActivatableComponent : ReactiveComponentBase<TestActivatableViewModel>
147-
{
148-
}
146+
public class TestActivatableComponent : ReactiveComponentBase<TestActivatableViewModel>;
149147
}

src/tests/ReactiveUI.Blazor.Tests/ReactiveInjectableComponentBaseTests.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,5 @@ protected override void OnAfterRender(bool firstRender)
194194
/// <summary>
195195
/// A concrete implementation of ReactiveInjectableComponentBase for testing with activatable ViewModels.
196196
/// </summary>
197-
public class TestActivatableInjectableComponent : ReactiveInjectableComponentBase<TestActivatableViewModel>
198-
{
199-
}
197+
public class TestActivatableInjectableComponent : ReactiveInjectableComponentBase<TestActivatableViewModel>;
200198
}

src/tests/ReactiveUI.Builder.Maui.Tests/Activation/ActivationForViewFetcherTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,10 @@ public async Task PageAndChildViewActivateAndDeactivate()
2828
{
2929
AppBuilder.ResetBuilderStateForTests();
3030
var resolver = new ModernDependencyResolver();
31-
resolver.InitializeSplat();
3231
resolver.CreateReactiveUIBuilder()
3332
.WithPlatformServices()
33+
.WithRegistrationOnBuild(r => r.RegisterConstant<IActivationForViewFetcher>(new ActivationForViewFetcher()))
3434
.BuildApp();
35-
resolver.RegisterConstant<IActivationForViewFetcher>(new ActivationForViewFetcher());
3635

3736
using (resolver.WithResolver())
3837
{

src/tests/ReactiveUI.Builder.Maui.Tests/AssemblyHooks.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@ public static class AssemblyHooks
1414
/// Called before any tests in this assembly start.
1515
/// </summary>
1616
[Before(HookType.Assembly)]
17-
public static void AssemblySetup()
18-
{
17+
public static void AssemblySetup() =>
1918
ModeDetector.OverrideModeDetector(new TestModeDetector());
20-
}
2119

2220
/// <summary>
2321
/// Called after all tests in this assembly complete.

src/tests/ReactiveUI.Builder.Maui.Tests/ReactiveUIBuilderMauiTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,9 @@ public async Task WithMaui_Should_Register_Services()
2929
{
3030
AppBuilder.ResetBuilderStateForTests();
3131
using var locator = new ModernDependencyResolver();
32-
3332
locator.CreateReactiveUIBuilder()
3433
.WithMaui()
35-
.Build();
34+
.BuildApp();
3635

3736
var observableProperty = locator.GetService<ICreatesObservableForProperty>();
3837
await Assert.That(observableProperty).IsNotNull();

0 commit comments

Comments
 (0)