From 5fdbd63d07bd2d8c0851e084664f74dad19c6d2c 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] 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")]