diff --git a/Rx.NET/Integration/WindowsDesktopTests/WindowsDesktopTests.csproj b/Rx.NET/Integration/WindowsDesktopTests/WindowsDesktopTests.csproj
index 74cc7e587..5ca0032de 100644
--- a/Rx.NET/Integration/WindowsDesktopTests/WindowsDesktopTests.csproj
+++ b/Rx.NET/Integration/WindowsDesktopTests/WindowsDesktopTests.csproj
@@ -27,8 +27,10 @@
-
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/Rx.NET/Source/CodeCoverage.runsettings b/Rx.NET/Source/CodeCoverage.runsettings
index 66e63003b..a5298935e 100644
--- a/Rx.NET/Source/CodeCoverage.runsettings
+++ b/Rx.NET/Source/CodeCoverage.runsettings
@@ -5,7 +5,8 @@
Workaround for failure to run tests on .NET FX on v3.3.1 MSTest packages
-->
- True
+ True
+ STA
diff --git a/Rx.NET/Source/System.Reactive.sln b/Rx.NET/Source/System.Reactive.sln
index 18ed2a45f..389fa7c95 100644
--- a/Rx.NET/Source/System.Reactive.sln
+++ b/Rx.NET/Source/System.Reactive.sln
@@ -66,6 +66,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks.System.Reactive"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{C8E480ED-B592-4341-A0C9-183E822EB6B9}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FrameworkIntegrations", "FrameworkIntegrations", "{1873A545-87AA-4C22-BA1A-8A6602F65749}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Reactive.For.WindowsForms", "src\System.Reactive.For.WindowsForms\System.Reactive.For.WindowsForms.csproj", "{DCD4D74D-FEED-4606-B9C6-6669A5D70B85}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Reactive.For.Wpf", "src\System.Reactive.For.Wpf\System.Reactive.For.Wpf.csproj", "{D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Reactive.For.Uwp", "src\System.Reactive.For.Uwp\System.Reactive.For.Uwp.csproj", "{C3FC6098-AC7F-4825-B292-4049BC6FC76E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Reactive.For.WindowsRuntime", "src\System.Reactive.For.WindowsRuntime\System.Reactive.For.WindowsRuntime.csproj", "{EB27A089-56EC-4621-BF88-E3B0DA8E6557}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug (with UWP)|Any CPU = Debug (with UWP)|Any CPU
@@ -498,6 +508,94 @@ Global
{5C7906F6-232E-455C-9269-68EF84F393C9}.Release|x64.Build.0 = Rx.net 4.0|Any CPU
{5C7906F6-232E-455C-9269-68EF84F393C9}.Release|x86.ActiveCfg = Rx.net 4.0|Any CPU
{5C7906F6-232E-455C-9269-68EF84F393C9}.Release|x86.Build.0 = Rx.net 4.0|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug (with UWP)|Any CPU.ActiveCfg = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug (with UWP)|Any CPU.Build.0 = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug (with UWP)|ARM.ActiveCfg = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug (with UWP)|ARM.Build.0 = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug (with UWP)|x64.ActiveCfg = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug (with UWP)|x64.Build.0 = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug (with UWP)|x86.ActiveCfg = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug (with UWP)|x86.Build.0 = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug|ARM.Build.0 = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug|x64.Build.0 = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Debug|x86.Build.0 = Debug|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Release|ARM.ActiveCfg = Release|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Release|ARM.Build.0 = Release|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Release|x64.ActiveCfg = Release|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Release|x64.Build.0 = Release|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Release|x86.ActiveCfg = Release|Any CPU
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85}.Release|x86.Build.0 = Release|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug (with UWP)|Any CPU.ActiveCfg = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug (with UWP)|Any CPU.Build.0 = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug (with UWP)|ARM.ActiveCfg = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug (with UWP)|ARM.Build.0 = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug (with UWP)|x64.ActiveCfg = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug (with UWP)|x64.Build.0 = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug (with UWP)|x86.ActiveCfg = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug (with UWP)|x86.Build.0 = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug|ARM.Build.0 = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug|x64.Build.0 = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Debug|x86.Build.0 = Debug|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Release|ARM.ActiveCfg = Release|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Release|ARM.Build.0 = Release|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Release|x64.ActiveCfg = Release|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Release|x64.Build.0 = Release|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Release|x86.ActiveCfg = Release|Any CPU
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4}.Release|x86.Build.0 = Release|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Debug (with UWP)|Any CPU.ActiveCfg = Debug|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Debug (with UWP)|ARM.ActiveCfg = Debug|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Debug (with UWP)|x64.ActiveCfg = Debug|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Debug (with UWP)|x86.ActiveCfg = Debug|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Debug|ARM.Build.0 = Debug|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Debug|x64.Build.0 = Debug|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Debug|x86.Build.0 = Debug|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Release|ARM.ActiveCfg = Release|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Release|ARM.Build.0 = Release|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Release|x64.ActiveCfg = Release|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Release|x64.Build.0 = Release|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Release|x86.ActiveCfg = Release|Any CPU
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E}.Release|x86.Build.0 = Release|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Debug (with UWP)|Any CPU.ActiveCfg = Debug|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Debug (with UWP)|ARM.ActiveCfg = Debug|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Debug (with UWP)|x64.ActiveCfg = Debug|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Debug (with UWP)|x86.ActiveCfg = Debug|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Debug|ARM.Build.0 = Debug|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Debug|x64.Build.0 = Debug|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Debug|x86.Build.0 = Debug|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Release|ARM.ActiveCfg = Release|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Release|ARM.Build.0 = Release|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Release|x64.ActiveCfg = Release|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Release|x64.Build.0 = Release|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Release|x86.ActiveCfg = Release|Any CPU
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -517,6 +615,10 @@ Global
{A31DDC7E-E883-4DBD-8FB8-D7DFC56059F6} = {A0F39260-B8F8-4FCB-9679-0ED917A22BDF}
{01CCDA6D-4D00-4DF2-82B0-359FD5E0CDC6} = {D324579D-CBE6-4867-8980-D7842C7C45A2}
{5C7906F6-232E-455C-9269-68EF84F393C9} = {C8E480ED-B592-4341-A0C9-183E822EB6B9}
+ {DCD4D74D-FEED-4606-B9C6-6669A5D70B85} = {1873A545-87AA-4C22-BA1A-8A6602F65749}
+ {D548C2CA-5C32-4FFF-B9E9-A251971ED7B4} = {1873A545-87AA-4C22-BA1A-8A6602F65749}
+ {C3FC6098-AC7F-4825-B292-4049BC6FC76E} = {1873A545-87AA-4C22-BA1A-8A6602F65749}
+ {EB27A089-56EC-4621-BF88-E3B0DA8E6557} = {1873A545-87AA-4C22-BA1A-8A6602F65749}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2483F58F-A8D6-4FFE-A3C1-10F3A36DBE69}
diff --git a/Rx.NET/Source/src/System.Reactive.For.Uwp/Strings_PlatformServices.Designer.cs b/Rx.NET/Source/src/System.Reactive.For.Uwp/Strings_PlatformServices.Designer.cs
new file mode 100644
index 000000000..6acaa5044
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.Uwp/Strings_PlatformServices.Designer.cs
@@ -0,0 +1,73 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace System.Reactive.Uwp {
+ using System;
+ using System.Reflection;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Strings_PlatformServices {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Strings_PlatformServices() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Reactive.Uwp.Strings_PlatformServices", typeof(Strings_PlatformServices).GetTypeInfo().Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The WinRT thread pool doesn't support creating periodic timers with a period below 1 millisecond..
+ ///
+ internal static string WINRT_NO_SUB1MS_TIMERS {
+ get {
+ return ResourceManager.GetString("WINRT_NO_SUB1MS_TIMERS", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.Uwp/Strings_PlatformServices.resx b/Rx.NET/Source/src/System.Reactive.For.Uwp/Strings_PlatformServices.resx
new file mode 100644
index 000000000..f796d0aee
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.Uwp/Strings_PlatformServices.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ The WinRT thread pool doesn't support creating periodic timers with a period below 1 millisecond.
+
+
\ No newline at end of file
diff --git a/Rx.NET/Source/src/System.Reactive.For.Uwp/Stubs.cs b/Rx.NET/Source/src/System.Reactive.For.Uwp/Stubs.cs
new file mode 100644
index 000000000..35d9cab81
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.Uwp/Stubs.cs
@@ -0,0 +1,12 @@
+// 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 more information.
+
+namespace System.Reactive.Uwp
+{
+ internal static class Stubs
+ {
+ public static readonly Func I = static _ => _;
+ }
+}
+
diff --git a/Rx.NET/Source/src/System.Reactive.For.Uwp/System.Reactive.For.Uwp.csproj b/Rx.NET/Source/src/System.Reactive.For.Uwp/System.Reactive.For.Uwp.csproj
new file mode 100644
index 000000000..d626d7b3e
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.Uwp/System.Reactive.For.Uwp.csproj
@@ -0,0 +1,59 @@
+
+
+
+
+ uap10.0.18362
+ System.Reactive.Uwp
+
+ Rx;Reactive;Extensions;Observable;LINQ;Events;Windows Forms
+ Reactive Extensions (Rx) for .NET
+
+ readme.md
+
+
+
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $([MSBuild]::ValueOrDefault('%(Filename)', '').Replace('.Designer.cs', '.resx'))
+
+
+
+
+
+ True
+ True
+ Strings_PlatformServices.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Strings_PlatformServices.Designer.cs
+
+
+
+
+
+
+
diff --git a/Rx.NET/Source/src/System.Reactive.For.Uwp/ThreadPoolTimerExtensions.cs b/Rx.NET/Source/src/System.Reactive.For.Uwp/ThreadPoolTimerExtensions.cs
new file mode 100644
index 000000000..60a91e462
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.Uwp/ThreadPoolTimerExtensions.cs
@@ -0,0 +1,18 @@
+// 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 more information.
+
+using System.Reactive.Disposables;
+
+using Windows.System.Threading;
+
+namespace System.Reactive.Uwp
+{
+ internal static class ThreadPoolTimerExtensions
+ {
+ public static IDisposable AsDisposable(this ThreadPoolTimer threadPoolTimer)
+ {
+ return Disposable.Create(threadPoolTimer, static t => t!.Cancel());
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.Uwp/UwpThreadPoolScheduler.cs b/Rx.NET/Source/src/System.Reactive.For.Uwp/UwpThreadPoolScheduler.cs
new file mode 100644
index 000000000..4a86a16e4
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.Uwp/UwpThreadPoolScheduler.cs
@@ -0,0 +1,184 @@
+// 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 more information.
+
+using System.Reactive.Concurrency;
+using System.Reactive.WindowsRuntime;
+using Windows.System.Threading;
+
+namespace System.Reactive.Uwp
+{
+ ///
+ /// Schedules units of work on the Windows Runtime thread pool.
+ ///
+ /// Singleton instance of this type exposed through this static property.
+ [CLSCompliant(false)]
+ public sealed class UwpThreadPoolScheduler : LocalScheduler, ISchedulerPeriodic
+ {
+ private static readonly Lazy LazyDefault = new(static () => new UwpThreadPoolScheduler());
+
+ ///
+ /// Constructs a ThreadPoolScheduler that schedules units of work on the Windows ThreadPool.
+ ///
+ public UwpThreadPoolScheduler()
+ {
+ }
+
+ ///
+ /// Constructs a ThreadPoolScheduler that schedules units of work on the Windows ThreadPool with the given priority.
+ ///
+ /// Priority for scheduled units of work.
+ public UwpThreadPoolScheduler(WorkItemPriority priority)
+ {
+ Priority = priority;
+ Options = WorkItemOptions.None;
+ }
+
+ ///
+ /// Constructs a ThreadPoolScheduler that schedules units of work on the Windows ThreadPool with the given priority.
+ ///
+ /// Priority for scheduled units of work.
+ /// Options that configure how work is scheduled.
+ public UwpThreadPoolScheduler(WorkItemPriority priority, WorkItemOptions options)
+ {
+ Priority = priority;
+ Options = options;
+ }
+
+ ///
+ /// Gets the singleton instance of the Windows Runtime thread pool scheduler.
+ ///
+ public static UwpThreadPoolScheduler Instance => LazyDefault.Value;
+
+ ///
+ /// Gets the priority at which work is scheduled.
+ ///
+ public WorkItemPriority Priority { get; }
+
+ ///
+ /// Gets the options that configure how work is scheduled.
+ ///
+ public WorkItemOptions Options { get; }
+
+ ///
+ /// Schedules an action to be executed.
+ ///
+ /// 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 userWorkItem = new UserWorkItem(this, state, action);
+
+ var res = ThreadPool.RunAsync(
+ iaa => userWorkItem.Run(),
+ Priority,
+ Options);
+
+ userWorkItem.CancelQueueDisposable = res.AsDisposable();
+
+ return userWorkItem;
+ }
+
+ ///
+ /// Schedules an action to be executed after dueTime, using a Windows.System.Threading.ThreadPoolTimer 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 userWorkItem = new UserWorkItem(this, state, action);
+
+ var res = ThreadPoolTimer.CreateTimer(
+ tpt => userWorkItem.Run(),
+ dueTime);
+
+ userWorkItem.CancelQueueDisposable = res.AsDisposable();
+
+ return userWorkItem;
+ }
+
+ ///
+ /// Schedules a periodic piece of work, using a Windows.System.Threading.ThreadPoolTimer 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 one millisecond.
+ public IDisposable SchedulePeriodic(TState state, TimeSpan period, Func action)
+ {
+ //
+ // The WinRT thread pool is based on the Win32 thread pool and cannot handle
+ // sub-1ms resolution. When passing a lower period, we get single-shot
+ // timer behavior instead. See MSDN documentation for CreatePeriodicTimer
+ // for more information.
+ //
+ if (period < TimeSpan.FromMilliseconds(1))
+ throw new ArgumentOutOfRangeException(nameof(period), Strings_PlatformServices.WINRT_NO_SUB1MS_TIMERS);
+ if (action == null)
+ throw new ArgumentNullException(nameof(action));
+
+ return new PeriodicallyScheduledWorkItem(state, period, action);
+ }
+
+ private sealed class PeriodicallyScheduledWorkItem : IDisposable
+ {
+ private TState _state;
+ private Func _action;
+
+ private readonly ThreadPoolTimer _timer;
+ private readonly AsyncLock _gate = new();
+
+ public PeriodicallyScheduledWorkItem(TState state, TimeSpan period, Func action)
+ {
+ _state = state;
+ _action = action;
+
+ _timer = ThreadPoolTimer.CreatePeriodicTimer(
+ Tick,
+ period);
+ }
+
+ private void Tick(ThreadPoolTimer timer)
+ {
+ _gate.Wait(
+ this,
+ static @this => @this._state = @this._action(@this._state));
+ }
+
+ public void Dispose()
+ {
+ _timer.Cancel();
+ _gate.Dispose();
+ _action = Stubs.I;
+ }
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.Uwp/build/NuGet.Readme.md b/Rx.NET/Source/src/System.Reactive.For.Uwp/build/NuGet.Readme.md
new file mode 100644
index 000000000..3ebfd13a5
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.Uwp/build/NuGet.Readme.md
@@ -0,0 +1,43 @@
+# UWP Support for Rx.NET (Reactive Extensions for .NET)
+
+This library provides UWP (Universal Windows Platform) support for the Reactive Extensions for .NET (Rx.NET).
+
+See the main Rx.NET package at https://www.nuget.org/packages/System.Reactive for more information about Rx.NET.
+
+## Rx.NET and UI Frameworks
+
+Up as far as Rx.NET v6.0, UI framework support has been built directly into the main `System.Reactive` package.
+Unfortunately, this has caused problems since support for WPF and Windows Forms was added in .NET Core 3.1.
+Because .NET Core 3.1, and all subsequent versions of .NET have supported cross-platform use, WPF and Windows
+Forms are not universally available. Rx.NET used to make WPF and Windows Forms support if you targetted a
+sufficiently recent version of Windows in your application TFM. But this turns out to cause problems in
+some deployment models, adding as much as 90MB to the deployable size of an application.
+
+Consequently, starting in Rx.NET v7.0 we are moving all UI-framework-specific types, and also platform-specific
+types out into separate packages.
+
+## Features
+
+This package defines one public type, `UwpThreadPoolScheduler`. It provides a replacement for deprecated functionality on the
+`ThreadPoolScheduler` in the `uap10.0.18362` target of `System.Reactive`. In a future version of Rx.NET, the UWP-specific
+target will be removed the main `System.Reactive` package, at which point UWP applications will use the `netstandard2.0`
+target, and will get only the common `ThreadPoolScheduler` functionality available on all platforms.
+
+The specialized `ThreadPoolScheduler` currently still available in the `uap10.0.18362` target of `System.Reactive` is
+very nearly the same as the common one. It has some extra properties and constructors providing access to some features
+specific to the `Windows.System.Threading` thread pool (which is available only on UWP). It allows the use of
+`WorkItemPriority` and `WorkItemOptions`. These features are now deprecated on the `ThreadPoolScheduler` in the
+`uap10.0.18362` target of `System.Reactive`, making its remaining non-deprecated surface area the same as in
+other targets.
+
+Applications still needing access to the UWP-specific functionality can switch to the `UwpThreadPoolScheduler`
+in this library.
+
+
+## Windows Runtime Support
+
+The `uap10.0.18362` target of `System.Reactive` also offers support for some Windows Runtime types, such as `IAsyncOperation`,
+that are often used in UWP applications. Those types are also available outside of UWP, so they are also available in
+the Windows-specific .NET target, but they have also been deprecated, as part of the drive to remove all platform- and
+UI-framework-specific code from `System.Reactive`. You can find support for those in the `System.Reactive.Integration.WindowsRuntime`
+NuGet package.
\ No newline at end of file
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsForms/ControlScheduler.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsForms/ControlScheduler.cs
new file mode 100644
index 000000000..386636583
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsForms/ControlScheduler.cs
@@ -0,0 +1,213 @@
+// 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 more information.
+
+using System.Reactive.Concurrency;
+using System.Reactive.Disposables;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace System.Reactive.WindowsForms
+{
+ ///
+ /// Represents an object that schedules units of work on the message loop associated with a Windows Forms control.
+ ///
+ public class ControlScheduler : LocalScheduler, ISchedulerPeriodic
+ {
+ private readonly Control _control;
+
+ ///
+ /// Constructs a ControlScheduler that schedules units of work on the message loop associated with the specified Windows Forms control.
+ ///
+ /// Windows Forms control to get the message loop from.
+ /// is null.
+ ///
+ /// This scheduler type is typically used indirectly through the and method overloads that take a Windows Forms control.
+ ///
+ public ControlScheduler(Control control)
+ {
+ _control = control ?? throw new ArgumentNullException(nameof(control));
+ }
+
+ ///
+ /// Gets the control associated with the ControlScheduler.
+ ///
+ public Control Control => _control;
+
+ ///
+ /// Schedules an action to be executed on the message loop associated with the control.
+ ///
+ /// 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));
+ }
+
+ if (_control.IsDisposed)
+ {
+ return Disposable.Empty;
+ }
+
+ var d = new SingleAssignmentDisposable();
+
+ _control.BeginInvoke(new Action(() =>
+ {
+ if (!_control.IsDisposed && !d.IsDisposed)
+ {
+ d.Disposable = action(this, state);
+ }
+ }));
+
+ return d;
+ }
+
+ ///
+ /// Schedules an action to be executed after dueTime on the message loop associated with the control, using a Windows Forms Timer 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);
+ }
+
+ var createTimer = new Func((scheduler1, state1) =>
+ {
+ var d = new MultipleAssignmentDisposable();
+
+ var timer = new System.Windows.Forms.Timer();
+
+ timer.Tick += (s, e) =>
+ {
+ var t = Interlocked.Exchange(ref timer, null);
+ if (t != null)
+ {
+ try
+ {
+ if (!_control.IsDisposed && !d.IsDisposed)
+ {
+ d.Disposable = action(scheduler1, state1);
+ }
+ }
+ finally
+ {
+ t.Stop();
+ action = static (s, t) => Disposable.Empty;
+ }
+ }
+ };
+
+ timer.Interval = (int)dt.TotalMilliseconds;
+ timer.Start();
+
+ d.Disposable = Disposable.Create(() =>
+ {
+ var t = Interlocked.Exchange(ref timer, null);
+ if (t != null)
+ {
+ t.Stop();
+ action = static (s, t) => Disposable.Empty;
+ }
+ });
+
+ return d;
+ });
+
+ //
+ // This check is critical. When creating and enabling a Timer object on another thread than
+ // the UI thread, it won't fire.
+ //
+ if (_control.InvokeRequired)
+ {
+ return Schedule(state, createTimer);
+ }
+ else
+ {
+ return createTimer(this, state);
+ }
+ }
+
+ ///
+ /// Schedules a periodic piece of work on the message loop associated with the control, using a Windows Forms Timer 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 one millisecond.
+ public IDisposable SchedulePeriodic(TState state, TimeSpan period, Func action)
+ {
+ //
+ // Threshold derived from Interval property setter in ndp\fx\src\winforms\managed\system\winforms\Timer.cs.
+ //
+ if (period.TotalMilliseconds < 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(period));
+ }
+
+ if (action == null)
+ {
+ throw new ArgumentNullException(nameof(action));
+ }
+
+ var createTimer = new Func((scheduler1, state1) =>
+ {
+ var timer = new System.Windows.Forms.Timer();
+
+ timer.Tick += (s, e) =>
+ {
+ if (!_control.IsDisposed)
+ {
+ state1 = action(state1);
+ }
+ };
+
+ timer.Interval = (int)period.TotalMilliseconds;
+ timer.Start();
+
+ return Disposable.Create(() =>
+ {
+ var t = Interlocked.Exchange(ref timer, null);
+ if (t != null)
+ {
+ t.Stop();
+ action = static _ => _;
+ }
+ });
+ });
+
+ //
+ // This check is critical. When creating and enabling a Timer object on another thread than
+ // the UI thread, it won't fire.
+ //
+ if (_control.InvokeRequired)
+ {
+ return Schedule(state, createTimer);
+ }
+ else
+ {
+ return createTimer(this, state);
+ }
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsForms/System.Reactive.For.WindowsForms.csproj b/Rx.NET/Source/src/System.Reactive.For.WindowsForms/System.Reactive.For.WindowsForms.csproj
new file mode 100644
index 000000000..8f57f238c
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsForms/System.Reactive.For.WindowsForms.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net472;net6.0-windows
+ true
+
+ Rx;Reactive;Extensions;Observable;LINQ;Events;Windows Forms
+ Reactive Extensions (Rx) for .NET
+
+ readme.md
+
+
+
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsForms/System.Reactive.Linq/WindowsFormsControlObservable.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsForms/System.Reactive.Linq/WindowsFormsControlObservable.cs
new file mode 100644
index 000000000..a62219643
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsForms/System.Reactive.Linq/WindowsFormsControlObservable.cs
@@ -0,0 +1,73 @@
+// 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 more information.
+
+using System.Windows.Forms;
+
+using ControlScheduler = System.Reactive.WindowsForms.ControlScheduler;
+using Synchronization = System.Reactive.Concurrency.Synchronization;
+
+namespace System.Reactive.Linq
+{
+ ///
+ /// Provides a set of static methods for subscribing to IObservables using Windows Forms controls.
+ ///
+ ///
+ ///
+ /// This replaces the obsolete System.Reactive.Linq.ControlObservable class in
+ /// System.Reactive.
+ ///
+ ///
+ public static class WindowsFormsControlObservable
+ {
+ ///
+ /// Wraps the source sequence in order to run its subscription and unsubscription logic on the Windows Forms message loop associated with the specified control.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Windows Forms control whose associated message loop is used to perform subscription and unsubscription actions on.
+ /// The source sequence whose subscriptions and unsubscriptions happen on the Windows Forms message loop associated with the specified control.
+ /// or is null.
+ ///
+ /// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the specified control.
+ /// In order to invoke observer callbacks on the specified control, e.g. to render results in a control, use .
+ ///
+ public static IObservable SubscribeOnWindowFormsControl(this IObservable source, Control control)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (control == null)
+ {
+ throw new ArgumentNullException(nameof(control));
+ }
+
+ return Synchronization.SubscribeOn(source, new ControlScheduler(control));
+ }
+
+ ///
+ /// Wraps the source sequence in order to run its observer callbacks on the Windows Forms message loop associated with the specified control.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Windows Forms control whose associated message loop is used to notify observers on.
+ /// The source sequence whose observations happen on the Windows Forms message loop associated with the specified control.
+ /// or is null.
+ public static IObservable ObserveOnWindowsFormsControl(this IObservable source, Control control)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (control == null)
+ {
+ throw new ArgumentNullException(nameof(control));
+ }
+
+ return Synchronization.ObserveOn(source, new ControlScheduler(control));
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsForms/build/NuGet.Readme.md b/Rx.NET/Source/src/System.Reactive.For.WindowsForms/build/NuGet.Readme.md
new file mode 100644
index 000000000..84d58409b
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsForms/build/NuGet.Readme.md
@@ -0,0 +1,26 @@
+# Windows Forms Support for Rx.NET (Reactive Extensions for .NET)
+
+This library provides Windows Forms support for the Reactive Extensions for .NET (Rx.NET).
+
+See the main Rx.NET package at https://www.nuget.org/packages/System.Reactive for more information about Rx.NET.
+
+## Rx.NET and UI Frameworks
+
+Up as far as Rx.NET v6.0, UI framework support has been built directly into the main `System.Reactive` package.
+Unfortunately, this has caused problems since support for WPF and Windows Forms was added in .NET Core 3.1.
+Because .NET Core 3.1, and all subsequent versions of .NET have supported cross-platform use, WPF and Windows
+Forms are not universally available. Rx.NET used to make WPF and Windows Forms support if you targetted a
+sufficiently recent version of Windows in your application TFM. But this turns out to cause problems in
+some deployment models, adding as much as 90MB to the deployable size of an application.
+
+Consequently, starting in Rx.NET v7.0 we are moving all UI-framework-specific types, and also platform-specific
+types out into separate packages.
+
+## Features
+
+This package provides replacements for two deprecated types in `System.Reactive`:
+
+|Type in `System.Reactive` | Replacement | Purpose |
+|---|---|---|
+|`ControlScheduler` (in `System.Reactive.Concurrency`) | `ControlScheduler` (in `System.Reactive.Integration.WindowsForms`) | Provides a scheduler that schedules work on the UI thread of a Windows Forms application. |
+|`ControlObservable` (in `System.Reactive.Linq`) | `WindowsFormsControlObservable` (in `System.Reactive.Linq`) | Provides a set of extension methods for scheduling work on the UI thread of a Windows Forms application. |
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/AsyncInfoExtensions.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/AsyncInfoExtensions.cs
new file mode 100644
index 000000000..f5145ca37
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/AsyncInfoExtensions.cs
@@ -0,0 +1,17 @@
+// 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 more information.
+
+using System.Reactive.Disposables;
+using Windows.Foundation;
+
+namespace System.Reactive.WindowsRuntime
+{
+ internal static class AsyncInfoExtensions
+ {
+ public static IDisposable AsDisposable(this IAsyncInfo asyncInfo)
+ {
+ return Disposable.Create(asyncInfo, static i => i!.Cancel());
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/AsyncInfoObservableExtensions.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/AsyncInfoObservableExtensions.cs
new file mode 100644
index 000000000..0d9730efc
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/AsyncInfoObservableExtensions.cs
@@ -0,0 +1,276 @@
+// 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 more information.
+
+using System.Reactive.Linq;
+using Windows.Foundation;
+
+namespace System.Reactive.WindowsRuntime
+{
+ ///
+ /// Provides conversions from Windows Runtime asynchronous actions and operations to observable sequences.
+ ///
+ [CLSCompliant(false)]
+ public static class AsyncInfoObservableExtensions
+ {
+ #region IAsyncAction and IAsyncActionWithProgress
+
+ ///
+ /// Converts a Windows Runtime asynchronous action to an observable sequence.
+ /// Each observer subscribed to the resulting observable sequence will be notified about the action's successful or exceptional completion.
+ ///
+ /// Asynchronous action to convert.
+ /// An observable sequence that produces a unit value when the asynchronous action completes, or propagates the exception produced by the asynchronous action.
+ /// is null.
+ public static IObservable ToObservable(this IAsyncAction source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return new AsyncInfoToObservableBridge(
+ source,
+ static (iai, a) => ((IAsyncAction)iai).Completed += new AsyncActionCompletedHandler((iaa, status) => a(iaa, status)),
+ iai => Unit.Default,
+ onProgress: null,
+ progress: null,
+ multiValue: false
+ );
+ }
+
+ ///
+ /// Converts a Windows Runtime asynchronous action to an observable sequence, ignoring its progress notifications.
+ /// Each observer subscribed to the resulting observable sequence will be notified about the action's successful or exceptional completion.
+ ///
+ /// The type of the reported progress objects, which get ignored by this conversion.
+ /// Asynchronous action to convert.
+ /// An observable sequence that produces a unit value when the asynchronous action completes, or propagates the exception produced by the asynchronous action.
+ /// is null.
+ public static IObservable ToObservable(this IAsyncActionWithProgress source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return source.ToObservable_(null);
+ }
+
+ ///
+ /// Converts a Windows Runtime asynchronous action to an observable sequence, reporting its progress through the supplied progress object.
+ /// Each observer subscribed to the resulting observable sequence will be notified about the action's successful or exceptional completion.
+ ///
+ /// The type of the reported progress objects.
+ /// Asynchronous action to convert.
+ /// Progress object to receive progress notifications on.
+ /// An observable sequence that produces a unit value when the asynchronous action completes, or propagates the exception produced by the asynchronous action.
+ /// or is null.
+ public static IObservable ToObservable(this IAsyncActionWithProgress source, IProgress progress)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (progress == null)
+ {
+ throw new ArgumentNullException(nameof(progress));
+ }
+
+ return source.ToObservable_(progress);
+ }
+
+ ///
+ /// Converts a Windows Runtime asynchronous action to an observable sequence reporting its progress.
+ /// Each observer subscribed to the resulting observable sequence will be notified about the action's successful or exceptional completion.
+ ///
+ /// The type of the reported progress objects.
+ /// Asynchronous action to convert.
+ /// An observable sequence that produces progress values from the asynchronous action and notifies observers about the action's completion.
+ /// is null.
+ public static IObservable ToObservableProgress(this IAsyncActionWithProgress source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return Observable.Create(observer =>
+ {
+ var progress = observer.ToProgress();
+ var src = source.ToObservable_(progress);
+ return src.Subscribe(static _ => { }, observer.OnError, observer.OnCompleted);
+ });
+ }
+
+ private static IObservable ToObservable_(this IAsyncActionWithProgress source, IProgress? progress)
+ {
+ return new AsyncInfoToObservableBridge(
+ source,
+ static (iai, a) => ((IAsyncActionWithProgress)iai).Completed += new AsyncActionWithProgressCompletedHandler((iaa, status) => a(iaa, status)),
+ iai => Unit.Default,
+ static (iai, a) => ((IAsyncActionWithProgress)iai).Progress += new AsyncActionProgressHandler((iap, p) => a(iap, p)),
+ progress,
+ multiValue: false
+ );
+ }
+
+ #endregion
+
+ #region IAsyncOperation and IAsyncOperationWithProgress
+
+ ///
+ /// Converts a Windows Runtime asynchronous operation to an observable sequence reporting its result.
+ /// Each observer subscribed to the resulting observable sequence will be notified about the operation's single result and its successful exceptional completion.
+ ///
+ /// The type of the asynchronous operation's result.
+ /// Asynchronous operation to convert.
+ /// An observable sequence that notifies observers about the asynchronous operation's result value and completion.
+ /// is null.
+ public static IObservable ToObservable(this IAsyncOperation source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return new AsyncInfoToObservableBridge(
+ source,
+ static (iai, a) => ((IAsyncOperation)iai).Completed += new AsyncOperationCompletedHandler((iao, status) => a(iao, status)),
+ static iai => ((IAsyncOperation)iai).GetResults(),
+ onProgress: null,
+ progress: null,
+ multiValue: false
+ );
+ }
+
+ ///
+ /// Converts a Windows Runtime asynchronous operation to an observable sequence reporting its result but ignoring its progress notifications.
+ /// Each observer subscribed to the resulting observable sequence will be notified about the operations's single result and its successful or exceptional completion.
+ ///
+ /// The type of the asynchronous operation's result.
+ /// The type of the reported progress objects, which get ignored by this conversion.
+ /// Asynchronous action to convert.
+ /// An observable sequence that notifies observers about the asynchronous operation's result value and completion.
+ /// is null.
+ public static IObservable ToObservable(this IAsyncOperationWithProgress source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return source.ToObservable_(null, false);
+ }
+
+ ///
+ /// Converts a Windows Runtime asynchronous operation to an observable sequence reporting its result and reporting its progress through the supplied progress object.
+ /// Each observer subscribed to the resulting observable sequence will be notified about the operations's single result and its successful or exceptional completion.
+ ///
+ /// The type of the asynchronous operation's result.
+ /// The type of the reported progress objects.
+ /// Asynchronous action to convert.
+ /// Progress object to receive progress notifications on.
+ /// An observable sequence that notifies observers about the asynchronous operation's result value and completion.
+ /// or is null.
+ public static IObservable ToObservable(this IAsyncOperationWithProgress source, IProgress progress)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (progress == null)
+ {
+ throw new ArgumentNullException(nameof(progress));
+ }
+
+ return source.ToObservable_(progress, false);
+ }
+
+ ///
+ /// Converts a Windows Runtime asynchronous operation to an observable sequence reporting its progress but ignoring its result value.
+ /// Each observer subscribed to the resulting observable sequence will be notified about the action's successful or exceptional completion.
+ ///
+ /// The type of the asynchronous operation's result, which gets ignored by this conversion.
+ /// The type of the reported progress objects.
+ /// Asynchronous action to convert.
+ /// An observable sequence that produces progress values from the asynchronous operation and notifies observers about the operations's completion.
+ /// is null.
+ public static IObservable ToObservableProgress(this IAsyncOperationWithProgress source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return Observable.Create(observer =>
+ {
+ var progress = observer.ToProgress();
+ var src = source.ToObservable_(progress, false);
+ return src.Subscribe(static _ => { }, observer.OnError, observer.OnCompleted);
+ });
+ }
+
+ ///
+ /// Converts a Windows Runtime asynchronous operation to an observable sequence by retrieving the operation's results whenever progress is reported and when the operation completes.
+ /// Each observer subscribed to the resulting observable sequence will be notified about the action's successful or exceptional completion.
+ ///
+ /// The type of the asynchronous operation's result.
+ /// The type of the reported progress objects, which are used internally in the conversion but aren't exposed.
+ /// Asynchronous operation to convert.
+ /// An observable sequence that notifies observers about the asynchronous operation's (incremental) result value(s) and completion.
+ /// This conversion can be used with Windows Runtime APIs that support incremental retrieval of results during an asynchronous operation's execution.
+ /// is null.
+ public static IObservable ToObservableMultiple(this IAsyncOperationWithProgress source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return source.ToObservable_(null, true);
+ }
+
+ ///
+ /// Converts a Windows Runtime asynchronous operation to an observable sequence by retrieving the operation's results whenever progress is reported and when the operation completes. The operation's progress is reported through the supplied progress object.
+ /// Each observer subscribed to the resulting observable sequence will be notified about the action's successful or exceptional completion.
+ ///
+ /// The type of the asynchronous operation's result.
+ /// The type of the reported progress objects.
+ /// Asynchronous operation to convert.
+ /// Progress object to receive progress notifications on.
+ /// An observable sequence that notifies observers about the asynchronous operation's (incremental) result value(s) and completion.
+ /// This conversion can be used with Windows Runtime APIs that support incremental retrieval of results during an asynchronous operation's execution.
+ /// or is null.
+ public static IObservable ToObservableMultiple(this IAsyncOperationWithProgress source, IProgress progress)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (progress == null)
+ {
+ throw new ArgumentNullException(nameof(progress));
+ }
+
+ return source.ToObservable_(progress, true);
+ }
+
+ private static IObservable ToObservable_(this IAsyncOperationWithProgress source, IProgress? progress, bool supportsMultiple)
+ {
+ return new AsyncInfoToObservableBridge(
+ source,
+ static (iai, a) => ((IAsyncOperationWithProgress)iai).Completed += new AsyncOperationWithProgressCompletedHandler((iao, status) => a(iao, status)),
+ iai => ((IAsyncOperationWithProgress)iai).GetResults(),
+ static (iai, a) => ((IAsyncOperationWithProgress)iai).Progress += new AsyncOperationProgressHandler((iap, p) => a(iap, p)),
+ progress,
+ supportsMultiple
+ );
+ }
+
+ #endregion
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/AsyncInfoToObservableBridge.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/AsyncInfoToObservableBridge.cs
new file mode 100644
index 000000000..2de450787
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/AsyncInfoToObservableBridge.cs
@@ -0,0 +1,108 @@
+// 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 more information.
+
+using System.Reactive.Subjects;
+using Windows.Foundation;
+
+namespace System.Reactive.WindowsRuntime
+{
+ internal sealed class AsyncInfoToObservableBridge : ObservableBase
+ {
+ private readonly Action> _onCompleted;
+ private readonly Func _getResult;
+ private readonly AsyncSubject _subject;
+
+ public AsyncInfoToObservableBridge(IAsyncInfo info, Action> onCompleted, Func getResult, Action>? onProgress, IProgress? progress, bool multiValue)
+ {
+ _onCompleted = onCompleted;
+ _getResult = getResult;
+
+ _subject = new AsyncSubject();
+
+ onProgress?.Invoke(info, (iai, p) =>
+ {
+ if (multiValue && getResult != null)
+ {
+ _subject.OnNext(getResult(iai));
+ }
+
+ progress?.Report(p);
+ });
+
+ Done(info, info.Status, true);
+ }
+
+ private void Done(IAsyncInfo info, AsyncStatus status, bool initial)
+ {
+ var error = default(Exception);
+ var result = default(TResult);
+
+ //
+ // Initial interactions with the IAsyncInfo object. Those could fail, which indicates
+ // a rogue implementation. Failure is just propagated out.
+ //
+ switch (status)
+ {
+ case AsyncStatus.Error:
+ error = info.ErrorCode;
+ if (error == null)
+ {
+ throw new InvalidOperationException("The asynchronous operation failed with a null error code.");
+ }
+
+ break;
+ case AsyncStatus.Canceled:
+ error = new OperationCanceledException();
+ break;
+ case AsyncStatus.Completed:
+ if (_getResult != null)
+ {
+ result = _getResult(info);
+ }
+
+ break;
+ default:
+ if (!initial)
+ {
+ throw new InvalidOperationException("The asynchronous operation completed unexpectedly.");
+ }
+
+ _onCompleted(info, (iai, s) => Done(iai, s, false));
+ return;
+ }
+
+ //
+ // Close as early as possible, before running continuations which could fail. In case of
+ // failure above, we don't close out the object in order to allow for debugging of the
+ // rogue implementation without losing state prematurely. Notice _getResult is merely
+ // an indirect call to the appropriate GetResults method, which is not supposed to throw.
+ // Instead, an Error status should be returned.
+ //
+ info.Close();
+
+ //
+ // Now we run the continuations, which could take a long time. Failure here is catastrophic
+ // and under control of the upstream subscriber.
+ //
+ if (error != null)
+ {
+ _subject.OnError(error);
+ }
+ else
+ {
+ if (_getResult != null)
+ {
+ _subject.OnNext(result!); // NB: Has been assigned in switch statement above.
+ }
+
+ _subject.OnCompleted();
+ }
+ }
+
+ protected override IDisposable SubscribeCore(IObserver observer)
+ {
+ return _subject.Subscribe(observer);
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/CoreDispatcherScheduler.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/CoreDispatcherScheduler.cs
new file mode 100644
index 000000000..b2e441d1c
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/CoreDispatcherScheduler.cs
@@ -0,0 +1,266 @@
+// 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 more information.
+
+using System.Reactive.Disposables;
+using System.Reactive.Concurrency;
+using System.Runtime.ExceptionServices;
+using System.Threading;
+using Windows.System;
+using Windows.UI.Core;
+
+namespace System.Reactive.WindowsRuntime
+{
+ ///
+ /// Schedules units of work on a .
+ ///
+ ///
+ /// This scheduler type is typically used indirectly through the extension methods defined by
+ /// .
+ ///
+ [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 = CoreWindow.GetForCurrentThread()
+ ?? throw new InvalidOperationException(Strings_WindowsThreading.NO_WINDOW_CURRENT);
+ return new CoreDispatcherScheduler(window.Dispatcher);
+ }
+ }
+
+ ///
+ /// Gets the associated with the .
+ ///
+ public CoreDispatcher Dispatcher { get; }
+
+ private DispatcherQueue? _dispatcherQueue;
+
+ ///
+ /// 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 = CreateDispatcherQueue().CreateTimer();
+ timer.Interval = TimeSpan.Zero;
+
+ timer.Tick += (o, e) =>
+ {
+ timer.Stop();
+ ExceptionDispatchInfo.Capture(ex).Throw();
+ };
+
+ timer.Start();
+ }
+ }
+ });
+
+ return StableCompositeDisposable.Create(
+ d,
+ res.AsDisposable()
+ );
+ }
+
+ private DispatcherQueue CreateDispatcherQueue()
+ {
+ if(_dispatcherQueue != null)
+ {
+ return _dispatcherQueue;
+ }
+
+ if(Dispatcher.HasThreadAccess)
+ {
+ _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
+ return _dispatcherQueue;
+ }
+
+ // We're on a different thread, get it from the right one
+ Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
+ {
+ _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
+ }).GetAwaiter().GetResult(); // This is a synchronous call and we need the result to proceed
+
+ return _dispatcherQueue!;
+ }
+
+ ///
+ /// 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 = CreateDispatcherQueue().CreateTimer();
+
+ timer.Tick += (o, e) =>
+ {
+ var t = Interlocked.Exchange(ref timer, null);
+ if (t != null)
+ {
+ try
+ {
+ d.Disposable = action(this, state);
+ }
+ finally
+ {
+ t.Stop();
+ action = static (s, t) => Disposable.Empty;
+ }
+ }
+ };
+
+ timer.Interval = dueTime;
+ timer.Start();
+
+ d.Disposable = Disposable.Create(() =>
+ {
+ var t = Interlocked.Exchange(ref timer, null);
+ if (t != null)
+ {
+ t.Stop();
+ action = static (s, t) => 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 = CreateDispatcherQueue().CreateTimer();
+
+ 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 = static _ => _;
+ }
+ });
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/EventPatternSource.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/EventPatternSource.cs
new file mode 100644
index 000000000..0ef4b83d6
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/EventPatternSource.cs
@@ -0,0 +1,29 @@
+// 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 more information.
+
+using Windows.Foundation;
+
+namespace System.Reactive.WindowsRuntime
+{
+ internal sealed class EventPatternSource : EventPatternSourceBase, ITypedEventPatternSource
+ {
+ public EventPatternSource(IObservable> source, Action, /*object,*/ EventPattern> invokeHandler)
+ : base(source, invokeHandler)
+ {
+ }
+
+ event TypedEventHandler ITypedEventPatternSource.OnNext
+ {
+ add
+ {
+ Add(value, (o, e) => value(o!, e));
+ }
+
+ remove
+ {
+ Remove(value);
+ }
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/ITypedEventPatternSource.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/ITypedEventPatternSource.cs
new file mode 100644
index 000000000..eab1e1914
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/ITypedEventPatternSource.cs
@@ -0,0 +1,25 @@
+// 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 more information.
+
+using Windows.Foundation;
+
+namespace System.Reactive.WindowsRuntime
+{
+ ///
+ /// Represents a data stream signaling its elements by means of a Windows Runtime
+ /// .
+ ///
+ /// Sender type.
+ /// Event arguments type.
+ [CLSCompliant(false)]
+ public interface ITypedEventPatternSource
+ {
+ ///
+ /// Event signaling the next element in the data stream.
+ ///
+#pragma warning disable CA1003 // (Use generic EventHandler.) The use of the Windows.Foundation handler type is by design
+ event TypedEventHandler OnNext;
+#pragma warning restore CA1003
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/StableCompositeDisposable.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/StableCompositeDisposable.cs
new file mode 100644
index 000000000..bfa22ab33
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/StableCompositeDisposable.cs
@@ -0,0 +1,65 @@
+// 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 more information.
+
+using System.Threading;
+
+namespace System.Reactive.WindowsRuntime
+{
+ ///
+ /// Represents a group of disposable resources that are disposed together.
+ ///
+ ///
+ /// This is a copy from System.Reactive and then trimmed down to provide just the one
+ /// feature
+ /// needs: access to the internal CreateTrusted method. I didn't want to make that a
+ /// public-facing part of the main library. (And InternalsVisibleTo effectively makes it
+ /// somewhat public: it means changes to the internals could break this library.) Better, then
+ /// to copy over just the functionality required in this library.
+ ///
+ internal abstract class StableUncheckedCompositeDisposable : IDisposable
+ {
+ ///
+ /// Creates a group of disposable resources that are disposed together
+ /// and without copying or checking for nulls inside the group.
+ ///
+ /// The array of disposables that is trusted
+ /// to not contain nulls and gives no need to defensively copy it.
+ /// Group of disposable resources that are disposed together.
+ internal static IDisposable CreateTrusted(params IDisposable[] disposables)
+ {
+ return new NAryTrustedArray(disposables);
+ }
+
+ ///
+ /// Disposes all disposables in the group.
+ ///
+ public abstract void Dispose();
+
+ ///
+ /// A stable composite that doesn't do defensive copy of
+ /// the input disposable array nor checks it for null.
+ ///
+ private sealed class NAryTrustedArray : StableUncheckedCompositeDisposable
+ {
+ private IDisposable[]? _disposables;
+
+ public NAryTrustedArray(IDisposable[] disposables)
+ {
+ Volatile.Write(ref _disposables, disposables);
+ }
+
+ public override void Dispose()
+ {
+ var old = Interlocked.Exchange(ref _disposables, null);
+ if (old != null)
+ {
+ foreach (var d in old)
+ {
+ d.Dispose();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/Strings_WindowsThreading.Designer.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/Strings_WindowsThreading.Designer.cs
new file mode 100644
index 000000000..6de4bd3ca
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/Strings_WindowsThreading.Designer.cs
@@ -0,0 +1,81 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace System.Reactive.WindowsRuntime {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Strings_WindowsThreading {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Strings_WindowsThreading() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Reactive.For.WindowsRuntime.Strings_WindowsThreading", typeof(Strings_WindowsThreading).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Disposables collection can not contain null values..
+ ///
+ internal static string DISPOSABLES_CANT_CONTAIN_NULL {
+ get {
+ return ResourceManager.GetString("DISPOSABLES_CANT_CONTAIN_NULL", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to No current Window object found to obtain a CoreDispatcher from..
+ ///
+ internal static string NO_WINDOW_CURRENT {
+ get {
+ return ResourceManager.GetString("NO_WINDOW_CURRENT", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/Strings_WindowsThreading.resx b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/Strings_WindowsThreading.resx
new file mode 100644
index 000000000..9070c521b
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/Strings_WindowsThreading.resx
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Disposables collection can not contain null values.
+
+
+ No current Window object found to obtain a CoreDispatcher from.
+
+
\ No newline at end of file
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.For.WindowsRuntime.csproj b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.For.WindowsRuntime.csproj
new file mode 100644
index 000000000..3302cb099
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.For.WindowsRuntime.csproj
@@ -0,0 +1,48 @@
+
+
+
+
+ net6.0-windows10.0.17763.0;uap10.0.18362
+
+ Rx;Reactive;Extensions;Observable;LINQ;Events;Windows Forms
+ Reactive Extensions (Rx) for .NET
+
+ readme.md
+
+
+
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.Linq/WindowsRuntimeAsyncInfoObservable.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.Linq/WindowsRuntimeAsyncInfoObservable.cs
new file mode 100644
index 000000000..0e9cc0d1d
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.Linq/WindowsRuntimeAsyncInfoObservable.cs
@@ -0,0 +1,230 @@
+// 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 more information.
+
+using System.Reactive.Threading.Tasks;
+using System.Reactive.WindowsRuntime;
+using System.Runtime.InteropServices.WindowsRuntime;
+using System.Threading.Tasks;
+using Windows.Foundation;
+
+namespace System.Reactive.Linq
+{
+ ///
+ /// Provides a set of extension methods to expose observable sequences as Windows Runtime
+ /// asynchronous actions and operations.
+ ///
+ [CLSCompliant(false)]
+ public static class WindowsRuntimeAsyncInfoObservable
+ {
+ #region IAsyncAction
+
+ ///
+ /// Creates a Windows Runtime asynchronous action that represents the completion of the observable sequence.
+ /// Upon cancellation of the asynchronous action, the subscription to the source sequence will be disposed.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence to expose as an asynchronous action.
+ /// Windows Runtime asynchronous action object representing the completion of the observable sequence.
+ /// is null.
+ public static IAsyncAction ToIAsyncAction(this IObservable source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return AsyncInfo.Run(ct => (Task)source.DefaultIfEmpty().ToTask(ct));
+ }
+
+ #region Progress
+
+ ///
+ /// Creates a Windows Runtime asynchronous action that represents the completion of the observable sequence, reporting incremental progress for each element produced by the sequence.
+ /// Upon cancellation of the asynchronous action, the subscription to the source sequence will be disposed.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence to expose as an asynchronous action.
+ /// Windows Runtime asynchronous action object representing the completion of the observable sequence, reporting incremental progress for each source sequence element.
+ /// is null.
+ public static IAsyncActionWithProgress ToIAsyncActionWithProgress(this IObservable source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return AsyncInfo.Run((ct, progress) =>
+ {
+ var i = 0;
+ return source.Do(_ => progress.Report(i++)).DefaultIfEmpty().ToTask(ct);
+ });
+ }
+
+ ///
+ /// Creates a Windows Runtime asynchronous action that represents the completion of the observable sequence, using a selector function to map the source sequence on a progress reporting sequence.
+ /// Upon cancellation of the asynchronous action, the subscription to the source sequence will be disposed.
+ ///
+ /// The type of the elements in the source sequence.
+ /// The type of the elements in the progress sequence.
+ /// Source sequence to expose as an asynchronous action and to compute a progress sequence that gets reported through the asynchronous action.
+ /// Selector function to map the source sequence on a progress reporting sequence.
+ /// Windows Runtime asynchronous action object representing the completion of the result sequence, reporting progress computed through the progress sequence.
+ /// or is null.
+ public static IAsyncActionWithProgress ToIAsyncActionWithProgress(this IObservable source, Func, IObservable> progressSelector)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (progressSelector == null)
+ {
+ throw new ArgumentNullException(nameof(progressSelector));
+ }
+
+ return AsyncInfo.Run((ct, progress) =>
+ {
+ return Observable.Create(observer =>
+ {
+ var obs = Observer.Synchronize(observer);
+
+ var data = source.Publish();
+
+ var progressSubscription = progressSelector(data).Subscribe(progress.Report, obs.OnError);
+ var dataSubscription = data.DefaultIfEmpty().Subscribe(obs);
+ var connection = data.Connect();
+
+ return StableUncheckedCompositeDisposable.CreateTrusted(progressSubscription, dataSubscription, connection);
+ }).ToTask(ct);
+ });
+ }
+
+ #endregion
+
+ #endregion
+
+ #region IAsyncOperation
+
+ ///
+ /// Creates a Windows Runtime asynchronous operation that returns the last element of the observable sequence.
+ /// Upon cancellation of the asynchronous operation, the subscription to the source sequence will be disposed.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence to expose as an asynchronous operation.
+ /// Windows Runtime asynchronous operation object that returns the last element of the observable sequence.
+ /// is null.
+ public static IAsyncOperation ToIAsyncOperation(this IObservable source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return AsyncInfo.Run(ct => source.ToTask(ct));
+ }
+
+ ///
+ /// Creates a Windows Runtime asynchronous operation that returns the last element of the observable sequence, reporting incremental progress for each element produced by the sequence.
+ /// Upon cancellation of the asynchronous operation, the subscription to the source sequence will be disposed.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence to expose as an asynchronous operation.
+ /// Windows Runtime asynchronous operation object that returns the last element of the observable sequence, reporting incremental progress for each source sequence element.
+ /// is null.
+ public static IAsyncOperationWithProgress ToIAsyncOperationWithProgress(this IObservable source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return AsyncInfo.Run((ct, progress) =>
+ {
+ var i = 0;
+ return source.Do(_ => progress.Report(i++)).ToTask(ct);
+ });
+ }
+
+ #region Progress
+
+ ///
+ /// Creates a Windows Runtime asynchronous operation that returns the last element of the result sequence, reporting incremental progress for each element produced by the source sequence.
+ /// Upon cancellation of the asynchronous operation, the subscription to the source sequence will be disposed.
+ ///
+ /// The type of the elements in the source sequence.
+ /// The type of the elements in the result sequence.
+ /// Source sequence to compute a result sequence that gets exposed as an asynchronous operation.
+ /// Selector function to map the source sequence on a result sequence.
+ /// Windows Runtime asynchronous operation object that returns the last element of the result sequence, reporting incremental progress for each source sequence element.
+ /// or is null.
+ public static IAsyncOperationWithProgress ToIAsyncOperationWithProgress(this IObservable source, Func, IObservable> resultSelector)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (resultSelector == null)
+ {
+ throw new ArgumentNullException(nameof(resultSelector));
+ }
+
+ return AsyncInfo.Run((ct, progress) =>
+ {
+ var i = 0;
+ return resultSelector(source.Do(_ => progress.Report(i++))).ToTask(ct);
+ });
+ }
+
+ ///
+ /// Creates a Windows Runtime asynchronous operation that returns the last element of the result sequence, using a selector function to map the source sequence on a progress reporting sequence.
+ /// Upon cancellation of the asynchronous operation, the subscription to the source sequence will be disposed.
+ ///
+ /// The type of the elements in the source sequence.
+ /// The type of the elements in the result sequence.
+ /// The type of the elements in the progress sequence.
+ /// Source sequence to compute a result sequence that gets exposed as an asynchronous operation and a progress sequence that gets reported through the asynchronous operation.
+ /// Selector function to map the source sequence on a result sequence.
+ /// Selector function to map the source sequence on a progress reporting sequence.
+ /// Windows Runtime asynchronous operation object that returns the last element of the result sequence, reporting progress computed through the progress sequence.
+ /// or or is null.
+ public static IAsyncOperationWithProgress ToIAsyncOperationWithProgress(this IObservable source, Func, IObservable> resultSelector, Func, IObservable> progressSelector)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (resultSelector == null)
+ {
+ throw new ArgumentNullException(nameof(resultSelector));
+ }
+
+ if (progressSelector == null)
+ {
+ throw new ArgumentNullException(nameof(progressSelector));
+ }
+
+ return AsyncInfo.Run((ct, progress) =>
+ {
+ return Observable.Create(observer =>
+ {
+ var obs = Observer.Synchronize(observer);
+
+ var data = source.Publish();
+
+ var progressSubscription = progressSelector(data).Subscribe(progress.Report, obs.OnError);
+ var dataSubscription = resultSelector(data).Subscribe(obs);
+ var connection = data.Connect();
+
+ return StableUncheckedCompositeDisposable.CreateTrusted(progressSubscription, dataSubscription, connection);
+ }).ToTask(ct);
+ });
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.Linq/WindowsRuntimeCoreDispatcherObservable.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.Linq/WindowsRuntimeCoreDispatcherObservable.cs
new file mode 100644
index 000000000..c599ad098
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.Linq/WindowsRuntimeCoreDispatcherObservable.cs
@@ -0,0 +1,315 @@
+// 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 more information.
+
+using Windows.UI.Core;
+
+#if HAS_OS_XAML
+using Windows.UI.Xaml;
+#endif
+
+using CoreDispatcherScheduler = System.Reactive.WindowsRuntime.CoreDispatcherScheduler;
+using Synchronization = System.Reactive.Concurrency.Synchronization;
+
+namespace System.Reactive.Linq
+{
+ ///
+ /// Provides a set of extension methods for scheduling actions performed through observable sequences on UI dispatchers.
+ ///
+ [CLSCompliant(false)]
+ public static class WindowsRuntimeCoreDispatcherObservable
+ {
+ #region ObserveOn[Current]CoreDispatcher
+
+ ///
+ /// Wraps the source sequence in order to run its observer callbacks on the specified dispatcher.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Dispatcher whose associated message loop is used to notify observers on.
+ /// The source sequence whose observations happen on the specified dispatcher.
+ /// or is null.
+ public static IObservable ObserveOnWindowsRuntimeCoreDispatcher(this IObservable source, CoreDispatcher dispatcher)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (dispatcher == null)
+ {
+ throw new ArgumentNullException(nameof(dispatcher));
+ }
+
+ return Synchronization.ObserveOn(source, new CoreDispatcherScheduler(dispatcher));
+ }
+
+ ///
+ /// Wraps the source sequence in order to run its observer callbacks on the specified dispatcher.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Dispatcher whose associated message loop is used to notify observers on.
+ /// Priority to schedule work items at.
+ /// The source sequence whose observations happen on the specified dispatcher.
+ /// or is null.
+ public static IObservable ObserveOnWindowsRuntimeCoreDispatcher(this IObservable source, CoreDispatcher dispatcher, CoreDispatcherPriority priority)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (dispatcher == null)
+ {
+ throw new ArgumentNullException(nameof(dispatcher));
+ }
+
+ return Synchronization.ObserveOn(source, new CoreDispatcherScheduler(dispatcher, priority));
+ }
+
+#if HAS_OS_XAML
+ ///
+ /// Wraps the source sequence in order to run its observer callbacks on the dispatcher associated with the specified object.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Object to get the dispatcher from.
+ /// The source sequence whose observations happen on the specified object's dispatcher.
+ /// or is null.
+ public static IObservable ObserveOnWindowsRuntimeCoreDispatcher(this IObservable source, DependencyObject dependencyObject)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (dependencyObject == null)
+ {
+ throw new ArgumentNullException(nameof(dependencyObject));
+ }
+
+ return Synchronization.ObserveOn(source, new CoreDispatcherScheduler(dependencyObject.Dispatcher));
+ }
+
+ ///
+ /// Wraps the source sequence in order to run its observer callbacks on the dispatcher associated with the specified object.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Object to get the dispatcher from.
+ /// Priority to schedule work items at.
+ /// The source sequence whose observations happen on the specified object's dispatcher.
+ /// or is null.
+ public static IObservable ObserveOnWindowsRuntimeCoreDispatcher(this IObservable source, DependencyObject dependencyObject, CoreDispatcherPriority priority)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (dependencyObject == null)
+ {
+ throw new ArgumentNullException(nameof(dependencyObject));
+ }
+
+ return Synchronization.ObserveOn(source, new CoreDispatcherScheduler(dependencyObject.Dispatcher, priority));
+ }
+#endif
+ ///
+ /// Wraps the source sequence in order to run its observer callbacks on the dispatcher associated with the current window.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// The source sequence whose observations happen on the current window's dispatcher.
+ /// is null.
+ public static IObservable ObserveOnCurrentWindowsRuntimeCoreDispatcher(this IObservable source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return Synchronization.ObserveOn(source, CoreDispatcherScheduler.Current);
+ }
+
+ ///
+ /// Wraps the source sequence in order to run its observer callbacks on the dispatcher associated with the current window.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Priority to schedule work items at.
+ /// The source sequence whose observations happen on the current window's dispatcher.
+ /// is null.
+ public static IObservable ObserveOnCurrentWindowsRuntimeCoreDispatcher(this IObservable source, CoreDispatcherPriority priority)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return Synchronization.ObserveOn(source, new CoreDispatcherScheduler(CoreDispatcherScheduler.Current.Dispatcher, priority));
+ }
+
+ #endregion
+
+ #region SubscribeOn[CoreDispatcher]
+
+ ///
+ /// Wraps the source sequence in order to run its subscription and unsubscription logic on the specified dispatcher.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Dispatcher whose associated message loop is used to perform subscription and unsubscription actions on.
+ /// The source sequence whose subscriptions and unsubscriptions happen on the specified dispatcher.
+ /// or is null.
+ ///
+ /// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the specified dispatcher.
+ /// In order to invoke observer callbacks on the specified dispatcher, e.g. to render results in a control, use .
+ ///
+ public static IObservable SubscribeOnWindowsRuntimeCoreDispatcher(this IObservable source, CoreDispatcher dispatcher)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (dispatcher == null)
+ {
+ throw new ArgumentNullException(nameof(dispatcher));
+ }
+
+ return Synchronization.SubscribeOn(source, new CoreDispatcherScheduler(dispatcher));
+ }
+
+ ///
+ /// Wraps the source sequence in order to run its subscription and unsubscription logic on the specified dispatcher.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Dispatcher whose associated message loop is used to perform subscription and unsubscription actions on.
+ /// Priority to schedule work items at.
+ /// The source sequence whose subscriptions and unsubscriptions happen on the specified dispatcher.
+ /// or is null.
+ ///
+ /// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the specified dispatcher.
+ /// In order to invoke observer callbacks on the specified dispatcher, e.g. to render results in a control, use .
+ ///
+ public static IObservable SubscribeOnWindowsRuntimeCoreDispatcher(this IObservable source, CoreDispatcher dispatcher, CoreDispatcherPriority priority)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (dispatcher == null)
+ {
+ throw new ArgumentNullException(nameof(dispatcher));
+ }
+
+ return Synchronization.SubscribeOn(source, new CoreDispatcherScheduler(dispatcher, priority));
+ }
+
+#if HAS_OS_XAML
+ ///
+ /// Wraps the source sequence in order to run its subscription and unsubscription logic on the dispatcher associated with the specified object.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Object to get the dispatcher from.
+ /// The source sequence whose subscriptions and unsubscriptions happen on the specified object's dispatcher.
+ /// or is null.
+ ///
+ /// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the dispatcher associated with the specified object.
+ /// In order to invoke observer callbacks on the dispatcher associated with the specified object, e.g. to render results in a control, use .
+ ///
+ public static IObservable SubscribeOnWindowsRuntimeCoreDispatcher(this IObservable source, DependencyObject dependencyObject)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (dependencyObject == null)
+ {
+ throw new ArgumentNullException(nameof(dependencyObject));
+ }
+
+ return Synchronization.SubscribeOn(source, new CoreDispatcherScheduler(dependencyObject.Dispatcher));
+ }
+
+ ///
+ /// Wraps the source sequence in order to run its subscription and unsubscription logic on the dispatcher associated with the specified object.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Object to get the dispatcher from.
+ /// Priority to schedule work items at.
+ /// The source sequence whose subscriptions and unsubscriptions happen on the specified object's dispatcher.
+ /// or is null.
+ ///
+ /// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the dispatcher associated with the specified object.
+ /// In order to invoke observer callbacks on the dispatcher associated with the specified object, e.g. to render results in a control, use .
+ ///
+ public static IObservable SubscribeOnWindowsRuntimeCoreDispatcher(this IObservable source, DependencyObject dependencyObject, CoreDispatcherPriority priority)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (dependencyObject == null)
+ {
+ throw new ArgumentNullException(nameof(dependencyObject));
+ }
+
+ return Synchronization.SubscribeOn(source, new CoreDispatcherScheduler(dependencyObject.Dispatcher, priority));
+ }
+#endif
+
+ ///
+ /// Wraps the source sequence in order to run its subscription and unsubscription logic on the dispatcher associated with the current window.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// The source sequence whose subscriptions and unsubscriptions happen on the current window's dispatcher.
+ /// is null.
+ ///
+ /// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the dispatcher associated with the current window.
+ /// In order to invoke observer callbacks on the dispatcher associated with the current window, e.g. to render results in a control, use .
+ ///
+ public static IObservable SubscribeOnCurrentWindowsRuntimeCoreDispatcher(this IObservable source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return Synchronization.SubscribeOn(source, CoreDispatcherScheduler.Current);
+ }
+
+ ///
+ /// Wraps the source sequence in order to run its subscription and unsubscription logic on the dispatcher associated with the current window.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Priority to schedule work items at.
+ /// The source sequence whose subscriptions and unsubscriptions happen on the current window's dispatcher.
+ /// is null.
+ ///
+ /// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the dispatcher associated with the current window.
+ /// In order to invoke observer callbacks on the dispatcher associated with the current window, e.g. to render results in a control, use .
+ ///
+ public static IObservable SubscribeOnCurrentWindowsRuntimeCoreDispatcher(this IObservable source, CoreDispatcherPriority priority)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return Synchronization.SubscribeOn(source, new CoreDispatcherScheduler(CoreDispatcherScheduler.Current.Dispatcher, priority));
+ }
+
+ #endregion
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.Linq/WindowsRuntimeObservable.Events.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.Linq/WindowsRuntimeObservable.Events.cs
new file mode 100644
index 000000000..c5fe8df15
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.Linq/WindowsRuntimeObservable.Events.cs
@@ -0,0 +1,116 @@
+// 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 more information.
+
+using Windows.Foundation;
+
+namespace System.Reactive.Linq
+{
+ ///
+ /// Provides a set of static methods for importing typed events from Windows Runtime APIs.
+ ///
+ [CLSCompliant(false)]
+ public static partial class WindowsRuntimeObservable
+ {
+ ///
+ /// Converts a typed event, conforming to the standard event pattern, to an observable sequence.
+ ///
+ /// The type of the sender that raises the event.
+ /// The type of the event data generated by the event.
+ /// Action that attaches the given event handler to the underlying .NET event.
+ /// Action that detaches the given event handler from the underlying .NET event.
+ /// The observable sequence that contains data representations of invocations of the underlying typed event.
+ /// or is null.
+ ///
+ public static IObservable> FromEventPattern(Action> addHandler, Action> removeHandler)
+ {
+ if (addHandler == null)
+ {
+ throw new ArgumentNullException(nameof(addHandler));
+ }
+
+ if (removeHandler == null)
+ {
+ throw new ArgumentNullException(nameof(removeHandler));
+ }
+
+ return Observable.Create>(observer =>
+ {
+ var h = new TypedEventHandler((sender, args) =>
+ {
+ observer.OnNext(new EventPattern(sender, args));
+ });
+
+ addHandler(h);
+
+ return () =>
+ {
+ removeHandler(h);
+ };
+ });
+ }
+
+ ///
+ /// Converts a typed event, conforming to the standard event pattern, to an observable sequence.
+ ///
+ /// The delegate type of the event to be converted.
+ /// The type of the sender that raises the event.
+ /// The type of the event data generated by the event.
+ /// A function used to convert the given event handler to a delegate compatible with the underlying typed event. The resulting delegate is used in calls to the addHandler and removeHandler action parameters.
+ /// Action that attaches the given event handler to the underlying .NET event.
+ /// Action that detaches the given event handler from the underlying .NET event.
+ /// The observable sequence that contains data representations of invocations of the underlying typed event.
+ /// or or is null.
+ ///
+ public static IObservable> FromEventPattern(Func, TDelegate> conversion, Action addHandler, Action removeHandler)
+ {
+ if (conversion == null)
+ {
+ throw new ArgumentNullException(nameof(conversion));
+ }
+
+ if (addHandler == null)
+ {
+ throw new ArgumentNullException(nameof(addHandler));
+ }
+
+ if (removeHandler == null)
+ {
+ throw new ArgumentNullException(nameof(removeHandler));
+ }
+
+ return Observable.Create>(observer =>
+ {
+ var h = conversion(new TypedEventHandler((sender, args) =>
+ {
+ observer.OnNext(new EventPattern(sender, args));
+ }));
+
+ addHandler(h);
+
+ return () =>
+ {
+ removeHandler(h);
+ };
+ });
+ }
+
+ ///
+ /// Exposes an observable sequence as an object with a typed event.
+ ///
+ /// The type of the sender that raises the event.
+ /// The type of the event data generated by the event.
+ /// Observable source sequence.
+ /// The event source object.
+ /// is null.
+ public static System.Reactive.WindowsRuntime.ITypedEventPatternSource ToWindowsFoundationEventPattern(this IObservable> source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ return new System.Reactive.WindowsRuntime.EventPatternSource(source, static (h, evt) => h(evt.Sender!, evt.EventArgs));
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.Linq/WindowsRuntimeObservable.StandardSequenceOperators.cs b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.Linq/WindowsRuntimeObservable.StandardSequenceOperators.cs
new file mode 100644
index 000000000..a8660bf97
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/System.Reactive.Linq/WindowsRuntimeObservable.StandardSequenceOperators.cs
@@ -0,0 +1,135 @@
+// 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 more information.
+
+using System.Reactive.WindowsRuntime;
+using Windows.Foundation;
+
+namespace System.Reactive.Linq
+{
+ public static partial class WindowsRuntimeObservable
+ {
+ ///
+ /// Projects each element of an observable sequence to a Windows Runtime asynchronous operation and merges all of the asynchronous operation results into one observable sequence.
+ ///
+ /// The type of the elements in the source sequence.
+ /// The type of the result produced by the projected asynchronous operations and the elements in the merged result sequence.
+ /// An observable sequence of elements to project.
+ /// A transform function to apply to each element.
+ /// An observable sequence whose elements are the result of the asynchronous operations executed for each element of the input sequence.
+ /// or is null.
+ ///
+ ///
+ /// This overload supports composition of observable sequences and Windows Runtime
+ /// asynchronous operations, without requiring manual conversion of the asynchronous
+ /// operations to observable sequences using
+ /// .
+ ///
+ ///
+ public static IObservable SelectManyIAsyncOperation(this IObservable source, Func> selector)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (selector == null)
+ {
+ throw new ArgumentNullException(nameof(selector));
+ }
+
+ return source.SelectMany(x => selector(x).ToObservable());
+ }
+
+ ///
+ /// Projects each element of an observable sequence to a Windows Runtime asynchronous operation and merges all of the asynchronous operation results into one observable sequence.
+ ///
+ /// The type of the elements in the source sequence.
+ /// The type of the result produced by the projected asynchronous operations and the elements in the merged result sequence.
+ /// The type of the reported progress objects, which get ignored by this query operator.
+ /// An observable sequence of elements to project.
+ /// A transform function to apply to each element.
+ /// An observable sequence whose elements are the result of the asynchronous operations executed for each element of the input sequence.
+ /// or is null.
+ /// This overload supports composition of observable sequences and Windows Runtime asynchronous operations, without requiring manual conversion of the asynchronous operations to observable sequences using .
+ public static IObservable SelectManyIAsyncOperationWithProgress(this IObservable source, Func> selector)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (selector == null)
+ {
+ throw new ArgumentNullException(nameof(selector));
+ }
+
+ return source.SelectMany(x => selector(x).ToObservable());
+ }
+
+ ///
+ /// Projects each element of an observable sequence to a Windows Runtime asynchronous operation, invokes the result selector for the source element and the asynchronous operation result, and merges the results into one observable sequence.
+ ///
+ /// The type of the elements in the source sequence.
+ /// The type of the results produced by the projected asynchronous operations.
+ /// The type of the elements in the result sequence, obtained by using the selector to combine source sequence elements with their corresponding intermediate asynchronous operation results.
+ /// An observable sequence of elements to project.
+ /// A transform function to apply to each element.
+ /// A transform function to apply to each element of the intermediate sequence.
+ /// An observable sequence whose elements are the result of obtaining an asynchronous operation for each element of the input sequence and then mapping the asynchronous operation's result and its corresponding source element to a result element.
+ /// or or is null.
+ /// This overload supports using LINQ query comprehension syntax in C# and Visual Basic to compose observable sequences and Windows Runtime asynchronous operations, without requiring manual conversion of the asynchronous operations to observable sequences using .
+ public static IObservable SelectManyIAsyncOperation(this IObservable source, Func> asyncOperationSelector, Func resultSelector)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (asyncOperationSelector == null)
+ {
+ throw new ArgumentNullException(nameof(asyncOperationSelector));
+ }
+
+ if (resultSelector == null)
+ {
+ throw new ArgumentNullException(nameof(resultSelector));
+ }
+
+ return source.SelectMany(x => asyncOperationSelector(x).ToObservable(), resultSelector);
+ }
+
+ ///
+ /// Projects each element of an observable sequence to a Windows Runtime asynchronous operation, invokes the result selector for the source element and the asynchronous operation result, and merges the results into one observable sequence.
+ ///
+ /// The type of the elements in the source sequence.
+ /// The type of the results produced by the projected asynchronous operations.
+ /// The type of the reported progress objects, which get ignored by this query operator.
+ /// The type of the elements in the result sequence, obtained by using the selector to combine source sequence elements with their corresponding intermediate asynchronous operation results.
+ /// An observable sequence of elements to project.
+ /// A transform function to apply to each element.
+ /// A transform function to apply to each element of the intermediate sequence.
+ /// An observable sequence whose elements are the result of obtaining an asynchronous operation for each element of the input sequence and then mapping the asynchronous operation's result and its corresponding source element to a result element.
+ /// or or is null.
+ /// This overload supports using LINQ query comprehension syntax in C# and Visual Basic to compose observable sequences and Windows Runtime asynchronous operations, without requiring manual conversion of the asynchronous operations to observable sequences using .
+ public static IObservable SelectManyIAsyncOperationWithProgress(this IObservable source, Func> asyncOperationSelector, Func resultSelector)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (asyncOperationSelector == null)
+ {
+ throw new ArgumentNullException(nameof(asyncOperationSelector));
+ }
+
+ if (resultSelector == null)
+ {
+ throw new ArgumentNullException(nameof(resultSelector));
+ }
+
+ return source.SelectMany(x => asyncOperationSelector(x).ToObservable(), resultSelector);
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/build/NuGet.Readme.md b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/build/NuGet.Readme.md
new file mode 100644
index 000000000..c7321c896
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.WindowsRuntime/build/NuGet.Readme.md
@@ -0,0 +1,30 @@
+# Windows Runtime Support for Rx.NET (Reactive Extensions for .NET)
+
+This library provides support for using some common Windows Runtime types from the Reactive Extensions for .NET (Rx.NET).
+
+See the main Rx.NET package at https://www.nuget.org/packages/System.Reactive for more information about Rx.NET.
+
+## Rx.NET and UI Frameworks
+
+Up as far as Rx.NET v6.0, UI framework support has been built directly into the main `System.Reactive` package.
+Unfortunately, this has caused problems since support for WPF and Windows Forms was added in .NET Core 3.1.
+Because .NET Core 3.1, and all subsequent versions of .NET have supported cross-platform use, WPF and Windows
+Forms are not universally available. Rx.NET used to make WPF and Windows Forms support if you targetted a
+sufficiently recent version of Windows in your application TFM. But this turns out to cause problems in
+some deployment models, adding as much as 90MB to the deployable size of an application.
+
+Consequently, starting in Rx.NET v7.0 we are moving all UI-framework-specific types, and also platform-specific
+types out into separate packages.
+
+## Features
+
+This package provides replacements for two deprecated types in `System.Reactive`:
+
+| Type in `System.Reactive` | Replacement | Purpose |
+|---|---|---|
+| `CoreDispatcherScheduler` (in `System.Reactive.Concurrency`) | `CoreDispatcherScheduler` (in `System.Reactive.Integration.WPF`) | Provides a scheduler that schedules work on the UI thread of applications using `CoreDispatcher` (e.g., UWP applications). |
+| `CoreDispatcherObservable` (in `System.Reactive.Linq`) | `WindowsRuntimeCoreDispatcherObservable` (in `System.Reactive.Linq`) | Provides a set of extension methods for scheduling work on the UI thread of an application using `CoreDispatcher` (e.g., UWP applications). WPF application. |
+| `WindowsObservable` (in `System.Reactive.Linq`) | `WindowsRuntimeObservable` (in `System.Reactive.Linq`) | Provides integration between `TypedEventHandler`, and also `SelectMany` support for callbacks using the Windows Runtime asynchronous operation types (`IAsyncOperation` etc.) and `IObservable`.
+| `AsyncInfoObservable` (in `System.Reactive.Linq`) | `WindowsRuntimeAsyncInfoObservable` (in `System.Reactive.Integration.WindowsRuntime`) | Provides conversions `IObservable` top Windows Runtime asynchronous operation types (`IAsyncOperation` etc.). |
+| `AsyncInfoObservableExtensions` (in `System.Reactive.Windows.Foundation`) | `AsyncInfoObservableExtensions` (in `System.Reactive.Integration.WindowsRuntime` | Provides conversion from Windows Runtime asynchronous operation types (`IAsyncOperation` etc.) and `IObservable`.
+| `IEventPatternSource` (in `System.Reactive`) | `ITypedEventPatternSource` in `System.Reactive.Integration.WindowsRuntime` | Represents a source of events exposed as a Windows Runtime `TypedEventHandler`. |
\ No newline at end of file
diff --git a/Rx.NET/Source/src/System.Reactive.For.Wpf/DispatcherScheduler.cs b/Rx.NET/Source/src/System.Reactive.For.Wpf/DispatcherScheduler.cs
new file mode 100644
index 000000000..5c8e8a76b
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.Wpf/DispatcherScheduler.cs
@@ -0,0 +1,215 @@
+// 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 more information.
+
+using System.Reactive.Disposables;
+using System.Reactive.Concurrency;
+using System.Threading;
+using System.Windows.Threading;
+
+namespace System.Reactive.Wpf
+{
+ ///
+ /// Represents an object that schedules units of work on a .
+ ///
+ ///
+ ///
+ /// This scheduler type is typically used indirectly through extension methods such as
+ /// and
+ ///
+ /// in cases where you have a you want to use, or methods such as
+ /// ,
+ ///
+ /// when you want to use the calling thread's Dispatcher.
+ ///
+ ///
+ public class DispatcherScheduler : LocalScheduler, ISchedulerPeriodic
+ {
+ ///
+ /// Gets the scheduler that schedules work on the for the current thread.
+ ///
+ public static DispatcherScheduler Current
+ {
+ get
+ {
+ var dispatcher = System.Windows.Threading.Dispatcher.FromThread(Thread.CurrentThread)
+ ?? throw new InvalidOperationException(Strings_WindowsThreading.NO_DISPATCHER_CURRENT_THREAD);
+ return new DispatcherScheduler(dispatcher);
+ }
+ }
+
+ ///
+ /// Constructs a that schedules units of work on the given .
+ ///
+ /// to schedule work on.
+ /// is null.
+ public DispatcherScheduler(System.Windows.Threading.Dispatcher dispatcher)
+ {
+ Dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
+ Priority = System.Windows.Threading.DispatcherPriority.Normal;
+
+ }
+
+ ///
+ /// Constructs a that schedules units of work on the given at the given priority.
+ ///
+ /// to schedule work on.
+ /// Priority at which units of work are scheduled.
+ /// is null.
+ public DispatcherScheduler(System.Windows.Threading.Dispatcher dispatcher, System.Windows.Threading.DispatcherPriority priority)
+ {
+ Dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
+ Priority = priority;
+ }
+
+ ///
+ /// Gets the associated with the .
+ ///
+ public System.Windows.Threading.Dispatcher Dispatcher { get; }
+
+ ///
+ /// Gets the priority at which work items will be dispatched.
+ ///
+ public System.Windows.Threading.DispatcherPriority 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();
+
+ Dispatcher.BeginInvoke(
+ new Action(() =>
+ {
+ if (!d.IsDisposed)
+ {
+ d.Disposable = action(this, state);
+ }
+ }),
+ Priority
+ );
+
+ return d;
+ }
+
+ ///
+ /// 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 System.Windows.Threading.DispatcherTimer(Priority, Dispatcher);
+
+ timer.Tick += (s, e) =>
+ {
+ var t = Interlocked.Exchange(ref timer, null);
+ if (t != null)
+ {
+ try
+ {
+ d.Disposable = action(this, state);
+ }
+ finally
+ {
+ t.Stop();
+ action = static (s, t) => Disposable.Empty;
+ }
+ }
+ };
+
+ timer.Interval = dueTime;
+ timer.Start();
+
+ d.Disposable = Disposable.Create(() =>
+ {
+ var t = Interlocked.Exchange(ref timer, null);
+ if (t != null)
+ {
+ t.Stop();
+ action = static (s, t) => 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)
+ {
+ if (period < TimeSpan.Zero)
+ {
+ throw new ArgumentOutOfRangeException(nameof(period));
+ }
+
+ if (action == null)
+ {
+ throw new ArgumentNullException(nameof(action));
+ }
+
+ var timer = new System.Windows.Threading.DispatcherTimer(Priority, Dispatcher);
+
+ var state1 = state;
+
+ timer.Tick += (s, 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 = static _ => _;
+ }
+ });
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.Wpf/Strings_WindowsThreading.Designer.cs b/Rx.NET/Source/src/System.Reactive.For.Wpf/Strings_WindowsThreading.Designer.cs
new file mode 100644
index 000000000..02e119223
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.Wpf/Strings_WindowsThreading.Designer.cs
@@ -0,0 +1,72 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace System.Reactive.Wpf {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Strings_WindowsThreading {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Strings_WindowsThreading() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Reactive.For.Wpf.Strings_WindowsThreading", typeof(Strings_WindowsThreading).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The current thread has no Dispatcher associated with it..
+ ///
+ internal static string NO_DISPATCHER_CURRENT_THREAD {
+ get {
+ return ResourceManager.GetString("NO_DISPATCHER_CURRENT_THREAD", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/Rx.NET/Source/src/System.Reactive.For.Wpf/Strings_WindowsThreading.resx b/Rx.NET/Source/src/System.Reactive.For.Wpf/Strings_WindowsThreading.resx
new file mode 100644
index 000000000..8384193b1
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.Wpf/Strings_WindowsThreading.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ The current thread has no Dispatcher associated with it.
+
+
\ No newline at end of file
diff --git a/Rx.NET/Source/src/System.Reactive.For.Wpf/System.Reactive.For.Wpf.csproj b/Rx.NET/Source/src/System.Reactive.For.Wpf/System.Reactive.For.Wpf.csproj
new file mode 100644
index 000000000..caf89a25d
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.Wpf/System.Reactive.For.Wpf.csproj
@@ -0,0 +1,44 @@
+
+
+
+ net472;net6.0-windows
+ true
+
+ Rx;Reactive;Extensions;Observable;LINQ;Events;WPF
+ Reactive Extensions (Rx) for .NET
+
+ readme.md
+
+
+
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Strings_WindowsThreading.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Strings_WindowsThreading.Designer.cs
+
+
+
+
+
+
+
+
diff --git a/Rx.NET/Source/src/System.Reactive.For.Wpf/System.Reactive.Linq/WpfDispatcherObservable.cs b/Rx.NET/Source/src/System.Reactive.For.Wpf/System.Reactive.Linq/WpfDispatcherObservable.cs
new file mode 100644
index 000000000..6f77e07f9
--- /dev/null
+++ b/Rx.NET/Source/src/System.Reactive.For.Wpf/System.Reactive.Linq/WpfDispatcherObservable.cs
@@ -0,0 +1,498 @@
+// 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 more information.
+
+using System.Windows.Threading;
+
+using DispatcherScheduler = System.Reactive.Wpf.DispatcherScheduler;
+using Synchronization = System.Reactive.Concurrency.Synchronization;
+
+namespace System.Reactive.Linq
+{
+ ///
+ /// Provides a set of extension methods for scheduling actions performed through observable
+ /// sequences on UI dispatchers.
+ ///
+ ///
+ ///
+ /// This replaces the obsolete System.Reactive.Linq.ControlObservable class in
+ /// System.Reactive.
+ ///
+ ///
+ public static class WpfDispatcherObservable
+
+ {
+ #region ObserveOn[Current]Dispatcher
+
+ ///
+ /// Wraps the source sequence in order to run its observer callbacks on the specified
+ /// dispatcher.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Dispatcher whose associated message loop is used to notify observers on.
+ /// The source sequence whose observations happen on the specified dispatcher.
+ ///
+ /// or is null.
+ ///
+ public static IObservable ObserveOnWpfDispatcher(
+ this IObservable source,
+ Dispatcher dispatcher)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (dispatcher == null)
+ {
+ throw new ArgumentNullException(nameof(dispatcher));
+ }
+
+ return ObserveOn_(source, dispatcher);
+ }
+
+ ///
+ /// Wraps the source sequence in order to run its observer callbacks on the specified
+ /// dispatcher.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Dispatcher whose associated message loop is used to notify observers on.
+ /// Priority to schedule work items at.
+ /// The source sequence whose observations happen on the specified dispatcher.
+ ///
+ /// or is null.
+ ///
+ public static IObservable ObserveOnWpfDispatcher(
+ this IObservable source,
+ Dispatcher dispatcher,
+ DispatcherPriority priority)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (dispatcher == null)
+ {
+ throw new ArgumentNullException(nameof(dispatcher));
+ }
+
+ return ObserveOn_(source, dispatcher, priority);
+ }
+
+ ///
+ /// Wraps the source sequence in order to run its observer callbacks on the specified
+ /// dispatcher scheduler.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Dispatcher scheduler to notify observers on.
+ /// The source sequence whose observations happen on the specified dispatcher scheduler.
+ ///
+ /// or is null.
+ ///
+ public static IObservable ObserveOn(
+ this IObservable source,
+ DispatcherScheduler scheduler)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (scheduler == null)
+ {
+ throw new ArgumentNullException(nameof(scheduler));
+ }
+
+ return ObserveOn_(source, scheduler.Dispatcher, scheduler.Priority);
+ }
+
+
+ ///
+ /// Wraps the source sequence in order to run its observer callbacks on the dispatcher
+ /// associated with the specified object.
+ ///
+ /// The type of the elements in the source sequence.
+ /// Source sequence.
+ /// Object to get the dispatcher from.
+ /// The source sequence whose observations happen on the specified object's dispatcher.
+ ///
+ /// or is null.
+ ///
+ public static IObservable ObserveOnWpfDispatcher(
+ this IObservable