Skip to content

Commit a2a4797

Browse files
authored
Update ReactiveProperty for multiple subscribers (#3953)
<!-- 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, ... --> fix for #3935 **What is the current behavior?** <!-- You can also link to an open issue here. --> #3935 **What is the new behavior?** <!-- If this is a feature change --> ReactiveProperty updated to handle multiple subscribers **What might this PR break?** None expected, core function still retained **Please check if the PR fulfills these requirements** - [x] Tests for the changes have been added (for bug fixes / features) - [ ] Docs have been added / updated (for bug fixes / features) **Other information**:
1 parent a100b0b commit a2a4797

20 files changed

+662
-442
lines changed

src/Directory.Packages.props

+12-10
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
55
</PropertyGroup>
66
<PropertyGroup>
7-
<SplatVersion>15.2.22</SplatVersion>
7+
<SplatVersion>15.3.1</SplatVersion>
8+
<XamarinAndroidXCoreVersion>1.13.1.4</XamarinAndroidXCoreVersion>
9+
<XamarinAndroidXLifecycleLiveDataVersion>2.8.4.1</XamarinAndroidXLifecycleLiveDataVersion>
810
</PropertyGroup>
911
<ItemGroup>
1012
<PackageVersion Include="coverlet.msbuild" Version="6.0.4" />
11-
<PackageVersion Include="DynamicData" Version="9.0.4" />
12-
<PackageVersion Include="FluentAssertions" Version="8.0.0" />
13+
<PackageVersion Include="DynamicData" Version="9.1.1" />
14+
<PackageVersion Include="FluentAssertions" Version="8.0.1" />
1315
<PackageVersion Include="JetBrains.DotMemoryUnit" Version="3.2.20220510" />
1416
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
1517
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.12.0" />
@@ -20,12 +22,12 @@
2022
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
2123
<PackageVersion Include="Mocks.Maui" Version="1.1.8" />
2224
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.7.115" />
23-
<PackageVersion Include="PublicApiGenerator" Version="11.3.0" />
25+
<PackageVersion Include="PublicApiGenerator" Version="11.4.1" />
2426
<PackageVersion Include="Reactive.Wasm" Version="2.1.2" />
25-
<PackageVersion Include="Roslynator.Analyzers" Version="4.12.10" />
27+
<PackageVersion Include="Roslynator.Analyzers" Version="4.12.11" />
2628
<PackageVersion Include="Splat" Version="$(SplatVersion)" />
2729
<PackageVersion Include="Splat.Autofac" Version="$(SplatVersion)" />
28-
<PackageVersion Include="Splat.Drawing" Version="$(SplatVersion)" />
30+
<PackageVersion Include="Splat.Drawing" Version="15.2.22" />
2931
<PackageVersion Include="Splat.DryIoc" Version="$(SplatVersion)" />
3032
<PackageVersion Include="Splat.Ninject" Version="$(SplatVersion)" />
3133
<PackageVersion Include="stylecop.analyzers" Version="1.2.0-beta.556" />
@@ -38,11 +40,11 @@
3840
<PackageVersion Include="xunit.runner.console" Version="2.9.3" />
3941
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.1" />
4042
<PackageVersion Include="Xunit.StaFact" Version="1.2.69" />
41-
<PackageVersion Include="Xamarin.AndroidX.Core" Version="1.15.0.1" />
43+
<PackageVersion Include="Xamarin.AndroidX.Core" Version="$(XamarinAndroidXCoreVersion)" />
4244
<PackageVersion Include="Xamarin.AndroidX.Preference" Version="1.2.1.9" />
4345
<PackageVersion Include="Xamarin.AndroidX.Legacy.Support.Core.UI" Version="1.0.0.29" />
4446
<PackageVersion Include="Xamarin.Google.Android.Material" Version="1.11.0.2" />
45-
<PackageVersion Include="Xamarin.AndroidX.Lifecycle.LiveData" Version="2.8.7.1" />
47+
<PackageVersion Include="Xamarin.AndroidX.Lifecycle.LiveData" Version="$(XamarinAndroidXLifecycleLiveDataVersion)" />
4648
</ItemGroup>
4749
<ItemGroup Condition="'$(UseMaui)' != 'true'">
4850
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.250108002" />
@@ -55,8 +57,8 @@
5557
</ItemGroup>
5658
<ItemGroup Condition="$(TargetFramework.StartsWith('net9'))">
5759
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.1" />
58-
<PackageVersion Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
59-
<PackageVersion Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
60+
<PackageVersion Include="Microsoft.Maui.Controls" Version="9.0.30" />
61+
<PackageVersion Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.30" />
6062
</ItemGroup>
6163
<ItemGroup Condition="$(TargetFramework.StartsWith('net8'))">
6264
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="8.0.12" />

src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.DotNet8_0.verified.txt

+15-5
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,14 @@ namespace ReactiveUI
9292
public class CommandBinderImplementation : Splat.IEnableLogger
9393
{
9494
public CommandBinderImplementation() { }
95+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
96+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
9597
public ReactiveUI.IReactiveBinding<TView, TProp> BindCommand<TView, TViewModel, TProp, TControl, TParam>(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression<System.Func<TViewModel, TProp?>> vmProperty, System.Linq.Expressions.Expression<System.Func<TView, TControl>> controlProperty, System.IObservable<TParam?> withParameter, string? toEvent = null)
9698
where TView : class, ReactiveUI.IViewFor
9799
where TViewModel : class
98100
where TProp : System.Windows.Input.ICommand { }
101+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
102+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
99103
public ReactiveUI.IReactiveBinding<TView, TProp> BindCommand<TView, TViewModel, TProp, TControl, TParam>(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression<System.Func<TViewModel, TProp?>> vmProperty, System.Linq.Expressions.Expression<System.Func<TView, TControl>> controlProperty, System.Linq.Expressions.Expression<System.Func<TViewModel, TParam?>> withParameter, string? toEvent = null)
100104
where TView : class, ReactiveUI.IViewFor
101105
where TViewModel : class
@@ -108,6 +112,8 @@ namespace ReactiveUI
108112
public static System.Collections.Generic.IComparer<T> ThenByDescending<T, TValue>(this System.Collections.Generic.IComparer<T>? parent, System.Func<T, TValue> selector) { }
109113
public static System.Collections.Generic.IComparer<T> ThenByDescending<T, TValue>(this System.Collections.Generic.IComparer<T>? parent, System.Func<T, TValue> selector, System.Collections.Generic.IComparer<TValue> comparer) { }
110114
}
115+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
116+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
111117
public class ComponentModelTypeConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger
112118
{
113119
public ComponentModelTypeConverter() { }
@@ -154,6 +160,8 @@ namespace ReactiveUI
154160
public static class DependencyResolverMixins
155161
{
156162
public static void InitializeReactiveUI(this Splat.IMutableDependencyResolver resolver, params ReactiveUI.RegistrationNamespace[] registrationNamespaces) { }
163+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
164+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
157165
public static void RegisterViewsForViewModels(this Splat.IMutableDependencyResolver resolver, System.Reflection.Assembly assembly) { }
158166
}
159167
public class DoubleToStringTypeConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger
@@ -766,6 +774,8 @@ namespace ReactiveUI
766774
}
767775
public static class ReactivePropertyMixins
768776
{
777+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
778+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
769779
public static ReactiveUI.ReactiveProperty<T> AddValidation<T>(this ReactiveUI.ReactiveProperty<T> self, System.Linq.Expressions.Expression<System.Func<ReactiveUI.ReactiveProperty<T>?>> selfSelector) { }
770780
public static System.IObservable<string?> ObserveValidationErrors<T>(this ReactiveUI.ReactiveProperty<T> self) { }
771781
}
@@ -874,19 +884,19 @@ namespace ReactiveUI
874884
public RoutingState(System.Reactive.Concurrency.IScheduler? scheduler = null) { }
875885
[System.Runtime.Serialization.IgnoreDataMember]
876886
[System.Text.Json.Serialization.JsonIgnore]
877-
public System.IObservable<ReactiveUI.IRoutableViewModel> CurrentViewModel { get; set; }
887+
public System.IObservable<ReactiveUI.IRoutableViewModel> CurrentViewModel { get; protected set; }
878888
[System.Runtime.Serialization.IgnoreDataMember]
879889
[System.Text.Json.Serialization.JsonIgnore]
880-
public ReactiveUI.ReactiveCommand<ReactiveUI.IRoutableViewModel, ReactiveUI.IRoutableViewModel> Navigate { get; set; }
890+
public ReactiveUI.ReactiveCommand<ReactiveUI.IRoutableViewModel, ReactiveUI.IRoutableViewModel> Navigate { get; protected set; }
881891
[System.Runtime.Serialization.IgnoreDataMember]
882892
[System.Text.Json.Serialization.JsonIgnore]
883-
public ReactiveUI.ReactiveCommand<ReactiveUI.IRoutableViewModel, ReactiveUI.IRoutableViewModel> NavigateAndReset { get; set; }
893+
public ReactiveUI.ReactiveCommand<ReactiveUI.IRoutableViewModel, ReactiveUI.IRoutableViewModel> NavigateAndReset { get; protected set; }
884894
[System.Runtime.Serialization.IgnoreDataMember]
885895
[System.Text.Json.Serialization.JsonIgnore]
886-
public ReactiveUI.ReactiveCommand<System.Reactive.Unit, ReactiveUI.IRoutableViewModel> NavigateBack { get; set; }
896+
public ReactiveUI.ReactiveCommand<System.Reactive.Unit, ReactiveUI.IRoutableViewModel> NavigateBack { get; protected set; }
887897
[System.Runtime.Serialization.IgnoreDataMember]
888898
[System.Text.Json.Serialization.JsonIgnore]
889-
public System.IObservable<DynamicData.IChangeSet<ReactiveUI.IRoutableViewModel>> NavigationChanged { get; set; }
899+
public System.IObservable<DynamicData.IChangeSet<ReactiveUI.IRoutableViewModel>> NavigationChanged { get; protected set; }
890900
[System.Runtime.Serialization.DataMember]
891901
[System.Text.Json.Serialization.JsonRequired]
892902
public System.Collections.ObjectModel.ObservableCollection<ReactiveUI.IRoutableViewModel> NavigationStack { get; set; }

src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.DotNet9_0.verified.txt

+15-5
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,14 @@ namespace ReactiveUI
9292
public class CommandBinderImplementation : Splat.IEnableLogger
9393
{
9494
public CommandBinderImplementation() { }
95+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
96+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
9597
public ReactiveUI.IReactiveBinding<TView, TProp> BindCommand<TView, TViewModel, TProp, TControl, TParam>(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression<System.Func<TViewModel, TProp?>> vmProperty, System.Linq.Expressions.Expression<System.Func<TView, TControl>> controlProperty, System.IObservable<TParam?> withParameter, string? toEvent = null)
9698
where TView : class, ReactiveUI.IViewFor
9799
where TViewModel : class
98100
where TProp : System.Windows.Input.ICommand { }
101+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
102+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
99103
public ReactiveUI.IReactiveBinding<TView, TProp> BindCommand<TView, TViewModel, TProp, TControl, TParam>(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression<System.Func<TViewModel, TProp?>> vmProperty, System.Linq.Expressions.Expression<System.Func<TView, TControl>> controlProperty, System.Linq.Expressions.Expression<System.Func<TViewModel, TParam?>> withParameter, string? toEvent = null)
100104
where TView : class, ReactiveUI.IViewFor
101105
where TViewModel : class
@@ -108,6 +112,8 @@ namespace ReactiveUI
108112
public static System.Collections.Generic.IComparer<T> ThenByDescending<T, TValue>(this System.Collections.Generic.IComparer<T>? parent, System.Func<T, TValue> selector) { }
109113
public static System.Collections.Generic.IComparer<T> ThenByDescending<T, TValue>(this System.Collections.Generic.IComparer<T>? parent, System.Func<T, TValue> selector, System.Collections.Generic.IComparer<TValue> comparer) { }
110114
}
115+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
116+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
111117
public class ComponentModelTypeConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger
112118
{
113119
public ComponentModelTypeConverter() { }
@@ -154,6 +160,8 @@ namespace ReactiveUI
154160
public static class DependencyResolverMixins
155161
{
156162
public static void InitializeReactiveUI(this Splat.IMutableDependencyResolver resolver, params ReactiveUI.RegistrationNamespace[] registrationNamespaces) { }
163+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
164+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
157165
public static void RegisterViewsForViewModels(this Splat.IMutableDependencyResolver resolver, System.Reflection.Assembly assembly) { }
158166
}
159167
public class DoubleToStringTypeConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger
@@ -766,6 +774,8 @@ namespace ReactiveUI
766774
}
767775
public static class ReactivePropertyMixins
768776
{
777+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
778+
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
769779
public static ReactiveUI.ReactiveProperty<T> AddValidation<T>(this ReactiveUI.ReactiveProperty<T> self, System.Linq.Expressions.Expression<System.Func<ReactiveUI.ReactiveProperty<T>?>> selfSelector) { }
770780
public static System.IObservable<string?> ObserveValidationErrors<T>(this ReactiveUI.ReactiveProperty<T> self) { }
771781
}
@@ -874,19 +884,19 @@ namespace ReactiveUI
874884
public RoutingState(System.Reactive.Concurrency.IScheduler? scheduler = null) { }
875885
[System.Runtime.Serialization.IgnoreDataMember]
876886
[System.Text.Json.Serialization.JsonIgnore]
877-
public System.IObservable<ReactiveUI.IRoutableViewModel> CurrentViewModel { get; set; }
887+
public System.IObservable<ReactiveUI.IRoutableViewModel> CurrentViewModel { get; protected set; }
878888
[System.Runtime.Serialization.IgnoreDataMember]
879889
[System.Text.Json.Serialization.JsonIgnore]
880-
public ReactiveUI.ReactiveCommand<ReactiveUI.IRoutableViewModel, ReactiveUI.IRoutableViewModel> Navigate { get; set; }
890+
public ReactiveUI.ReactiveCommand<ReactiveUI.IRoutableViewModel, ReactiveUI.IRoutableViewModel> Navigate { get; protected set; }
881891
[System.Runtime.Serialization.IgnoreDataMember]
882892
[System.Text.Json.Serialization.JsonIgnore]
883-
public ReactiveUI.ReactiveCommand<ReactiveUI.IRoutableViewModel, ReactiveUI.IRoutableViewModel> NavigateAndReset { get; set; }
893+
public ReactiveUI.ReactiveCommand<ReactiveUI.IRoutableViewModel, ReactiveUI.IRoutableViewModel> NavigateAndReset { get; protected set; }
884894
[System.Runtime.Serialization.IgnoreDataMember]
885895
[System.Text.Json.Serialization.JsonIgnore]
886-
public ReactiveUI.ReactiveCommand<System.Reactive.Unit, ReactiveUI.IRoutableViewModel> NavigateBack { get; set; }
896+
public ReactiveUI.ReactiveCommand<System.Reactive.Unit, ReactiveUI.IRoutableViewModel> NavigateBack { get; protected set; }
887897
[System.Runtime.Serialization.IgnoreDataMember]
888898
[System.Text.Json.Serialization.JsonIgnore]
889-
public System.IObservable<DynamicData.IChangeSet<ReactiveUI.IRoutableViewModel>> NavigationChanged { get; set; }
899+
public System.IObservable<DynamicData.IChangeSet<ReactiveUI.IRoutableViewModel>> NavigationChanged { get; protected set; }
890900
[System.Runtime.Serialization.DataMember]
891901
[System.Text.Json.Serialization.JsonRequired]
892902
public System.Collections.ObjectModel.ObservableCollection<ReactiveUI.IRoutableViewModel> NavigationStack { get; set; }

src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.Net4_7.verified.txt

+5-5
Original file line numberDiff line numberDiff line change
@@ -856,19 +856,19 @@ namespace ReactiveUI
856856
public RoutingState(System.Reactive.Concurrency.IScheduler? scheduler = null) { }
857857
[System.Runtime.Serialization.IgnoreDataMember]
858858
[System.Text.Json.Serialization.JsonIgnore]
859-
public System.IObservable<ReactiveUI.IRoutableViewModel> CurrentViewModel { get; set; }
859+
public System.IObservable<ReactiveUI.IRoutableViewModel> CurrentViewModel { get; protected set; }
860860
[System.Runtime.Serialization.IgnoreDataMember]
861861
[System.Text.Json.Serialization.JsonIgnore]
862-
public ReactiveUI.ReactiveCommand<ReactiveUI.IRoutableViewModel, ReactiveUI.IRoutableViewModel> Navigate { get; set; }
862+
public ReactiveUI.ReactiveCommand<ReactiveUI.IRoutableViewModel, ReactiveUI.IRoutableViewModel> Navigate { get; protected set; }
863863
[System.Runtime.Serialization.IgnoreDataMember]
864864
[System.Text.Json.Serialization.JsonIgnore]
865-
public ReactiveUI.ReactiveCommand<ReactiveUI.IRoutableViewModel, ReactiveUI.IRoutableViewModel> NavigateAndReset { get; set; }
865+
public ReactiveUI.ReactiveCommand<ReactiveUI.IRoutableViewModel, ReactiveUI.IRoutableViewModel> NavigateAndReset { get; protected set; }
866866
[System.Runtime.Serialization.IgnoreDataMember]
867867
[System.Text.Json.Serialization.JsonIgnore]
868-
public ReactiveUI.ReactiveCommand<System.Reactive.Unit, ReactiveUI.IRoutableViewModel> NavigateBack { get; set; }
868+
public ReactiveUI.ReactiveCommand<System.Reactive.Unit, ReactiveUI.IRoutableViewModel> NavigateBack { get; protected set; }
869869
[System.Runtime.Serialization.IgnoreDataMember]
870870
[System.Text.Json.Serialization.JsonIgnore]
871-
public System.IObservable<DynamicData.IChangeSet<ReactiveUI.IRoutableViewModel>> NavigationChanged { get; set; }
871+
public System.IObservable<DynamicData.IChangeSet<ReactiveUI.IRoutableViewModel>> NavigationChanged { get; protected set; }
872872
[System.Runtime.Serialization.DataMember]
873873
[System.Text.Json.Serialization.JsonRequired]
874874
public System.Collections.ObjectModel.ObservableCollection<ReactiveUI.IRoutableViewModel> NavigationStack { get; set; }

src/ReactiveUI.Tests/Platforms/winforms/API/WinformsApiApprovalTests.Winforms.DotNet8_0.verified.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ namespace ReactiveUI.Winforms
9494
[System.ComponentModel.Category("ReactiveUI")]
9595
[System.ComponentModel.Description("The Current View")]
9696
[System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Content)]
97-
public object? Content { get; set; }
97+
public object? Content { get; protected set; }
9898
public System.Windows.Forms.Control? CurrentView { get; }
9999
[System.ComponentModel.Category("ReactiveUI")]
100100
[System.ComponentModel.Description("The default control when no viewmodel is specified")]

src/ReactiveUI.Tests/Platforms/winforms/API/WinformsApiApprovalTests.Winforms.DotNet9_0.verified.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ namespace ReactiveUI.Winforms
9494
[System.ComponentModel.Category("ReactiveUI")]
9595
[System.ComponentModel.Description("The Current View")]
9696
[System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Content)]
97-
public object? Content { get; set; }
97+
public object? Content { get; protected set; }
9898
public System.Windows.Forms.Control? CurrentView { get; }
9999
[System.ComponentModel.Category("ReactiveUI")]
100100
[System.ComponentModel.Description("The default control when no viewmodel is specified")]

0 commit comments

Comments
 (0)