From 121458e7c1c425649b253546c7bda7873e00f644 Mon Sep 17 00:00:00 2001 From: Shimmy <2716316+weitzhandler@users.noreply.github.com> Date: Wed, 26 Jun 2019 07:46:52 +0300 Subject: [PATCH 01/19] feature: Added Uno support (#2067) --- build.cake | 1 + ...provalTests.ReactiveUI.net461.approved.txt | 1 + ...ests.ReactiveUI.netcoreapp2.0.approved.txt | 1 + .../ActivationForViewFetcher.cs | 66 +++++ src/ReactiveUI.Uno/CoreDispatcherScheduler.cs | 251 ++++++++++++++++++ src/ReactiveUI.Uno/PlatformRegistrations.cs | 42 +++ src/ReactiveUI.Uno/ReactiveUI.Uno.csproj | 43 +++ src/ReactiveUI.Uno/WinRTAppDataDriver.cs | 74 ++++++ src/ReactiveUI.sln | 67 ++++- .../Mixins/DependencyResolverMixins.cs | 5 +- .../Platforms/uap/ActivationForViewFetcher.cs | 4 +- .../uap/TransitioningContentControl.Empty.cs | 8 +- .../AutoDataTemplateBindingHook.cs | 11 +- .../windows-common/BooleanToVisibilityHint.cs | 4 +- .../BooleanToVisibilityTypeConverter.cs | 4 +- .../windows-common/PlatformOperations.cs | 2 +- .../Platforms/windows-common/ReactivePage.cs | 10 +- .../windows-common/ReactiveUserControl.cs | 14 +- .../windows-common/RoutedViewHost.cs | 10 +- .../windows-common/ViewModelViewHost.cs | 15 +- src/ReactiveUI/Properties/AssemblyInfo.cs | 1 + 21 files changed, 603 insertions(+), 31 deletions(-) create mode 100644 src/ReactiveUI.Uno/ActivationForViewFetcher.cs create mode 100644 src/ReactiveUI.Uno/CoreDispatcherScheduler.cs create mode 100644 src/ReactiveUI.Uno/PlatformRegistrations.cs create mode 100644 src/ReactiveUI.Uno/ReactiveUI.Uno.csproj create mode 100644 src/ReactiveUI.Uno/WinRTAppDataDriver.cs diff --git a/build.cake b/build.cake index 0c0e13e2fb..f5c7106f7e 100644 --- a/build.cake +++ b/build.cake @@ -24,6 +24,7 @@ var packageWhitelist = new List MakeAbsolute(File("./src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj")), MakeAbsolute(File("./src/ReactiveUI.AndroidSupport/ReactiveUI.AndroidSupport.csproj")), MakeAbsolute(File("./src/ReactiveUI.XamForms/ReactiveUI.XamForms.csproj")), + MakeAbsolute(File("./src/ReactiveUI.Uno/ReactiveUI.Uno.csproj")), }; if (IsRunningOnWindows()) diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt index 6b7eb626dc..b3963ee25d 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt @@ -1,5 +1,6 @@ [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.AndroidSupport")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Tests")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Uno")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Winforms")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Wpf")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.XamForms")] diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt index 26820301a8..1d7945ae69 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt @@ -1,5 +1,6 @@ [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.AndroidSupport")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Tests")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Uno")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Winforms")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Wpf")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.XamForms")] diff --git a/src/ReactiveUI.Uno/ActivationForViewFetcher.cs b/src/ReactiveUI.Uno/ActivationForViewFetcher.cs new file mode 100644 index 0000000000..2c0304a17f --- /dev/null +++ b/src/ReactiveUI.Uno/ActivationForViewFetcher.cs @@ -0,0 +1,66 @@ +// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using System.Reflection; + +using Windows.Foundation; +using Windows.UI.Xaml; + +namespace ReactiveUI +{ + /// + /// ActiveationForViewFetcher is how ReactiveUI determine when a + /// View is activated or deactivated. This is usually only used when porting + /// ReactiveUI to a new UI framework. + /// + public class ActivationForViewFetcher : IActivationForViewFetcher + { + /// + public int GetAffinityForView(Type view) + { + return typeof(FrameworkElement).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? 10 : 0; + } + + /// + public IObservable GetActivationForView(IActivatable view) + { + var fe = view as FrameworkElement; + + if (fe == null) + { + return Observable.Empty; + } + +#pragma warning disable SA1114 // Parameter list after. +#if NETSTANDARD || MAC + var viewLoaded = Observable.FromEvent( +#else + var viewLoaded = Observable.FromEvent, bool>( +#endif + eventHandler => (_, __) => eventHandler(true), + x => fe.Loading += x, + x => fe.Loading -= x); + + var viewUnloaded = Observable.FromEvent( + handler => + { + void EventHandler(object sender, RoutedEventArgs e) => handler(false); + return EventHandler; + }, + x => fe.Unloaded += x, + x => fe.Unloaded -= x); + + return viewLoaded + .Merge(viewUnloaded) + .Select(b => b ? fe.WhenAnyValue(x => x.IsHitTestVisible).SkipWhile(x => !x) : Observables.False) + .Switch() + .DistinctUntilChanged(); + } + } +} diff --git a/src/ReactiveUI.Uno/CoreDispatcherScheduler.cs b/src/ReactiveUI.Uno/CoreDispatcherScheduler.cs new file mode 100644 index 0000000000..de999eff51 --- /dev/null +++ b/src/ReactiveUI.Uno/CoreDispatcherScheduler.cs @@ -0,0 +1,251 @@ +// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +// + +using System; +using System.Collections.Generic; +using System.Reactive.Disposables; +using System.Runtime.ExceptionServices; +using System.Text; +using System.Threading; + +using Windows.UI.Core; +using Windows.UI.Xaml; + +namespace System.Reactive.Concurrency +{ + /// + /// Represents an object that schedules units of work on a . + /// + /// + /// This scheduler type is typically used indirectly through the and methods that use the current Dispatcher. + /// + [CLSCompliant(false)] + public sealed class CoreDispatcherScheduler : LocalScheduler, ISchedulerPeriodic + { + /// + /// Constructs a that schedules units of work on the given . + /// + /// Dispatcher to schedule work on. + /// is null. + public CoreDispatcherScheduler(CoreDispatcher dispatcher) + { + Dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); + Priority = CoreDispatcherPriority.Normal; + } + + /// + /// Constructs a that schedules units of work on the given with the given priority. + /// + /// Dispatcher to schedule work on. + /// Priority for scheduled units of work. + /// is null. + public CoreDispatcherScheduler(CoreDispatcher dispatcher, CoreDispatcherPriority priority) + { + Dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); + Priority = priority; + } + + /// + /// Gets the scheduler that schedules work on the associated with the current Window. + /// + public static CoreDispatcherScheduler Current + { + get + { + var window = Window.Current; + if (window == null) + { + throw new InvalidOperationException("There is no current window that has been created."); + } + + return new CoreDispatcherScheduler(window.Dispatcher); + } + } + + /// + /// Gets the associated with the . + /// + public CoreDispatcher Dispatcher { get; } + + /// + /// Gets the priority at which work is scheduled. + /// + public CoreDispatcherPriority Priority { get; } + + /// + /// Schedules an action to be executed on the dispatcher. + /// + /// The type of the state passed to the scheduled action. + /// State passed to the action to be executed. + /// Action to be executed. + /// The disposable object used to cancel the scheduled action (best effort). + /// is null. + public override IDisposable Schedule(TState state, Func action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + var d = new SingleAssignmentDisposable(); + + var res = Dispatcher.RunAsync(Priority, () => + { + if (!d.IsDisposed) + { + try + { + d.Disposable = action(this, state); + } + catch (Exception ex) + { + // + // Work-around for the behavior of throwing from RunAsync not propagating + // the exception to the Application.UnhandledException event (as of W8RP) + // as our users have come to expect from previous XAML stacks using Rx. + // + // If we wouldn't do this, there'd be an observable behavioral difference + // between scheduling with TimeSpan.Zero or using this overload. + // + // For scheduler implementation guidance rules, see TaskPoolScheduler.cs + // in System.Reactive.PlatformServices\Reactive\Concurrency. + // + var timer = new DispatcherTimer + { + Interval = TimeSpan.Zero + }; + timer.Tick += (o, e) => + { + timer.Stop(); + ExceptionDispatchInfo.Capture(ex).Throw(); + }; + + timer.Start(); + } + } + }); + + return StableCompositeDisposable.Create( + d, + Disposable.Create(res, _ => _.Cancel()) + ); + } + + /// + /// Schedules an action to be executed after on the dispatcher, using a object. + /// + /// The type of the state passed to the scheduled action. + /// State passed to the action to be executed. + /// Action to be executed. + /// Relative time after which to execute the action. + /// The disposable object used to cancel the scheduled action (best effort). + /// is null. + public override IDisposable Schedule(TState state, TimeSpan dueTime, Func action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + var dt = Scheduler.Normalize(dueTime); + if (dt.Ticks == 0) + { + return Schedule(state, action); + } + + return ScheduleSlow(state, dt, action); + } + + private IDisposable ScheduleSlow(TState state, TimeSpan dueTime, Func action) + { + var d = new MultipleAssignmentDisposable(); + + var timer = new DispatcherTimer(); + + timer.Tick += (o, e) => + { + var t = Interlocked.Exchange(ref timer, null); + if (t != null) + { + try + { + d.Disposable = action(this, state); + } + finally + { + t.Stop(); + action = null; + } + } + }; + + timer.Interval = dueTime; + timer.Start(); + + d.Disposable = Disposable.Create(() => + { + var t = Interlocked.Exchange(ref timer, null); + if (t != null) + { + t.Stop(); + action = (_, __) => Disposable.Empty; + } + }); + + return d; + } + + /// + /// Schedules a periodic piece of work on the dispatcher, using a object. + /// + /// The type of the state passed to the scheduled action. + /// Initial state passed to the action upon the first iteration. + /// Period for running the work periodically. + /// Action to be executed, potentially updating the state. + /// The disposable object used to cancel the scheduled recurring action (best effort). + /// is null. + /// is less than . + public IDisposable SchedulePeriodic(TState state, TimeSpan period, Func action) + { + // + // According to MSDN documentation, the default is TimeSpan.Zero, so that's definitely valid. + // Empirical observation - negative values seem to be normalized to TimeSpan.Zero, but let's not go there. + // + if (period < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(period)); + } + + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + var timer = new DispatcherTimer(); + + var state1 = state; + + timer.Tick += (o, e) => + { + state1 = action(state1); + }; + + timer.Interval = period; + timer.Start(); + + return Disposable.Create(() => + { + var t = Interlocked.Exchange(ref timer, null); + if (t != null) + { + t.Stop(); + action = _ => _; + } + }); + } + } +} diff --git a/src/ReactiveUI.Uno/PlatformRegistrations.cs b/src/ReactiveUI.Uno/PlatformRegistrations.cs new file mode 100644 index 0000000000..3da21b3400 --- /dev/null +++ b/src/ReactiveUI.Uno/PlatformRegistrations.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Reactive.Concurrency; +using System.Reactive.PlatformServices; + +namespace ReactiveUI +{ + /// + /// UWP platform registrations. + /// + /// + public class PlatformRegistrations : IWantsToRegisterStuff + { + /// + public void Register(Action, Type> registerFunction) + { + registerFunction(() => new PlatformOperations(), typeof(IPlatformOperations)); + registerFunction(() => new ActivationForViewFetcher(), typeof(IActivationForViewFetcher)); + registerFunction(() => new DependencyObjectObservableForProperty(), typeof(ICreatesObservableForProperty)); + registerFunction(() => new BooleanToVisibilityTypeConverter(), typeof(IBindingTypeConverter)); + registerFunction(() => new AutoDataTemplateBindingHook(), typeof(IPropertyBindingHook)); + registerFunction(() => new WinRTAppDataDriver(), typeof(ISuspensionDriver)); + +#if NETSTANDARD + if (WasmPlatformEnlightenmentProvider.IsWasm) + { + RxApp.TaskpoolScheduler = WasmScheduler.Default; + RxApp.MainThreadScheduler = WasmScheduler.Default; + } + else +#endif + { + RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; + RxApp.MainThreadScheduler = new WaitForDispatcherScheduler(() => CoreDispatcherScheduler.Current); + } + } + } +} diff --git a/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj b/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj new file mode 100644 index 0000000000..df310a8c91 --- /dev/null +++ b/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj @@ -0,0 +1,43 @@ + + + netstandard20;MonoAndroid81;MonoAndroid90;Xamarin.iOS10;Xamarin.Mac20 + ReactiveUI.Uno + Uno Platform specific extensions for ReactiveUI + HAS_UNO + $(NoWarn);SA1648;CA1816;CA1001;CS0108;CS0114;CS3021;CS1574;CA1303 + + + + HAS_UNO;WASM + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ReactiveUI.Uno/WinRTAppDataDriver.cs b/src/ReactiveUI.Uno/WinRTAppDataDriver.cs new file mode 100644 index 0000000000..0c36002ff0 --- /dev/null +++ b/src/ReactiveUI.Uno/WinRTAppDataDriver.cs @@ -0,0 +1,74 @@ +// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reactive; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Serialization; +using Windows.Storage; +using UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding; + +namespace ReactiveUI +{ + /// + /// Loads and saves state to persistent storage. + /// + public class WinRTAppDataDriver : ISuspensionDriver + { + /// + public IObservable LoadState() + { + return Observable.FromAsync(() => ApplicationData.Current.RoamingFolder.GetFileAsync("appData.xmlish").AsTask()) + .SelectMany(x => FileIO.ReadTextAsync(x, UnicodeEncoding.Utf8).AsTask()) + .SelectMany(x => + { + var line = x.IndexOf('\n'); + var typeName = x.Substring(0, line - 1); // -1 for CR + var serializer = new DataContractSerializer(Type.GetType(typeName)); + + // NB: WinRT is terrible + var obj = serializer.ReadObject(new MemoryStream(Encoding.UTF8.GetBytes(x.Substring(line + 1)))); + return Observable.Return(obj); + }); + } + + /// + public IObservable SaveState(object state) + { + try + { + var ms = new MemoryStream(); + var writer = new StreamWriter(ms, Encoding.UTF8); + var serializer = new DataContractSerializer(state.GetType()); + writer.WriteLine(state.GetType().AssemblyQualifiedName); + writer.Flush(); + + serializer.WriteObject(ms, state); + + return Observable.FromAsync(() => ApplicationData.Current.RoamingFolder.CreateFileAsync("appData.xmlish", CreationCollisionOption.ReplaceExisting).AsTask()) + .SelectMany(x => Observable.FromAsync(() => FileIO.WriteBytesAsync(x, ms.ToArray()).AsTask())); + } + catch (Exception ex) + { + return Observable.Throw(ex); + } + } + + /// + public IObservable InvalidateState() + { + return Observable.FromAsync(() => ApplicationData.Current.RoamingFolder.GetFileAsync("appData.xmlish").AsTask()) + .SelectMany(x => Observable.FromAsync(() => x.DeleteAsync().AsTask())); + } + } +} diff --git a/src/ReactiveUI.sln b/src/ReactiveUI.sln index f9c29ccc32..9fcb731103 100644 --- a/src/ReactiveUI.sln +++ b/src/ReactiveUI.sln @@ -1,6 +1,7 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.15 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29001.49 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BD9762CF-E104-481C-96A6-26E624B86283}" ProjectSection(SolutionItems) = preProject @@ -38,7 +39,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUI.Fody.Tests", "Re EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUI.Splat.Tests", "ReactiveUI.Splat.Tests\ReactiveUI.Splat.Tests.csproj", "{7ED6D69F-138F-40BD-9F37-3E4050E4D19B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveUI.Testing.Tests", "ReactiveUI.Testing.Tests\ReactiveUI.Testing.Tests.csproj", "{CD8B19A9-316E-4FBC-8F0C-87ADC6AAD684}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUI.Testing.Tests", "ReactiveUI.Testing.Tests\ReactiveUI.Testing.Tests.csproj", "{CD8B19A9-316E-4FBC-8F0C-87ADC6AAD684}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveUI.Uno", "ReactiveUI.Uno\ReactiveUI.Uno.csproj", "{36FC3269-B7D0-4D79-A54A-B26B6190E8A2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -816,6 +819,62 @@ Global {CD8B19A9-316E-4FBC-8F0C-87ADC6AAD684}.Release|x64.Build.0 = Release|Any CPU {CD8B19A9-316E-4FBC-8F0C-87ADC6AAD684}.Release|x86.ActiveCfg = Release|Any CPU {CD8B19A9-316E-4FBC-8F0C-87ADC6AAD684}.Release|x86.Build.0 = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|Mixed Platforms.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|Mixed Platforms.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|ARM.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|iPhone.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|Mixed Platforms.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|Mixed Platforms.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|x64.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|x64.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|x86.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.AppStore|x86.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|ARM.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|ARM.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|iPhone.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|x64.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|x64.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|x86.ActiveCfg = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Debug|x86.Build.0 = Debug|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|Any CPU.Build.0 = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|ARM.ActiveCfg = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|ARM.Build.0 = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|iPhone.ActiveCfg = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|iPhone.Build.0 = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|x64.ActiveCfg = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|x64.Build.0 = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|x86.ActiveCfg = Release|Any CPU + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/ReactiveUI/Mixins/DependencyResolverMixins.cs b/src/ReactiveUI/Mixins/DependencyResolverMixins.cs index 8f500dabb5..6cb135c258 100644 --- a/src/ReactiveUI/Mixins/DependencyResolverMixins.cs +++ b/src/ReactiveUI/Mixins/DependencyResolverMixins.cs @@ -31,7 +31,8 @@ public static void InitializeReactiveUI(this IMutableDependencyResolver resolver { "ReactiveUI.XamForms", "ReactiveUI.Winforms", - "ReactiveUI.Wpf" + "ReactiveUI.Wpf", + "ReactiveUI.Uno" }; // Set up the built-in registration @@ -90,7 +91,7 @@ private static void RegisterType(IMutableDependencyResolver resolver, TypeInfo t [SuppressMessage("Redundancy", "CA1801: Redundant parameter", Justification = "Used on some platforms")] private static Func TypeFactory(TypeInfo typeInfo) { -#if PORTABLE +#if PORTABLE && !WASM throw new Exception("You are referencing the Portable version of ReactiveUI in an App. Reference the platform-specific version."); #else return Expression.Lambda>(Expression.New( diff --git a/src/ReactiveUI/Platforms/uap/ActivationForViewFetcher.cs b/src/ReactiveUI/Platforms/uap/ActivationForViewFetcher.cs index c2070d81de..b9ed713dda 100644 --- a/src/ReactiveUI/Platforms/uap/ActivationForViewFetcher.cs +++ b/src/ReactiveUI/Platforms/uap/ActivationForViewFetcher.cs @@ -7,8 +7,6 @@ using System.Linq; using System.Reactive.Linq; using System.Reflection; -using System.Windows; - using Windows.UI.Xaml; namespace ReactiveUI @@ -51,4 +49,4 @@ public IObservable GetActivationForView(IActivatable view) .DistinctUntilChanged(); } } -} \ No newline at end of file +} diff --git a/src/ReactiveUI/Platforms/uap/TransitioningContentControl.Empty.cs b/src/ReactiveUI/Platforms/uap/TransitioningContentControl.Empty.cs index 7d6707470c..0771c0478f 100644 --- a/src/ReactiveUI/Platforms/uap/TransitioningContentControl.Empty.cs +++ b/src/ReactiveUI/Platforms/uap/TransitioningContentControl.Empty.cs @@ -3,6 +3,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Diagnostics.CodeAnalysis; using Windows.UI.Xaml.Controls; namespace ReactiveUI @@ -10,7 +11,12 @@ namespace ReactiveUI /// /// A control with a single transition. /// - public class TransitioningContentControl : ContentControl + [SuppressMessage("Design", "CA1010:Collections should implement generic interface", Justification = "Deliberate usage")] + public +#if HAS_UNO + partial +#endif + class TransitioningContentControl : ContentControl { } } diff --git a/src/ReactiveUI/Platforms/windows-common/AutoDataTemplateBindingHook.cs b/src/ReactiveUI/Platforms/windows-common/AutoDataTemplateBindingHook.cs index 9625e81af3..66488417ee 100644 --- a/src/ReactiveUI/Platforms/windows-common/AutoDataTemplateBindingHook.cs +++ b/src/ReactiveUI/Platforms/windows-common/AutoDataTemplateBindingHook.cs @@ -6,7 +6,7 @@ using System; using System.Linq; -#if NETFX_CORE +#if NETFX_CORE || HAS_UNO using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Markup; @@ -30,10 +30,11 @@ public class AutoDataTemplateBindingHook : IPropertyBindingHook /// public static Lazy DefaultItemTemplate { get; } = new Lazy(() => { -#if NETFX_CORE - const string template = "" + - "" + - ""; +#if NETFX_CORE || HAS_UNO + const string template = +@" + +"; return (DataTemplate)XamlReader.Load(template); #else const string template = " Inverse = 1 << 1, -#if !NETFX_CORE +#if !NETFX_CORE && !HAS_UNO /// /// Use the hidden version rather than the Collapsed. /// diff --git a/src/ReactiveUI/Platforms/windows-common/BooleanToVisibilityTypeConverter.cs b/src/ReactiveUI/Platforms/windows-common/BooleanToVisibilityTypeConverter.cs index a93f03c301..9836336249 100644 --- a/src/ReactiveUI/Platforms/windows-common/BooleanToVisibilityTypeConverter.cs +++ b/src/ReactiveUI/Platforms/windows-common/BooleanToVisibilityTypeConverter.cs @@ -4,7 +4,7 @@ // See the LICENSE file in the project root for full license information. using System; -#if NETFX_CORE +#if NETFX_CORE || HAS_UNO using Windows.UI.Xaml; #else using System.Windows; @@ -44,7 +44,7 @@ public bool TryConvert(object from, Type toType, object conversionHint, out obje if (toType == typeof(Visibility)) { var fromAsBool = hint.HasFlag(BooleanToVisibilityHint.Inverse) ? !(bool)@from : (bool)from; -#if !NETFX_CORE +#if !NETFX_CORE && !HAS_UNO var notVisible = hint.HasFlag(BooleanToVisibilityHint.UseHidden) ? Visibility.Hidden : Visibility.Collapsed; #else var notVisible = Visibility.Collapsed; diff --git a/src/ReactiveUI/Platforms/windows-common/PlatformOperations.cs b/src/ReactiveUI/Platforms/windows-common/PlatformOperations.cs index dd872681f5..85146be577 100644 --- a/src/ReactiveUI/Platforms/windows-common/PlatformOperations.cs +++ b/src/ReactiveUI/Platforms/windows-common/PlatformOperations.cs @@ -19,7 +19,7 @@ public class PlatformOperations : IPlatformOperations /// public string GetOrientation() { -#if NETFX_CORE +#if NETFX_CORE || HAS_UNO return Windows.Graphics.Display.DisplayInformation.GetForCurrentView().CurrentOrientation.ToString(); #else return null; diff --git a/src/ReactiveUI/Platforms/windows-common/ReactivePage.cs b/src/ReactiveUI/Platforms/windows-common/ReactivePage.cs index 2a98bafeae..7a69a74ea9 100644 --- a/src/ReactiveUI/Platforms/windows-common/ReactivePage.cs +++ b/src/ReactiveUI/Platforms/windows-common/ReactivePage.cs @@ -78,9 +78,13 @@ namespace ReactiveUI /// The type of the view model backing the view. /// [SuppressMessage("Design", "CA1010:Collections should implement generic interface", Justification = "Deliberate usage")] - public abstract class ReactivePage : - Page, IViewFor - where TViewModel : class + public abstract +#if HAS_UNO + partial +#endif + class ReactivePage : + Page, IViewFor + where TViewModel : class { /// /// The view model dependency property. diff --git a/src/ReactiveUI/Platforms/windows-common/ReactiveUserControl.cs b/src/ReactiveUI/Platforms/windows-common/ReactiveUserControl.cs index a9b7b2a705..5514a7fdba 100755 --- a/src/ReactiveUI/Platforms/windows-common/ReactiveUserControl.cs +++ b/src/ReactiveUI/Platforms/windows-common/ReactiveUserControl.cs @@ -5,7 +5,8 @@ namespace ReactiveUI { -#if NETFX_CORE + using System.Diagnostics.CodeAnalysis; +#if NETFX_CORE || HAS_UNO using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; #else @@ -76,9 +77,14 @@ namespace ReactiveUI /// /// The type of the view model backing the view. /// - public abstract class ReactiveUserControl : - UserControl, IViewFor - where TViewModel : class + [SuppressMessage("Design", "CA1010:Collections should implement generic interface", Justification = "Deliberate usage")] + public abstract +#if HAS_UNO + partial +#endif + class ReactiveUserControl : + UserControl, IViewFor + where TViewModel : class { /// /// The view model dependency property. diff --git a/src/ReactiveUI/Platforms/windows-common/RoutedViewHost.cs b/src/ReactiveUI/Platforms/windows-common/RoutedViewHost.cs index e48eb30616..e32fe5b870 100644 --- a/src/ReactiveUI/Platforms/windows-common/RoutedViewHost.cs +++ b/src/ReactiveUI/Platforms/windows-common/RoutedViewHost.cs @@ -5,13 +5,14 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reactive.Linq; using System.Windows; using ReactiveUI; using Splat; -#if NETFX_CORE +#if NETFX_CORE || HAS_UNO using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; #else @@ -25,7 +26,12 @@ namespace ReactiveUI /// the View and wire up the ViewModel whenever a new ViewModel is /// navigated to. Put this control as the only control in your Window. /// - public class RoutedViewHost : TransitioningContentControl, IActivatable, IEnableLogger + [SuppressMessage("Design", "CA1010:Collections should implement generic interface", Justification = "Deliberate usage")] + public +#if HAS_UNO + partial +#endif + class RoutedViewHost : TransitioningContentControl, IActivatable, IEnableLogger { /// /// The router dependency property. diff --git a/src/ReactiveUI/Platforms/windows-common/ViewModelViewHost.cs b/src/ReactiveUI/Platforms/windows-common/ViewModelViewHost.cs index 3f7ccf1154..cf1845a752 100644 --- a/src/ReactiveUI/Platforms/windows-common/ViewModelViewHost.cs +++ b/src/ReactiveUI/Platforms/windows-common/ViewModelViewHost.cs @@ -4,6 +4,7 @@ // See the LICENSE file in the project root for full license information. using System; +using System.Diagnostics.CodeAnalysis; using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; @@ -11,7 +12,7 @@ using System.Windows; using Splat; -#if NETFX_CORE +#if NETFX_CORE || HAS_UNO using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; #else @@ -25,7 +26,17 @@ namespace ReactiveUI /// the ViewModel property and display it. This control is very useful /// inside a DataTemplate to display the View associated with a ViewModel. /// - public class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger, IDisposable + [SuppressMessage("Design", "CA1010:Collections should implement generic interface", Justification = "Deliberate usage")] + public +#if HAS_UNO + partial +#endif + class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger +#if !HAS_UNO +#pragma warning disable SA1001 // Commas should be spaced correctly + , IDisposable +#pragma warning restore SA1001 // Commas should be spaced correctly +#endif { /// /// The default content dependency property. diff --git a/src/ReactiveUI/Properties/AssemblyInfo.cs b/src/ReactiveUI/Properties/AssemblyInfo.cs index a4cc641e82..2ae17c8332 100644 --- a/src/ReactiveUI/Properties/AssemblyInfo.cs +++ b/src/ReactiveUI/Properties/AssemblyInfo.cs @@ -10,3 +10,4 @@ [assembly: InternalsVisibleTo("ReactiveUI.Wpf")] [assembly: InternalsVisibleTo("ReactiveUI.XamForms")] [assembly: InternalsVisibleTo("ReactiveUI.AndroidSupport")] +[assembly: InternalsVisibleTo("ReactiveUI.Uno")] From 7d16f2064624161e10c5073593c32775472767de Mon Sep 17 00:00:00 2001 From: Glenn <5834289+glennawatson@users.noreply.github.com> Date: Thu, 27 Jun 2019 14:09:53 +1000 Subject: [PATCH 02/19] fix: uno not including ReactiveUI NuGet package (#2091) --- src/ReactiveUI.Uno/ActivationForViewFetcher.cs | 2 +- src/ReactiveUI.Uno/PlatformRegistrations.cs | 6 ++++-- src/ReactiveUI.Uno/ReactiveUI.Uno.csproj | 17 ++++------------- src/ReactiveUI.Uno/WinRTAppDataDriver.cs | 2 +- .../DependencyObjectObservableForProperty.cs | 4 ++++ .../uap/TransitioningContentControl.Empty.cs | 4 ++++ .../AutoDataTemplateBindingHook.cs | 4 ++++ .../windows-common/BooleanToVisibilityHint.cs | 4 ++++ .../BooleanToVisibilityTypeConverter.cs | 4 ++++ .../windows-common/PlatformOperations.cs | 4 ++++ .../Platforms/windows-common/ReactivePage.cs | 18 +++++++++++------- .../windows-common/ReactiveUserControl.cs | 18 +++++++++++------- .../Platforms/windows-common/RoutedViewHost.cs | 4 ++++ .../windows-common/ViewModelViewHost.cs | 4 ++++ 14 files changed, 64 insertions(+), 31 deletions(-) diff --git a/src/ReactiveUI.Uno/ActivationForViewFetcher.cs b/src/ReactiveUI.Uno/ActivationForViewFetcher.cs index 2c0304a17f..7308e4ce75 100644 --- a/src/ReactiveUI.Uno/ActivationForViewFetcher.cs +++ b/src/ReactiveUI.Uno/ActivationForViewFetcher.cs @@ -12,7 +12,7 @@ using Windows.Foundation; using Windows.UI.Xaml; -namespace ReactiveUI +namespace ReactiveUI.Uno { /// /// ActiveationForViewFetcher is how ReactiveUI determine when a diff --git a/src/ReactiveUI.Uno/PlatformRegistrations.cs b/src/ReactiveUI.Uno/PlatformRegistrations.cs index 3da21b3400..1cce38d661 100644 --- a/src/ReactiveUI.Uno/PlatformRegistrations.cs +++ b/src/ReactiveUI.Uno/PlatformRegistrations.cs @@ -7,7 +7,7 @@ using System.Reactive.Concurrency; using System.Reactive.PlatformServices; -namespace ReactiveUI +namespace ReactiveUI.Uno { /// /// UWP platform registrations. @@ -23,7 +23,9 @@ public void Register(Action, Type> registerFunction) registerFunction(() => new DependencyObjectObservableForProperty(), typeof(ICreatesObservableForProperty)); registerFunction(() => new BooleanToVisibilityTypeConverter(), typeof(IBindingTypeConverter)); registerFunction(() => new AutoDataTemplateBindingHook(), typeof(IPropertyBindingHook)); - registerFunction(() => new WinRTAppDataDriver(), typeof(ISuspensionDriver)); + + // Re-enable once the obsolete code in Uno has been worked out. + ////registerFunction(() => new WinRTAppDataDriver(), typeof(ISuspensionDriver)); #if NETSTANDARD if (WasmPlatformEnlightenmentProvider.IsWasm) diff --git a/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj b/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj index df310a8c91..8cac9bc3fa 100644 --- a/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj +++ b/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj @@ -12,22 +12,10 @@ - - - - + - - - - - - - - - @@ -40,4 +28,7 @@ + + + diff --git a/src/ReactiveUI.Uno/WinRTAppDataDriver.cs b/src/ReactiveUI.Uno/WinRTAppDataDriver.cs index 0c36002ff0..2ca238b409 100644 --- a/src/ReactiveUI.Uno/WinRTAppDataDriver.cs +++ b/src/ReactiveUI.Uno/WinRTAppDataDriver.cs @@ -18,7 +18,7 @@ using Windows.Storage; using UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding; -namespace ReactiveUI +namespace ReactiveUI.Uno { /// /// Loads and saves state to persistent storage. diff --git a/src/ReactiveUI/Platforms/uap/DependencyObjectObservableForProperty.cs b/src/ReactiveUI/Platforms/uap/DependencyObjectObservableForProperty.cs index 92b1e8513f..bdaf2f7631 100644 --- a/src/ReactiveUI/Platforms/uap/DependencyObjectObservableForProperty.cs +++ b/src/ReactiveUI/Platforms/uap/DependencyObjectObservableForProperty.cs @@ -13,7 +13,11 @@ using Splat; using Windows.UI.Xaml; +#if HAS_UNO +namespace ReactiveUI.Uno +#else namespace ReactiveUI +#endif { /// /// Creates a observable for a property if available that is based on a DependencyProperty. diff --git a/src/ReactiveUI/Platforms/uap/TransitioningContentControl.Empty.cs b/src/ReactiveUI/Platforms/uap/TransitioningContentControl.Empty.cs index 0771c0478f..0d35186d41 100644 --- a/src/ReactiveUI/Platforms/uap/TransitioningContentControl.Empty.cs +++ b/src/ReactiveUI/Platforms/uap/TransitioningContentControl.Empty.cs @@ -6,7 +6,11 @@ using System.Diagnostics.CodeAnalysis; using Windows.UI.Xaml.Controls; +#if HAS_UNO +namespace ReactiveUI.Uno +#else namespace ReactiveUI +#endif { /// /// A control with a single transition. diff --git a/src/ReactiveUI/Platforms/windows-common/AutoDataTemplateBindingHook.cs b/src/ReactiveUI/Platforms/windows-common/AutoDataTemplateBindingHook.cs index 66488417ee..5d0ab431e0 100644 --- a/src/ReactiveUI/Platforms/windows-common/AutoDataTemplateBindingHook.cs +++ b/src/ReactiveUI/Platforms/windows-common/AutoDataTemplateBindingHook.cs @@ -16,7 +16,11 @@ using System.Windows.Markup; #endif +#if HAS_UNO +namespace ReactiveUI.Uno +#else namespace ReactiveUI +#endif { /// /// AutoDataTemplateBindingHook is a binding hook that checks ItemsControls diff --git a/src/ReactiveUI/Platforms/windows-common/BooleanToVisibilityHint.cs b/src/ReactiveUI/Platforms/windows-common/BooleanToVisibilityHint.cs index 630789ba90..3d55eb2886 100644 --- a/src/ReactiveUI/Platforms/windows-common/BooleanToVisibilityHint.cs +++ b/src/ReactiveUI/Platforms/windows-common/BooleanToVisibilityHint.cs @@ -11,7 +11,11 @@ using System.Windows; #endif +#if HAS_UNO +namespace ReactiveUI.Uno +#else namespace ReactiveUI +#endif { /// /// Enum that hints at the visibility of a ui element. diff --git a/src/ReactiveUI/Platforms/windows-common/BooleanToVisibilityTypeConverter.cs b/src/ReactiveUI/Platforms/windows-common/BooleanToVisibilityTypeConverter.cs index 9836336249..c24efa8775 100644 --- a/src/ReactiveUI/Platforms/windows-common/BooleanToVisibilityTypeConverter.cs +++ b/src/ReactiveUI/Platforms/windows-common/BooleanToVisibilityTypeConverter.cs @@ -10,7 +10,11 @@ using System.Windows; #endif +#if HAS_UNO +namespace ReactiveUI.Uno +#else namespace ReactiveUI +#endif { /// /// This type convert converts between Boolean and XAML Visibility - the diff --git a/src/ReactiveUI/Platforms/windows-common/PlatformOperations.cs b/src/ReactiveUI/Platforms/windows-common/PlatformOperations.cs index 85146be577..efe6305f8e 100644 --- a/src/ReactiveUI/Platforms/windows-common/PlatformOperations.cs +++ b/src/ReactiveUI/Platforms/windows-common/PlatformOperations.cs @@ -9,7 +9,11 @@ using System.Text; using System.Threading.Tasks; +#if HAS_UNO +namespace ReactiveUI.Uno +#else namespace ReactiveUI +#endif { /// /// Returns the current orientation of the device on Windows. diff --git a/src/ReactiveUI/Platforms/windows-common/ReactivePage.cs b/src/ReactiveUI/Platforms/windows-common/ReactivePage.cs index 7a69a74ea9..ccc840c3fa 100644 --- a/src/ReactiveUI/Platforms/windows-common/ReactivePage.cs +++ b/src/ReactiveUI/Platforms/windows-common/ReactivePage.cs @@ -3,17 +3,21 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace ReactiveUI -{ - using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; #if NETFX_CORE || HAS_UNO - using Windows.UI.Xaml; - using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; #else - using System.Windows; - using System.Windows.Controls; +using System.Windows; +using System.Windows.Controls; #endif +#if HAS_UNO +namespace ReactiveUI.Uno +#else +namespace ReactiveUI +#endif +{ /// /// A that is reactive. /// diff --git a/src/ReactiveUI/Platforms/windows-common/ReactiveUserControl.cs b/src/ReactiveUI/Platforms/windows-common/ReactiveUserControl.cs index 5514a7fdba..24bfdb12a6 100755 --- a/src/ReactiveUI/Platforms/windows-common/ReactiveUserControl.cs +++ b/src/ReactiveUI/Platforms/windows-common/ReactiveUserControl.cs @@ -3,17 +3,21 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace ReactiveUI -{ - using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; #if NETFX_CORE || HAS_UNO - using Windows.UI.Xaml; - using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; #else - using System.Windows; - using System.Windows.Controls; +using System.Windows; +using System.Windows.Controls; #endif +#if HAS_UNO +namespace ReactiveUI.Uno +#else +namespace ReactiveUI +#endif +{ /// /// A that is reactive. /// diff --git a/src/ReactiveUI/Platforms/windows-common/RoutedViewHost.cs b/src/ReactiveUI/Platforms/windows-common/RoutedViewHost.cs index e32fe5b870..9bda853982 100644 --- a/src/ReactiveUI/Platforms/windows-common/RoutedViewHost.cs +++ b/src/ReactiveUI/Platforms/windows-common/RoutedViewHost.cs @@ -19,7 +19,11 @@ using System.Windows.Controls; #endif +#if HAS_UNO +namespace ReactiveUI.Uno +#else namespace ReactiveUI +#endif { /// /// This control hosts the View associated with a Router, and will display diff --git a/src/ReactiveUI/Platforms/windows-common/ViewModelViewHost.cs b/src/ReactiveUI/Platforms/windows-common/ViewModelViewHost.cs index cf1845a752..050ce5c3f9 100644 --- a/src/ReactiveUI/Platforms/windows-common/ViewModelViewHost.cs +++ b/src/ReactiveUI/Platforms/windows-common/ViewModelViewHost.cs @@ -19,7 +19,11 @@ using System.Windows.Controls; #endif +#if HAS_UNO +namespace ReactiveUI.Uno +#else namespace ReactiveUI +#endif { /// /// This content control will automatically load the View associated with From 780a4bb0d7660c2e9d577b74f2da02ea6b275055 Mon Sep 17 00:00:00 2001 From: Glenn <5834289+glennawatson@users.noreply.github.com> Date: Thu, 27 Jun 2019 14:57:38 +1000 Subject: [PATCH 03/19] housekeeping: Update alumni section. --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 02be2fb064..6160586e7e 100644 --- a/README.md +++ b/README.md @@ -298,17 +298,11 @@ ReactiveUI is part of the [.NET Foundation](https://www.dotnetfoundation.org/). -
- -
- Geoffrey Huntley -

Sydney, Australia

-

Glenn Watson -

Washington, USA

+

Melbourne, Australia

@@ -339,6 +333,12 @@ The following have been core team members in the past. +
+ +
+ Geoffrey Huntley +

Sydney, Australia

+

From 3c185b10397fe2409db0ad146b5f05be832e230f Mon Sep 17 00:00:00 2001 From: Glenn <5834289+glennawatson@users.noreply.github.com> Date: Thu, 27 Jun 2019 20:10:31 +1000 Subject: [PATCH 04/19] housekeeping: fix analyzer warnings that have appeared in the fxcop (#2093) --- .../ControlFetcherMixin.cs | 5 + .../ReactivePagerAdapter.cs | 18 ++- .../ReactiveRecyclerViewAdapter.cs | 12 +- .../ReactiveUI.AndroidSupport.csproj | 1 + .../FollowObservableStateBehavior.cs | 25 ++-- .../Platforms/net461/ObservableTrigger.cs | 6 +- src/ReactiveUI.Blend/ReactiveUI.Blend.csproj | 1 + .../ObservableAsPropertyExtensions.cs | 16 ++- .../ReactiveUI.Fody.Helpers.csproj | 1 + ...alTests.ReactiveUIFody.net461.approved.txt | 2 +- ....ReactiveUIFody.netcoreapp2.0.approved.txt | 2 +- .../ReactiveUI.Fody.Tests.csproj | 1 + src/ReactiveUI.Fody/CecilExtensions.cs | 111 +++++++++++------- .../ObservableAsPropertyWeaver.cs | 10 ++ src/ReactiveUI.Fody/ReactiveUI.Fody.csproj | 1 + .../ReactiveUI.LeakTests.csproj | 1 + .../ReactiveUI.Splat.Tests.csproj | 1 + .../ReactiveUI.Testing.Tests.csproj | 1 + src/ReactiveUI.Testing.Tests/TestFixture.cs | 2 + .../TestFixtureBuilder.cs | 9 +- src/ReactiveUI.Testing/IBuilderExtensions.cs | 25 ++++ .../MessageBusExtensions.cs | 10 ++ .../ReactiveUI.Testing.csproj | 1 + src/ReactiveUI.Testing/SchedulerExtensions.cs | 20 ++++ ...provalTests.ReactiveUI.net461.approved.txt | 52 ++++---- ...ests.ReactiveUI.netcoreapp2.0.approved.txt | 52 ++++---- src/ReactiveUI.Tests/ReactiveUI.Tests.csproj | 1 + src/ReactiveUI.Uno/PlatformRegistrations.cs | 5 + src/ReactiveUI.Uno/ReactiveUI.Uno.csproj | 1 + src/ReactiveUI.Uno/WinRTAppDataDriver.cs | 7 +- .../ActivationForViewFetcher.cs | 2 +- .../ContentControlBindingHook.cs | 5 + .../CreatesWinformsCommandBinding.cs | 15 +++ .../PanelSetMethodBindingConverter.cs | 7 +- .../ReactiveUI.Winforms.csproj | 1 + src/ReactiveUI.Winforms/Registrations.cs | 5 + .../TableContentSetMethodBindingConverter.cs | 13 +- .../WinformsCreatesObservableForProperty.cs | 5 + .../DependencyObjectObservableForProperty.cs | 5 + src/ReactiveUI.Wpf/ReactiveUI.Wpf.csproj | 1 + src/ReactiveUI.Wpf/Registrations.cs | 5 + .../ReactiveUI.XamForms.csproj | 1 + src/ReactiveUI.XamForms/Registrations.cs | 5 + .../Activation/CanActivateViewFetcher.cs | 11 +- src/ReactiveUI/Activation/ViewForMixins.cs | 85 +++++++++----- .../Command/CommandBinderImplementation.cs | 20 ++++ .../CommandBinderImplementationMixins.cs | 5 + ...reatesCommandBindingViaCommandParameter.cs | 5 + .../Command/CreatesCommandBindingViaEvent.cs | 5 + .../Converter/EqualityTypeConverter.cs | 10 ++ .../Bindings/Converter/StringConverter.cs | 2 +- .../Property/PropertyBinderImplementation.cs | 45 +++++++ src/ReactiveUI/Expression/Reflection.cs | 16 ++- src/ReactiveUI/Legacy/ReactiveList.cs | 1 + src/ReactiveUI/Mixins/ChangeSetMixin.cs | 5 + .../Mixins/DependencyResolverMixins.cs | 13 +- src/ReactiveUI/Mixins/DisposableMixins.cs | 17 ++- src/ReactiveUI/Mixins/ExpressionMixins.cs | 19 ++- src/ReactiveUI/Mixins/ObservedChangedMixin.cs | 50 ++++---- .../ReactiveNotifyPropertyChangedMixin.cs | 27 +++-- .../INPCObservableForProperty.cs | 5 + .../IROObservableForProperty.cs | 5 + .../POCOObservableForProperty.cs | 5 + .../android/AndroidObservableForWidgets.cs | 5 + .../Platforms/android/AutoSuspendHelper.cs | 5 + .../Platforms/android/ControlFetcherMixin.cs | 23 +++- .../android/FlexibleCommandBinder.cs | 10 ++ .../Platforms/android/LayoutViewHost.cs | 6 +- .../android/PlatformRegistrations.cs | 5 + .../Platforms/android/ReactiveListAdapter.cs | 4 +- .../android/ViewCommandExtensions.cs | 27 +++-- .../Platforms/android/ViewMixins.cs | 20 ++-- .../Converters/DateTimeNSDateConverter.cs | 4 +- .../apple-common/ObservableForPropertyBase.cs | 5 + .../apple-common/TargetActionCommandBinder.cs | 23 +++- .../Platforms/mac/PlatformRegistrations.cs | 5 + .../Platforms/net461/PlatformRegistrations.cs | 5 + .../Platforms/tizen/PlatformRegistrations.cs | 5 + .../Platforms/uap/AutoSuspendHelper.cs | 5 + .../DependencyObjectObservableForProperty.cs | 23 ++-- .../Platforms/uap/PlatformRegistrations.cs | 5 + .../Platforms/uap/WinRTAppDataDriver.cs | 5 + .../uikit-common/FlexibleCommandBinder.cs | 15 +++ .../uikit-common/PlatformRegistrations.cs | 5 + .../ReactiveCollectionViewSource.cs | 15 +++ .../ReactiveCollectionViewSourceExtensions.cs | 5 + .../uikit-common/ReactiveTableViewSource.cs | 44 +++++-- .../ReactiveTableViewSourceExtensions.cs | 5 + .../Platforms/uikit-common/RoutedViewHost.cs | 5 + .../UIControlCommandExtensions.cs | 27 +++-- .../AutoDataTemplateBindingHook.cs | 12 +- .../ReactiveCommand/ReactiveCommandMixins.cs | 58 +++++---- src/ReactiveUI/ReactiveUI.csproj | 1 + src/ReactiveUI/Registration/Registrations.cs | 5 + src/ReactiveUI/Routing/MessageBus.cs | 42 +++---- .../Routing/RoutableViewModelMixin.cs | 43 ++++--- src/ReactiveUI/Routing/RoutingStateMixins.cs | 22 +++- .../Suspension/SuspensionHostExtensions.cs | 48 +++++--- src/analyzers.ruleset | 2 + src/analyzers.tests.ruleset | 3 + 100 files changed, 1006 insertions(+), 362 deletions(-) diff --git a/src/ReactiveUI.AndroidSupport/ControlFetcherMixin.cs b/src/ReactiveUI.AndroidSupport/ControlFetcherMixin.cs index b7da77fa45..467aea0766 100644 --- a/src/ReactiveUI.AndroidSupport/ControlFetcherMixin.cs +++ b/src/ReactiveUI.AndroidSupport/ControlFetcherMixin.cs @@ -26,6 +26,11 @@ public static class ControlFetcherMixin /// The resolve members. public static void WireUpControls(this Fragment fragment, View inflatedView, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) { + if (fragment == null) + { + throw new ArgumentNullException(nameof(fragment)); + } + var members = fragment.GetWireUpMembers(resolveMembers); foreach (var member in members) diff --git a/src/ReactiveUI.AndroidSupport/ReactivePagerAdapter.cs b/src/ReactiveUI.AndroidSupport/ReactivePagerAdapter.cs index 39d36cd90d..69a8a6deae 100644 --- a/src/ReactiveUI.AndroidSupport/ReactivePagerAdapter.cs +++ b/src/ReactiveUI.AndroidSupport/ReactivePagerAdapter.cs @@ -81,9 +81,23 @@ public override Object InstantiateItem(ViewGroup container, int position) } /// - public override void DestroyItem(ViewGroup container, int position, Object @object) + public override void DestroyItem(ViewGroup container, int position, Object item) { - var view = (View)@object; + if (container == null) + { + throw new ArgumentNullException(nameof(container)); + } + + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + if (!(item is View view)) + { + throw new ArgumentException("Item must be of type View", nameof(item)); + } + container.RemoveView(view); } diff --git a/src/ReactiveUI.AndroidSupport/ReactiveRecyclerViewAdapter.cs b/src/ReactiveUI.AndroidSupport/ReactiveRecyclerViewAdapter.cs index f3f237d272..6b2e3b9370 100644 --- a/src/ReactiveUI.AndroidSupport/ReactiveRecyclerViewAdapter.cs +++ b/src/ReactiveUI.AndroidSupport/ReactiveRecyclerViewAdapter.cs @@ -73,7 +73,17 @@ public virtual int GetItemViewType(int position, TViewModel viewModel) /// public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position) { - ((IViewFor)holder).ViewModel = GetViewModelByPosition(position); + if (holder == null) + { + throw new ArgumentNullException(nameof(holder)); + } + + if (!(holder is IViewFor viewForHolder)) + { + throw new ArgumentException("Holder must be derived from IViewFor", nameof(holder)); + } + + viewForHolder.ViewModel = GetViewModelByPosition(position); } /// diff --git a/src/ReactiveUI.AndroidSupport/ReactiveUI.AndroidSupport.csproj b/src/ReactiveUI.AndroidSupport/ReactiveUI.AndroidSupport.csproj index 170652a6c5..0a91757270 100644 --- a/src/ReactiveUI.AndroidSupport/ReactiveUI.AndroidSupport.csproj +++ b/src/ReactiveUI.AndroidSupport/ReactiveUI.AndroidSupport.csproj @@ -4,6 +4,7 @@ MonoAndroid81 ReactiveUI extensions for the Android Support Library ReactiveUI.AndroidSupport + latest diff --git a/src/ReactiveUI.Blend/FollowObservableStateBehavior.cs b/src/ReactiveUI.Blend/FollowObservableStateBehavior.cs index 940ef27625..7c7a82e5f9 100644 --- a/src/ReactiveUI.Blend/FollowObservableStateBehavior.cs +++ b/src/ReactiveUI.Blend/FollowObservableStateBehavior.cs @@ -89,17 +89,26 @@ public FrameworkElement TargetObject /// The instance containing the event data. protected static void OnStateObservableChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { - var @this = (FollowObservableStateBehavior)sender; - if (@this._watcher != null) + if (e == null) { - @this._watcher.Dispose(); - @this._watcher = null; + throw new ArgumentNullException(nameof(e)); } - @this._watcher = ((IObservable)e.NewValue).ObserveOn(RxApp.MainThreadScheduler).Subscribe( + if (!(sender is FollowObservableStateBehavior item)) + { + throw new ArgumentException("Sender must be of type " + nameof(FollowObservableStateBehavior), nameof(sender)); + } + + if (item._watcher != null) + { + item._watcher.Dispose(); + item._watcher = null; + } + + item._watcher = ((IObservable)e.NewValue).ObserveOn(RxApp.MainThreadScheduler).Subscribe( x => { - var target = @this.TargetObject ?? @this.AssociatedObject; + var target = item.TargetObject ?? item.AssociatedObject; #if NETFX_CORE VisualStateManager.GoToState(target, x, true); #else @@ -115,12 +124,12 @@ protected static void OnStateObservableChanged(DependencyObject sender, Dependen }, ex => { - if (!@this.AutoResubscribeOnError) + if (!item.AutoResubscribeOnError) { return; } - OnStateObservableChanged(@this, e); + OnStateObservableChanged(item, e); }); } diff --git a/src/ReactiveUI.Blend/Platforms/net461/ObservableTrigger.cs b/src/ReactiveUI.Blend/Platforms/net461/ObservableTrigger.cs index 13fdcc6dec..bc03e7d8a7 100644 --- a/src/ReactiveUI.Blend/Platforms/net461/ObservableTrigger.cs +++ b/src/ReactiveUI.Blend/Platforms/net461/ObservableTrigger.cs @@ -47,7 +47,11 @@ public IObservable Observable /// The instance containing the event data. protected static void OnObservableChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { - ObservableTrigger triggerItem = (ObservableTrigger)sender; + if (!(sender is ObservableTrigger triggerItem)) + { + throw new ArgumentException("Sender must be of type " + nameof(ObservableTrigger), nameof(sender)); + } + if (triggerItem._watcher != null) { triggerItem._watcher.Dispose(); diff --git a/src/ReactiveUI.Blend/ReactiveUI.Blend.csproj b/src/ReactiveUI.Blend/ReactiveUI.Blend.csproj index 57b3e7f55f..08003a6b1e 100644 --- a/src/ReactiveUI.Blend/ReactiveUI.Blend.csproj +++ b/src/ReactiveUI.Blend/ReactiveUI.Blend.csproj @@ -6,6 +6,7 @@ ReactiveUI.Blend Blend behaviors for ReactiveUI ReactiveUI.Blend + latest diff --git a/src/ReactiveUI.Fody.Helpers/ObservableAsPropertyExtensions.cs b/src/ReactiveUI.Fody.Helpers/ObservableAsPropertyExtensions.cs index 5000a31687..b85596a9e7 100644 --- a/src/ReactiveUI.Fody.Helpers/ObservableAsPropertyExtensions.cs +++ b/src/ReactiveUI.Fody.Helpers/ObservableAsPropertyExtensions.cs @@ -20,7 +20,7 @@ public static class ObservableAsPropertyExtensions /// /// The type of the object. /// The type of the ret. - /// The this. + /// The observable with the return value. /// The source. /// The property. /// The initial value. @@ -32,10 +32,20 @@ public static class ObservableAsPropertyExtensions /// or /// Backing field not found for " + propertyInfo. /// - public static ObservableAsPropertyHelper ToPropertyEx(this IObservable @this, TObj source, Expression> property, TRet initialValue = default(TRet), bool deferSubscription = false, IScheduler scheduler = null) + public static ObservableAsPropertyHelper ToPropertyEx(this IObservable item, TObj source, Expression> property, TRet initialValue = default, bool deferSubscription = false, IScheduler scheduler = null) where TObj : ReactiveObject { - var result = @this.ToProperty(source, property, initialValue, deferSubscription, scheduler); + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + if (property == null) + { + throw new ArgumentNullException(nameof(property)); + } + + var result = item.ToProperty(source, property, initialValue, deferSubscription, scheduler); // Now assign the field via reflection. var propertyInfo = property.GetPropertyInfo(); diff --git a/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj b/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj index dda795dc48..85784b602f 100644 --- a/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj +++ b/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj @@ -11,6 +11,7 @@ false + latest diff --git a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.net461.approved.txt b/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.net461.approved.txt index 4f772e7c29..540a1203c9 100644 --- a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.net461.approved.txt +++ b/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.net461.approved.txt @@ -13,7 +13,7 @@ namespace ReactiveUI.Fody.Helpers } public class static ObservableAsPropertyExtensions { - public static ReactiveUI.ObservableAsPropertyHelper ToPropertyEx(this System.IObservable @this, TObj source, System.Linq.Expressions.Expression> property, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) + public static ReactiveUI.ObservableAsPropertyHelper ToPropertyEx(this System.IObservable item, TObj source, System.Linq.Expressions.Expression> property, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) where TObj : ReactiveUI.ReactiveObject { } } [System.AttributeUsageAttribute(System.AttributeTargets.Property | System.AttributeTargets.All)] diff --git a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.netcoreapp2.0.approved.txt b/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.netcoreapp2.0.approved.txt index 4c792d9add..5dd4c8e08c 100644 --- a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.netcoreapp2.0.approved.txt +++ b/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.netcoreapp2.0.approved.txt @@ -13,7 +13,7 @@ namespace ReactiveUI.Fody.Helpers } public class static ObservableAsPropertyExtensions { - public static ReactiveUI.ObservableAsPropertyHelper ToPropertyEx(this System.IObservable @this, TObj source, System.Linq.Expressions.Expression> property, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) + public static ReactiveUI.ObservableAsPropertyHelper ToPropertyEx(this System.IObservable item, TObj source, System.Linq.Expressions.Expression> property, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) where TObj : ReactiveUI.ReactiveObject { } } [System.AttributeUsageAttribute(System.AttributeTargets.Property | System.AttributeTargets.All)] diff --git a/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj b/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj index 60b5891d24..981ea9b798 100644 --- a/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj +++ b/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj @@ -4,6 +4,7 @@ $(TargetFrameworks);net461 netstandard2.0 $(TargetFramework) + latest diff --git a/src/ReactiveUI.Fody/CecilExtensions.cs b/src/ReactiveUI.Fody/CecilExtensions.cs index e7f9b0f57b..e507923e4a 100644 --- a/src/ReactiveUI.Fody/CecilExtensions.cs +++ b/src/ReactiveUI.Fody/CecilExtensions.cs @@ -23,6 +23,16 @@ public static class CecilExtensions /// The il. public static void Emit(this MethodBody body, Action il) { + if (body == null) + { + throw new ArgumentNullException(nameof(body)); + } + + if (il == null) + { + throw new ArgumentNullException(nameof(il)); + } + il(body.GetILProcessor()); } @@ -34,6 +44,11 @@ public static void Emit(this MethodBody body, Action il) /// A generic method with generic typed arguments. public static GenericInstanceMethod MakeGenericMethod(this MethodReference method, params TypeReference[] genericArguments) { + if (genericArguments == null) + { + throw new ArgumentNullException(nameof(genericArguments)); + } + var result = new GenericInstanceMethod(method); foreach (var argument in genericArguments) { @@ -54,6 +69,16 @@ public static GenericInstanceMethod MakeGenericMethod(this MethodReference metho /// public static bool IsAssignableFrom(this TypeReference baseType, TypeReference type, Action logger = null) { + if (baseType == null) + { + throw new ArgumentNullException(nameof(baseType)); + } + + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + return baseType.Resolve().IsAssignableFrom(type.Resolve(), logger); } @@ -107,6 +132,11 @@ public static bool IsAssignableFrom(this TypeDefinition baseType, TypeDefinition /// public static bool IsDefined(this IMemberDefinition member, TypeReference attributeType) { + if (member == null) + { + throw new ArgumentNullException(nameof(member)); + } + return member.HasCustomAttributes && member.CustomAttributes.Any(x => x.AttributeType.FullName == attributeType.FullName); } @@ -118,41 +148,25 @@ public static bool IsDefined(this IMemberDefinition member, TypeReference attrib /// The method bound to the generic type. public static MethodReference Bind(this MethodReference method, GenericInstanceType genericType) { - var reference = new MethodReference(method.Name, method.ReturnType, genericType); - reference.HasThis = method.HasThis; - reference.ExplicitThis = method.ExplicitThis; - reference.CallingConvention = method.CallingConvention; - - foreach (var parameter in method.Parameters) + if (method == null) { - reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType)); + throw new ArgumentNullException(nameof(method)); } - return reference; - } - - /* - public static MethodReference BindDefinition(this MethodReference method, TypeReference genericTypeDefinition) - { - if (!genericTypeDefinition.HasGenericParameters) - return method; - - var genericDeclaration = new GenericInstanceType(genericTypeDefinition); - foreach (var parameter in genericTypeDefinition.GenericParameters) - { - genericDeclaration.GenericArguments.Add(parameter); - } - var reference = new MethodReference(method.Name, method.ReturnType, genericDeclaration); - reference.HasThis = method.HasThis; - reference.ExplicitThis = method.ExplicitThis; - reference.CallingConvention = method.CallingConvention; + var reference = new MethodReference(method.Name, method.ReturnType, genericType) + { + HasThis = method.HasThis, + ExplicitThis = method.ExplicitThis, + CallingConvention = method.CallingConvention + }; foreach (var parameter in method.Parameters) + { reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType)); + } return reference; } - */ /// /// Binds the generic type definition to a field. @@ -162,6 +176,16 @@ public static MethodReference BindDefinition(this MethodReference method, TypeRe /// The field bound to the generic type. public static FieldReference BindDefinition(this FieldReference field, TypeReference genericTypeDefinition) { + if (field == null) + { + throw new ArgumentNullException(nameof(field)); + } + + if (genericTypeDefinition == null) + { + throw new ArgumentNullException(nameof(genericTypeDefinition)); + } + if (!genericTypeDefinition.HasGenericParameters) { return field; @@ -185,6 +209,11 @@ public static FieldReference BindDefinition(this FieldReference field, TypeRefer /// The assembly if found, null if not. public static AssemblyNameReference FindAssembly(this ModuleDefinition currentModule, string assemblyName) { + if (currentModule == null) + { + throw new ArgumentNullException(nameof(currentModule)); + } + return currentModule.AssemblyReferences.SingleOrDefault(x => x.Name == assemblyName); } @@ -199,6 +228,11 @@ public static AssemblyNameReference FindAssembly(this ModuleDefinition currentMo /// The type reference. public static TypeReference FindType(this ModuleDefinition currentModule, string @namespace, string typeName, IMetadataScope scope = null, params string[] typeParameters) { + if (typeParameters == null) + { + throw new ArgumentNullException(nameof(typeParameters)); + } + var result = new TypeReference(@namespace, typeName, currentModule, scope); foreach (var typeParameter in typeParameters) { @@ -216,28 +250,17 @@ public static TypeReference FindType(this ModuleDefinition currentModule, string /// A value indicating the result of the comparison. public static bool CompareTo(this TypeReference type, TypeReference compareTo) { - return type.FullName == compareTo.FullName; - } - -/* - public static IEnumerable GetAllTypes(this ModuleDefinition module) - { - var stack = new Stack(); - foreach (var type in module.Types) + if (type == null) { - stack.Push(type); + throw new ArgumentNullException(nameof(type)); } - while (stack.Any()) - { - var current = stack.Pop(); - yield return current; - foreach (var nestedType in current.NestedTypes) - { - stack.Push(nestedType); - } + if (compareTo == null) + { + throw new ArgumentNullException(nameof(compareTo)); } + + return type.FullName == compareTo.FullName; } -*/ } } diff --git a/src/ReactiveUI.Fody/ObservableAsPropertyWeaver.cs b/src/ReactiveUI.Fody/ObservableAsPropertyWeaver.cs index 43b42f2916..0482060df6 100644 --- a/src/ReactiveUI.Fody/ObservableAsPropertyWeaver.cs +++ b/src/ReactiveUI.Fody/ObservableAsPropertyWeaver.cs @@ -129,6 +129,16 @@ public void Execute() /// The type. public void EmitDefaultValue(MethodBody methodBody, ILProcessor il, TypeReference type) { + if (methodBody == null) + { + throw new ArgumentNullException(nameof(methodBody)); + } + + if (il == null) + { + throw new ArgumentNullException(nameof(il)); + } + if (type.CompareTo(ModuleDefinition.TypeSystem.Boolean) || type.CompareTo(ModuleDefinition.TypeSystem.Byte) || type.CompareTo(ModuleDefinition.TypeSystem.Int16) || type.CompareTo(ModuleDefinition.TypeSystem.Int32)) { diff --git a/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj b/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj index 6a4dbea8c0..a677b7871f 100644 --- a/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj +++ b/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj @@ -4,6 +4,7 @@ $(TargetFrameworks);net461 Fody Weavers for ReactiveUI.Fody. False + latest diff --git a/src/ReactiveUI.LeakTests/ReactiveUI.LeakTests.csproj b/src/ReactiveUI.LeakTests/ReactiveUI.LeakTests.csproj index b70f06637b..2661a3dbe2 100644 --- a/src/ReactiveUI.LeakTests/ReactiveUI.LeakTests.csproj +++ b/src/ReactiveUI.LeakTests/ReactiveUI.LeakTests.csproj @@ -1,6 +1,7 @@  net461 + latest diff --git a/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj b/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj index a0d6aed0a5..8696ca0748 100644 --- a/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj +++ b/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj @@ -4,6 +4,7 @@ netcoreapp2.2 false + latest diff --git a/src/ReactiveUI.Testing.Tests/ReactiveUI.Testing.Tests.csproj b/src/ReactiveUI.Testing.Tests/ReactiveUI.Testing.Tests.csproj index 79162b61fd..30057d031a 100644 --- a/src/ReactiveUI.Testing.Tests/ReactiveUI.Testing.Tests.csproj +++ b/src/ReactiveUI.Testing.Tests/ReactiveUI.Testing.Tests.csproj @@ -4,6 +4,7 @@ $(TargetFrameworks);net461 netstandard2.0 $(TargetFramework) + latest diff --git a/src/ReactiveUI.Testing.Tests/TestFixture.cs b/src/ReactiveUI.Testing.Tests/TestFixture.cs index ab66321b2d..2e67133245 100644 --- a/src/ReactiveUI.Testing.Tests/TestFixture.cs +++ b/src/ReactiveUI.Testing.Tests/TestFixture.cs @@ -4,6 +4,7 @@ // See the LICENSE file in the project root for full license information. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace ReactiveUI.Testing.Tests { @@ -30,6 +31,7 @@ public class TestFixture /// /// Gets or sets the variables. /// + [SuppressMessage("Design", "CA2227: Read only dictionary", Justification = "Used in mock.")] public Dictionary Variables { get; set; } } } diff --git a/src/ReactiveUI.Testing.Tests/TestFixtureBuilder.cs b/src/ReactiveUI.Testing.Tests/TestFixtureBuilder.cs index 65e67133e4..b91e86bb6b 100644 --- a/src/ReactiveUI.Testing.Tests/TestFixtureBuilder.cs +++ b/src/ReactiveUI.Testing.Tests/TestFixtureBuilder.cs @@ -22,7 +22,14 @@ public class TestFixtureBuilder : IBuilder /// /// The builder. /// The test fixture. - public static implicit operator TestFixture(TestFixtureBuilder builder) => builder.Build(); + public static implicit operator TestFixture(TestFixtureBuilder builder) => ToTestFixture(builder); + + /// + /// Performs conversion from to . + /// + /// The builder. + /// The test fixture. + public static TestFixture ToTestFixture(TestFixtureBuilder builder) => builder.Build(); /// /// Adds the count to the builder. diff --git a/src/ReactiveUI.Testing/IBuilderExtensions.cs b/src/ReactiveUI.Testing/IBuilderExtensions.cs index a6beb9ff05..3e98c819aa 100644 --- a/src/ReactiveUI.Testing/IBuilderExtensions.cs +++ b/src/ReactiveUI.Testing/IBuilderExtensions.cs @@ -51,6 +51,11 @@ public static TBuilder With( IEnumerable values) where TBuilder : IBuilder { + if (field == null) + { + throw new System.ArgumentNullException(nameof(field)); + } + if (values == null) { field = null; @@ -75,6 +80,11 @@ public static TBuilder With( public static TBuilder With(this TBuilder builder, ref List field, TField value) where TBuilder : IBuilder { + if (field == null) + { + throw new System.ArgumentNullException(nameof(field)); + } + field.Add(value); return builder; } @@ -95,6 +105,11 @@ public static TBuilder With( KeyValuePair keyValuePair) where TBuilder : IBuilder { + if (dictionary == null) + { + throw new System.ArgumentNullException(nameof(dictionary)); + } + dictionary.Add(keyValuePair.Key, keyValuePair.Value); return builder; } @@ -117,6 +132,11 @@ public static TBuilder With( TField value) where TBuilder : IBuilder { + if (dictionary == null) + { + throw new System.ArgumentNullException(nameof(dictionary)); + } + dictionary.Add(key, value); return builder; } @@ -136,6 +156,11 @@ public static TBuilder With( ref Dictionary dictionary, IDictionary keyValuePair) { + if (dictionary == null) + { + throw new System.ArgumentNullException(nameof(dictionary)); + } + dictionary = (Dictionary)keyValuePair; return builder; } diff --git a/src/ReactiveUI.Testing/MessageBusExtensions.cs b/src/ReactiveUI.Testing/MessageBusExtensions.cs index 5f9614d548..64ef6f1fc1 100644 --- a/src/ReactiveUI.Testing/MessageBusExtensions.cs +++ b/src/ReactiveUI.Testing/MessageBusExtensions.cs @@ -25,6 +25,11 @@ public static class MessageBusExtensions /// The return value of the function. public static TRet With(this IMessageBus messageBus, Func block) { + if (block == null) + { + throw new ArgumentNullException(nameof(block)); + } + using (messageBus.WithMessageBus()) { return block(); @@ -60,6 +65,11 @@ public static IDisposable WithMessageBus(this IMessageBus messageBus) /// The action to execute. public static void With(this IMessageBus messageBus, Action block) { + if (block == null) + { + throw new ArgumentNullException(nameof(block)); + } + using (messageBus.WithMessageBus()) { block(); diff --git a/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj b/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj index c5d3abf429..5ed54f732a 100644 --- a/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj +++ b/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj @@ -6,6 +6,7 @@ ReactiveUI.Testing A library to aid in writing unit tests for ReactiveUI projects ReactiveUI.Testing + latest diff --git a/src/ReactiveUI.Testing/SchedulerExtensions.cs b/src/ReactiveUI.Testing/SchedulerExtensions.cs index 404423076f..7f88119182 100644 --- a/src/ReactiveUI.Testing/SchedulerExtensions.cs +++ b/src/ReactiveUI.Testing/SchedulerExtensions.cs @@ -60,6 +60,11 @@ public static IDisposable WithScheduler(IScheduler sched) public static TRet With(this T sched, Func block) where T : IScheduler { + if (block == null) + { + throw new ArgumentNullException(nameof(block)); + } + TRet ret; using (WithScheduler(sched)) { @@ -83,6 +88,11 @@ public static TRet With(this T sched, Func block) public static async Task WithAsync(this T sched, Func> block) where T : IScheduler { + if (block == null) + { + throw new ArgumentNullException(nameof(block)); + } + TRet ret; using (WithScheduler(sched)) { @@ -137,6 +147,11 @@ public static Task WithAsync(this T sched, Func block) /// incremental, it sets the time. public static void AdvanceToMs(this TestScheduler sched, double milliseconds) { + if (sched == null) + { + throw new ArgumentNullException(nameof(sched)); + } + sched.AdvanceTo(sched.FromTimeSpan(TimeSpan.FromMilliseconds(milliseconds))); } @@ -149,6 +164,11 @@ public static void AdvanceToMs(this TestScheduler sched, double milliseconds) /// by, in milliseconds. public static void AdvanceByMs(this TestScheduler sched, double milliseconds) { + if (sched == null) + { + throw new ArgumentNullException(nameof(sched)); + } + sched.AdvanceBy(sched.FromTimeSpan(TimeSpan.FromMilliseconds(milliseconds))); } diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt index b3963ee25d..b671259841 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt @@ -395,9 +395,9 @@ namespace ReactiveUI } public class static ObservedChangedMixin { - public static string GetPropertyName(this ReactiveUI.IObservedChange @this) { } - public static TValue GetValue(this ReactiveUI.IObservedChange @this) { } - public static System.IObservable Value(this System.IObservable> @this) { } + public static string GetPropertyName(this ReactiveUI.IObservedChange item) { } + public static TValue GetValue(this ReactiveUI.IObservedChange item) { } + public static System.IObservable Value(this System.IObservable> item) { } } public class static OrderedComparer { @@ -504,11 +504,11 @@ namespace ReactiveUI } public class static ReactiveCommandMixins { - public static System.IDisposable InvokeCommand(this System.IObservable @this, System.Windows.Input.ICommand command) { } - public static System.IDisposable InvokeCommand(this System.IObservable @this, ReactiveUI.ReactiveCommandBase command) { } - public static System.IDisposable InvokeCommand(this System.IObservable @this, TTarget target, System.Linq.Expressions.Expression> commandProperty) + public static System.IDisposable InvokeCommand(this System.IObservable item, System.Windows.Input.ICommand command) { } + public static System.IDisposable InvokeCommand(this System.IObservable item, ReactiveUI.ReactiveCommandBase command) { } + public static System.IDisposable InvokeCommand(this System.IObservable item, TTarget target, System.Linq.Expressions.Expression> commandProperty) where TTarget : class { } - public static System.IDisposable InvokeCommand(this System.IObservable @this, TTarget target, System.Linq.Expressions.Expression>> commandProperty) + public static System.IDisposable InvokeCommand(this System.IObservable item, TTarget target, System.Linq.Expressions.Expression>> commandProperty) where TTarget : class { } } public class ReactiveCommand : ReactiveUI.ReactiveCommandBase @@ -523,8 +523,8 @@ namespace ReactiveUI } public class static ReactiveNotifyPropertyChangedMixin { - public static System.IObservable> ObservableForProperty(this TSender @this, System.Linq.Expressions.Expression> property, bool beforeChange = False, bool skipInitial = True) { } - public static System.IObservable ObservableForProperty(this TSender @this, System.Linq.Expressions.Expression> property, System.Func selector, bool beforeChange = False) + public static System.IObservable> ObservableForProperty(this TSender item, System.Linq.Expressions.Expression> property, bool beforeChange = False, bool skipInitial = True) { } + public static System.IObservable ObservableForProperty(this TSender item, System.Linq.Expressions.Expression> property, System.Func selector, bool beforeChange = False) where TSender : class { } public static System.IObservable> SubscribeToExpressionChain(this TSender source, System.Linq.Expressions.Expression expression, bool beforeChange = False, bool skipInitial = True, bool suppressWarnings = False) { } } @@ -562,7 +562,7 @@ namespace ReactiveUI public static System.Func GetValueFetcherOrThrow(System.Reflection.MemberInfo member) { } public static System.Action GetValueSetterForProperty(System.Reflection.MemberInfo member) { } public static System.Action GetValueSetterOrThrow(System.Reflection.MemberInfo member) { } - public static bool IsStatic(this System.Reflection.PropertyInfo @this) { } + public static bool IsStatic(this System.Reflection.PropertyInfo item) { } public static System.Type ReallyFindType(string type, bool throwOnFailure) { } public static System.Linq.Expressions.Expression Rewrite(System.Linq.Expressions.Expression expression) { } public static void ThrowIfMethodsNotOverloaded(string callingTypeName, object targetObject, params string[] methodsToCheck) { } @@ -577,9 +577,9 @@ namespace ReactiveUI } public class static RoutableViewModelMixin { - public static System.IDisposable WhenNavigatedTo(this ReactiveUI.IRoutableViewModel @this, System.Func onNavigatedTo) { } - public static System.IObservable WhenNavigatedToObservable(this ReactiveUI.IRoutableViewModel @this) { } - public static System.IObservable WhenNavigatingFromObservable(this ReactiveUI.IRoutableViewModel @this) { } + public static System.IDisposable WhenNavigatedTo(this ReactiveUI.IRoutableViewModel item, System.Func onNavigatedTo) { } + public static System.IObservable WhenNavigatedToObservable(this ReactiveUI.IRoutableViewModel item) { } + public static System.IObservable WhenNavigatingFromObservable(this ReactiveUI.IRoutableViewModel item) { } } [System.Runtime.Serialization.DataContractAttribute()] public class RoutingState : ReactiveUI.ReactiveObject @@ -603,9 +603,9 @@ namespace ReactiveUI } public class static RoutingStateMixins { - public static T FindViewModelInStack(this ReactiveUI.RoutingState @this) + public static T FindViewModelInStack(this ReactiveUI.RoutingState item) where T : ReactiveUI.IRoutableViewModel { } - public static ReactiveUI.IRoutableViewModel GetCurrentViewModel(this ReactiveUI.RoutingState @this) { } + public static ReactiveUI.IRoutableViewModel GetCurrentViewModel(this ReactiveUI.RoutingState item) { } } public class static RxApp { @@ -641,10 +641,10 @@ namespace ReactiveUI } public class static SuspensionHostExtensions { - public static T GetAppState(this ReactiveUI.ISuspensionHost @this) { } - public static System.IObservable ObserveAppState(this ReactiveUI.ISuspensionHost @this) + public static T GetAppState(this ReactiveUI.ISuspensionHost item) { } + public static System.IObservable ObserveAppState(this ReactiveUI.ISuspensionHost item) where T : class { } - public static System.IDisposable SetupDefaultSuspendResume(this ReactiveUI.ISuspensionHost @this, ReactiveUI.ISuspensionDriver driver = null) { } + public static System.IDisposable SetupDefaultSuspendResume(this ReactiveUI.ISuspensionHost item, ReactiveUI.ISuspensionDriver driver = null) { } } public class UnhandledErrorException : System.Exception { @@ -672,14 +672,14 @@ namespace ReactiveUI } public class static ViewForMixins { - public static void WhenActivated(this ReactiveUI.ISupportsActivation @this, System.Func> block) { } - public static void WhenActivated(this ReactiveUI.ISupportsActivation @this, System.Action> block) { } - public static void WhenActivated(this ReactiveUI.ISupportsActivation @this, System.Action block) { } - public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Func> block) { } - public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Func> block, ReactiveUI.IViewFor view) { } - public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Action> block) { } - public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Action> block, ReactiveUI.IViewFor view) { } - public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Action block, ReactiveUI.IViewFor view = null) { } + public static void WhenActivated(this ReactiveUI.ISupportsActivation item, System.Func> block) { } + public static void WhenActivated(this ReactiveUI.ISupportsActivation item, System.Action> block) { } + public static void WhenActivated(this ReactiveUI.ISupportsActivation item, System.Action block) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable item, System.Func> block) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable item, System.Func> block, ReactiveUI.IViewFor view) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable item, System.Action> block) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable item, System.Action> block, ReactiveUI.IViewFor view) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable item, System.Action block, ReactiveUI.IViewFor view = null) { } } public class static ViewLocator { diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt index 1d7945ae69..b25be35e00 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt @@ -389,9 +389,9 @@ namespace ReactiveUI } public class static ObservedChangedMixin { - public static string GetPropertyName(this ReactiveUI.IObservedChange @this) { } - public static TValue GetValue(this ReactiveUI.IObservedChange @this) { } - public static System.IObservable Value(this System.IObservable> @this) { } + public static string GetPropertyName(this ReactiveUI.IObservedChange item) { } + public static TValue GetValue(this ReactiveUI.IObservedChange item) { } + public static System.IObservable Value(this System.IObservable> item) { } } public class static OrderedComparer { @@ -498,11 +498,11 @@ namespace ReactiveUI } public class static ReactiveCommandMixins { - public static System.IDisposable InvokeCommand(this System.IObservable @this, System.Windows.Input.ICommand command) { } - public static System.IDisposable InvokeCommand(this System.IObservable @this, ReactiveUI.ReactiveCommandBase command) { } - public static System.IDisposable InvokeCommand(this System.IObservable @this, TTarget target, System.Linq.Expressions.Expression> commandProperty) + public static System.IDisposable InvokeCommand(this System.IObservable item, System.Windows.Input.ICommand command) { } + public static System.IDisposable InvokeCommand(this System.IObservable item, ReactiveUI.ReactiveCommandBase command) { } + public static System.IDisposable InvokeCommand(this System.IObservable item, TTarget target, System.Linq.Expressions.Expression> commandProperty) where TTarget : class { } - public static System.IDisposable InvokeCommand(this System.IObservable @this, TTarget target, System.Linq.Expressions.Expression>> commandProperty) + public static System.IDisposable InvokeCommand(this System.IObservable item, TTarget target, System.Linq.Expressions.Expression>> commandProperty) where TTarget : class { } } public class ReactiveCommand : ReactiveUI.ReactiveCommandBase @@ -517,8 +517,8 @@ namespace ReactiveUI } public class static ReactiveNotifyPropertyChangedMixin { - public static System.IObservable> ObservableForProperty(this TSender @this, System.Linq.Expressions.Expression> property, bool beforeChange = False, bool skipInitial = True) { } - public static System.IObservable ObservableForProperty(this TSender @this, System.Linq.Expressions.Expression> property, System.Func selector, bool beforeChange = False) + public static System.IObservable> ObservableForProperty(this TSender item, System.Linq.Expressions.Expression> property, bool beforeChange = False, bool skipInitial = True) { } + public static System.IObservable ObservableForProperty(this TSender item, System.Linq.Expressions.Expression> property, System.Func selector, bool beforeChange = False) where TSender : class { } public static System.IObservable> SubscribeToExpressionChain(this TSender source, System.Linq.Expressions.Expression expression, bool beforeChange = False, bool skipInitial = True, bool suppressWarnings = False) { } } @@ -556,7 +556,7 @@ namespace ReactiveUI public static System.Func GetValueFetcherOrThrow(System.Reflection.MemberInfo member) { } public static System.Action GetValueSetterForProperty(System.Reflection.MemberInfo member) { } public static System.Action GetValueSetterOrThrow(System.Reflection.MemberInfo member) { } - public static bool IsStatic(this System.Reflection.PropertyInfo @this) { } + public static bool IsStatic(this System.Reflection.PropertyInfo item) { } public static System.Type ReallyFindType(string type, bool throwOnFailure) { } public static System.Linq.Expressions.Expression Rewrite(System.Linq.Expressions.Expression expression) { } public static void ThrowIfMethodsNotOverloaded(string callingTypeName, object targetObject, params string[] methodsToCheck) { } @@ -571,9 +571,9 @@ namespace ReactiveUI } public class static RoutableViewModelMixin { - public static System.IDisposable WhenNavigatedTo(this ReactiveUI.IRoutableViewModel @this, System.Func onNavigatedTo) { } - public static System.IObservable WhenNavigatedToObservable(this ReactiveUI.IRoutableViewModel @this) { } - public static System.IObservable WhenNavigatingFromObservable(this ReactiveUI.IRoutableViewModel @this) { } + public static System.IDisposable WhenNavigatedTo(this ReactiveUI.IRoutableViewModel item, System.Func onNavigatedTo) { } + public static System.IObservable WhenNavigatedToObservable(this ReactiveUI.IRoutableViewModel item) { } + public static System.IObservable WhenNavigatingFromObservable(this ReactiveUI.IRoutableViewModel item) { } } [System.Runtime.Serialization.DataContractAttribute()] public class RoutingState : ReactiveUI.ReactiveObject @@ -597,9 +597,9 @@ namespace ReactiveUI } public class static RoutingStateMixins { - public static T FindViewModelInStack(this ReactiveUI.RoutingState @this) + public static T FindViewModelInStack(this ReactiveUI.RoutingState item) where T : ReactiveUI.IRoutableViewModel { } - public static ReactiveUI.IRoutableViewModel GetCurrentViewModel(this ReactiveUI.RoutingState @this) { } + public static ReactiveUI.IRoutableViewModel GetCurrentViewModel(this ReactiveUI.RoutingState item) { } } public class static RxApp { @@ -635,10 +635,10 @@ namespace ReactiveUI } public class static SuspensionHostExtensions { - public static T GetAppState(this ReactiveUI.ISuspensionHost @this) { } - public static System.IObservable ObserveAppState(this ReactiveUI.ISuspensionHost @this) + public static T GetAppState(this ReactiveUI.ISuspensionHost item) { } + public static System.IObservable ObserveAppState(this ReactiveUI.ISuspensionHost item) where T : class { } - public static System.IDisposable SetupDefaultSuspendResume(this ReactiveUI.ISuspensionHost @this, ReactiveUI.ISuspensionDriver driver = null) { } + public static System.IDisposable SetupDefaultSuspendResume(this ReactiveUI.ISuspensionHost item, ReactiveUI.ISuspensionDriver driver = null) { } } public class UnhandledErrorException : System.Exception { @@ -666,14 +666,14 @@ namespace ReactiveUI } public class static ViewForMixins { - public static void WhenActivated(this ReactiveUI.ISupportsActivation @this, System.Func> block) { } - public static void WhenActivated(this ReactiveUI.ISupportsActivation @this, System.Action> block) { } - public static void WhenActivated(this ReactiveUI.ISupportsActivation @this, System.Action block) { } - public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Func> block) { } - public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Func> block, ReactiveUI.IViewFor view) { } - public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Action> block) { } - public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Action> block, ReactiveUI.IViewFor view) { } - public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Action block, ReactiveUI.IViewFor view = null) { } + public static void WhenActivated(this ReactiveUI.ISupportsActivation item, System.Func> block) { } + public static void WhenActivated(this ReactiveUI.ISupportsActivation item, System.Action> block) { } + public static void WhenActivated(this ReactiveUI.ISupportsActivation item, System.Action block) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable item, System.Func> block) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable item, System.Func> block, ReactiveUI.IViewFor view) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable item, System.Action> block) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable item, System.Action> block, ReactiveUI.IViewFor view) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable item, System.Action block, ReactiveUI.IViewFor view = null) { } } public class static ViewLocator { diff --git a/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj b/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj index 486a6cddc3..f4895c2d26 100644 --- a/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj +++ b/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj @@ -3,6 +3,7 @@ netcoreapp2.0 $(TargetFrameworks);net461 + latest diff --git a/src/ReactiveUI.Uno/PlatformRegistrations.cs b/src/ReactiveUI.Uno/PlatformRegistrations.cs index 1cce38d661..3ce709dcc8 100644 --- a/src/ReactiveUI.Uno/PlatformRegistrations.cs +++ b/src/ReactiveUI.Uno/PlatformRegistrations.cs @@ -18,6 +18,11 @@ public class PlatformRegistrations : IWantsToRegisterStuff /// public void Register(Action, Type> registerFunction) { + if (registerFunction == null) + { + throw new ArgumentNullException(nameof(registerFunction)); + } + registerFunction(() => new PlatformOperations(), typeof(IPlatformOperations)); registerFunction(() => new ActivationForViewFetcher(), typeof(IActivationForViewFetcher)); registerFunction(() => new DependencyObjectObservableForProperty(), typeof(ICreatesObservableForProperty)); diff --git a/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj b/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj index 8cac9bc3fa..7c4e6bc212 100644 --- a/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj +++ b/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj @@ -5,6 +5,7 @@ Uno Platform specific extensions for ReactiveUI HAS_UNO $(NoWarn);SA1648;CA1816;CA1001;CS0108;CS0114;CS3021;CS1574;CA1303 + latest diff --git a/src/ReactiveUI.Uno/WinRTAppDataDriver.cs b/src/ReactiveUI.Uno/WinRTAppDataDriver.cs index 2ca238b409..124096c021 100644 --- a/src/ReactiveUI.Uno/WinRTAppDataDriver.cs +++ b/src/ReactiveUI.Uno/WinRTAppDataDriver.cs @@ -32,7 +32,7 @@ public IObservable LoadState() .SelectMany(x => FileIO.ReadTextAsync(x, UnicodeEncoding.Utf8).AsTask()) .SelectMany(x => { - var line = x.IndexOf('\n'); + var line = x.IndexOf("\n", StringComparison.InvariantCulture); var typeName = x.Substring(0, line - 1); // -1 for CR var serializer = new DataContractSerializer(Type.GetType(typeName)); @@ -45,6 +45,11 @@ public IObservable LoadState() /// public IObservable SaveState(object state) { + if (state == null) + { + throw new ArgumentNullException(nameof(state)); + } + try { var ms = new MemoryStream(); diff --git a/src/ReactiveUI.Winforms/ActivationForViewFetcher.cs b/src/ReactiveUI.Winforms/ActivationForViewFetcher.cs index 1a296a9030..68163e8c50 100644 --- a/src/ReactiveUI.Winforms/ActivationForViewFetcher.cs +++ b/src/ReactiveUI.Winforms/ActivationForViewFetcher.cs @@ -62,7 +62,7 @@ public IObservable GetActivationForView(IActivatable view) this.Log().Warn( CultureInfo.InvariantCulture, "Expected a view of type System.Windows.Forms.Control but it is {0}.\r\nYou need to implement your own IActivationForViewFetcher for {0}.", - view.GetType()); + view?.GetType()); return Observable.Empty; } diff --git a/src/ReactiveUI.Winforms/ContentControlBindingHook.cs b/src/ReactiveUI.Winforms/ContentControlBindingHook.cs index 46960d3d68..a908523aff 100644 --- a/src/ReactiveUI.Winforms/ContentControlBindingHook.cs +++ b/src/ReactiveUI.Winforms/ContentControlBindingHook.cs @@ -19,6 +19,11 @@ public class ContentControlBindingHook : IPropertyBindingHook /// public bool ExecuteHook(object source, object target, Func[]> getCurrentViewModelProperties, Func[]> getCurrentViewProperties, BindingDirection direction) { + if (getCurrentViewProperties == null) + { + throw new ArgumentNullException(nameof(getCurrentViewProperties)); + } + var viewProperties = getCurrentViewProperties(); var lastViewProperty = viewProperties.LastOrDefault(); diff --git a/src/ReactiveUI.Winforms/CreatesWinformsCommandBinding.cs b/src/ReactiveUI.Winforms/CreatesWinformsCommandBinding.cs index 945fd3d2c6..92f5b05d73 100644 --- a/src/ReactiveUI.Winforms/CreatesWinformsCommandBinding.cs +++ b/src/ReactiveUI.Winforms/CreatesWinformsCommandBinding.cs @@ -54,6 +54,11 @@ public int GetAffinityForObject(Type type, bool hasEventTarget) /// public IDisposable BindCommandToObject(ICommand command, object target, IObservable commandParameter) { + if (target == null) + { + throw new ArgumentNullException(nameof(target)); + } + const BindingFlags bf = BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy; var type = target.GetType(); @@ -75,6 +80,16 @@ public IDisposable BindCommandToObject(ICommand command, object target, IObserva /// public IDisposable BindCommandToObject(ICommand command, object target, IObservable commandParameter, string eventName) { + if (command == null) + { + throw new ArgumentNullException(nameof(command)); + } + + if (target == null) + { + throw new ArgumentNullException(nameof(target)); + } + var ret = new CompositeDisposable(); object latestParameter = null; diff --git a/src/ReactiveUI.Winforms/PanelSetMethodBindingConverter.cs b/src/ReactiveUI.Winforms/PanelSetMethodBindingConverter.cs index 65c295ad7b..a390e0a3e5 100644 --- a/src/ReactiveUI.Winforms/PanelSetMethodBindingConverter.cs +++ b/src/ReactiveUI.Winforms/PanelSetMethodBindingConverter.cs @@ -26,7 +26,7 @@ public int GetAffinityForObjects(Type fromType, Type toType) return 0; } - if (fromType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>) && x.GetGenericArguments().First().IsSubclassOf(typeof(Control)))) + if (fromType?.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>) && x.GetGenericArguments().First().IsSubclassOf(typeof(Control))) ?? false) { return 10; } @@ -37,6 +37,11 @@ public int GetAffinityForObjects(Type fromType, Type toType) /// public object PerformSet(object toTarget, object newValue, object[] arguments) { + if (toTarget == null) + { + throw new ArgumentNullException(nameof(toTarget)); + } + IEnumerable newValueEnumerable = (IEnumerable)newValue; Control.ControlCollection targetCollection = (Control.ControlCollection)toTarget; diff --git a/src/ReactiveUI.Winforms/ReactiveUI.Winforms.csproj b/src/ReactiveUI.Winforms/ReactiveUI.Winforms.csproj index 8e783dc7d4..87e15cf989 100644 --- a/src/ReactiveUI.Winforms/ReactiveUI.Winforms.csproj +++ b/src/ReactiveUI.Winforms/ReactiveUI.Winforms.csproj @@ -8,6 +8,7 @@ ReactiveUI.WinForms true true + latest diff --git a/src/ReactiveUI.Winforms/Registrations.cs b/src/ReactiveUI.Winforms/Registrations.cs index 6161c01f0b..384dc1525e 100644 --- a/src/ReactiveUI.Winforms/Registrations.cs +++ b/src/ReactiveUI.Winforms/Registrations.cs @@ -19,6 +19,11 @@ public class Registrations : IWantsToRegisterStuff /// public void Register(Action, Type> registerFunction) { + if (registerFunction == null) + { + throw new ArgumentNullException(nameof(registerFunction)); + } + registerFunction(() => new PlatformOperations(), typeof(IPlatformOperations)); registerFunction(() => new CreatesWinformsCommandBinding(), typeof(ICreatesCommandBinding)); diff --git a/src/ReactiveUI.Winforms/TableContentSetMethodBindingConverter.cs b/src/ReactiveUI.Winforms/TableContentSetMethodBindingConverter.cs index 337914b382..8c79a0fa82 100644 --- a/src/ReactiveUI.Winforms/TableContentSetMethodBindingConverter.cs +++ b/src/ReactiveUI.Winforms/TableContentSetMethodBindingConverter.cs @@ -25,7 +25,7 @@ public int GetAffinityForObjects(Type fromType, Type toType) return 0; } - if (fromType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>) && x.GetGenericArguments().First().IsSubclassOf(typeof(Control)))) + if (fromType?.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>) && x.GetGenericArguments().First().IsSubclassOf(typeof(Control))) ?? false) { return 15; } @@ -36,8 +36,17 @@ public int GetAffinityForObjects(Type fromType, Type toType) /// public object PerformSet(object toTarget, object newValue, object[] arguments) { + if (toTarget == null) + { + throw new ArgumentNullException(nameof(toTarget)); + } + + if (!(toTarget is TableLayoutControlCollection targetCollection)) + { + throw new ArgumentException($"{nameof(toTarget)} must be of type {nameof(TableLayoutControlCollection)}"); + } + IEnumerable newValueEnumerable = (IEnumerable)newValue; - TableLayoutControlCollection targetCollection = (TableLayoutControlCollection)toTarget; targetCollection.Container.SuspendLayout(); diff --git a/src/ReactiveUI.Winforms/WinformsCreatesObservableForProperty.cs b/src/ReactiveUI.Winforms/WinformsCreatesObservableForProperty.cs index 2c5ada068d..8eb208f42f 100644 --- a/src/ReactiveUI.Winforms/WinformsCreatesObservableForProperty.cs +++ b/src/ReactiveUI.Winforms/WinformsCreatesObservableForProperty.cs @@ -45,6 +45,11 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang /// public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { + if (sender == null) + { + throw new ArgumentNullException(nameof(sender)); + } + var ei = eventInfoCache.Get(Tuple.Create(sender.GetType(), propertyName)); return Observable.Create>(subj => diff --git a/src/ReactiveUI.Wpf/DependencyObjectObservableForProperty.cs b/src/ReactiveUI.Wpf/DependencyObjectObservableForProperty.cs index 9de7a6c0de..b645baa989 100644 --- a/src/ReactiveUI.Wpf/DependencyObjectObservableForProperty.cs +++ b/src/ReactiveUI.Wpf/DependencyObjectObservableForProperty.cs @@ -36,6 +36,11 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang /// public IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { + if (sender == null) + { + throw new ArgumentNullException(nameof(sender)); + } + var type = sender.GetType(); var dpd = DependencyPropertyDescriptor.FromProperty(GetDependencyProperty(type, propertyName), type); diff --git a/src/ReactiveUI.Wpf/ReactiveUI.Wpf.csproj b/src/ReactiveUI.Wpf/ReactiveUI.Wpf.csproj index 8a485de71e..60052fcc48 100644 --- a/src/ReactiveUI.Wpf/ReactiveUI.Wpf.csproj +++ b/src/ReactiveUI.Wpf/ReactiveUI.Wpf.csproj @@ -5,6 +5,7 @@ ReactiveUI.WPF true true + latest diff --git a/src/ReactiveUI.Wpf/Registrations.cs b/src/ReactiveUI.Wpf/Registrations.cs index e9a5b803ae..3e9690c4b3 100644 --- a/src/ReactiveUI.Wpf/Registrations.cs +++ b/src/ReactiveUI.Wpf/Registrations.cs @@ -16,6 +16,11 @@ public class Registrations : IWantsToRegisterStuff /// public void Register(Action, Type> registerFunction) { + if (registerFunction == null) + { + throw new ArgumentNullException(nameof(registerFunction)); + } + registerFunction(() => new PlatformOperations(), typeof(IPlatformOperations)); registerFunction(() => new ActivationForViewFetcher(), typeof(IActivationForViewFetcher)); diff --git a/src/ReactiveUI.XamForms/ReactiveUI.XamForms.csproj b/src/ReactiveUI.XamForms/ReactiveUI.XamForms.csproj index 76886b934d..6d7978f3c7 100644 --- a/src/ReactiveUI.XamForms/ReactiveUI.XamForms.csproj +++ b/src/ReactiveUI.XamForms/ReactiveUI.XamForms.csproj @@ -3,6 +3,7 @@ netstandard2.0 Xamarin Forms specific extensions to ReactiveUI ReactiveUI.XamForms + latest diff --git a/src/ReactiveUI.XamForms/Registrations.cs b/src/ReactiveUI.XamForms/Registrations.cs index 77c515416a..ae907edc94 100644 --- a/src/ReactiveUI.XamForms/Registrations.cs +++ b/src/ReactiveUI.XamForms/Registrations.cs @@ -21,6 +21,11 @@ public class Registrations : IWantsToRegisterStuff /// public void Register(Action, Type> registerFunction) { + if (registerFunction == null) + { + throw new ArgumentNullException(nameof(registerFunction)); + } + registerFunction(() => new ActivationForViewFetcher(), typeof(IActivationForViewFetcher)); } } diff --git a/src/ReactiveUI/Activation/CanActivateViewFetcher.cs b/src/ReactiveUI/Activation/CanActivateViewFetcher.cs index e2a9dbc163..637c95e6cb 100644 --- a/src/ReactiveUI/Activation/CanActivateViewFetcher.cs +++ b/src/ReactiveUI/Activation/CanActivateViewFetcher.cs @@ -5,6 +5,7 @@ using System; using System.Linq; +using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reflection; @@ -34,8 +35,14 @@ public int GetAffinityForView(Type view) => typeof(ICanActivate).GetTypeInfo().I /// An observable tracking whether the view is active. public IObservable GetActivationForView(IActivatable view) { - var ca = view as ICanActivate; - return ca.Activated.Select(_ => true).Merge(ca.Deactivated.Select(_ => false)); + var canActivate = view as ICanActivate; + + if (canActivate == null) + { + return Observable.Empty(false); + } + + return canActivate.Activated.Select(_ => true).Merge(canActivate.Deactivated.Select(_ => false)); } } } diff --git a/src/ReactiveUI/Activation/ViewForMixins.cs b/src/ReactiveUI/Activation/ViewForMixins.cs index 586afb97b2..aae9e19d21 100644 --- a/src/ReactiveUI/Activation/ViewForMixins.cs +++ b/src/ReactiveUI/Activation/ViewForMixins.cs @@ -23,11 +23,11 @@ public static class ViewForMixins (t, _) => Locator.Current .GetServices() - .Aggregate(Tuple.Create(0, default(IActivationForViewFetcher)), (acc, x) => + .Aggregate((count: 0, viewFetcher: default(IActivationForViewFetcher)), (acc, x) => { int score = x.GetAffinityForView(t); - return (score > acc.Item1) ? Tuple.Create(score, x) : acc; - }).Item2, RxApp.SmallCacheLimit); + return score > acc.count ? (score, x) : acc; + }).viewFetcher, RxApp.SmallCacheLimit); static ViewForMixins() { @@ -38,31 +38,41 @@ static ViewForMixins() /// WhenActivated allows you to register a Func to be called when a /// ViewModel's View is Activated. /// - /// Object that supports activation. + /// Object that supports activation. /// /// The method to be called when the corresponding /// View is activated. It returns a list of Disposables that will be /// cleaned up when the View is deactivated. /// - public static void WhenActivated(this ISupportsActivation @this, Func> block) + public static void WhenActivated(this ISupportsActivation item, Func> block) { - @this.Activator.AddActivationBlock(block); + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + item.Activator.AddActivationBlock(block); } /// /// WhenActivated allows you to register a Func to be called when a /// ViewModel's View is Activated. /// - /// Object that supports activation. + /// Object that supports activation. /// /// The method to be called when the corresponding /// View is activated. The Action parameter (usually called 'd') allows /// you to register Disposables to be cleaned up when the View is /// deactivated (i.e. "d(someObservable.Subscribe());"). /// - public static void WhenActivated(this ISupportsActivation @this, Action> block) + public static void WhenActivated(this ISupportsActivation item, Action> block) { - @this.Activator.AddActivationBlock(() => + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + item.Activator.AddActivationBlock(() => { var ret = new List(); block(ret.Add); @@ -74,15 +84,20 @@ public static void WhenActivated(this ISupportsActivation @this, Action - /// Object that supports activation. + /// Object that supports activation. /// /// The method to be called when the corresponding /// View is activated. The Action parameter (usually called 'disposables') allows /// you to collate all the disposables to be cleaned up during deactivation. /// - public static void WhenActivated(this ISupportsActivation @this, Action block) + public static void WhenActivated(this ISupportsActivation item, Action block) { - @this.Activator.AddActivationBlock(() => + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + item.Activator.AddActivationBlock(() => { var d = new CompositeDisposable(); block(d); @@ -94,23 +109,28 @@ public static void WhenActivated(this ISupportsActivation @this, Action - /// Object that supports activation. + /// Object that supports activation. /// /// The method to be called when the corresponding /// View is activated. It returns a list of Disposables that will be /// cleaned up when the View is deactivated. /// /// A Disposable that deactivates this registration. - public static IDisposable WhenActivated(this IActivatable @this, Func> block) + public static IDisposable WhenActivated(this IActivatable item, Func> block) { - return @this.WhenActivated(block, null); + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + return item.WhenActivated(block, null); } /// /// WhenActivated allows you to register a Func to be called when a /// View is Activated. /// - /// Object that supports activation. + /// Object that supports activation. /// /// The method to be called when the corresponding /// View is activated. It returns a list of Disposables that will be @@ -122,19 +142,24 @@ public static IDisposable WhenActivated(this IActivatable @this, Func /// A Disposable that deactivates this registration. - public static IDisposable WhenActivated(this IActivatable @this, Func> block, IViewFor view) + public static IDisposable WhenActivated(this IActivatable item, Func> block, IViewFor view) { - var activationFetcher = activationFetcherCache.Get(@this.GetType()); + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + var activationFetcher = activationFetcherCache.Get(item.GetType()); if (activationFetcher == null) { const string msg = "Don't know how to detect when {0} is activated/deactivated, you may need to implement IActivationForViewFetcher"; - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, msg, @this.GetType().FullName)); + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, msg, item.GetType().FullName)); } - var activationEvents = activationFetcher.GetActivationForView(@this); + var activationEvents = activationFetcher.GetActivationForView(item); var vmDisposable = Disposable.Empty; - if ((view ?? @this) is IViewFor v) + if ((view ?? item) is IViewFor v) { vmDisposable = HandleViewModelActivation(v, activationEvents); } @@ -147,7 +172,7 @@ public static IDisposable WhenActivated(this IActivatable @this, Func - /// Object that supports activation. + /// Object that supports activation. /// /// The method to be called when the corresponding /// View is activated. The Action parameter (usually called 'd') allows @@ -155,16 +180,16 @@ public static IDisposable WhenActivated(this IActivatable @this, Func /// A Disposable that deactivates this registration. - public static IDisposable WhenActivated(this IActivatable @this, Action> block) + public static IDisposable WhenActivated(this IActivatable item, Action> block) { - return @this.WhenActivated(block, null); + return item.WhenActivated(block, null); } /// /// WhenActivated allows you to register a Func to be called when a /// View is Activated. /// - /// Object that supports activation. + /// Object that supports activation. /// /// The method to be called when the corresponding /// View is activated. The Action parameter (usually called 'd') allows @@ -177,9 +202,9 @@ public static IDisposable WhenActivated(this IActivatable @this, Action /// A Disposable that deactivates this registration. - public static IDisposable WhenActivated(this IActivatable @this, Action> block, IViewFor view) + public static IDisposable WhenActivated(this IActivatable item, Action> block, IViewFor view) { - return @this.WhenActivated( + return item.WhenActivated( () => { var ret = new List(); @@ -192,7 +217,7 @@ public static IDisposable WhenActivated(this IActivatable @this, Action - /// Object that supports activation. + /// Object that supports activation. /// /// The method to be called when the corresponding /// View is activated. The Action parameter (usually called 'disposables') allows @@ -204,9 +229,9 @@ public static IDisposable WhenActivated(this IActivatable @this, Action /// A Disposable that deactivates this registration. - public static IDisposable WhenActivated(this IActivatable @this, Action block, IViewFor view = null) + public static IDisposable WhenActivated(this IActivatable item, Action block, IViewFor view = null) { - return @this.WhenActivated( + return item.WhenActivated( () => { var d = new CompositeDisposable(); diff --git a/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs b/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs index f22198b3b0..4afdc57785 100644 --- a/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs @@ -51,6 +51,16 @@ public IReactiveBinding BindCommand where TProp : ICommand { + if (vmProperty == null) + { + throw new ArgumentNullException(nameof(vmProperty)); + } + + if (controlProperty == null) + { + throw new ArgumentNullException(nameof(controlProperty)); + } + var vmExpression = Reflection.Rewrite(vmProperty.Body); var controlExpression = Reflection.Rewrite(controlProperty.Body); var source = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Cast(); @@ -108,6 +118,16 @@ public IReactiveBinding BindCommand where TProp : ICommand { + if (vmProperty == null) + { + throw new ArgumentNullException(nameof(vmProperty)); + } + + if (controlProperty == null) + { + throw new ArgumentNullException(nameof(controlProperty)); + } + var vmExpression = Reflection.Rewrite(vmProperty.Body); var controlExpression = Reflection.Rewrite(controlProperty.Body); var source = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Cast(); diff --git a/src/ReactiveUI/Bindings/Command/CommandBinderImplementationMixins.cs b/src/ReactiveUI/Bindings/Command/CommandBinderImplementationMixins.cs index ac785229d1..8e6a7743d0 100644 --- a/src/ReactiveUI/Bindings/Command/CommandBinderImplementationMixins.cs +++ b/src/ReactiveUI/Bindings/Command/CommandBinderImplementationMixins.cs @@ -45,6 +45,11 @@ public static IReactiveBinding BindCommand where TProp : ICommand { + if (withParameter == null) + { + throw new ArgumentNullException(nameof(withParameter)); + } + var paramExpression = Reflection.Rewrite(withParameter.Body); var param = Reflection.ViewModelWhenAnyValue(viewModel, view, paramExpression); diff --git a/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaCommandParameter.cs b/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaCommandParameter.cs index a06c416e6e..8986c71e9b 100644 --- a/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaCommandParameter.cs +++ b/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaCommandParameter.cs @@ -40,6 +40,11 @@ public int GetAffinityForObject(Type type, bool hasEventTarget) /// public IDisposable BindCommandToObject(ICommand command, object target, IObservable commandParameter) { + if (target == null) + { + throw new ArgumentNullException(nameof(target)); + } + var type = target.GetType(); var cmdPi = type.GetRuntimeProperty("Command"); var cmdParamPi = type.GetRuntimeProperty("CommandParameter"); diff --git a/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaEvent.cs b/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaEvent.cs index 5dc0a1d0c3..09a3cd7061 100644 --- a/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaEvent.cs +++ b/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaEvent.cs @@ -51,6 +51,11 @@ public int GetAffinityForObject(Type type, bool hasEventTarget) /// public IDisposable BindCommandToObject(ICommand command, object target, IObservable commandParameter) { + if (target == null) + { + throw new ArgumentNullException(nameof(target)); + } + var type = target.GetType(); var eventInfo = defaultEventsToBind .Select(x => new { EventInfo = type.GetRuntimeEvent(x.Item1), Args = x.Item2 }) diff --git a/src/ReactiveUI/Bindings/Converter/EqualityTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/EqualityTypeConverter.cs index b86a218e52..93aee64aff 100644 --- a/src/ReactiveUI/Bindings/Converter/EqualityTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/EqualityTypeConverter.cs @@ -123,6 +123,16 @@ public bool TryConvert(object from, Type toType, object conversionHint, out obje private static bool IsInstanceOfType(object from, Type targetType) { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (targetType == null) + { + throw new ArgumentNullException(nameof(targetType)); + } + #if NETFX_CORE || PORTABLE return targetType.GetTypeInfo().IsAssignableFrom(from.GetType().GetTypeInfo()); #else diff --git a/src/ReactiveUI/Bindings/Converter/StringConverter.cs b/src/ReactiveUI/Bindings/Converter/StringConverter.cs index bcebaf47cf..f815c775d1 100644 --- a/src/ReactiveUI/Bindings/Converter/StringConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringConverter.cs @@ -23,7 +23,7 @@ public int GetAffinityForObjects(Type fromType, Type toType) public bool TryConvert(object from, Type toType, object conversionHint, out object result) { // XXX: All Of The Localization - result = from.ToString(); + result = from?.ToString(); return true; } } diff --git a/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs b/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs index d001c67bf6..6d1f6e5b1c 100644 --- a/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs @@ -188,6 +188,16 @@ public IReactiveBinding> Bind OneWayBind OneWayBind( throw new ArgumentNullException(nameof(target)); } + if (propertyExpression == null) + { + throw new ArgumentNullException(nameof(propertyExpression)); + } + var viewExpression = Reflection.Rewrite(propertyExpression.Body); var ret = EvalBindingHooks(observedChange, target, null, viewExpression, BindingDirection.OneWay); @@ -538,6 +573,16 @@ private IReactiveBinding> BindImpl(); var vmExpression = Reflection.Rewrite(vmProperty.Body); var viewExpression = Reflection.Rewrite(viewProperty.Body); diff --git a/src/ReactiveUI/Expression/Reflection.cs b/src/ReactiveUI/Expression/Reflection.cs index 27ad876119..e0528c6cbe 100644 --- a/src/ReactiveUI/Expression/Reflection.cs +++ b/src/ReactiveUI/Expression/Reflection.cs @@ -352,6 +352,11 @@ public static Type ReallyFindType(string type, bool throwOnFailure) /// If there is no event matching the name on the target type. public static Type GetEventArgsTypeForEvent(Type type, string eventName) { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + Type ti = type; EventInfo ei = ti.GetRuntimeEvent(eventName); if (ei == null) @@ -390,11 +395,16 @@ public static void ThrowIfMethodsNotOverloaded(string callingTypeName, object ta /// /// Determines if the specified property is static or not. /// - /// The property information to check. + /// The property information to check. /// If the property is static or not. - public static bool IsStatic(this PropertyInfo @this) + public static bool IsStatic(this PropertyInfo item) { - return (@this.GetMethod ?? @this.SetMethod).IsStatic; + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + return (item.GetMethod ?? item.SetMethod).IsStatic; } [SuppressMessage("Microsoft.Performance", "CA1801", Justification = "TViewModel used to help generic calling.")] diff --git a/src/ReactiveUI/Legacy/ReactiveList.cs b/src/ReactiveUI/Legacy/ReactiveList.cs index b262e735c2..0ad4ebb8dc 100644 --- a/src/ReactiveUI/Legacy/ReactiveList.cs +++ b/src/ReactiveUI/Legacy/ReactiveList.cs @@ -26,6 +26,7 @@ #pragma warning disable SA1124 // Do not use regions -- not used for legacy #pragma warning disable RCS1165 // Unconstrained null check -- not used for legacy #pragma warning disable CA1001 // Undisposed type -- not used for legacy +#pragma warning disable CA1062 // null check namespace ReactiveUI.Legacy { diff --git a/src/ReactiveUI/Mixins/ChangeSetMixin.cs b/src/ReactiveUI/Mixins/ChangeSetMixin.cs index b4041e530d..e55544419c 100644 --- a/src/ReactiveUI/Mixins/ChangeSetMixin.cs +++ b/src/ReactiveUI/Mixins/ChangeSetMixin.cs @@ -21,6 +21,11 @@ public static class ChangeSetMixin /// If the change set is caused by the count being changed. public static bool HasCountChanged(this IChangeSet changeSet) { + if (changeSet == null) + { + throw new ArgumentNullException(nameof(changeSet)); + } + return changeSet.Adds > 0 || changeSet.Removes > 0; } diff --git a/src/ReactiveUI/Mixins/DependencyResolverMixins.cs b/src/ReactiveUI/Mixins/DependencyResolverMixins.cs index 6cb135c258..6d4cb2eeaf 100644 --- a/src/ReactiveUI/Mixins/DependencyResolverMixins.cs +++ b/src/ReactiveUI/Mixins/DependencyResolverMixins.cs @@ -55,10 +55,19 @@ public static void InitializeReactiveUI(this IMutableDependencyResolver resolver /// The assembly to search using reflection for IViewFor classes. public static void RegisterViewsForViewModels(this IMutableDependencyResolver resolver, Assembly assembly) { + if (resolver == null) + { + throw new ArgumentNullException(nameof(resolver)); + } + + if (assembly == null) + { + throw new ArgumentNullException(nameof(assembly)); + } + // for each type that implements IViewFor foreach (var ti in assembly.DefinedTypes - .Where(ti => ti.ImplementedInterfaces.Contains(typeof(IViewFor))) - .Where(ti => !ti.IsAbstract)) + .Where(ti => ti.ImplementedInterfaces.Contains(typeof(IViewFor)) && !ti.IsAbstract)) { // grab the first _implemented_ interface that also implements IViewFor, this should be the expected IViewFor<> var ivf = ti.ImplementedInterfaces.FirstOrDefault(t => t.GetTypeInfo().ImplementedInterfaces.Contains(typeof(IViewFor))); diff --git a/src/ReactiveUI/Mixins/DisposableMixins.cs b/src/ReactiveUI/Mixins/DisposableMixins.cs index 6f3149a8ee..0406a0448f 100644 --- a/src/ReactiveUI/Mixins/DisposableMixins.cs +++ b/src/ReactiveUI/Mixins/DisposableMixins.cs @@ -16,20 +16,25 @@ public static class DisposableMixins /// /// The type of the disposable. /// - /// + /// /// The disposable we are going to want to be disposed by the CompositeDisposable. /// /// - /// The to which will be added. + /// The to which will be added. /// /// /// The disposable. /// - public static T DisposeWith(this T @this, CompositeDisposable compositeDisposable) + public static T DisposeWith(this T item, CompositeDisposable compositeDisposable) where T : IDisposable { - compositeDisposable.Add(@this); - return @this; + if (compositeDisposable == null) + { + throw new ArgumentNullException(nameof(compositeDisposable)); + } + + compositeDisposable.Add(item); + return item; } } -} \ No newline at end of file +} diff --git a/src/ReactiveUI/Mixins/ExpressionMixins.cs b/src/ReactiveUI/Mixins/ExpressionMixins.cs index 2b7cfaa8c9..97fb1307fd 100644 --- a/src/ReactiveUI/Mixins/ExpressionMixins.cs +++ b/src/ReactiveUI/Mixins/ExpressionMixins.cs @@ -74,10 +74,15 @@ public static IEnumerable GetExpressionChain(this Expression @this) /// going through the Conversion Expressions. /// /// The expression. - /// The member info from the epxression. + /// The member info from the expression. public static MemberInfo GetMemberInfo(this Expression expression) { - MemberInfo info = null; + if (expression == null) + { + throw new ArgumentNullException(nameof(expression)); + } + + MemberInfo info; switch (expression.NodeType) { case ExpressionType.Index: @@ -103,6 +108,11 @@ public static MemberInfo GetMemberInfo(this Expression expression) /// The parent expression. public static Expression GetParent(this Expression expression) { + if (expression == null) + { + throw new ArgumentNullException(nameof(expression)); + } + switch (expression.NodeType) { case ExpressionType.Index: @@ -122,6 +132,11 @@ public static Expression GetParent(this Expression expression) /// An array of arguments. public static object[] GetArgumentsArray(this Expression expression) { + if (expression == null) + { + throw new ArgumentNullException(nameof(expression)); + } + if (expression.NodeType == ExpressionType.Index) { return ((IndexExpression)expression).Arguments.Cast().Select(c => c.Value).ToArray(); diff --git a/src/ReactiveUI/Mixins/ObservedChangedMixin.cs b/src/ReactiveUI/Mixins/ObservedChangedMixin.cs index 0af139eddc..0ae15c0ce4 100644 --- a/src/ReactiveUI/Mixins/ObservedChangedMixin.cs +++ b/src/ReactiveUI/Mixins/ObservedChangedMixin.cs @@ -4,7 +4,6 @@ // See the LICENSE file in the project root for full license information. using System; -using System.Linq; using System.Linq.Expressions; using System.Reactive.Linq; @@ -20,13 +19,18 @@ public static class ObservedChangedMixin /// /// The sender type. /// The value type. - /// The observed change. + /// The observed change. /// /// The name of the property which has changed. /// - public static string GetPropertyName(this IObservedChange @this) + public static string GetPropertyName(this IObservedChange item) { - return Reflection.ExpressionToPropertyNames(@this.Expression); + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + return Reflection.ExpressionToPropertyNames(item.Expression); } /// @@ -35,21 +39,25 @@ public static string GetPropertyName(this IObservedChange /// The sender. /// The changed value. - /// + /// /// The instance to get the value of. /// /// /// The current value of the property. /// - public static TValue GetValue(this IObservedChange @this) + public static TValue GetValue(this IObservedChange item) { - TValue ret; - if (!@this.TryGetValue(out ret)) + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + if (!item.TryGetValue(out var returnValue)) { - throw new Exception($"One of the properties in the expression '{@this.GetPropertyName()}' was null"); + throw new Exception($"One of the properties in the expression '{item.GetPropertyName()}' was null"); } - return ret; + return returnValue; } /// @@ -58,7 +66,7 @@ public static TValue GetValue(this IObservedChange /// The sender type. /// The value type. - /// + /// /// The change notification stream to get the values of. /// /// @@ -66,9 +74,9 @@ public static TValue GetValue(this IObservedChange public static IObservable Value( - this IObservable> @this) + this IObservable> item) { - return @this.Select(GetValue); + return item.Select(GetValue); } /// @@ -78,7 +86,7 @@ public static IObservable Value( /// /// The sender type. /// The value type. - /// + /// /// The instance to get the value of. /// /// @@ -87,15 +95,15 @@ public static IObservable Value( /// /// True if the entire expression was able to be followed, false otherwise. /// - internal static bool TryGetValue(this IObservedChange @this, out TValue changeValue) + internal static bool TryGetValue(this IObservedChange item, out TValue changeValue) { - if (!Equals(@this.Value, default(TValue))) + if (!Equals(item.Value, default(TValue))) { - changeValue = @this.Value; + changeValue = item.Value; return true; } - return Reflection.TryGetValueForPropertyChain(out changeValue, @this.Sender, @this.Expression.GetExpressionChain()); + return Reflection.TryGetValueForPropertyChain(out changeValue, item.Sender, item.Expression.GetExpressionChain()); } /// @@ -107,7 +115,7 @@ internal static bool TryGetValue(this IObservedChangeThe sender type. /// The value type. /// The target type. - /// + /// /// The instance to use as a /// value to apply. /// @@ -118,11 +126,11 @@ internal static bool TryGetValue(this IObservedChange internal static void SetValueToProperty( - this IObservedChange @this, + this IObservedChange item, TTarget target, Expression> property) { - Reflection.TrySetValueToPropertyChain(target, Reflection.Rewrite(property.Body).GetExpressionChain(), @this.GetValue()); + Reflection.TrySetValueToPropertyChain(target, Reflection.Rewrite(property.Body).GetExpressionChain(), item.GetValue()); } } } diff --git a/src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs b/src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs index 6b7dc518e7..7f8f287335 100644 --- a/src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs +++ b/src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs @@ -45,7 +45,7 @@ static ReactiveNotifyPropertyChangedMixin() /// /// The sender type. /// The value type. - /// The source object to observe properties of. + /// The source object to observe properties of. /// An Expression representing the property (i.e. /// 'x => x.SomeProperty.SomeOtherProperty'. /// If True, the Observable will notify @@ -55,14 +55,19 @@ static ReactiveNotifyPropertyChangedMixin() /// An Observable representing the property change /// notifications for the given property. public static IObservable> ObservableForProperty( - this TSender @this, + this TSender item, Expression> property, bool beforeChange = false, bool skipInitial = true) { - if (@this == null) + if (item == null) { - throw new ArgumentNullException(nameof(@this)); + throw new ArgumentNullException(nameof(item)); + } + + if (property == null) + { + throw new ArgumentNullException(nameof(property)); } /* x => x.Foo.Bar.Baz; @@ -81,7 +86,7 @@ public static IObservable> ObservableForPropert */ return SubscribeToExpressionChain( - @this, + item, property.Body, beforeChange, skipInitial); @@ -96,7 +101,7 @@ public static IObservable> ObservableForPropert /// The sender type. /// The value type. /// The return value type. - /// The source object to observe properties of. + /// The source object to observe properties of. /// An Expression representing the property (i.e. /// 'x => x.SomeProperty'. /// A Select function that will be run on each @@ -106,14 +111,18 @@ public static IObservable> ObservableForPropert /// An Observable representing the property change /// notifications for the given property. public static IObservable ObservableForProperty( - this TSender @this, + this TSender item, Expression> property, Func selector, bool beforeChange = false) where TSender : class { - Contract.Requires(selector != null); - return @this.ObservableForProperty(property, beforeChange).Select(x => selector(x.Value)); + if (selector == null) + { + throw new ArgumentNullException(nameof(property)); + } + + return item.ObservableForProperty(property, beforeChange).Select(x => selector(x.Value)); } /// diff --git a/src/ReactiveUI/ObservableForProperty/INPCObservableForProperty.cs b/src/ReactiveUI/ObservableForProperty/INPCObservableForProperty.cs index c6422029ba..56f3588d59 100644 --- a/src/ReactiveUI/ObservableForProperty/INPCObservableForProperty.cs +++ b/src/ReactiveUI/ObservableForProperty/INPCObservableForProperty.cs @@ -28,6 +28,11 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang [SuppressMessage("Roslynator", "RCS1211", Justification = "Neater with else clause.")] public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged, bool suppressWarnings = false) { + if (expression == null) + { + throw new ArgumentNullException(nameof(expression)); + } + var before = sender as INotifyPropertyChanging; var after = sender as INotifyPropertyChanged; diff --git a/src/ReactiveUI/ObservableForProperty/IROObservableForProperty.cs b/src/ReactiveUI/ObservableForProperty/IROObservableForProperty.cs index db7ebc634e..f0ba606cc7 100644 --- a/src/ReactiveUI/ObservableForProperty/IROObservableForProperty.cs +++ b/src/ReactiveUI/ObservableForProperty/IROObservableForProperty.cs @@ -27,6 +27,11 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang /// public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { + if (expression == null) + { + throw new ArgumentNullException(nameof(expression)); + } + var iro = sender as IReactiveObject; if (iro == null) { diff --git a/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs b/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs index 2ba461032a..33864132f7 100644 --- a/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs +++ b/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs @@ -30,6 +30,11 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang /// public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { + if (sender == null) + { + throw new ArgumentNullException(nameof(sender)); + } + var type = sender.GetType(); if (!hasWarned.ContainsKey((type, propertyName)) && !suppressWarnings) { diff --git a/src/ReactiveUI/Platforms/android/AndroidObservableForWidgets.cs b/src/ReactiveUI/Platforms/android/AndroidObservableForWidgets.cs index 14cc0d6167..77f64447b1 100644 --- a/src/ReactiveUI/Platforms/android/AndroidObservableForWidgets.cs +++ b/src/ReactiveUI/Platforms/android/AndroidObservableForWidgets.cs @@ -54,6 +54,11 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang /// public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { + if (sender == null) + { + throw new ArgumentNullException(nameof(sender)); + } + var type = sender.GetType(); var tableItem = dispatchTable.Keys.First(x => x.Item1.IsAssignableFrom(type) && x.Item2 == propertyName); diff --git a/src/ReactiveUI/Platforms/android/AutoSuspendHelper.cs b/src/ReactiveUI/Platforms/android/AutoSuspendHelper.cs index 6e90b9f31a..903b703f79 100644 --- a/src/ReactiveUI/Platforms/android/AutoSuspendHelper.cs +++ b/src/ReactiveUI/Platforms/android/AutoSuspendHelper.cs @@ -43,6 +43,11 @@ static AutoSuspendHelper() /// The host application. public AutoSuspendHelper(Application hostApplication) { + if (hostApplication == null) + { + throw new ArgumentNullException(nameof(hostApplication)); + } + hostApplication.RegisterActivityLifecycleCallbacks(new ObservableLifecycle(this)); Observable.Merge(_onCreate, _onSaveInstanceState).Subscribe(x => LatestBundle = x); diff --git a/src/ReactiveUI/Platforms/android/ControlFetcherMixin.cs b/src/ReactiveUI/Platforms/android/ControlFetcherMixin.cs index 83bdebb565..51b53e63a9 100644 --- a/src/ReactiveUI/Platforms/android/ControlFetcherMixin.cs +++ b/src/ReactiveUI/Platforms/android/ControlFetcherMixin.cs @@ -53,6 +53,11 @@ public static View GetControl(this View view, Assembly assembly, [CallerMemberNa /// The resolve members. public static void WireUpControls(this ILayoutViewHost layoutHost, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) { + if (layoutHost == null) + { + throw new ArgumentNullException(nameof(layoutHost)); + } + var members = layoutHost.GetWireUpMembers(resolveMembers).ToList(); foreach (var member in members) { @@ -76,6 +81,11 @@ public static void WireUpControls(this ILayoutViewHost layoutHost, ResolveStrate /// The resolve members. public static void WireUpControls(this View view, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) { + if (view == null) + { + throw new ArgumentNullException(nameof(view)); + } + var members = view.GetWireUpMembers(resolveMembers); foreach (var member in members) @@ -105,6 +115,11 @@ public static void WireUpControls(this View view, ResolveStrategy resolveMembers /// The resolve members. public static void WireUpControls(this Fragment fragment, View inflatedView, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) { + if (fragment == null) + { + throw new ArgumentNullException(nameof(fragment)); + } + var members = fragment.GetWireUpMembers(resolveMembers); foreach (var member in members) @@ -132,6 +147,11 @@ public static void WireUpControls(this Fragment fragment, View inflatedView, Res /// The resolve members. public static void WireUpControls(this Activity activity, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) { + if (activity == null) + { + throw new ArgumentNullException(nameof(activity)); + } + var members = activity.GetWireUpMembers(resolveMembers); foreach (var member in members) @@ -179,10 +199,9 @@ internal static string GetResourceName(this PropertyInfo member) private static View GetCachedControl(string propertyName, object rootView, Func fetchControlFromView) { - View ret; var ourViewCache = viewCache.GetOrCreateValue(rootView); - if (ourViewCache.TryGetValue(propertyName, out ret)) + if (ourViewCache.TryGetValue(propertyName, out View ret)) { return ret; } diff --git a/src/ReactiveUI/Platforms/android/FlexibleCommandBinder.cs b/src/ReactiveUI/Platforms/android/FlexibleCommandBinder.cs index 633c392500..3e879f1dbe 100644 --- a/src/ReactiveUI/Platforms/android/FlexibleCommandBinder.cs +++ b/src/ReactiveUI/Platforms/android/FlexibleCommandBinder.cs @@ -48,6 +48,11 @@ public int GetAffinityForObject(Type type, bool hasEventTarget) /// public IDisposable BindCommandToObject(ICommand command, object target, IObservable commandParameter) { + if (target == null) + { + throw new ArgumentNullException(nameof(target)); + } + var type = target.GetType(); var match = _config.Keys @@ -85,6 +90,11 @@ public IDisposable BindCommandToObject(ICommand command, object targ /// Enabled property name. protected static IDisposable ForEvent(ICommand command, object target, IObservable commandParameter, string eventName, PropertyInfo enabledProperty) { + if (command == null) + { + throw new ArgumentNullException(nameof(command)); + } + commandParameter = commandParameter ?? Observable.Return(target); object latestParam = null; diff --git a/src/ReactiveUI/Platforms/android/LayoutViewHost.cs b/src/ReactiveUI/Platforms/android/LayoutViewHost.cs index d3ecbe703c..7285c0ab7e 100644 --- a/src/ReactiveUI/Platforms/android/LayoutViewHost.cs +++ b/src/ReactiveUI/Platforms/android/LayoutViewHost.cs @@ -58,15 +58,15 @@ public View View } _view = value; - _view.SetTag(ViewMixins.ViewHostTag, this.ToJavaObject()); + _view?.SetTag(ViewMixins.ViewHostTag, this.ToJavaObject()); } } /// /// Casts the LayoutViewHost to a View. /// - /// The LayoutViewHost to cast. + /// The LayoutViewHost to cast. [SuppressMessage("Usage", "CA2225: Provide a method named ToView", Justification = "A property is already provided.")] - public static implicit operator View(LayoutViewHost @this) => @this.View; + public static implicit operator View(LayoutViewHost layoutViewHost) => layoutViewHost?.View; } } diff --git a/src/ReactiveUI/Platforms/android/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/android/PlatformRegistrations.cs index 5a820d3cd3..275babbbfb 100644 --- a/src/ReactiveUI/Platforms/android/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/android/PlatformRegistrations.cs @@ -17,6 +17,11 @@ public class PlatformRegistrations : IWantsToRegisterStuff /// public void Register(Action, Type> registerFunction) { + if (registerFunction == null) + { + throw new ArgumentNullException(nameof(registerFunction)); + } + registerFunction(() => new PlatformOperations(), typeof(IPlatformOperations)); registerFunction(() => new ComponentModelTypeConverter(), typeof(IBindingTypeConverter)); registerFunction(() => new AndroidObservableForWidgets(), typeof(ICreatesObservableForProperty)); diff --git a/src/ReactiveUI/Platforms/android/ReactiveListAdapter.cs b/src/ReactiveUI/Platforms/android/ReactiveListAdapter.cs index 0632786ed8..f1c1f39610 100644 --- a/src/ReactiveUI/Platforms/android/ReactiveListAdapter.cs +++ b/src/ReactiveUI/Platforms/android/ReactiveListAdapter.cs @@ -11,10 +11,12 @@ using Android.Widget; using Splat; +#pragma warning disable CA1062 // null check -- legacy. +#pragma warning disable SA1600 // Elements should be documented + namespace ReactiveUI.Legacy { [Obsolete("ReactiveList is no longer supported. We suggest replacing it with DynamicData https://github.com/rolandpheasant/dynamicdata")] -#pragma warning disable SA1600 // Elements should be documented public class ReactiveListAdapter : BaseAdapter, IEnableLogger where TViewModel : class { diff --git a/src/ReactiveUI/Platforms/android/ViewCommandExtensions.cs b/src/ReactiveUI/Platforms/android/ViewCommandExtensions.cs index 7b48f92851..8006fe322b 100644 --- a/src/ReactiveUI/Platforms/android/ViewCommandExtensions.cs +++ b/src/ReactiveUI/Platforms/android/ViewCommandExtensions.cs @@ -21,35 +21,44 @@ public static class ViewCommandExtensions /// /// Binds the command to target view control. /// - /// The this. + /// The command. /// The control. /// A disposable. - public static IDisposable BindToTarget(this ICommand @this, View control) + public static IDisposable BindToTarget(this ICommand command, View control) { + if (command == null) + { + throw new ArgumentNullException(nameof(command)); + } + + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + var ev = new EventHandler((o, e) => { - if (!@this.CanExecute(null)) + if (!command.CanExecute(null)) { return; } - @this.Execute(null); + command.Execute(null); }); var cech = new EventHandler((o, e) => { - var canExecute = @this.CanExecute(null); - control.Enabled = canExecute; + control.Enabled = command.CanExecute(null); }); - @this.CanExecuteChanged += cech; + command.CanExecuteChanged += cech; control.Click += ev; - control.Enabled = @this.CanExecute(null); + control.Enabled = command.CanExecute(null); return Disposable.Create(() => { - @this.CanExecuteChanged -= cech; + command.CanExecuteChanged -= cech; control.Click -= ev; }); } diff --git a/src/ReactiveUI/Platforms/android/ViewMixins.cs b/src/ReactiveUI/Platforms/android/ViewMixins.cs index f4a326c55f..34dfb82ce3 100644 --- a/src/ReactiveUI/Platforms/android/ViewMixins.cs +++ b/src/ReactiveUI/Platforms/android/ViewMixins.cs @@ -19,35 +19,29 @@ public static class ViewMixins /// Tag of the View. /// /// The layout view host type. - /// The view. + /// The view. /// The view host. - public static T GetViewHost(this View @this) + public static T GetViewHost(this View item) where T : ILayoutViewHost { - var tagData = @this.GetTag(ViewHostTag); + var tagData = item?.GetTag(ViewHostTag); if (tagData != null) { return tagData.ToNetObject(); } - return default(T); + return default; } /// /// Gets the ViewHost associated with a given View by accessing the /// Tag of the View. /// - /// The view. + /// The view. /// The view host. - public static ILayoutViewHost GetViewHost(this View @this) + public static ILayoutViewHost GetViewHost(this View item) { - var tagData = @this.GetTag(ViewHostTag); - if (tagData != null) - { - return tagData.ToNetObject(); - } - - return null; + return item?.GetTag(ViewHostTag)?.ToNetObject(); } } } diff --git a/src/ReactiveUI/Platforms/apple-common/Converters/DateTimeNSDateConverter.cs b/src/ReactiveUI/Platforms/apple-common/Converters/DateTimeNSDateConverter.cs index c646e7264e..08f863f031 100644 --- a/src/ReactiveUI/Platforms/apple-common/Converters/DateTimeNSDateConverter.cs +++ b/src/ReactiveUI/Platforms/apple-common/Converters/DateTimeNSDateConverter.cs @@ -27,13 +27,13 @@ public bool TryConvert(object val, Type toType, object conversionHint, out objec { result = null; - if (val.GetType() == typeof(DateTime) && toType == typeof(NSDate)) + if (val?.GetType() == typeof(DateTime) && toType == typeof(NSDate)) { var dt = (DateTime)val; result = (NSDate)dt; return true; } - else if (val.GetType() == typeof(NSDate) && toType == typeof(DateTime)) + else if (val?.GetType() == typeof(NSDate) && toType == typeof(DateTime)) { var dt = (NSDate)val; result = (DateTime)dt; diff --git a/src/ReactiveUI/Platforms/apple-common/ObservableForPropertyBase.cs b/src/ReactiveUI/Platforms/apple-common/ObservableForPropertyBase.cs index 00fdc33be7..69e72301ea 100644 --- a/src/ReactiveUI/Platforms/apple-common/ObservableForPropertyBase.cs +++ b/src/ReactiveUI/Platforms/apple-common/ObservableForPropertyBase.cs @@ -59,6 +59,11 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang /// public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { + if (sender == null) + { + throw new ArgumentNullException(nameof(sender)); + } + if (beforeChanged) { return Observable>.Never; diff --git a/src/ReactiveUI/Platforms/apple-common/TargetActionCommandBinder.cs b/src/ReactiveUI/Platforms/apple-common/TargetActionCommandBinder.cs index c84ecaa026..6e1ec04e4d 100644 --- a/src/ReactiveUI/Platforms/apple-common/TargetActionCommandBinder.cs +++ b/src/ReactiveUI/Platforms/apple-common/TargetActionCommandBinder.cs @@ -67,16 +67,27 @@ public int GetAffinityForObject(Type type, bool hasEventTarget) /// public IDisposable BindCommandToObject(ICommand command, object target, IObservable commandParameter) { + if (command == null) + { + throw new ArgumentNullException(nameof(command)); + } + + if (target == null) + { + throw new ArgumentNullException(nameof(target)); + } + commandParameter = commandParameter ?? Observable.Return(target); object latestParam = null; - var ctlDelegate = new ControlDelegate(x => - { - if (command.CanExecute(latestParam)) + var ctlDelegate = new ControlDelegate( + x => { - command.Execute(latestParam); - } - }) { IsEnabled = command.CanExecute(latestParam) }; + if (command.CanExecute(latestParam)) + { + command.Execute(latestParam); + } + }) { IsEnabled = command.CanExecute(latestParam) }; var sel = new Selector("theAction:"); diff --git a/src/ReactiveUI/Platforms/mac/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/mac/PlatformRegistrations.cs index c65282d0ac..01a3c7b7bf 100644 --- a/src/ReactiveUI/Platforms/mac/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/mac/PlatformRegistrations.cs @@ -17,6 +17,11 @@ public class PlatformRegistrations : IWantsToRegisterStuff /// public void Register(Action, Type> registerFunction) { + if (registerFunction == null) + { + throw new ArgumentNullException(nameof(registerFunction)); + } + registerFunction(() => new PlatformOperations(), typeof(IPlatformOperations)); registerFunction(() => new ComponentModelTypeConverter(), typeof(IBindingTypeConverter)); registerFunction(() => new AppKitObservableForProperty(), typeof(ICreatesObservableForProperty)); diff --git a/src/ReactiveUI/Platforms/net461/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/net461/PlatformRegistrations.cs index b6574dd1e4..1a982f8477 100644 --- a/src/ReactiveUI/Platforms/net461/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/net461/PlatformRegistrations.cs @@ -17,6 +17,11 @@ public class PlatformRegistrations : IWantsToRegisterStuff /// public void Register(Action, Type> registerFunction) { + if (registerFunction == null) + { + throw new ArgumentNullException(nameof(registerFunction)); + } + registerFunction(() => new ComponentModelTypeConverter(), typeof(IBindingTypeConverter)); RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; RxApp.MainThreadScheduler = DefaultScheduler.Instance; diff --git a/src/ReactiveUI/Platforms/tizen/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/tizen/PlatformRegistrations.cs index 322e018bcd..23a2069de4 100644 --- a/src/ReactiveUI/Platforms/tizen/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/tizen/PlatformRegistrations.cs @@ -17,6 +17,11 @@ public class PlatformRegistrations : IWantsToRegisterStuff /// public void Register(Action, Type> registerFunction) { + if (registerFunction == null) + { + throw new ArgumentNullException(nameof(registerFunction)); + } + registerFunction(() => new PlatformOperations(), typeof(IPlatformOperations)); registerFunction(() => new ComponentModelTypeConverter(), typeof(IBindingTypeConverter)); RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; diff --git a/src/ReactiveUI/Platforms/uap/AutoSuspendHelper.cs b/src/ReactiveUI/Platforms/uap/AutoSuspendHelper.cs index a2fb23f208..366bbfb33a 100644 --- a/src/ReactiveUI/Platforms/uap/AutoSuspendHelper.cs +++ b/src/ReactiveUI/Platforms/uap/AutoSuspendHelper.cs @@ -36,6 +36,11 @@ public class AutoSuspendHelper : IEnableLogger, IDisposable /// The application. public AutoSuspendHelper(Application app) { + if (app == null) + { + throw new ArgumentNullException(nameof(app)); + } + Reflection.ThrowIfMethodsNotOverloaded("AutoSuspendHelper", app, "OnLaunched"); var launchNew = new[] { ApplicationExecutionState.ClosedByUser, ApplicationExecutionState.NotRunning, }; diff --git a/src/ReactiveUI/Platforms/uap/DependencyObjectObservableForProperty.cs b/src/ReactiveUI/Platforms/uap/DependencyObjectObservableForProperty.cs index bdaf2f7631..a2700240b4 100644 --- a/src/ReactiveUI/Platforms/uap/DependencyObjectObservableForProperty.cs +++ b/src/ReactiveUI/Platforms/uap/DependencyObjectObservableForProperty.cs @@ -43,26 +43,21 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang /// public IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) { - Contract.Requires(sender != null && sender is DependencyObject); - var type = sender.GetType(); + if (sender == null) + { + throw new ArgumentNullException(nameof(sender)); + } + var depSender = sender as DependencyObject; if (depSender == null) { - if (!suppressWarnings) - { - this.Log().Warn( - CultureInfo.InvariantCulture, - "Tried to bind DP on a non-DependencyObject. Binding as POCO object", - type.FullName, - propertyName); - } - - var ret = new POCOObservableForProperty(); - return ret.GetNotificationForProperty(sender, expression, propertyName, beforeChanged); + throw new ArgumentException("The sender must be a DependencyObject", nameof(sender)); } - if (beforeChanged == true) + var type = sender.GetType(); + + if (beforeChanged) { this.Log().Warn( CultureInfo.InvariantCulture, diff --git a/src/ReactiveUI/Platforms/uap/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/uap/PlatformRegistrations.cs index 8b681d820c..a7ba71d4d4 100644 --- a/src/ReactiveUI/Platforms/uap/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/uap/PlatformRegistrations.cs @@ -17,6 +17,11 @@ public class PlatformRegistrations : IWantsToRegisterStuff /// public void Register(Action, Type> registerFunction) { + if (registerFunction == null) + { + throw new ArgumentNullException(nameof(registerFunction)); + } + registerFunction(() => new PlatformOperations(), typeof(IPlatformOperations)); registerFunction(() => new ActivationForViewFetcher(), typeof(IActivationForViewFetcher)); registerFunction(() => new DependencyObjectObservableForProperty(), typeof(ICreatesObservableForProperty)); diff --git a/src/ReactiveUI/Platforms/uap/WinRTAppDataDriver.cs b/src/ReactiveUI/Platforms/uap/WinRTAppDataDriver.cs index e4902f4e3d..6821973336 100644 --- a/src/ReactiveUI/Platforms/uap/WinRTAppDataDriver.cs +++ b/src/ReactiveUI/Platforms/uap/WinRTAppDataDriver.cs @@ -45,6 +45,11 @@ public IObservable LoadState() /// public IObservable SaveState(object state) { + if (state == null) + { + throw new ArgumentNullException(nameof(state)); + } + try { var ms = new MemoryStream(); diff --git a/src/ReactiveUI/Platforms/uikit-common/FlexibleCommandBinder.cs b/src/ReactiveUI/Platforms/uikit-common/FlexibleCommandBinder.cs index 360a5b3d9f..204e11072f 100644 --- a/src/ReactiveUI/Platforms/uikit-common/FlexibleCommandBinder.cs +++ b/src/ReactiveUI/Platforms/uikit-common/FlexibleCommandBinder.cs @@ -51,6 +51,11 @@ public int GetAffinityForObject(Type type, bool hasEventTarget) /// public IDisposable BindCommandToObject(ICommand command, object target, IObservable commandParameter) { + if (target == null) + { + throw new ArgumentNullException(nameof(target)); + } + var type = target.GetType(); var match = _config.Keys @@ -86,6 +91,11 @@ public IDisposable BindCommandToObject(ICommand command, object targ /// Enabled Property. protected static IDisposable ForEvent(ICommand command, object target, IObservable commandParameter, string eventName, PropertyInfo enabledProperty) { + if (command == null) + { + throw new ArgumentNullException(nameof(command)); + } + commandParameter = commandParameter ?? Observable.Return(target); object latestParam = null; @@ -126,6 +136,11 @@ protected static IDisposable ForEvent(ICommand command, object target, IObservab /// Returns a disposable. protected static IDisposable ForTargetAction(ICommand command, object target, IObservable commandParameter, PropertyInfo enabledProperty) { + if (command == null) + { + throw new ArgumentNullException(nameof(command)); + } + commandParameter = commandParameter ?? Observable.Return(target); object latestParam = null; diff --git a/src/ReactiveUI/Platforms/uikit-common/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/uikit-common/PlatformRegistrations.cs index beb124f108..bf6671360e 100644 --- a/src/ReactiveUI/Platforms/uikit-common/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/uikit-common/PlatformRegistrations.cs @@ -17,6 +17,11 @@ public class PlatformRegistrations : IWantsToRegisterStuff /// public void Register(Action, Type> registerFunction) { + if (registerFunction == null) + { + throw new ArgumentNullException(nameof(registerFunction)); + } + registerFunction(() => new PlatformOperations(), typeof(IPlatformOperations)); registerFunction(() => new ComponentModelTypeConverter(), typeof(IBindingTypeConverter)); registerFunction(() => new UIKitObservableForProperty(), typeof(ICreatesObservableForProperty)); diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSource.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSource.cs index 6dcd0e1123..8c71f3a36b 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSource.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSource.cs @@ -122,6 +122,11 @@ public IReadOnlyList> Data /// public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath) { + if (indexPath == null) + { + throw new ArgumentNullException(nameof(indexPath)); + } + return _commonSource.GetCell(indexPath); } @@ -140,6 +145,11 @@ public override nint GetItemsCount(UICollectionView collectionView, nint section /// public override void ItemSelected(UICollectionView collectionView, NSIndexPath indexPath) { + if (indexPath == null) + { + throw new ArgumentNullException(nameof(indexPath)); + } + _elementSelected.OnNext(_commonSource.ItemAt(indexPath)); } @@ -150,6 +160,11 @@ public override void ItemSelected(UICollectionView collectionView, NSIndexPath i /// The object at the specified index. public object ItemAt(NSIndexPath indexPath) { + if (indexPath == null) + { + throw new ArgumentNullException(nameof(indexPath)); + } + return _commonSource.ItemAt(indexPath); } diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSourceExtensions.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSourceExtensions.cs index 2bfc20ab3e..a24f944e40 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSourceExtensions.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSourceExtensions.cs @@ -103,6 +103,11 @@ public static IDisposable BindTo( Func, IDisposable> initSource = null) where TCell : UICollectionViewCell { + if (collectionView == null) + { + throw new ArgumentNullException(nameof(collectionView)); + } + var type = typeof(TCell); var cellKey = new NSString(type.ToString()); collectionView.RegisterClassForCell(type, new NSString(cellKey)); diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSource.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSource.cs index 2181649896..30b8e089cc 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSource.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSource.cs @@ -7,10 +7,6 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; -using System.Diagnostics; -using System.Linq; -using System.Reactive.Concurrency; -using System.Reactive.Linq; using System.Reactive.Subjects; using Foundation; using ReactiveUI.Legacy; @@ -175,6 +171,11 @@ public UITableViewRowAnimation ReloadRowsAnimation /// public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) { + if (indexPath == null) + { + throw new ArgumentNullException(nameof(indexPath)); + } + return _commonSource.GetCell(indexPath); } @@ -206,11 +207,24 @@ public override nint RowsInSection(UITableView tableview, nint section) /// public override void RowSelected(UITableView tableView, NSIndexPath indexPath) { + if (indexPath == null) + { + throw new ArgumentNullException(nameof(indexPath)); + } + _elementSelected.OnNext(_commonSource.ItemAt(indexPath)); } /// - public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath) => _commonSource.SectionInfo[indexPath.Section].SizeHint; + public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath) + { + if (indexPath == null) + { + throw new ArgumentNullException(nameof(indexPath)); + } + + return _commonSource.SectionInfo[indexPath.Section].SizeHint; + } /// public override nfloat GetHeightForHeader(UITableView tableView, nint section) @@ -239,35 +253,35 @@ public override nfloat GetHeightForFooter(UITableView tableView, nint section) } var footer = _commonSource.SectionInfo[(int)section].Footer; - return footer == null || footer.View == null ? -1 : footer.Height; + return footer?.View == null ? -1 : footer.Height; } /// public override string TitleForHeader(UITableView tableView, nint section) { var header = _commonSource.SectionInfo[(int)section].Header; - return header == null || header.Title == null ? null : header.Title; + return header?.Title; } /// public override string TitleForFooter(UITableView tableView, nint section) { var footer = _commonSource.SectionInfo[(int)section].Footer; - return footer == null || footer.Title == null ? null : footer.Title; + return footer?.Title; } /// public override UIView GetViewForHeader(UITableView tableView, nint section) { var header = _commonSource.SectionInfo[(int)section].Header; - return header == null || header.View == null ? null : header.View.Invoke(); + return header?.View?.Invoke(); } /// public override UIView GetViewForFooter(UITableView tableView, nint section) { var footer = _commonSource.SectionInfo[(int)section].Footer; - return footer == null || footer.View == null ? null : footer.View.Invoke(); + return footer?.View?.Invoke(); } /// @@ -275,7 +289,15 @@ public override UIView GetViewForFooter(UITableView tableView, nint section) /// /// The index path. /// The item. - public object ItemAt(NSIndexPath indexPath) => _commonSource.ItemAt(indexPath); + public object ItemAt(NSIndexPath indexPath) + { + if (indexPath == null) + { + throw new ArgumentNullException(nameof(indexPath)); + } + + return _commonSource.ItemAt(indexPath); + } /// /// When this method is called, an object will not fire change diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSourceExtensions.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSourceExtensions.cs index 319fdae352..b956e8530b 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSourceExtensions.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSourceExtensions.cs @@ -109,6 +109,11 @@ public static IDisposable BindTo( Func, IDisposable> initSource = null) where TCell : UITableViewCell { + if (tableView == null) + { + throw new ArgumentNullException(nameof(tableView)); + } + var type = typeof(TCell); var cellKey = new NSString(type.ToString()); tableView.RegisterClassForCellReuse(type, new NSString(cellKey)); diff --git a/src/ReactiveUI/Platforms/uikit-common/RoutedViewHost.cs b/src/ReactiveUI/Platforms/uikit-common/RoutedViewHost.cs index 2adfcd6270..a073e2e834 100644 --- a/src/ReactiveUI/Platforms/uikit-common/RoutedViewHost.cs +++ b/src/ReactiveUI/Platforms/uikit-common/RoutedViewHost.cs @@ -133,6 +133,11 @@ public IObservable ViewContractObservable /// public override void PushViewController(NSViewController viewController, bool animated) { + if (viewController == null) + { + throw new ArgumentNullException(nameof(viewController)); + } + base.PushViewController(viewController, animated); if (!_routerInstigated) diff --git a/src/ReactiveUI/Platforms/uikit-common/UIControlCommandExtensions.cs b/src/ReactiveUI/Platforms/uikit-common/UIControlCommandExtensions.cs index 0c87a1c2e9..e62abe187d 100644 --- a/src/ReactiveUI/Platforms/uikit-common/UIControlCommandExtensions.cs +++ b/src/ReactiveUI/Platforms/uikit-common/UIControlCommandExtensions.cs @@ -18,37 +18,46 @@ public static class UIControlCommandExtensions /// /// Binds the to target . /// - /// The this. + /// The command to bind to. /// The control. /// The events. /// A disposable. - public static IDisposable BindToTarget(this ICommand @this, UIControl control, UIControlEvent events) + public static IDisposable BindToTarget(this ICommand item, UIControl control, UIControlEvent events) { + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + var ev = new EventHandler((o, e) => { - if (!@this.CanExecute(null)) + if (!item.CanExecute(null)) { return; } - @this.Execute(null); + item.Execute(null); }); var cech = new EventHandler((o, e) => { - var canExecute = @this.CanExecute(null); - control.Enabled = canExecute; + control.Enabled = item.CanExecute(null); }); - @this.CanExecuteChanged += cech; + item.CanExecuteChanged += cech; control.AddTarget(ev, events); - control.Enabled = @this.CanExecute(null); + control.Enabled = item.CanExecute(null); return Disposable.Create(() => { control.RemoveTarget(ev, events); - @this.CanExecuteChanged -= cech; + item.CanExecuteChanged -= cech; }); } } diff --git a/src/ReactiveUI/Platforms/windows-common/AutoDataTemplateBindingHook.cs b/src/ReactiveUI/Platforms/windows-common/AutoDataTemplateBindingHook.cs index 5d0ab431e0..8efa1e98fa 100644 --- a/src/ReactiveUI/Platforms/windows-common/AutoDataTemplateBindingHook.cs +++ b/src/ReactiveUI/Platforms/windows-common/AutoDataTemplateBindingHook.cs @@ -56,15 +56,15 @@ public class AutoDataTemplateBindingHook : IPropertyBindingHook /// public bool ExecuteHook(object source, object target, Func[]> getCurrentViewModelProperties, Func[]> getCurrentViewProperties, BindingDirection direction) { - var viewProperties = getCurrentViewProperties(); - var lastViewProperty = viewProperties.LastOrDefault(); - if (lastViewProperty == null) + if (getCurrentViewProperties == null) { - return true; + throw new ArgumentNullException(nameof(getCurrentViewProperties)); } - var itemsControl = lastViewProperty.Sender as ItemsControl; - if (itemsControl == null) + var viewProperties = getCurrentViewProperties(); + var lastViewProperty = viewProperties.LastOrDefault(); + + if (!(lastViewProperty?.Sender is ItemsControl itemsControl)) { return true; } diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommandMixins.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommandMixins.cs index 37cddaf83b..82d9aab1e1 100644 --- a/src/ReactiveUI/ReactiveCommand/ReactiveCommandMixins.cs +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommandMixins.cs @@ -22,18 +22,18 @@ public static class ReactiveCommandMixins /// the command can be executed, Execute() will be called). /// /// The type. - /// The source observable to pipe into the command. + /// The source observable to pipe into the command. /// The command to be executed. /// An object that when disposes, disconnects the Observable /// from the command. - public static IDisposable InvokeCommand(this IObservable @this, ICommand command) + public static IDisposable InvokeCommand(this IObservable item, ICommand command) { var canExecuteChanged = Observable .FromEventPattern(h => command.CanExecuteChanged += h, h => command.CanExecuteChanged -= h) .Select(_ => Unit.Default) .StartWith(Unit.Default); - return WithLatestFromFixed(@this, canExecuteChanged, (value, _) => InvokeCommandInfo.From(command, command.CanExecute(value), value)) + return WithLatestFromFixed(item, canExecuteChanged, (value, _) => InvokeCommandInfo.From(command, command.CanExecute(value), value)) .Where(ii => ii.CanExecute) .Do(ii => command.Execute(ii.Value)) .Subscribe(); @@ -46,13 +46,18 @@ public static IDisposable InvokeCommand(this IObservable @this, ICommand c /// /// The type. /// The result type. - /// The source observable to pipe into the command. + /// The source observable to pipe into the command. /// The command to be executed. /// An object that when disposes, disconnects the Observable /// from the command. - public static IDisposable InvokeCommand(this IObservable @this, ReactiveCommandBase command) + public static IDisposable InvokeCommand(this IObservable item, ReactiveCommandBase command) { - return WithLatestFromFixed(@this, command.CanExecute, (value, canExecute) => InvokeCommandInfo.From(command, canExecute, value)) + if (command == null) + { + throw new ArgumentNullException(nameof(command)); + } + + return WithLatestFromFixed(item, command.CanExecute, (value, canExecute) => InvokeCommandInfo.From(command, canExecute, value)) .Where(ii => ii.CanExecute) .SelectMany(ii => command.Execute(ii.Value).Catch(Observable.Empty)) .Subscribe(); @@ -65,12 +70,12 @@ public static IDisposable InvokeCommand(this IObservable @this, R /// /// The type. /// The target type. - /// The source observable to pipe into the command. + /// The source observable to pipe into the command. /// The root object which has the Command. /// The expression to reference the Command. /// An object that when disposes, disconnects the Observable /// from the command. - public static IDisposable InvokeCommand(this IObservable @this, TTarget target, Expression> commandProperty) + public static IDisposable InvokeCommand(this IObservable item, TTarget target, Expression> commandProperty) where TTarget : class { var command = target.WhenAnyValue(commandProperty); @@ -81,7 +86,7 @@ public static IDisposable InvokeCommand(this IObservable @this, T .StartWith(c)) .Switch(); - return WithLatestFromFixed(@this, commandCanExecuteChanged, (value, cmd) => InvokeCommandInfo.From(cmd, cmd.CanExecute(value), value)) + return WithLatestFromFixed(item, commandCanExecuteChanged, (value, cmd) => InvokeCommandInfo.From(cmd, cmd.CanExecute(value), value)) .Where(ii => ii.CanExecute) .Do(ii => ii.Command.Execute(ii.Value)) .Subscribe(); @@ -95,12 +100,12 @@ public static IDisposable InvokeCommand(this IObservable @this, T /// The type. /// The result type. /// The target type. - /// The source observable to pipe into the command. + /// The source observable to pipe into the command. /// The root object which has the Command. /// The expression to reference the Command. /// An object that when disposes, disconnects the Observable /// from the command. - public static IDisposable InvokeCommand(this IObservable @this, TTarget target, Expression>> commandProperty) + public static IDisposable InvokeCommand(this IObservable item, TTarget target, Expression>> commandProperty) where TTarget : class { var command = target.WhenAnyValue(commandProperty); @@ -110,7 +115,7 @@ public static IDisposable InvokeCommand(this IObservable .Select(canExecute => InvokeCommandInfo.From(cmd, canExecute, default(T)))) .Switch(); - return WithLatestFromFixed(@this, invocationInfo, (value, ii) => ii.WithValue(value)) + return WithLatestFromFixed(item, invocationInfo, (value, ii) => ii.WithValue(value)) .Where(ii => ii.CanExecute) .SelectMany(ii => ii.Command.Execute(ii.Value).Catch(Observable.Empty)) .Subscribe(); @@ -118,10 +123,10 @@ public static IDisposable InvokeCommand(this IObservable // See https://github.com/Reactive-Extensions/Rx.NET/issues/444 private static IObservable WithLatestFromFixed( - IObservable @this, + IObservable item, IObservable other, Func resultSelector) => - @this + item .Publish( os => other @@ -133,30 +138,21 @@ private static IObservable WithLatestFromFixed( private struct InvokeCommandInfo { - private readonly TCommand _command; - private readonly bool _canExecute; - private readonly TValue _value; - - public InvokeCommandInfo(TCommand command, bool canExecute) - : this(command, canExecute, default(TValue)) - { - } - - public InvokeCommandInfo(TCommand command, bool canExecute, TValue value) + public InvokeCommandInfo(TCommand command, bool canExecute, TValue value = default) { - _command = command; - _canExecute = canExecute; - _value = value; + Command = command; + CanExecute = canExecute; + Value = value; } - public TCommand Command => _command; + public TCommand Command { get; } - public bool CanExecute => _canExecute; + public bool CanExecute { get; } - public TValue Value => _value; + public TValue Value { get; } public InvokeCommandInfo WithValue(TValue value) => - new InvokeCommandInfo(_command, _canExecute, value); + new InvokeCommandInfo(Command, CanExecute, value); } private static class InvokeCommandInfo diff --git a/src/ReactiveUI/ReactiveUI.csproj b/src/ReactiveUI/ReactiveUI.csproj index 4b1fa38a16..0b27f991bf 100644 --- a/src/ReactiveUI/ReactiveUI.csproj +++ b/src/ReactiveUI/ReactiveUI.csproj @@ -6,6 +6,7 @@ ReactiveUI A MVVM framework that integrates with the Reactive Extensions for .NET to create elegant, testable User Interfaces that run on any mobile or desktop platform. Supports Xamarin.iOS, Xamarin.Android, Xamarin.Mac, Xamarin Forms, WPF, Windows Forms, Windows Phone 8.1, Windows Store and Universal Windows Platform (UWP). ReactiveUI + latest diff --git a/src/ReactiveUI/Registration/Registrations.cs b/src/ReactiveUI/Registration/Registrations.cs index 7ff0448e07..8d6f26deba 100644 --- a/src/ReactiveUI/Registration/Registrations.cs +++ b/src/ReactiveUI/Registration/Registrations.cs @@ -20,6 +20,11 @@ public class Registrations : IWantsToRegisterStuff /// public void Register(Action, Type> registerFunction) { + if (registerFunction == null) + { + throw new ArgumentNullException(nameof(registerFunction)); + } + registerFunction(() => new INPCObservableForProperty(), typeof(ICreatesObservableForProperty)); registerFunction(() => new IROObservableForProperty(), typeof(ICreatesObservableForProperty)); registerFunction(() => new POCOObservableForProperty(), typeof(ICreatesObservableForProperty)); diff --git a/src/ReactiveUI/Routing/MessageBus.cs b/src/ReactiveUI/Routing/MessageBus.cs index b4bdcc43fa..fff0ff7704 100644 --- a/src/ReactiveUI/Routing/MessageBus.cs +++ b/src/ReactiveUI/Routing/MessageBus.cs @@ -25,27 +25,21 @@ namespace ReactiveUI /// public class MessageBus : IMessageBus { - private static IMessageBus current = new MessageBus(); + private readonly Dictionary<(Type type, string contract), NotAWeakReference> _messageBus = + new Dictionary<(Type type, string contract), NotAWeakReference>(); - private readonly Dictionary, NotAWeakReference> _messageBus = - new Dictionary, NotAWeakReference>(); - - private readonly IDictionary, IScheduler> _schedulerMappings = - new Dictionary, IScheduler>(); + private readonly IDictionary<(Type type, string contract), IScheduler> _schedulerMappings = + new Dictionary<(Type type, string contract), IScheduler>(); /// /// Gets or sets the Current MessageBus. /// - public static IMessageBus Current - { - get => current; - set => current = value; - } + public static IMessageBus Current { get; set; } = new MessageBus(); /// /// Registers a scheduler for the type, which may be specified at runtime, and the contract. /// - /// If a scheduler is already registered for the specified runtime and contract, this will overrwrite the existing registration. + /// If a scheduler is already registered for the specified runtime and contract, this will overwrite the existing registration. /// The type of the message to listen to. /// The scheduler on which to post the /// notifications for the specified type and contract. CurrentThreadScheduler by default. @@ -54,7 +48,7 @@ public static IMessageBus Current /// only used for one purpose, leave this as null. public void RegisterScheduler(IScheduler scheduler, string contract = null) { - _schedulerMappings[new Tuple(typeof(T), contract)] = scheduler; + _schedulerMappings[(typeof(T), contract)] = scheduler; } /// @@ -121,7 +115,15 @@ public bool IsRegistered(Type type, string contract = null) /// a Disposable. public IDisposable RegisterMessageSource( IObservable source, - string contract = null) => source.Subscribe(SetupSubjectIfNecessary(contract)); + string contract = null) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return source.Subscribe(SetupSubjectIfNecessary(contract)); + } /// /// Sends a single message using the specified Type and contract. @@ -145,8 +147,7 @@ private ISubject SetupSubjectIfNecessary(string contract) WithMessageBus(typeof(T), contract, (mb, tuple) => { - NotAWeakReference subjRef; - if (mb.TryGetValue(tuple, out subjRef) && subjRef.IsAlive) + if (mb.TryGetValue(tuple, out NotAWeakReference subjRef) && subjRef.IsAlive) { ret = (ISubject)subjRef.Target; return; @@ -162,11 +163,11 @@ private ISubject SetupSubjectIfNecessary(string contract) private void WithMessageBus( Type type, string contract, - Action, NotAWeakReference>, Tuple> block) + Action, (Type type, string contract)> block) { lock (_messageBus) { - var tuple = new Tuple(type, contract); + var tuple = (type, contract); block(_messageBus, tuple); if (_messageBus.ContainsKey(tuple) && !_messageBus[tuple].IsAlive) { @@ -175,10 +176,9 @@ private void WithMessageBus( } } - private IScheduler GetScheduler(Tuple tuple) + private IScheduler GetScheduler((Type type, string contract) tuple) { - IScheduler scheduler; - _schedulerMappings.TryGetValue(tuple, out scheduler); + _schedulerMappings.TryGetValue(tuple, out IScheduler scheduler); return scheduler ?? CurrentThreadScheduler.Instance; } } diff --git a/src/ReactiveUI/Routing/RoutableViewModelMixin.cs b/src/ReactiveUI/Routing/RoutableViewModelMixin.cs index fdf5d38a4e..9f907a8543 100644 --- a/src/ReactiveUI/Routing/RoutableViewModelMixin.cs +++ b/src/ReactiveUI/Routing/RoutableViewModelMixin.cs @@ -24,21 +24,26 @@ public static class RoutableViewModelMixin /// while the ViewModel has focus, and cleans up when the ViewModel /// loses focus. /// - /// The ViewModel to watch for focus changes. + /// The ViewModel to watch for focus changes. /// Called when the ViewModel is navigated /// to - return an IDisposable that cleans up all of the things that are /// configured in the method. /// An IDisposable that lets you disconnect the entire process /// earlier than normal. - public static IDisposable WhenNavigatedTo(this IRoutableViewModel @this, Func onNavigatedTo) + public static IDisposable WhenNavigatedTo(this IRoutableViewModel item, Func onNavigatedTo) { + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + IDisposable inner = null; - var router = @this.HostScreen.Router; + var router = item.HostScreen.Router; var navigationStackChanged = router.NavigationChanged.CountChanged(); return navigationStackChanged.Subscribe(_ => { - if (router.GetCurrentViewModel() == @this) + if (router.GetCurrentViewModel() == item) { inner?.Dispose(); @@ -62,21 +67,26 @@ public static IDisposable WhenNavigatedTo(this IRoutableViewModel @this, Func - /// The viewmodel to watch for navigation changes. + /// The viewmodel to watch for navigation changes. /// An IObservable{Unit} that signals when the ViewModel has /// been added or brought to the top of the navigation stack. The /// observable completes when the ViewModel is no longer a part of the /// navigation stack. - public static IObservable WhenNavigatedToObservable(this IRoutableViewModel @this) + public static IObservable WhenNavigatedToObservable(this IRoutableViewModel item) { - var router = @this.HostScreen.Router; + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + var router = item.HostScreen.Router; var navigationStackChanged = router.NavigationChanged.CountChanged(); var itemRemoved = navigationStackChanged - .Where(x => x.Any(change => change.Reason == ListChangeReason.Remove && change.Item.Current == @this)); + .Where(x => x.Any(change => change.Reason == ListChangeReason.Remove && change.Item.Current == item)); return navigationStackChanged - .Where(_ => router.GetCurrentViewModel() == @this) + .Where(_ => router.GetCurrentViewModel() == item) .Select(_ => Unit.Default) .TakeUntil(itemRemoved); } @@ -91,21 +101,26 @@ public static IObservable WhenNavigatedToObservable(this IRoutableViewMode /// the navigation stack and then reused later, you must call this method /// and resubscribe each time it is reused. /// - /// /// The viewmodel to watch for navigation changes. + /// /// The viewmodel to watch for navigation changes. /// An IObservable{Unit} that signals when the ViewModel is no /// longer the topmost ViewModel in the navigation stack. The observable /// completes when the ViewModel is no longer a part of the navigation /// stack. - public static IObservable WhenNavigatingFromObservable(this IRoutableViewModel @this) + public static IObservable WhenNavigatingFromObservable(this IRoutableViewModel item) { - var router = @this.HostScreen.Router; + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + var router = item.HostScreen.Router; var navigationStackChanged = router.NavigationChanged.CountChanged(); bool StackIsCleared(Change change) => change.Reason == ListChangeReason.Clear; - bool ThisViewModelIsRemoved(Change change) => NavigationStackRemovalOperations.Contains(change.Reason) && change.Item.Current == @this; + bool ThisViewModelIsRemoved(Change change) => NavigationStackRemovalOperations.Contains(change.Reason) && change.Item.Current == item; var itemRemoved = navigationStackChanged.Where(x => x.Any(change => StackIsCleared(change) || ThisViewModelIsRemoved(change))); var viewModelsChanged = navigationStackChanged.Scan(new IRoutableViewModel[2], (previous, current) => new[] { previous[1], router.GetCurrentViewModel() }); return viewModelsChanged - .Where(x => x[0] == @this) + .Where(x => x[0] == item) .Select(_ => Unit.Default) .TakeUntil(itemRemoved); } diff --git a/src/ReactiveUI/Routing/RoutingStateMixins.cs b/src/ReactiveUI/Routing/RoutingStateMixins.cs index d8c75c399f..475754dcc2 100644 --- a/src/ReactiveUI/Routing/RoutingStateMixins.cs +++ b/src/ReactiveUI/Routing/RoutingStateMixins.cs @@ -16,22 +16,32 @@ public static class RoutingStateMixins /// Locate the first ViewModel in the stack that matches a certain Type. /// /// The view model type. - /// The routing state. + /// The routing state. /// The matching ViewModel or null if none exists. - public static T FindViewModelInStack(this RoutingState @this) + public static T FindViewModelInStack(this RoutingState item) where T : IRoutableViewModel { - return @this.NavigationStack.Reverse().OfType().FirstOrDefault(); + if (item == null) + { + throw new System.ArgumentNullException(nameof(item)); + } + + return item.NavigationStack.Reverse().OfType().FirstOrDefault(); } /// /// Returns the currently visible ViewModel. /// - /// The routing state. + /// The routing state. /// The matching ViewModel or null if none exists. - public static IRoutableViewModel GetCurrentViewModel(this RoutingState @this) + public static IRoutableViewModel GetCurrentViewModel(this RoutingState item) { - return @this.NavigationStack.LastOrDefault(); + if (item == null) + { + throw new System.ArgumentNullException(nameof(item)); + } + + return item.NavigationStack.LastOrDefault(); } } } diff --git a/src/ReactiveUI/Suspension/SuspensionHostExtensions.cs b/src/ReactiveUI/Suspension/SuspensionHostExtensions.cs index be2be9b3cd..7caaa5c0a5 100644 --- a/src/ReactiveUI/Suspension/SuspensionHostExtensions.cs +++ b/src/ReactiveUI/Suspension/SuspensionHostExtensions.cs @@ -19,12 +19,12 @@ public static class SuspensionHostExtensions /// Observe changes to the AppState of a class derived from ISuspensionHost. /// /// The observable type. - /// The suspension host. + /// The suspension host. /// An observable of the app state. - public static IObservable ObserveAppState(this ISuspensionHost @this) + public static IObservable ObserveAppState(this ISuspensionHost item) where T : class { - return @this.WhenAny(x => x.AppState, x => (T)x.Value) + return item.WhenAny(x => x.AppState, x => (T)x.Value) .Where(x => x != null); } @@ -32,42 +32,52 @@ public static IObservable ObserveAppState(this ISuspensionHost @this) /// Get the current App State of a class derived from ISuspensionHost. /// /// The app state type. - /// The suspenstion host. + /// The suspension host. /// The app state. - public static T GetAppState(this ISuspensionHost @this) + public static T GetAppState(this ISuspensionHost item) { - return (T)@this.AppState; + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + return (T)item.AppState; } /// /// Setup our suspension driver for a class derived off ISuspensionHost interface. /// This will make your suspension host respond to suspend and resume requests. /// - /// The suspension host. + /// The suspension host. /// The suspension driver. /// A disposable which will stop responding to Suspend and Resume requests. - public static IDisposable SetupDefaultSuspendResume(this ISuspensionHost @this, ISuspensionDriver driver = null) + public static IDisposable SetupDefaultSuspendResume(this ISuspensionHost item, ISuspensionDriver driver = null) { + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + var ret = new CompositeDisposable(); driver = driver ?? Locator.Current.GetService(); - ret.Add(@this.ShouldInvalidateState + ret.Add(item.ShouldInvalidateState .SelectMany(_ => driver.InvalidateState()) - .LoggedCatch(@this, Observables.Unit, "Tried to invalidate app state") - .Subscribe(_ => @this.Log().Info("Invalidated app state"))); + .LoggedCatch(item, Observables.Unit, "Tried to invalidate app state") + .Subscribe(_ => item.Log().Info("Invalidated app state"))); - ret.Add(@this.ShouldPersistState - .SelectMany(x => driver.SaveState(@this.AppState).Finally(x.Dispose)) - .LoggedCatch(@this, Observables.Unit, "Tried to persist app state") - .Subscribe(_ => @this.Log().Info("Persisted application state"))); + ret.Add(item.ShouldPersistState + .SelectMany(x => driver.SaveState(item.AppState).Finally(x.Dispose)) + .LoggedCatch(item, Observables.Unit, "Tried to persist app state") + .Subscribe(_ => item.Log().Info("Persisted application state"))); - ret.Add(Observable.Merge(@this.IsResuming, @this.IsLaunchingNew) + ret.Add(Observable.Merge(item.IsResuming, item.IsLaunchingNew) .SelectMany(x => driver.LoadState()) .LoggedCatch( - @this, - Observable.Defer(() => Observable.Return(@this.CreateNewAppState())), + item, + Observable.Defer(() => Observable.Return(item.CreateNewAppState())), "Failed to restore app state from storage, creating from scratch") - .Subscribe(x => @this.AppState = x ?? @this.CreateNewAppState())); + .Subscribe(x => item.AppState = x ?? item.CreateNewAppState())); return ret; } diff --git a/src/analyzers.ruleset b/src/analyzers.ruleset index 3207eb52f3..4761df9d0b 100644 --- a/src/analyzers.ruleset +++ b/src/analyzers.ruleset @@ -21,6 +21,7 @@ + @@ -33,6 +34,7 @@ + diff --git a/src/analyzers.tests.ruleset b/src/analyzers.tests.ruleset index aa1d237117..096ccf609e 100644 --- a/src/analyzers.tests.ruleset +++ b/src/analyzers.tests.ruleset @@ -21,6 +21,7 @@ + @@ -31,12 +32,14 @@ + + From 73b225b00a3b4cf365e51b8a5a9faa29dedc8616 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2019 10:42:28 +1000 Subject: [PATCH 05/19] build(deps): bump FodyHelpers from 5.1.0 to 5.1.1 (#2094) Bumps [FodyHelpers](https://github.com/Fody/Fody) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/Fody/Fody/releases) - [Commits](https://github.com/Fody/Fody/commits) Signed-off-by: dependabot-preview[bot] --- src/ReactiveUI.Fody/ReactiveUI.Fody.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj b/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj index a677b7871f..b920db4fd7 100644 --- a/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj +++ b/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj @@ -8,6 +8,6 @@ - + \ No newline at end of file From ffac20784511770ef303aa261dee585713d9c488 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2019 10:43:57 +1000 Subject: [PATCH 06/19] build(deps): bump FodyPackaging from 5.1.0 to 5.1.1 (#2095) Bumps [FodyPackaging](https://github.com/Fody/Fody) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/Fody/Fody/releases) - [Commits](https://github.com/Fody/Fody/commits) Signed-off-by: dependabot-preview[bot] --- src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj b/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj index 85784b602f..4a0c03965b 100644 --- a/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj +++ b/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj @@ -16,7 +16,7 @@ - + From bc4c4da748c37b4896df161644d1c1d8cfecd5b1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2019 10:51:20 +1000 Subject: [PATCH 07/19] build(deps): bump Fody from 5.1.0 to 5.1.1 (#2096) Bumps [Fody](https://github.com/Fody/Fody) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/Fody/Fody/releases) - [Commits](https://github.com/Fody/Fody/commits) Signed-off-by: dependabot-preview[bot] --- src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj b/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj index 4a0c03965b..5812e6a7bf 100644 --- a/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj +++ b/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj @@ -15,7 +15,7 @@ - + From 6846fb124e4f0b96f5c83f1ece660b733f86bf1d Mon Sep 17 00:00:00 2001 From: Glenn <5834289+glennawatson@users.noreply.github.com> Date: Fri, 28 Jun 2019 16:11:52 +1000 Subject: [PATCH 08/19] housekeeping: Release ReactiveUI 9.18 --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index fcff7f18ca..3ce3ab76a7 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "9.17", + "version": "9.18", "publicReleaseRefSpec": [ "^refs/heads/master$", // we release out of master "^refs/heads/develop$", // we release out of develop From 2089f59e3c779175113401ecf3852b80d3ec95be Mon Sep 17 00:00:00 2001 From: Glenn <5834289+glennawatson@users.noreply.github.com> Date: Sat, 29 Jun 2019 18:08:18 +1000 Subject: [PATCH 09/19] feature: allow uno namespace for uap17.0.17763 (#2099) --- src/ReactiveUI.Uno/ReactiveUI.Uno.csproj | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj b/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj index 7c4e6bc212..1fbacd2ddb 100644 --- a/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj +++ b/src/ReactiveUI.Uno/ReactiveUI.Uno.csproj @@ -1,6 +1,7 @@  netstandard20;MonoAndroid81;MonoAndroid90;Xamarin.iOS10;Xamarin.Mac20 + $(TargetFrameworks);uap10.0.17763 ReactiveUI.Uno Uno Platform specific extensions for ReactiveUI HAS_UNO @@ -12,10 +13,19 @@ HAS_UNO;WASM - + + + + + + + + + + @@ -24,11 +34,6 @@ - - - - - From 8200d378c5794b5a165b47faed6f9b71d7c250df Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2019 22:52:12 +1000 Subject: [PATCH 10/19] build(deps): bump Roslynator.Analyzers from 2.1.0-rc to 2.1.0 (#2111) Bumps [Roslynator.Analyzers](https://github.com/JosefPihrt/Roslynator) from 2.1.0-rc to 2.1.0. - [Release notes](https://github.com/JosefPihrt/Roslynator/releases) - [Changelog](https://github.com/JosefPihrt/Roslynator/blob/master/ChangeLog.md) - [Commits](https://github.com/JosefPihrt/Roslynator/commits/v2.1.0) Signed-off-by: dependabot-preview[bot] --- src/Directory.build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index 9d798df9df..c16c9163d0 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -74,7 +74,7 @@ - + From 312c0f872f0890ed521abeacbbea29a5f40f27d0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2019 22:52:30 +1000 Subject: [PATCH 11/19] build(deps): update Splat requirement from 7.* to 8.* (#2110) Updates the requirements on [Splat](https://github.com/reactiveui/splat) to permit the latest version. - [Release notes](https://github.com/reactiveui/splat/releases) - [Changelog](https://github.com/reactiveui/splat/blob/master/RELEASENOTES.md) - [Commits](https://github.com/reactiveui/splat/compare/7.0.1...8.0.1) Signed-off-by: dependabot-preview[bot] --- src/ReactiveUI/ReactiveUI.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReactiveUI/ReactiveUI.csproj b/src/ReactiveUI/ReactiveUI.csproj index 0b27f991bf..f79a6da9c2 100644 --- a/src/ReactiveUI/ReactiveUI.csproj +++ b/src/ReactiveUI/ReactiveUI.csproj @@ -13,7 +13,7 @@ - + From 1faeeda54eacce35dc16775e0a520a34f9db5ab6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2019 22:53:01 +1000 Subject: [PATCH 12/19] build(deps): bump Microsoft.NET.Test.Sdk from 16.1.1 to 16.2.0 (#2109) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.1.1 to 16.2.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.1.1...v16.2.0) Signed-off-by: dependabot-preview[bot] --- src/Directory.build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index c16c9163d0..9fb94d1410 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -41,7 +41,7 @@ - + From 4d4a6afb4bf912e9d3ea8c13365db7ea3bfa60e7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2019 22:53:19 +1000 Subject: [PATCH 13/19] build(deps): bump Microsoft.SourceLink.GitHub (#2108) Bumps [Microsoft.SourceLink.GitHub](https://github.com/dotnet/sourcelink) from 1.0.0-beta2-19270-01 to 1.0.0-beta2-19324-01. - [Release notes](https://github.com/dotnet/sourcelink/releases) - [Commits](https://github.com/dotnet/sourcelink/compare/1.0.0-beta2-19270-01...1.0.0-beta2-19324-01) Signed-off-by: dependabot-preview[bot] --- src/Directory.build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index 9fb94d1410..fa04fd850e 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -56,7 +56,7 @@ - + From e53ec4cc0b9d8e34410d6e32b207aa06c927a784 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2019 22:53:35 +1000 Subject: [PATCH 14/19] build(deps): update Splat.Autofac requirement from 7.* to 8.* (#2107) Updates the requirements on [Splat.Autofac](https://github.com/reactiveui/splat) to permit the latest version. - [Release notes](https://github.com/reactiveui/splat/releases) - [Changelog](https://github.com/reactiveui/splat/blob/master/RELEASENOTES.md) - [Commits](https://github.com/reactiveui/splat/compare/7.0.1...8.0.1) Signed-off-by: dependabot-preview[bot] --- src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj b/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj index 8696ca0748..088dbf793b 100644 --- a/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj +++ b/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj @@ -8,7 +8,7 @@ - + From 698f0fcf623ad84f4c8b50a7f178ef5e76aad359 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2019 22:53:53 +1000 Subject: [PATCH 15/19] build(deps): bump PublicApiGenerator from 9.0.0 to 9.1.0 (#2104) Bumps [PublicApiGenerator](https://github.com/JakeGinnivan/ApiApprover) from 9.0.0 to 9.1.0. - [Release notes](https://github.com/JakeGinnivan/ApiApprover/releases) - [Commits](https://github.com/JakeGinnivan/ApiApprover/compare/9.0.0...9.1.0) Signed-off-by: dependabot-preview[bot] --- src/Directory.build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index fa04fd850e..2d91afdf3c 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -48,7 +48,7 @@ - + From fbad0f91f0fb886ce2fddfc2f4213b0df40e697d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2019 22:54:45 +1000 Subject: [PATCH 16/19] build(deps): update Splat.Ninject requirement from 7.* to 8.* (#2105) Updates the requirements on [Splat.Ninject](https://github.com/reactiveui/splat) to permit the latest version. - [Release notes](https://github.com/reactiveui/splat/releases) - [Changelog](https://github.com/reactiveui/splat/blob/master/RELEASENOTES.md) - [Commits](https://github.com/reactiveui/splat/compare/7.0.1...8.0.1) Signed-off-by: dependabot-preview[bot] --- src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj b/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj index 088dbf793b..e13fba30f7 100644 --- a/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj +++ b/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj @@ -10,7 +10,7 @@ - + From bb9ed3ecd3c4cd1ce3566a36a8e46f6cffe18df9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2019 22:57:15 +1000 Subject: [PATCH 17/19] build(deps): update Splat.DryIoc requirement from 7.* to 8.* (#2106) Updates the requirements on [Splat.DryIoc](https://github.com/reactiveui/splat) to permit the latest version. - [Release notes](https://github.com/reactiveui/splat/releases) - [Changelog](https://github.com/reactiveui/splat/blob/master/RELEASENOTES.md) - [Commits](https://github.com/reactiveui/splat/compare/7.0.1...8.0.1) Signed-off-by: dependabot-preview[bot] --- src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj b/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj index e13fba30f7..a9bdd79c4c 100644 --- a/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj +++ b/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj @@ -9,7 +9,7 @@ - + From 2fc4e6e323c364bbb245e1199e3d7b50a7766829 Mon Sep 17 00:00:00 2001 From: Trevor Kennedy Date: Mon, 1 Jul 2019 18:42:57 -0400 Subject: [PATCH 18/19] fix: Replace default `MainThreadScheduler` on UAP (#2100) See #2092, #2032 for more info. This replaces the usage of `WaitForDispatcherScheduler` and `CoreDispatcherScheduler` with a scheduler that explicitly dispatches to the main app window regardless of how many windows are created by the application. --- .../Platforms/uap/PlatformRegistrations.cs | 2 +- .../uap/SingleWindowDispatcherScheduler.cs | 203 ++++++++++++++++++ 2 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 src/ReactiveUI/Platforms/uap/SingleWindowDispatcherScheduler.cs diff --git a/src/ReactiveUI/Platforms/uap/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/uap/PlatformRegistrations.cs index a7ba71d4d4..3967ec94fc 100644 --- a/src/ReactiveUI/Platforms/uap/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/uap/PlatformRegistrations.cs @@ -28,7 +28,7 @@ public void Register(Action, Type> registerFunction) registerFunction(() => new BooleanToVisibilityTypeConverter(), typeof(IBindingTypeConverter)); registerFunction(() => new AutoDataTemplateBindingHook(), typeof(IPropertyBindingHook)); RxApp.TaskpoolScheduler = TaskPoolScheduler.Default; - RxApp.MainThreadScheduler = new WaitForDispatcherScheduler(() => CoreDispatcherScheduler.Current); + RxApp.MainThreadScheduler = new SingleWindowDispatcherScheduler(); registerFunction(() => new WinRTAppDataDriver(), typeof(ISuspensionDriver)); } } diff --git a/src/ReactiveUI/Platforms/uap/SingleWindowDispatcherScheduler.cs b/src/ReactiveUI/Platforms/uap/SingleWindowDispatcherScheduler.cs new file mode 100644 index 0000000000..925e6e49a5 --- /dev/null +++ b/src/ReactiveUI/Platforms/uap/SingleWindowDispatcherScheduler.cs @@ -0,0 +1,203 @@ +// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Reactive.PlatformServices; +using System.Runtime.ExceptionServices; +using System.Threading; +using Windows.ApplicationModel.Core; +using Windows.System.Threading; +using Windows.UI.Core; +using Windows.UI.Xaml; + +namespace ReactiveUI +{ + /// + /// This scheduler forces all dispatching to go to the first window of the enumeration. + /// This makes the intended behavior of only supporting single window apps on UWP explicit. + /// If your app creates multiple windows, you should explicitly supply a scheduler which marshals + /// back to that window's . + /// + /// + /// This follows patterns set out in with some minor tweaks + /// for thread-safety and performance. + /// + /// + public class SingleWindowDispatcherScheduler : IScheduler + { + private static CoreDispatcher _dispatcher; + private readonly CoreDispatcherPriority _priority; + + /// + /// Initializes a new instance of the class. + /// + public SingleWindowDispatcherScheduler() + { + if (CoreApplication.Views.Count > 0) + { + Interlocked.CompareExchange(ref _dispatcher, CoreApplication.Views[0].Dispatcher, null); + } + } + + /// + /// Initializes a new instance of the class with an explicit dispatcher. + /// + /// + /// The explicit to use. If you supply a dispatcher here then all instances of + /// will dispatch to that dispatcher from instantiation on. + /// + /// + /// dispatcher - To override the scheduler you must supply a non-null instance of CoreDispatcher. + /// + public SingleWindowDispatcherScheduler(CoreDispatcher dispatcher) + { + _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher), "To override the scheduler you must supply a non-null instance of CoreDispatcher."); + } + + /// + public DateTimeOffset Now => SystemClock.UtcNow; + + /// + public IDisposable Schedule(TState state, Func action) + { + if (action is null) + { + throw new ArgumentNullException(nameof(action)); + } + + if (CoreApplication.Views.Count == 0) + { + return CurrentThreadScheduler.Instance.Schedule(state, action); + } + + return ScheduleOnDispatcherNow(state, action); + } + + /// + public IDisposable Schedule(TState state, TimeSpan dueTime, Func action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + if (CoreApplication.Views.Count == 0) + { + return CurrentThreadScheduler.Instance.Schedule(state, dueTime, action); + } + + var dt = Scheduler.Normalize(dueTime); + if (dt.Ticks == 0) + { + return ScheduleOnDispatcherNow(state, action); + } + + return ScheduleSlow(state, dt, action); + } + + /// + public IDisposable Schedule(TState state, DateTimeOffset dueTime, Func action) + { + if (action is null) + { + throw new ArgumentNullException(nameof(action)); + } + + if (CoreApplication.Views.Count == 0) + { + return CurrentThreadScheduler.Instance.Schedule(state, dueTime, action); + } + + var dt = Scheduler.Normalize(dueTime - DateTimeOffset.Now); + if (dt.Ticks == 0) + { + return ScheduleOnDispatcherNow(state, action); + } + + return ScheduleSlow(state, dt, action); + } + + /// + /// Work-around for the behavior of throwing from "async void" or an not propagating + /// the exception to the event as users have come to expect from + /// previous XAML stacks using Rx. + /// + /// The exception. + private void RaiseUnhandledException(Exception ex) + { + var timer = new DispatcherTimer(); + timer.Interval = TimeSpan.Zero; + timer.Tick += RaiseToDispatcher; + + timer.Start(); + void RaiseToDispatcher(object sender, object e) + { + timer.Stop(); + timer.Tick -= RaiseToDispatcher; + timer = null; + + ExceptionDispatchInfo.Capture(ex).Throw(); + } + } + + private IDisposable ScheduleOnDispatcherNow(TState state, Func action) + { + Interlocked.CompareExchange(ref _dispatcher, CoreApplication.Views[0].Dispatcher, null); + + if (_dispatcher.HasThreadAccess) + { + return action(this, state); + } + + var d = new SingleAssignmentDisposable(); + + var dispatchResult = _dispatcher.RunAsync( + _priority, + () => + { + if (!d.IsDisposed) + { + try + { + d.Disposable = action(this, state); + } + catch (Exception ex) + { + RaiseUnhandledException(ex); + } + } + }); + + return StableCompositeDisposable.Create( + d, + Disposable.Create(() => dispatchResult.Cancel())); + } + + private IDisposable ScheduleSlow(TState state, TimeSpan dueTime, Func action) + { + var d = new MultipleAssignmentDisposable(); + + // Why ThreadPoolTimer? + // -- + // Because, we can't guarantee that DispatcherTimer will dispatch to the correct CoreDispatcher if there are multiple + // so we dispatch explicitly from our own method. + var timer = ThreadPoolTimer.CreateTimer(_ => d.Disposable = ScheduleOnDispatcherNow(state, action), dueTime); + + d.Disposable = Disposable.Create(() => + { + var t = Interlocked.Exchange(ref timer, null); + if (t != null) + { + t.Cancel(); + action = (_, __) => Disposable.Empty; + } + }); + + return d; + } + } +} From ebd58cb9f261cd543fe403cf361b95db92162877 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2019 15:09:44 +1000 Subject: [PATCH 19/19] build(deps): bump Microsoft.SourceLink.GitHub (#2113) Bumps [Microsoft.SourceLink.GitHub](https://github.com/dotnet/sourcelink) from 1.0.0-beta2-19324-01 to 1.0.0-beta2-19351-01. - [Release notes](https://github.com/dotnet/sourcelink/releases) - [Commits](https://github.com/dotnet/sourcelink/compare/1.0.0-beta2-19324-01...1.0.0-beta2-19351-01) Signed-off-by: dependabot-preview[bot] --- src/Directory.build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index 2d91afdf3c..68f49d8479 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -56,7 +56,7 @@ - +