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/Directory.build.props b/src/Directory.build.props
index 3be0c1cb2b..dfc3fd9dff 100644
--- a/src/Directory.build.props
+++ b/src/Directory.build.props
@@ -67,7 +67,7 @@
-
+
diff --git a/src/EventBuilder/Platforms/NetCoreAppWpf.cs b/src/EventBuilder/Platforms/NetCoreAppWpf.cs
deleted file mode 100644
index 4222921a0e..0000000000
--- a/src/EventBuilder/Platforms/NetCoreAppWpf.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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.IO;
-using System.Threading.Tasks;
-using EventBuilder.NuGet;
-using NuGet.Frameworks;
-using NuGet.Packaging.Core;
-using NuGet.Versioning;
-using Serilog;
-
-namespace EventBuilder.Platforms
-{
- ///
- /// WPF platform assemblies and events for netcoreapp3.0 and above.
- ///
- ///
- public class NetCoreAppWPF : BasePlatform
- {
- private readonly PackageIdentity[] _packageNames = new[]
- {
- new PackageIdentity("Microsoft.WindowsDesktop.App", new NuGetVersion("3.0.0-alpha-27128-4")),
- new PackageIdentity("NetStandard.Library", new NuGetVersion("2.0.0")),
- };
-
- ///
- public override AutoPlatform Platform => AutoPlatform.NetCoreAppWPF;
-
- ///
- public async override Task Extract()
- {
- var packageUnzipPath = await NuGetPackageHelper.InstallPackages(_packageNames, Platform).ConfigureAwait(false);
-
- Log.Debug($"Package unzip path is {packageUnzipPath}");
-
- Assemblies.AddRange(Directory.GetFiles(packageUnzipPath, "WindowsBase.dll", SearchOption.AllDirectories));
- Assemblies.AddRange(Directory.GetFiles(packageUnzipPath, "PresentationCore.dll", SearchOption.AllDirectories));
- Assemblies.AddRange(Directory.GetFiles(packageUnzipPath, "PresentationFramework.dll", SearchOption.AllDirectories));
-
- foreach (var directory in Directory.GetDirectories(packageUnzipPath, "*.*", SearchOption.AllDirectories))
- {
- CecilSearchDirectories.Add(directory);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj b/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj
index 233f49d956..b7b9060957 100644
--- a/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj
+++ b/src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj
@@ -14,8 +14,8 @@
-
-
+
+
diff --git a/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj b/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj
index b98b86f887..6a4dbea8c0 100644
--- a/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj
+++ b/src/ReactiveUI.Fody/ReactiveUI.Fody.csproj
@@ -7,6 +7,6 @@
-
+
\ No newline at end of file
diff --git a/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj b/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj
index c5d3abf429..cb61ef3b56 100644
--- a/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj
+++ b/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt
index 8098ad958a..c6f379e389 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 b1b2ded040..8451c49469 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..15883551a8
--- /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