diff --git a/src/Directory.build.props b/src/Directory.build.props
index d22018a..4e3fddd 100644
--- a/src/Directory.build.props
+++ b/src/Directory.build.props
@@ -28,15 +28,20 @@
previewTruelatest
+ $(NoWarn);SA1648;CA1816;CA1001;CS0108;CS0114;CS3021;CS1574;CA1303;NETSDK1206CS8600;CS8602;CS8603;CS8604;CS8605;CS8606;CS8607;CS8608;CS8609;CS8610;CS8611;CS8612;CS8613;CS8614;CS8615;CS8616;CS8617;CS8618;CS8619;CS8620;CS8621;CS8622;CS8623;CS8624;CS8625;CS8626;CS8627;CS8628;CS8629;CS8630;CS8634;CS8766;CS8767truetrue
- true
-
+ true
+ mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;frp;net;unoplatform
+ net7.0;net7.0-android;net7.0-ios;net7.0-maccatalyst;net7.0-macos;net8.0;net8.0-android;net8.0-ios;net8.0-maccatalyst;net8.0-macos
+
+ 14.2
+ 14.0
+ 30.0
+ 10.0.19041.0
+ 10.0.19041.0
-
-
- portable
@@ -44,10 +49,10 @@
-
-
+
+
-
+
@@ -57,7 +62,7 @@
-
+
@@ -72,8 +77,8 @@
-
-
+
+
diff --git a/src/Directory.build.targets b/src/Directory.build.targets
index d3b2d43..0871acf 100644
--- a/src/Directory.build.targets
+++ b/src/Directory.build.targets
@@ -7,49 +7,19 @@
false
-
- $(DefineConstants);NETSTANDARD;PORTABLE
+
+ $(DefineConstants);IOS
-
- $(DefineConstants);NET_461;XAML
+
+ $(DefineConstants);MAC
-
- 10.0.16299.0
- $(DefineConstants);NETFX_CORE;XAML;WINDOWS_UWP
+
+ $(DefineConstants);TVOS
-
- $(DefineConstants);MONO;UIKIT;COCOA;IOS
+
+ $(DefineConstants);ANDROID
-
- $(DefineConstants);MONO;UIKIT;COCOA;IOS
+
+ $(DefineConstants);MACCATALYST
-
- $(DefineConstants);MONO;COCOA;MAC
-
-
- $(DefineConstants);MONO;COCOA;MAC
-
-
- $(DefineConstants);MONO;UIKIT;COCOA;TVOS
-
-
- $(DefineConstants);MONO;UIKIT;COCOA;TVOS
-
-
- $(DefineConstants);MONO;UIKIT;COCOA;WATCHOS
-
-
- $(DefineConstants);MONO;ANDROID
- false
-
-
- $(DefineConstants);MONO;ANDROID
-
-
- $(DefineConstants);TIZEN
-
-
-
diff --git a/src/ReactiveUI.Uno.WinUI/DispatcherQueueScheduler.cs b/src/ReactiveUI.Uno.WinUI/DispatcherQueueScheduler.cs
new file mode 100644
index 0000000..5a42a46
--- /dev/null
+++ b/src/ReactiveUI.Uno.WinUI/DispatcherQueueScheduler.cs
@@ -0,0 +1,206 @@
+// 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.
+//
+#if WINDOWS10_0_19041_0
+
+using System.Reactive.Disposables;
+using System.Threading;
+using Microsoft.UI.Dispatching;
+
+namespace System.Reactive.Concurrency;
+
+///
+/// Represents an object that schedules units of work on a .
+///
+public class DispatcherQueueScheduler : LocalScheduler, ISchedulerPeriodic
+{
+ ///
+ /// Gets the scheduler that schedules work on the for the current thread.
+ ///
+ public static DispatcherQueueScheduler Current
+ {
+ get
+ {
+ var dispatcher = DispatcherQueue.GetForCurrentThread();
+ if (dispatcher == null)
+ {
+ throw new InvalidOperationException("There is no current dispatcher thread");
+ }
+
+ return new DispatcherQueueScheduler(dispatcher);
+ }
+ }
+
+ ///
+ /// Constructs a that schedules units of work on the given .
+ ///
+ /// to schedule work on.
+ /// is null.
+ public DispatcherQueueScheduler(DispatcherQueue dispatcherQueue)
+ {
+ DispatcherQueue = dispatcherQueue ?? throw new ArgumentNullException(nameof(dispatcherQueue));
+ Priority = DispatcherQueuePriority.Normal;
+ }
+
+ ///
+ /// Constructs a DispatcherScheduler 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 DispatcherQueueScheduler(DispatcherQueue dispatcherQueue, DispatcherQueuePriority priority)
+ {
+ DispatcherQueue = dispatcherQueue ?? throw new ArgumentNullException(nameof(dispatcherQueue));
+ Priority = priority;
+ }
+
+ ///
+ /// Gets the associated with the .
+ ///
+ public DispatcherQueue DispatcherQueue { get; }
+
+ ///
+ /// Gets the priority at which work items will be dispatched.
+ ///
+ public DispatcherQueuePriority 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();
+
+ DispatcherQueue.TryEnqueue(Priority,
+ () =>
+ {
+ if (!d.IsDisposed)
+ {
+ d.Disposable = action(this, state);
+ }
+ });
+
+ return d;
+ }
+
+ ///
+ /// Schedules an action to be executed after on the dispatcherQueue, 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 = DispatcherQueue.CreateTimer();
+
+ 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 dispatcherQueue, 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 = DispatcherQueue.CreateTimer();
+
+ 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 _ => _;
+ }
+ });
+ }
+}
+#endif
diff --git a/src/ReactiveUI.Uno.WinUI/ReactiveUI.Uno.WinUI.csproj b/src/ReactiveUI.Uno.WinUI/ReactiveUI.Uno.WinUI.csproj
index 4f5c3c8..95295c2 100644
--- a/src/ReactiveUI.Uno.WinUI/ReactiveUI.Uno.WinUI.csproj
+++ b/src/ReactiveUI.Uno.WinUI/ReactiveUI.Uno.WinUI.csproj
@@ -1,50 +1,26 @@
-
+
- netstandard2.0;MonoAndroid12.0;MonoAndroid12.1;MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;net6.0-android;net6.0-ios;net6.0-macos;net6.0-maccatalyst;net6.0-windows10.0.17763.0;net7.0-android;net7.0-ios;net7.0-macos;net7.0-maccatalyst;net7.0-windows10.0.17763.0
+ $(UnoTargetFrameworks);net7.0-windows10.0.19041.0;net8.0-windows10.0.19041.0ReactiveUI.Uno.WinUIContains the ReactiveUI platform specific extensions for Uno WinUI
- $(DefineConstants);HAS_UNO;HAS_WINUI
- $(NoWarn);SA1648;CA1816;CA1001;CS0108;CS0114;CS3021;CS1574;CA1303
- mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;frp;net;unoplatform;winui
+ $(DefineConstants);HAS_WINUI
+ $(PackageTags);winui
-
- $(DefineConstants);WASM
-
-
+ $(DefineConstants);HAS_UNO_WINUI
+ win-x64;win-x86;win-arm64
-
-
-
-
-
+
-
-
-
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/src/ReactiveUI.Uno.sln b/src/ReactiveUI.Uno.sln
index f267b09..903f92d 100644
--- a/src/ReactiveUI.Uno.sln
+++ b/src/ReactiveUI.Uno.sln
@@ -6,6 +6,7 @@ MinimumVisualStudioVersion = 16.0.31613.86
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BD9762CF-E104-481C-96A6-26E624B86283}"
ProjectSection(SolutionItems) = preProject
..\.editorconfig = ..\.editorconfig
+ ..\.gitignore = ..\.gitignore
Directory.build.props = Directory.build.props
Directory.build.targets = Directory.build.targets
global.json = global.json
diff --git a/src/ReactiveUI.Uno/ActivationForViewFetcher.cs b/src/ReactiveUI.Uno/ActivationForViewFetcher.cs
index 4793483..de95136 100644
--- a/src/ReactiveUI.Uno/ActivationForViewFetcher.cs
+++ b/src/ReactiveUI.Uno/ActivationForViewFetcher.cs
@@ -7,56 +7,54 @@
using System.Linq;
using System.Reactive.Linq;
using System.Reflection;
+using Windows.Foundation;
#if HAS_UNO_WINUI
using Microsoft.UI.Xaml;
-using Windows.Foundation;
#else
-using Windows.Foundation;
using Windows.UI.Xaml;
#endif
-namespace ReactiveUI.Uno
+namespace ReactiveUI.Uno;
+
+///
+/// ActivationForViewFetcher is how ReactiveUI determine when a
+/// View is activated or deactivated. This is usually only used when porting
+/// ReactiveUI to a new UI framework.
+///
+public class ActivationForViewFetcher : IActivationForViewFetcher
{
- ///
- /// ActivationForViewFetcher is how ReactiveUI determine when a
- /// View is activated or deactivated. This is usually only used when porting
- /// ReactiveUI to a new UI framework.
- ///
- public class ActivationForViewFetcher : IActivationForViewFetcher
- {
- ///
- public int GetAffinityForView(Type view) => typeof(FrameworkElement).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? 10 : 0;
+ ///
+ public int GetAffinityForView(Type view) => typeof(FrameworkElement).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? 10 : 0;
- ///
- public IObservable GetActivationForView(IActivatableView view)
+ ///
+ public IObservable GetActivationForView(IActivatableView view)
+ {
+ if (view is not FrameworkElement fe)
{
- if (view is not FrameworkElement fe)
- {
- return Observable.Empty;
- }
+ return Observable.Empty;
+ }
#pragma warning disable SA1114 // Parameter list after.
- var viewLoaded = Observable.FromEvent, bool>(
-
- eventHandler => (_, _) => eventHandler(true),
- x => fe.Loading += x,
- x => fe.Loading -= x);
-
- var viewUnloaded = Observable.FromEvent(
- handler =>
- {
- void EventHandler(object sender, RoutedEventArgs e) => handler(false);
- return EventHandler;
- },
- x => fe.Unloaded += x,
- x => fe.Unloaded -= x);
-
- return viewLoaded
- .Merge(viewUnloaded)
- .Select(b => b ? fe.WhenAnyValue(x => x.IsHitTestVisible).SkipWhile(x => !x) : Observables.False)
- .Switch()
- .DistinctUntilChanged();
- }
+ var viewLoaded = Observable.FromEvent, bool>(
+
+ eventHandler => (_, _) => eventHandler(true),
+ x => fe.Loading += x,
+ x => fe.Loading -= x);
+
+ var viewUnloaded = Observable.FromEvent(
+ handler =>
+ {
+ void EventHandler(object sender, RoutedEventArgs e) => handler(false);
+ return EventHandler;
+ },
+ x => fe.Unloaded += x,
+ x => fe.Unloaded -= x);
+
+ return viewLoaded
+ .Merge(viewUnloaded)
+ .Select(b => b ? fe.WhenAnyValue(x => x.IsHitTestVisible).SkipWhile(x => !x) : Observables.False)
+ .Switch()
+ .DistinctUntilChanged();
}
}
diff --git a/src/ReactiveUI.Uno/ActivationHelper.cs b/src/ReactiveUI.Uno/ActivationHelper.cs
index 02db312..482d0f2 100644
--- a/src/ReactiveUI.Uno/ActivationHelper.cs
+++ b/src/ReactiveUI.Uno/ActivationHelper.cs
@@ -5,30 +5,28 @@
using Splat;
-namespace ReactiveUI.Uno
+namespace ReactiveUI.Uno;
+
+internal static class ActivationHelper
{
- internal static class ActivationHelper
+ static ActivationHelper()
{
+ if (UnoActivated)
+ {
+ return;
+ }
- static ActivationHelper()
+ UnoActivated = true;
+ Locator.RegisterResolverCallbackChanged(() =>
{
- if (UnoActivated)
+ if (Locator.CurrentMutable is null)
{
return;
}
- UnoActivated = true;
- Locator.RegisterResolverCallbackChanged(() =>
- {
- if (Locator.CurrentMutable is null)
- {
- return;
- }
-
- new Registrations().Register((f, t) => Locator.CurrentMutable.RegisterConstant(f(), t));
- });
- }
-
- internal static bool UnoActivated { get; }
+ new Registrations().Register((f, t) => Locator.CurrentMutable.RegisterConstant(f(), t));
+ });
}
+
+ internal static bool UnoActivated { get; }
}
diff --git a/src/ReactiveUI.Uno/Common/AutoDataTemplateBindingHook.cs b/src/ReactiveUI.Uno/Common/AutoDataTemplateBindingHook.cs
index 60f01f0..30331be 100644
--- a/src/ReactiveUI.Uno/Common/AutoDataTemplateBindingHook.cs
+++ b/src/ReactiveUI.Uno/Common/AutoDataTemplateBindingHook.cs
@@ -4,103 +4,73 @@
// See the LICENSE file in the project root for full license information.
using System;
-using System.Diagnostics.CodeAnalysis;
using System.Linq;
#if HAS_WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Markup;
-#elif NETFX_CORE || HAS_UNO
+#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Markup;
-#else
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Markup;
#endif
-#if HAS_UNO
-namespace ReactiveUI.Uno
-#else
-namespace ReactiveUI
-#endif
+namespace ReactiveUI.Uno;
+
+///
+/// AutoDataTemplateBindingHook is a binding hook that checks ItemsControls
+/// that don't have DataTemplates, and assigns a default DataTemplate that
+/// loads the View associated with each ViewModel.
+///
+public class AutoDataTemplateBindingHook : IPropertyBindingHook
{
///
- /// AutoDataTemplateBindingHook is a binding hook that checks ItemsControls
- /// that don't have DataTemplates, and assigns a default DataTemplate that
- /// loads the View associated with each ViewModel.
+ /// Gets the default item template.
///
- public class AutoDataTemplateBindingHook : IPropertyBindingHook
+ public static Lazy DefaultItemTemplate { get; } = new(() =>
{
- ///
- /// Gets the default item template.
- ///
- [SuppressMessage("Design", "CA1307: Use the currency locale settings", Justification = "Not available on all platforms.")]
- public static Lazy DefaultItemTemplate { get; } = new(() =>
- {
-#if NETFX_CORE || HAS_UNO
- const string template =
+ const string template =
@"";
- return (DataTemplate)XamlReader.Load(template);
-#else
- const string template = " " +
- "" +
- "";
+ return (DataTemplate)XamlReader.Load(template);
+ });
- var assemblyName = typeof(AutoDataTemplateBindingHook).Assembly.FullName;
- assemblyName = assemblyName?.Substring(0, assemblyName.IndexOf(','));
+ ///
+ public bool ExecuteHook(object? source, object target, Func[]> getCurrentViewModelProperties, Func[]> getCurrentViewProperties, BindingDirection direction)
+ {
+ ArgumentNullException.ThrowIfNull(getCurrentViewProperties);
-#if HAS_WINUI
- return (DataTemplate)XamlReader.Load(template.Replace("__ASSEMBLYNAME__", assemblyName));
-#else
- return (DataTemplate)XamlReader.Parse(template.Replace("__ASSEMBLYNAME__", assemblyName));
-#endif
-#endif
- });
+ var viewProperties = getCurrentViewProperties();
+ var lastViewProperty = viewProperties.LastOrDefault();
- ///
- public bool ExecuteHook(object? source, object target, Func[]> getCurrentViewModelProperties, Func[]> getCurrentViewProperties, BindingDirection direction)
+ if (lastViewProperty?.Sender is not ItemsControl itemsControl)
{
- if (getCurrentViewProperties is null)
- {
- throw new ArgumentNullException(nameof(getCurrentViewProperties));
- }
-
- var viewProperties = getCurrentViewProperties();
- var lastViewProperty = viewProperties.LastOrDefault();
-
- if (lastViewProperty?.Sender is not ItemsControl itemsControl)
- {
- return true;
- }
-
- if (!string.IsNullOrEmpty(itemsControl.DisplayMemberPath))
- {
- return true;
- }
+ return true;
+ }
- if (viewProperties.Last().GetPropertyName() != "ItemsSource")
- {
- return true;
- }
+ if (!string.IsNullOrEmpty(itemsControl.DisplayMemberPath))
+ {
+ return true;
+ }
- if (itemsControl.ItemTemplate is not null)
- {
- return true;
- }
+ if (viewProperties.Last().GetPropertyName() != "ItemsSource")
+ {
+ return true;
+ }
- if (itemsControl.ItemTemplateSelector is not null)
- {
- return true;
- }
+ if (itemsControl.ItemTemplate is not null)
+ {
+ return true;
+ }
- itemsControl.ItemTemplate = DefaultItemTemplate.Value;
+ if (itemsControl.ItemTemplateSelector is not null)
+ {
return true;
}
+
+ itemsControl.ItemTemplate = DefaultItemTemplate.Value;
+ return true;
}
}
diff --git a/src/ReactiveUI.Uno/Common/BooleanToVisibilityHint.cs b/src/ReactiveUI.Uno/Common/BooleanToVisibilityHint.cs
index 772378e..e85f467 100644
--- a/src/ReactiveUI.Uno/Common/BooleanToVisibilityHint.cs
+++ b/src/ReactiveUI.Uno/Common/BooleanToVisibilityHint.cs
@@ -4,43 +4,28 @@
// See the LICENSE file in the project root for full license information.
using System;
-using System.Diagnostics.CodeAnalysis;
+
#if HAS_WINUI
using Microsoft.UI.Xaml;
-#elif NETFX_CORE || HAS_UNO
-using Windows.UI.Xaml;
#else
-using System.Windows;
+using Windows.UI.Xaml;
#endif
-#if HAS_UNO
-namespace ReactiveUI.Uno
-#else
-namespace ReactiveUI
-#endif
+namespace ReactiveUI.Uno;
+
+///
+/// Enum that hints at the visibility of a ui element.
+///
+[Flags]
+public enum BooleanToVisibilityHint
{
///
- /// Enum that hints at the visibility of a ui element.
+ /// Do not modify the boolean type conversion from it's default action of using the Visibility.Collapsed.
///
- [SuppressMessage("Name", "CA1714: Flags enums should have plural names", Justification = "For legacy support")]
- [Flags]
- public enum BooleanToVisibilityHint
- {
- ///
- /// Do not modify the boolean type conversion from it's default action of using the Visibility.Collapsed.
- ///
- None = 0,
-
- ///
- /// Inverse the action of the boolean type conversion, when it's true collapse the visibility.
- ///
- Inverse = 1 << 1,
+ None = 0,
-#if !NETFX_CORE && !HAS_UNO
- ///
- /// Use the hidden version rather than the Collapsed.
- ///
- UseHidden = 1 << 2,
-#endif
- }
+ ///
+ /// Inverse the action of the boolean type conversion, when it's true collapse the visibility.
+ ///
+ Inverse = 1 << 1,
}
diff --git a/src/ReactiveUI.Uno/Common/BooleanToVisibilityTypeConverter.cs b/src/ReactiveUI.Uno/Common/BooleanToVisibilityTypeConverter.cs
index c945fa3..ec220d8 100644
--- a/src/ReactiveUI.Uno/Common/BooleanToVisibilityTypeConverter.cs
+++ b/src/ReactiveUI.Uno/Common/BooleanToVisibilityTypeConverter.cs
@@ -4,72 +4,60 @@
// See the LICENSE file in the project root for full license information.
using System;
+
#if HAS_WINUI
using Microsoft.UI.Xaml;
-#elif NETFX_CORE || HAS_UNO
-using Windows.UI.Xaml;
#else
-using System.Windows;
+using Windows.UI.Xaml;
#endif
-#if HAS_UNO
-namespace ReactiveUI.Uno
-#else
-namespace ReactiveUI
-#endif
+namespace ReactiveUI.Uno;
+
+///
+/// This type convert converts between Boolean and XAML Visibility - the
+/// conversionHint is a BooleanToVisibilityHint.
+///
+public class BooleanToVisibilityTypeConverter : IBindingTypeConverter
{
- ///
- /// This type convert converts between Boolean and XAML Visibility - the
- /// conversionHint is a BooleanToVisibilityHint.
- ///
- public class BooleanToVisibilityTypeConverter : IBindingTypeConverter
+ ///
+ public int GetAffinityForObjects(Type fromType, Type toType)
{
- ///
- public int GetAffinityForObjects(Type fromType, Type toType)
+ if (fromType == typeof(bool) && toType == typeof(Visibility))
{
- if (fromType == typeof(bool) && toType == typeof(Visibility))
- {
- return 10;
- }
-
- if (fromType == typeof(Visibility) && toType == typeof(bool))
- {
- return 10;
- }
-
- return 0;
+ return 10;
}
- ///
- public bool TryConvert(object? from, Type toType, object? conversionHint, out object result)
+ if (fromType == typeof(Visibility) && toType == typeof(bool))
{
- var hint = conversionHint is BooleanToVisibilityHint visibilityHint ?
- visibilityHint :
- BooleanToVisibilityHint.None;
-
- if (toType == typeof(Visibility) && from is bool fromBool)
- {
- var fromAsBool = (hint & BooleanToVisibilityHint.Inverse) != 0 ? !fromBool : fromBool;
+ return 10;
+ }
-#if !NETFX_CORE && !HAS_UNO && !HAS_WINUI
- var notVisible = (hint & BooleanToVisibilityHint.UseHidden) != 0 ? Visibility.Hidden : Visibility.Collapsed;
-#else
- var notVisible = Visibility.Collapsed;
-#endif
- result = fromAsBool ? Visibility.Visible : notVisible;
- return true;
- }
+ return 0;
+ }
- if (from is Visibility fromAsVis)
- {
- result = fromAsVis == Visibility.Visible ^ (hint & BooleanToVisibilityHint.Inverse) == 0;
- }
- else
- {
- result = Visibility.Visible;
- }
+ ///
+ public bool TryConvert(object? from, Type toType, object? conversionHint, out object result)
+ {
+ var hint = conversionHint is BooleanToVisibilityHint visibilityHint ?
+ visibilityHint :
+ BooleanToVisibilityHint.None;
+ if (toType == typeof(Visibility) && from is bool fromBool)
+ {
+ var fromAsBool = (hint & BooleanToVisibilityHint.Inverse) != 0 ? !fromBool : fromBool;
+ result = fromAsBool ? Visibility.Visible : Visibility.Collapsed;
return true;
}
+
+ if (from is Visibility fromAsVis)
+ {
+ result = fromAsVis == Visibility.Visible ^ (hint & BooleanToVisibilityHint.Inverse) == 0;
+ }
+ else
+ {
+ result = Visibility.Visible;
+ }
+
+ return true;
}
}
diff --git a/src/ReactiveUI.Uno/Common/PlatformOperations.cs b/src/ReactiveUI.Uno/Common/PlatformOperations.cs
index 0d8850a..192b177 100644
--- a/src/ReactiveUI.Uno/Common/PlatformOperations.cs
+++ b/src/ReactiveUI.Uno/Common/PlatformOperations.cs
@@ -5,32 +5,23 @@
using System;
-#if HAS_UNO
-namespace ReactiveUI.Uno
-#else
-namespace ReactiveUI
-#endif
+namespace ReactiveUI.Uno;
+
+///
+/// Returns the current orientation of the device on Windows.
+///
+public class PlatformOperations : IPlatformOperations
{
- ///
- /// Returns the current orientation of the device on Windows.
- ///
- public class PlatformOperations : IPlatformOperations
+ ///
+ public string? GetOrientation()
{
- ///
- public string? GetOrientation()
+ try
+ {
+ return Windows.Graphics.Display.DisplayInformation.GetForCurrentView().CurrentOrientation.ToString();
+ }
+ catch (Exception)
{
-#if NETFX_CORE || HAS_UNO
- try
- {
- return Windows.Graphics.Display.DisplayInformation.GetForCurrentView().CurrentOrientation.ToString();
- }
- catch (Exception)
- {
- return null;
- }
-#else
return null;
-#endif
}
}
}
diff --git a/src/ReactiveUI.Uno/Common/ReactivePage.cs b/src/ReactiveUI.Uno/Common/ReactivePage.cs
index 4b2f5d2..1cef2ae 100644
--- a/src/ReactiveUI.Uno/Common/ReactivePage.cs
+++ b/src/ReactiveUI.Uno/Common/ReactivePage.cs
@@ -4,171 +4,155 @@
// See the LICENSE file in the project root for full license information.
using System;
+
#if HAS_WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
-#elif NETFX_CORE || HAS_UNO
+#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
-#else
-using System.Windows;
-using System.Windows.Controls;
#endif
-#if HAS_UNO
-namespace ReactiveUI.Uno
-#else
-namespace ReactiveUI
+namespace ReactiveUI.Uno;
+
+///
+/// A that is reactive.
+///
+///
+///
+/// This class is a that is also reactive. That is, it implements .
+/// You can extend this class to get an implementation of rather than writing one yourself.
+///
+///
+/// Note that the XAML for your control must specify the same base class, including the generic argument you provide for your view
+/// model. To do this, use the TypeArguments attribute as follows:
+///
+///
+///
+///
+/// ]]>
+///
+///
+///
+/// Note that UWP and WinUI projects do not support the TypeArguments attribute. The XAML designer window in WPF projects also does not
+/// support generic types. To use in XAML documents you need to create a base class
+/// where you derive from with the type argument filled in.
+///
+/// { /* No code needed here */ }
+///
+/// public partial class YourView : YourViewBase
+/// {
+/// /* Your code */
+/// }
+/// ]]>
+///
+/// Then you can use this base class as root in your XAML document.
+///
+///
+///
+///
+/// ]]>
+///
+///
+///
+///
+/// The type of the view model backing the view.
+///
+#if IOS
+[global::Foundation.Register]
#endif
+public partial class ReactivePage :
+ Page, IViewFor
+ where TViewModel : class
{
///
- /// A that is reactive.
+ /// The view model dependency property.
///
- ///
- ///
- /// This class is a that is also reactive. That is, it implements .
- /// You can extend this class to get an implementation of rather than writing one yourself.
- ///
- ///
- /// Note that the XAML for your control must specify the same base class, including the generic argument you provide for your view
- /// model. To do this, use the TypeArguments attribute as follows:
- ///
- ///
- ///
- ///
- /// ]]>
- ///
- ///
- ///
- /// Note that UWP and WinUI projects do not support the TypeArguments attribute. The XAML designer window in WPF projects also does not
- /// support generic types. To use in XAML documents you need to create a base class
- /// where you derive from with the type argument filled in.
- ///
- /// { /* No code needed here */ }
- ///
- /// public partial class YourView : YourViewBase
- /// {
- /// /* Your code */
- /// }
- /// ]]>
- ///
- /// Then you can use this base class as root in your XAML document.
- ///
- ///
- ///
- ///
- /// ]]>
- ///
- ///
- ///
- ///
- /// The type of the view model backing the view.
- ///
-#if HAS_UNO && IOS
- [global::Foundation.Register]
-#endif
- public
-#if HAS_UNO
- partial
-#endif
- class ReactivePage :
- Page, IViewFor
- where TViewModel : class
- {
- ///
- /// The view model dependency property.
- ///
- public static readonly DependencyProperty ViewModelProperty =
- DependencyProperty.Register(
- "ViewModel",
- typeof(TViewModel),
- typeof(ReactivePage),
- new PropertyMetadata(null));
+ public static readonly DependencyProperty ViewModelProperty =
+ DependencyProperty.Register(
+ "ViewModel",
+ typeof(TViewModel),
+ typeof(ReactivePage),
+ new PropertyMetadata(null));
- static ReactivePage()
- {
- var a = ActivationHelper.UnoActivated;
- }
+ static ReactivePage() => _ = ActivationHelper.UnoActivated;
-#if HAS_UNO
- ///
- /// Initializes a new instance of the class.
- ///
- protected ReactivePage()
- {
- // needed so the others are optional.
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected ReactivePage()
+ {
+ // needed so the others are optional.
+ }
#if ANDROID
- ///
- /// Initializes a new instance of the class.
- /// Native constructor, do not use explicitly.
- ///
- ///
- /// Used by the Xamarin Runtime to materialize native
- /// objects that may have been collected in the managed world.
- ///
- /// A containing a Java Native Interface (JNI) object reference.
- /// A indicating how to handle.
- protected ReactivePage(IntPtr javaReference, global::Android.Runtime.JniHandleOwnership transfer)
- : base(javaReference, transfer)
- {
- }
+ ///
+ /// Initializes a new instance of the class.
+ /// Native constructor, do not use explicitly.
+ ///
+ ///
+ /// Used by the Xamarin Runtime to materialize native
+ /// objects that may have been collected in the managed world.
+ ///
+ /// A containing a Java Native Interface (JNI) object reference.
+ /// A indicating how to handle.
+ protected ReactivePage(IntPtr javaReference, global::Android.Runtime.JniHandleOwnership transfer)
+ : base(javaReference, transfer)
+ {
+ }
#endif
#if IOS
- ///
- /// Initializes a new instance of the class.
- /// Native constructor, do not use explicitly.
- ///
- /// Handle to the native control.
- ///
- /// Used by the Xamarin Runtime to materialize native.
- /// objects that may have been collected in the managed world.
- ///
- protected ReactivePage(IntPtr handle)
- : base(handle)
- {
- }
-#endif
+ ///
+ /// Initializes a new instance of the class.
+ /// Native constructor, do not use explicitly.
+ ///
+ /// Handle to the native control.
+ ///
+ /// Used by the Xamarin Runtime to materialize native.
+ /// objects that may have been collected in the managed world.
+ ///
+ protected ReactivePage(IntPtr handle)
+ : base(handle)
+ {
+ }
#endif
- ///
- /// Gets the binding root view model.
- ///
- public TViewModel? BindingRoot => ViewModel;
+ ///
+ /// Gets the binding root view model.
+ ///
+ public TViewModel? BindingRoot => ViewModel;
- ///
- public TViewModel? ViewModel
- {
- get => (TViewModel)GetValue(ViewModelProperty);
- set => SetValue(ViewModelProperty, value);
- }
+ ///
+ public TViewModel? ViewModel
+ {
+ get => (TViewModel)GetValue(ViewModelProperty);
+ set => SetValue(ViewModelProperty, value);
+ }
- ///
- object? IViewFor.ViewModel
- {
- get => ViewModel;
- set => ViewModel = (TViewModel?)value;
- }
+ ///
+ object? IViewFor.ViewModel
+ {
+ get => ViewModel;
+ set => ViewModel = (TViewModel?)value;
}
}
diff --git a/src/ReactiveUI.Uno/Common/ReactiveUserControl.cs b/src/ReactiveUI.Uno/Common/ReactiveUserControl.cs
index aab9bca..1b8596e 100644
--- a/src/ReactiveUI.Uno/Common/ReactiveUserControl.cs
+++ b/src/ReactiveUI.Uno/Common/ReactiveUserControl.cs
@@ -4,173 +4,155 @@
// See the LICENSE file in the project root for full license information.
using System;
-using System.Diagnostics.CodeAnalysis;
+
#if HAS_WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
-#elif NETFX_CORE || HAS_UNO
+#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
-#else
-using System.Windows;
-using System.Windows.Controls;
#endif
-#if HAS_UNO
-namespace ReactiveUI.Uno
-#else
-namespace ReactiveUI
+namespace ReactiveUI.Uno;
+
+///
+/// A that is reactive.
+///
+///
+///
+/// This class is a that is also reactive. That is, it implements .
+/// You can extend this class to get an implementation of rather than writing one yourself.
+///
+///
+/// Note that the XAML for your control must specify the same base class, including the generic argument you provide for your view
+/// model. To do this, use the TypeArguments attribute as follows:
+///
+///
+///
+///
+/// ]]>
+///
+///
+///
+/// Note that UWP and WinUI projects do not support the TypeArguments attribute. The XAML designer window in WPF projects also does not
+/// support generic types. To use in XAML documents you need to create a base class
+/// where you derive from with the type argument filled in.
+///
+/// { /* No code needed here */ }
+///
+/// public partial class YourView : YourViewBase
+/// {
+/// /* Your code */
+/// }
+/// ]]>
+///
+/// Then you can use this base class as root in your XAML document.
+///
+///
+///
+///
+/// ]]>
+///
+///
+///
+///
+/// The type of the view model backing the view.
+///
+#if IOS
+[global::Foundation.Register]
#endif
+public partial class ReactiveUserControl :
+ UserControl, IViewFor
+ where TViewModel : class
{
///
- /// A that is reactive.
+ /// The view model dependency property.
///
- ///
- ///
- /// This class is a that is also reactive. That is, it implements .
- /// You can extend this class to get an implementation of rather than writing one yourself.
- ///
- ///
- /// Note that the XAML for your control must specify the same base class, including the generic argument you provide for your view
- /// model. To do this, use the TypeArguments attribute as follows:
- ///
- ///
- ///
- ///
- /// ]]>
- ///
- ///
- ///
- /// Note that UWP and WinUI projects do not support the TypeArguments attribute. The XAML designer window in WPF projects also does not
- /// support generic types. To use in XAML documents you need to create a base class
- /// where you derive from with the type argument filled in.
- ///
- /// { /* No code needed here */ }
- ///
- /// public partial class YourView : YourViewBase
- /// {
- /// /* Your code */
- /// }
- /// ]]>
- ///
- /// Then you can use this base class as root in your XAML document.
- ///
- ///
- ///
- ///
- /// ]]>
- ///
- ///
- ///
- ///
- /// The type of the view model backing the view.
- ///
-#if HAS_UNO && IOS
- [global::Foundation.Register]
-#endif
- [SuppressMessage("Design", "CA1010:Collections should implement generic interface", Justification = "Deliberate usage")]
- public
-#if HAS_UNO
- partial
-#endif
- class ReactiveUserControl :
- UserControl, IViewFor
- where TViewModel : class
- {
- ///
- /// The view model dependency property.
- ///
- public static readonly DependencyProperty ViewModelProperty =
- DependencyProperty.Register(
- "ViewModel",
- typeof(TViewModel),
- typeof(ReactiveUserControl),
- new PropertyMetadata(null));
+ public static readonly DependencyProperty ViewModelProperty =
+ DependencyProperty.Register(
+ "ViewModel",
+ typeof(TViewModel),
+ typeof(ReactiveUserControl),
+ new PropertyMetadata(null));
- static ReactiveUserControl()
- {
- var a = ActivationHelper.UnoActivated;
- }
+ static ReactiveUserControl() => _ = ActivationHelper.UnoActivated;
-#if HAS_UNO
- ///
- /// Initializes a new instance of the class.
- ///
- protected ReactiveUserControl()
- {
- // needed so the others are optional.
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected ReactiveUserControl()
+ {
+ // needed so the others are optional.
+ }
#if ANDROID
- ///
- /// Initializes a new instance of the class.
- /// Native constructor, do not use explicitly.
- ///
- ///
- /// Used by the Xamarin Runtime to materialize native
- /// objects that may have been collected in the managed world.
- ///
- /// A containing a Java Native Interface (JNI) object reference.
- /// A indicating how to handle handle.
- protected ReactiveUserControl(IntPtr javaReference, global::Android.Runtime.JniHandleOwnership transfer)
- : base(javaReference, transfer)
- {
- }
+ ///
+ /// Initializes a new instance of the class.
+ /// Native constructor, do not use explicitly.
+ ///
+ ///
+ /// Used by the Xamarin Runtime to materialize native
+ /// objects that may have been collected in the managed world.
+ ///
+ /// A containing a Java Native Interface (JNI) object reference.
+ /// A indicating how to handle handle.
+ protected ReactiveUserControl(IntPtr javaReference, global::Android.Runtime.JniHandleOwnership transfer)
+ : base(javaReference, transfer)
+ {
+ }
#endif
#if IOS
- ///
- /// Initializes a new instance of the class.
- /// Native constructor, do not use explicitly.
- ///
- /// Handle to the native control.
- ///
- /// Used by the Xamarin Runtime to materialize native.
- /// objects that may have been collected in the managed world.
- ///
- protected ReactiveUserControl(IntPtr handle)
- : base(handle)
- {
- }
-#endif
+ ///
+ /// Initializes a new instance of the class.
+ /// Native constructor, do not use explicitly.
+ ///
+ /// Handle to the native control.
+ ///
+ /// Used by the Xamarin Runtime to materialize native.
+ /// objects that may have been collected in the managed world.
+ ///
+ protected ReactiveUserControl(IntPtr handle)
+ : base(handle)
+ {
+ }
#endif
- ///
- /// Gets the binding root view model.
- ///
- public TViewModel? BindingRoot => ViewModel;
+ ///
+ /// Gets the binding root view model.
+ ///
+ public TViewModel? BindingRoot => ViewModel;
- ///
- public TViewModel? ViewModel
- {
- get => (TViewModel)GetValue(ViewModelProperty);
- set => SetValue(ViewModelProperty, value);
- }
+ ///
+ public TViewModel? ViewModel
+ {
+ get => (TViewModel)GetValue(ViewModelProperty);
+ set => SetValue(ViewModelProperty, value);
+ }
- ///
- object? IViewFor.ViewModel
- {
- get => ViewModel;
- set => ViewModel = (TViewModel?)value;
- }
+ ///
+ object? IViewFor.ViewModel
+ {
+ get => ViewModel;
+ set => ViewModel = (TViewModel?)value;
}
}
diff --git a/src/ReactiveUI.Uno/Common/RoutedViewHost.cs b/src/ReactiveUI.Uno/Common/RoutedViewHost.cs
index f4d12dd..4147571 100644
--- a/src/ReactiveUI.Uno/Common/RoutedViewHost.cs
+++ b/src/ReactiveUI.Uno/Common/RoutedViewHost.cs
@@ -10,181 +10,151 @@
#if HAS_WINUI
using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Controls;
-#elif NETFX_CORE || HAS_UNO
-using System.Windows;
-using Windows.UI.Xaml;
-using Windows.UI.Xaml.Controls;
#else
-
-using System.Windows;
-using System.Windows.Controls;
-
+using Windows.UI.Xaml;
#endif
-#if HAS_UNO
-namespace ReactiveUI.Uno
-#else
+namespace ReactiveUI.Uno;
-namespace ReactiveUI
-#endif
+///
+/// This control hosts the View associated with a Router, and will display
+/// the View and wire up the ViewModel whenever a new ViewModel is
+/// navigated to. Put this control as the only control in your Window.
+///
+public partial class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger
{
///
- /// This control hosts the View associated with a Router, and will display
- /// the View and wire up the ViewModel whenever a new ViewModel is
- /// navigated to. Put this control as the only control in your Window.
+ /// The router dependency property.
///
- public
-#if HAS_UNO
- partial
-#endif
- class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger
+ public static readonly DependencyProperty RouterProperty =
+ DependencyProperty.Register("Router", typeof(RoutingState), typeof(RoutedViewHost), new PropertyMetadata(null));
+
+ ///
+ /// The default content property.
+ ///
+ public static readonly DependencyProperty DefaultContentProperty =
+ DependencyProperty.Register("DefaultContent", typeof(object), typeof(RoutedViewHost), new PropertyMetadata(null));
+
+ ///
+ /// The view contract observable property.
+ ///
+ public static readonly DependencyProperty ViewContractObservableProperty =
+ DependencyProperty.Register("ViewContractObservable", typeof(IObservable), typeof(RoutedViewHost), new PropertyMetadata(Observable.Default));
+
+ private string? _viewContract;
+
+ static RoutedViewHost() => _ = ActivationHelper.UnoActivated;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public RoutedViewHost()
{
- ///
- /// The router dependency property.
- ///
- public static readonly DependencyProperty RouterProperty =
- DependencyProperty.Register("Router", typeof(RoutingState), typeof(RoutedViewHost), new PropertyMetadata(null));
-
- ///
- /// The default content property.
- ///
- public static readonly DependencyProperty DefaultContentProperty =
- DependencyProperty.Register("DefaultContent", typeof(object), typeof(RoutedViewHost), new PropertyMetadata(null));
-
- ///
- /// The view contract observable property.
- ///
- public static readonly DependencyProperty ViewContractObservableProperty =
- DependencyProperty.Register("ViewContractObservable", typeof(IObservable), typeof(RoutedViewHost), new PropertyMetadata(Observable.Default));
-
- private string? _viewContract;
-
- static RoutedViewHost()
- {
- var a = ActivationHelper.UnoActivated;
- }
+ HorizontalContentAlignment = HorizontalAlignment.Stretch;
+ VerticalContentAlignment = VerticalAlignment.Stretch;
- ///
- /// Initializes a new instance of the class.
- ///
- public RoutedViewHost()
- {
-#if NETFX_CORE
- DefaultStyleKey = typeof(RoutedViewHost);
-#endif
- HorizontalContentAlignment = HorizontalAlignment.Stretch;
- VerticalContentAlignment = VerticalAlignment.Stretch;
-
- var platform = Locator.Current.GetService();
- Func platformGetter = () => default;
-
- if (platform is null)
- {
- // NB: This used to be an error but WPF design mode can't read
- // good or do other stuff good.
- this.Log().Error("Couldn't find an IPlatformOperations implementation. Please make sure you have installed the latest version of the ReactiveUI packages for your platform. See https://reactiveui.net/docs/getting-started/installation for guidance.");
- }
- else
- {
- platformGetter = () => platform.GetOrientation();
- }
-
- ViewContractObservable = ModeDetector.InUnitTestRunner()
- ? Observable.Never
- : Observable.FromEvent(
- eventHandler =>
- {
- void Handler(object sender, SizeChangedEventArgs e) => eventHandler(platformGetter());
- return Handler;
- },
- x => SizeChanged += x,
- x => SizeChanged -= x)
- .StartWith(platformGetter())
- .DistinctUntilChanged();
-
- IRoutableViewModel? currentViewModel = null;
- var vmAndContract = this.WhenAnyObservable(x => x.Router.CurrentViewModel).Do(x => currentViewModel = x).StartWith(currentViewModel).CombineLatest(
- this.WhenAnyObservable(x => x.ViewContractObservable).Do(x => _viewContract = x).StartWith(ViewContract),
- (viewModel, contract) => (viewModel, contract));
-
- this.WhenActivated(d =>
- {
- // NB: The DistinctUntilChanged is useful because most views in
- // WinRT will end up getting here twice - once for configuring
- // the RoutedViewHost's ViewModel, and once on load via SizeChanged
- d(vmAndContract.DistinctUntilChanged<(IRoutableViewModel? viewModel, string? contract)>().Subscribe(
- ResolveViewForViewModel,
- ex => RxApp.DefaultExceptionHandler.OnNext(ex)));
- });
- }
+ var platform = Locator.Current.GetService();
+ Func platformGetter = () => default;
- ///
- /// Gets or sets the of the view model stack.
- ///
- public RoutingState Router
+ if (platform is null)
{
- get => (RoutingState)GetValue(RouterProperty);
- set => SetValue(RouterProperty, value);
+ // NB: This used to be an error but WPF design mode can't read
+ // good or do other stuff good.
+ this.Log().Error("Couldn't find an IPlatformOperations implementation. Please make sure you have installed the latest version of the ReactiveUI packages for your platform. See https://reactiveui.net/docs/getting-started/installation for guidance.");
}
-
- ///
- /// Gets or sets the content displayed whenever there is no page currently
- /// routed.
- ///
- public object DefaultContent
+ else
{
- get => GetValue(DefaultContentProperty);
- set => SetValue(DefaultContentProperty, value);
+ platformGetter = () => platform.GetOrientation();
}
- ///
- /// Gets or sets the view contract observable.
- ///
- ///
- /// The view contract observable.
- ///
- public IObservable ViewContractObservable
+ ViewContractObservable = ModeDetector.InUnitTestRunner()
+ ? Observable.Never
+ : Observable.FromEvent(
+ eventHandler =>
+ {
+ void Handler(object sender, SizeChangedEventArgs e) => eventHandler(platformGetter());
+ return Handler;
+ },
+ x => SizeChanged += x,
+ x => SizeChanged -= x)
+ .StartWith(platformGetter())
+ .DistinctUntilChanged();
+
+ IRoutableViewModel? currentViewModel = null;
+ var vmAndContract = this.WhenAnyObservable(x => x.Router.CurrentViewModel).Do(x => currentViewModel = x).StartWith(currentViewModel).CombineLatest(
+ this.WhenAnyObservable(x => x.ViewContractObservable).Do(x => _viewContract = x).StartWith(ViewContract),
+ (viewModel, contract) => (viewModel, contract));
+
+ this.WhenActivated(d =>
{
- get => (IObservable)GetValue(ViewContractObservableProperty);
- set => SetValue(ViewContractObservableProperty, value);
- }
+ // NB: The DistinctUntilChanged is useful because most views in
+ // WinRT will end up getting here twice - once for configuring
+ // the RoutedViewHost's ViewModel, and once on load via SizeChanged
+ d(vmAndContract.DistinctUntilChanged<(IRoutableViewModel? viewModel, string? contract)>().Subscribe(
+ ResolveViewForViewModel,
+ ex => RxApp.DefaultExceptionHandler.OnNext(ex)));
+ });
+ }
- ///
- /// Gets or sets the view contract.
- ///
- public string? ViewContract
- {
- get => _viewContract;
- set => ViewContractObservable = Observable.Return(value);
- }
+ ///
+ /// Gets or sets the of the view model stack.
+ ///
+ public RoutingState Router
+ {
+ get => (RoutingState)GetValue(RouterProperty);
+ set => SetValue(RouterProperty, value);
+ }
- ///
- /// Gets or sets the view locator.
- ///
- ///
- /// The view locator.
- ///
- public IViewLocator? ViewLocator { get; set; }
+ ///
+ /// Gets or sets the content displayed whenever there is no page currently
+ /// routed.
+ ///
+ public object DefaultContent
+ {
+ get => GetValue(DefaultContentProperty);
+ set => SetValue(DefaultContentProperty, value);
+ }
- private void ResolveViewForViewModel((IRoutableViewModel? viewModel, string? contract) x)
+ ///
+ /// Gets or sets the view contract observable.
+ ///
+ ///
+ /// The view contract observable.
+ ///
+ public IObservable ViewContractObservable
+ {
+ get => (IObservable)GetValue(ViewContractObservableProperty);
+ set => SetValue(ViewContractObservableProperty, value);
+ }
+
+ ///
+ /// Gets or sets the view contract.
+ ///
+ public string? ViewContract
+ {
+ get => _viewContract;
+ set => ViewContractObservable = Observable.Return(value);
+ }
+
+ ///
+ /// Gets or sets the view locator.
+ ///
+ ///
+ /// The view locator.
+ ///
+ public IViewLocator? ViewLocator { get; set; }
+
+ private void ResolveViewForViewModel((IRoutableViewModel? viewModel, string? contract) x)
+ {
+ if (x.viewModel is null)
{
- if (x.viewModel is null)
- {
- Content = DefaultContent;
- return;
- }
-
- var viewLocator = ViewLocator ?? ReactiveUI.ViewLocator.Current;
- var view = viewLocator.ResolveView(x.viewModel, x.contract) ?? viewLocator.ResolveView(x.viewModel);
-
- if (view is null)
- {
- throw new Exception($"Couldn't find view for '{x.viewModel}'.");
- }
-
- view.ViewModel = x.viewModel;
- Content = view;
+ Content = DefaultContent;
+ return;
}
+
+ var viewLocator = ViewLocator ?? ReactiveUI.ViewLocator.Current;
+ var view = (viewLocator.ResolveView(x.viewModel, x.contract) ?? viewLocator.ResolveView(x.viewModel)) ?? throw new Exception($"Couldn't find view for '{x.viewModel}'.");
+ view.ViewModel = x.viewModel;
+ Content = view;
}
}
diff --git a/src/ReactiveUI.Uno/Common/ViewModelViewHost.cs b/src/ReactiveUI.Uno/Common/ViewModelViewHost.cs
index cc14f38..6da5f87 100644
--- a/src/ReactiveUI.Uno/Common/ViewModelViewHost.cs
+++ b/src/ReactiveUI.Uno/Common/ViewModelViewHost.cs
@@ -8,180 +8,150 @@
using Splat;
#if HAS_WINUI
-
using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Controls;
-
-#elif NETFX_CORE || HAS_UNO
-
-using System.Windows;
+#else
using Windows.UI.Xaml;
-using Windows.UI.Xaml.Controls;
+#endif
-#else
+namespace ReactiveUI.Uno;
+
+///
+/// This content control will automatically load the View associated with
+/// the ViewModel property and display it. This control is very useful
+/// inside a DataTemplate to display the View associated with a ViewModel.
+///
+public partial class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger
+{
+ ///
+ /// The default content dependency property.
+ ///
+ public static readonly DependencyProperty DefaultContentProperty =
+ DependencyProperty.Register(nameof(DefaultContent), typeof(object), typeof(ViewModelViewHost), new PropertyMetadata(null));
-using System.Windows;
-using System.Windows.Controls;
+ ///
+ /// The view model dependency property.
+ ///
+ public static readonly DependencyProperty ViewModelProperty =
+ DependencyProperty.Register(nameof(ViewModel), typeof(object), typeof(ViewModelViewHost), new PropertyMetadata(null));
-#endif
+ ///
+ /// The view contract observable dependency property.
+ ///
+ public static readonly DependencyProperty ViewContractObservableProperty =
+ DependencyProperty.Register(nameof(ViewContractObservable), typeof(IObservable), typeof(ViewModelViewHost), new PropertyMetadata(Observable.Default));
-#if HAS_UNO
+ private string? _viewContract;
-namespace ReactiveUI.Uno
-#else
+ static ViewModelViewHost() => _ = ActivationHelper.UnoActivated;
-namespace ReactiveUI
-#endif
-{
///
- /// This content control will automatically load the View associated with
- /// the ViewModel property and display it. This control is very useful
- /// inside a DataTemplate to display the View associated with a ViewModel.
+ /// Initializes a new instance of the class.
///
- public
-#if HAS_UNO
- partial
-#endif
- class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger
+ public ViewModelViewHost()
{
- ///
- /// The default content dependency property.
- ///
- public static readonly DependencyProperty DefaultContentProperty =
- DependencyProperty.Register(nameof(DefaultContent), typeof(object), typeof(ViewModelViewHost), new PropertyMetadata(null));
-
- ///
- /// The view model dependency property.
- ///
- public static readonly DependencyProperty ViewModelProperty =
- DependencyProperty.Register(nameof(ViewModel), typeof(object), typeof(ViewModelViewHost), new PropertyMetadata(null));
-
- ///
- /// The view contract observable dependency property.
- ///
- public static readonly DependencyProperty ViewContractObservableProperty =
- DependencyProperty.Register(nameof(ViewContractObservable), typeof(IObservable), typeof(ViewModelViewHost), new PropertyMetadata(Observable.Default));
-
- private string? _viewContract;
-
- static ViewModelViewHost()
- {
- var a = ActivationHelper.UnoActivated;
- }
+ var platform = Locator.Current.GetService();
+ Func platformGetter = () => default;
- ///
- /// Initializes a new instance of the class.
- ///
- public ViewModelViewHost()
+ if (platform is null)
{
-#if NETFX_CORE
- DefaultStyleKey = typeof(ViewModelViewHost);
-#endif
-
- var platform = Locator.Current.GetService();
- Func platformGetter = () => default;
-
- if (platform is null)
- {
- // NB: This used to be an error but WPF design mode can't read
- // good or do other stuff good.
- this.Log().Error("Couldn't find an IPlatformOperations implementation. Please make sure you have installed the latest version of the ReactiveUI packages for your platform. See https://reactiveui.net/docs/getting-started/installation for guidance.");
- }
- else
- {
- platformGetter = () => platform.GetOrientation();
- }
-
- ViewContractObservable = ModeDetector.InUnitTestRunner()
- ? Observable.Never
- : Observable.FromEvent(
- eventHandler =>
- {
- void Handler(object? sender, SizeChangedEventArgs e) => eventHandler(platformGetter()!);
- return Handler;
- },
- x => SizeChanged += x,
- x => SizeChanged -= x)
- .StartWith(platformGetter())
- .DistinctUntilChanged();
-
- var contractChanged = this.WhenAnyObservable(x => x.ViewContractObservable).Do(x => _viewContract = x).StartWith(ViewContract);
- var viewModelChanged = this.WhenAnyValue(x => x.ViewModel).StartWith(ViewModel);
- var vmAndContract = contractChanged
- .CombineLatest(viewModelChanged, (contract, vm) => (ViewModel: vm, Contract: contract));
-
- this.WhenActivated(d =>
- {
- d(contractChanged
- .ObserveOn(RxApp.MainThreadScheduler)
- .Subscribe(x => _viewContract = x ?? string.Empty));
-
- d(vmAndContract.DistinctUntilChanged().Subscribe(x => ResolveViewForViewModel(x.ViewModel, x.Contract)));
- });
+ // NB: This used to be an error but WPF design mode can't read
+ // good or do other stuff good.
+ this.Log().Error("Couldn't find an IPlatformOperations implementation. Please make sure you have installed the latest version of the ReactiveUI packages for your platform. See https://reactiveui.net/docs/getting-started/installation for guidance.");
}
-
- ///
- /// Gets or sets the view contract observable.
- ///
- public IObservable ViewContractObservable
+ else
{
- get => (IObservable)GetValue(ViewContractObservableProperty);
- set => SetValue(ViewContractObservableProperty, value);
+ platformGetter = () => platform.GetOrientation();
}
- ///
- /// Gets or sets the content displayed by default when no content is set.
- ///
- public object DefaultContent
+ ViewContractObservable = ModeDetector.InUnitTestRunner()
+ ? Observable.Never
+ : Observable.FromEvent(
+ eventHandler =>
+ {
+ void Handler(object? sender, SizeChangedEventArgs e) => eventHandler(platformGetter());
+ return Handler;
+ },
+ x => SizeChanged += x,
+ x => SizeChanged -= x)
+ .StartWith(platformGetter())
+ .DistinctUntilChanged();
+
+ var contractChanged = this.WhenAnyObservable(x => x.ViewContractObservable).Do(x => _viewContract = x).StartWith(ViewContract);
+ var viewModelChanged = this.WhenAnyValue(x => x.ViewModel).StartWith(ViewModel);
+ var vmAndContract = contractChanged
+ .CombineLatest(viewModelChanged, (contract, vm) => (ViewModel: vm, Contract: contract));
+
+ this.WhenActivated(d =>
{
- get => GetValue(DefaultContentProperty);
- set => SetValue(DefaultContentProperty, value);
- }
+ d(contractChanged
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(x => _viewContract = x ?? string.Empty));
- ///
- /// Gets or sets the ViewModel to display.
- ///
- public object? ViewModel
- {
- get => GetValue(ViewModelProperty);
- set => SetValue(ViewModelProperty, value);
- }
+ d(vmAndContract.DistinctUntilChanged().Subscribe(x => ResolveViewForViewModel(x.ViewModel, x.Contract)));
+ });
+ }
- ///
- /// Gets or sets the view contract.
- ///
- public string? ViewContract
- {
- get => _viewContract;
- set => ViewContractObservable = Observable.Return(value);
- }
+ ///
+ /// Gets or sets the view contract observable.
+ ///
+ public IObservable ViewContractObservable
+ {
+ get => (IObservable)GetValue(ViewContractObservableProperty);
+ set => SetValue(ViewContractObservableProperty, value);
+ }
- ///
- /// Gets or sets the view locator.
- ///
- public IViewLocator? ViewLocator { get; set; }
+ ///
+ /// Gets or sets the content displayed by default when no content is set.
+ ///
+ public object DefaultContent
+ {
+ get => GetValue(DefaultContentProperty);
+ set => SetValue(DefaultContentProperty, value);
+ }
- private void ResolveViewForViewModel(object? viewModel, string? contract)
- {
- if (viewModel is null)
- {
- Content = DefaultContent;
- return;
- }
+ ///
+ /// Gets or sets the ViewModel to display.
+ ///
+ public object? ViewModel
+ {
+ get => GetValue(ViewModelProperty);
+ set => SetValue(ViewModelProperty, value);
+ }
- var viewLocator = ViewLocator ?? ReactiveUI.ViewLocator.Current;
- var viewInstance = viewLocator.ResolveView(viewModel, contract) ?? viewLocator.ResolveView(viewModel);
+ ///
+ /// Gets or sets the view contract.
+ ///
+ public string? ViewContract
+ {
+ get => _viewContract;
+ set => ViewContractObservable = Observable.Return(value);
+ }
+
+ ///
+ /// Gets or sets the view locator.
+ ///
+ public IViewLocator? ViewLocator { get; set; }
- if (viewInstance is null)
- {
- Content = DefaultContent;
- this.Log().Warn($"The {nameof(ViewModelViewHost)} could not find a valid view for the view model of type {viewModel.GetType()} and value {viewModel}.");
- return;
- }
+ private void ResolveViewForViewModel(object? viewModel, string? contract)
+ {
+ if (viewModel is null)
+ {
+ Content = DefaultContent;
+ return;
+ }
- viewInstance.ViewModel = viewModel;
+ var viewLocator = ViewLocator ?? ReactiveUI.ViewLocator.Current;
+ var viewInstance = viewLocator.ResolveView(viewModel, contract) ?? viewLocator.ResolveView(viewModel);
- Content = viewInstance;
+ if (viewInstance is null)
+ {
+ Content = DefaultContent;
+ this.Log().Warn($"The {nameof(ViewModelViewHost)} could not find a valid view for the view model of type {viewModel.GetType()} and value {viewModel}.");
+ return;
}
+
+ viewInstance.ViewModel = viewModel;
+
+ Content = viewInstance;
}
}
diff --git a/src/ReactiveUI.Uno/CoreDispatcherScheduler.cs b/src/ReactiveUI.Uno/CoreDispatcherScheduler.cs
index f82b710..2117170 100644
--- a/src/ReactiveUI.Uno/CoreDispatcherScheduler.cs
+++ b/src/ReactiveUI.Uno/CoreDispatcherScheduler.cs
@@ -4,252 +4,251 @@
// See the LICENSE file in the project root for full license information.
//
-
+#if !(WINDOWS10_0_19041_0)
using System.Reactive.Disposables;
using System.Runtime.ExceptionServices;
using System.Threading;
+using Windows.UI.Core;
#if HAS_UNO_WINUI
using Microsoft.UI.Xaml;
-using Windows.UI.Core;
#else
-using Windows.UI.Core;
using Windows.UI.Xaml;
#endif
-namespace System.Reactive.Concurrency
+namespace System.Reactive.Concurrency;
+
+///
+/// Represents an object that schedules units of work on a .
+///
+///
+/// This scheduler type is typically used indirectly through the and methods that use the current Dispatcher.
+///
+[CLSCompliant(false)]
+public sealed class CoreDispatcherScheduler : LocalScheduler, ISchedulerPeriodic
{
///
- /// Represents an object that schedules units of work on a .
+ /// Constructs a that schedules units of work on the given .
///
- ///
- /// This scheduler type is typically used indirectly through the and methods that use the current Dispatcher.
- ///
- [CLSCompliant(false)]
- public sealed class CoreDispatcherScheduler : LocalScheduler, ISchedulerPeriodic
+ /// Dispatcher to schedule work on.
+ /// is null.
+ public CoreDispatcherScheduler(CoreDispatcher dispatcher)
{
- ///
- /// 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;
- }
+ 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;
- }
+ ///
+ /// 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
+ ///
+ /// Gets the scheduler that schedules work on the associated with the current Window.
+ ///
+ public static CoreDispatcherScheduler Current
+ {
+ get
{
- get
+ var window = Window.Current;
+ if (window == null)
{
- var window = Window.Current;
- if (window == null)
- {
- throw new InvalidOperationException("There is no current window that has been created.");
- }
-
- return new CoreDispatcherScheduler(window.Dispatcher);
+ throw new InvalidOperationException("There is no current window that has been created.");
}
+
+ return new CoreDispatcherScheduler(window.Dispatcher);
}
+ }
- ///
- /// Gets the associated with the .
- ///
- public CoreDispatcher Dispatcher { get; }
-
- ///
- /// Gets the priority at which work is scheduled.
- ///
- public CoreDispatcherPriority Priority { get; }
-
- ///
- /// Schedules an action to be executed on the dispatcher.
- ///
- /// The type of the state passed to the scheduled action.
- /// State passed to the action to be executed.
- /// Action to be executed.
- /// The disposable object used to cancel the scheduled action (best effort).
- /// is null.
- public override IDisposable Schedule(TState state, Func action)
+ ///
+ /// Gets the associated with the .
+ ///
+ public CoreDispatcher Dispatcher { get; }
+
+ ///
+ /// Gets the priority at which work is scheduled.
+ ///
+ public CoreDispatcherPriority Priority { get; }
+
+ ///
+ /// Schedules an action to be executed on the dispatcher.
+ ///
+ /// The type of the state passed to the scheduled action.
+ /// State passed to the action to be executed.
+ /// Action to be executed.
+ /// The disposable object used to cancel the scheduled action (best effort).
+ /// is null.
+ public override IDisposable Schedule(TState state, Func action)
+ {
+ if (action == null)
{
- if (action == null)
- {
- throw new ArgumentNullException(nameof(action));
- }
+ throw new ArgumentNullException(nameof(action));
+ }
- var d = new SingleAssignmentDisposable();
+ var d = new SingleAssignmentDisposable();
- var res = Dispatcher.RunAsync(Priority, () =>
+ var res = Dispatcher.RunAsync(Priority, () =>
+ {
+ if (!d.IsDisposed)
{
- if (!d.IsDisposed)
+ try
+ {
+ d.Disposable = action(this, state);
+ }
+ catch (Exception ex)
{
- try
+ //
+ // 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 be an observable behavioral difference
+ // between scheduling with TimeSpan.Zero or using this overload.
+ //
+ // For scheduler implementation guidance rules, see TaskPoolScheduler.cs
+ // in System.Reactive.PlatformServices\Reactive\Concurrency.
+ //
+ var timer = new DispatcherTimer
{
- d.Disposable = action(this, state);
- }
- catch (Exception ex)
+ Interval = TimeSpan.Zero
+ };
+ timer.Tick += (_, _) =>
{
- //
- // 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 be an observable behavioral difference
- // between scheduling with TimeSpan.Zero or using this overload.
- //
- // For scheduler implementation guidance rules, see TaskPoolScheduler.cs
- // in System.Reactive.PlatformServices\Reactive\Concurrency.
- //
- var timer = new DispatcherTimer
- {
- Interval = TimeSpan.Zero
- };
- timer.Tick += (_, _) =>
- {
- timer.Stop();
- ExceptionDispatchInfo.Capture(ex).Throw();
- };
-
- timer.Start();
- }
- }
- });
+ timer.Stop();
+ ExceptionDispatchInfo.Capture(ex).Throw();
+ };
- return StableCompositeDisposable.Create(
- d,
- Disposable.Create(res, _ => _.Cancel())
- );
- }
-
- ///
- /// Schedules an action to be executed after on the dispatcher, using a object.
- ///
- /// The type of the state passed to the scheduled action.
- /// State passed to the action to be executed.
- /// Action to be executed.
- /// Relative time after which to execute the action.
- /// The disposable object used to cancel the scheduled action (best effort).
- /// is null.
- public override IDisposable Schedule(TState state, TimeSpan dueTime, Func action)
- {
- if (action == null)
- {
- throw new ArgumentNullException(nameof(action));
+ timer.Start();
+ }
}
+ });
- var dt = Scheduler.Normalize(dueTime);
- if (dt.Ticks == 0)
- {
- return Schedule(state, action);
- }
+ return StableCompositeDisposable.Create(
+ d,
+ Disposable.Create(res, _ => _.Cancel())
+ );
+ }
- return ScheduleSlow(state, dt, action);
+ ///
+ /// 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));
}
- private IDisposable ScheduleSlow(TState state, TimeSpan dueTime, Func action)
+ var dt = Scheduler.Normalize(dueTime);
+ if (dt.Ticks == 0)
{
- var d = new MultipleAssignmentDisposable();
+ return Schedule(state, action);
+ }
- var timer = new DispatcherTimer();
+ return ScheduleSlow(state, dt, action);
+ }
- timer.Tick += (_, _) =>
- {
- var t = Interlocked.Exchange(ref timer, null);
- if (t != null)
- {
- try
- {
- d.Disposable = action(this, state);
- }
- finally
- {
- t.Stop();
-#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
- action = null;
-#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
- }
- }
- };
+ private IDisposable ScheduleSlow(TState state, TimeSpan dueTime, Func action)
+ {
+ var d = new MultipleAssignmentDisposable();
- timer.Interval = dueTime;
- timer.Start();
+ var timer = new DispatcherTimer();
- d.Disposable = Disposable.Create(() =>
+ timer.Tick += (_, _) =>
+ {
+ var t = Interlocked.Exchange(ref timer, null);
+ if (t != null)
{
- var t = Interlocked.Exchange(ref timer, null);
- if (t != null)
+ try
+ {
+ d.Disposable = action(this, state);
+ }
+ finally
{
t.Stop();
- action = (_, _) => Disposable.Empty;
+#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
+ action = null;
+#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
}
- });
+ }
+ };
- return d;
- }
+ timer.Interval = dueTime;
+ timer.Start();
- ///
- /// 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)
+ d.Disposable = Disposable.Create(() =>
{
- //
- // 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)
+ var t = Interlocked.Exchange(ref timer, null);
+ if (t != null)
{
- throw new ArgumentOutOfRangeException(nameof(period));
+ t.Stop();
+ action = (_, _) => Disposable.Empty;
}
+ });
- if (action == null)
- {
- throw new ArgumentNullException(nameof(action));
- }
+ return d;
+ }
+
+ ///
+ /// Schedules a periodic piece of work on the dispatcher, using a object.
+ ///
+ /// The type of the state passed to the scheduled action.
+ /// Initial state passed to the action upon the first iteration.
+ /// Period for running the work periodically.
+ /// Action to be executed, potentially updating the state.
+ /// The disposable object used to cancel the scheduled recurring action (best effort).
+ /// is null.
+ /// is less than .
+ public IDisposable SchedulePeriodic(TState state, TimeSpan period, Func action)
+ {
+ //
+ // According to MSDN documentation, the default is TimeSpan.Zero, so that's definitely valid.
+ // Empirical observation - negative values seem to be normalized to TimeSpan.Zero, but let's not go there.
+ //
+ if (period < TimeSpan.Zero)
+ {
+ throw new ArgumentOutOfRangeException(nameof(period));
+ }
+
+ if (action == null)
+ {
+ throw new ArgumentNullException(nameof(action));
+ }
- var timer = new DispatcherTimer();
+ var timer = new DispatcherTimer();
- var state1 = state;
+ var state1 = state;
- timer.Tick += (_, _) =>
- {
- state1 = action(state1);
- };
+ timer.Tick += (_, _) =>
+ {
+ state1 = action(state1);
+ };
- timer.Interval = period;
- timer.Start();
+ timer.Interval = period;
+ timer.Start();
- return Disposable.Create(() =>
+ return Disposable.Create(() =>
+ {
+ var t = Interlocked.Exchange(ref timer, null);
+ if (t != null)
{
- var t = Interlocked.Exchange(ref timer, null);
- if (t != null)
- {
- t.Stop();
- action = _ => _;
- }
- });
- }
+ t.Stop();
+ action = _ => _;
+ }
+ });
}
}
+#endif
diff --git a/src/ReactiveUI.Uno/DependencyObjectObservableForProperty.cs b/src/ReactiveUI.Uno/DependencyObjectObservableForProperty.cs
index a106e56..c92a3f8 100644
--- a/src/ReactiveUI.Uno/DependencyObjectObservableForProperty.cs
+++ b/src/ReactiveUI.Uno/DependencyObjectObservableForProperty.cs
@@ -10,156 +10,149 @@
using System.Reactive.Linq;
using System.Reflection;
using Splat;
+
#if HAS_WINUI
using Microsoft.UI.Xaml;
#else
using Windows.UI.Xaml;
#endif
-#if HAS_UNO
-namespace ReactiveUI.Uno
-#else
-namespace ReactiveUI
-#endif
+namespace ReactiveUI.Uno;
+
+///
+/// Creates a observable for a property if available that is based on a DependencyProperty.
+///
+public class DependencyObjectObservableForProperty : ICreatesObservableForProperty
{
- ///
- /// Creates a observable for a property if available that is based on a DependencyProperty.
- ///
- public class DependencyObjectObservableForProperty : ICreatesObservableForProperty
+ ///
+ public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false)
{
- ///
- public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false)
+ if (!typeof(DependencyObject).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
{
- if (!typeof(DependencyObject).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
- {
- return 0;
- }
-
- if (GetDependencyPropertyFetcher(type, propertyName) is null)
- {
- return 0;
- }
-
- return 6;
+ return 0;
}
- ///
- public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false)
+ if (GetDependencyPropertyFetcher(type, propertyName) is null)
{
- if (sender is null)
- {
- throw new ArgumentNullException(nameof(sender));
- }
+ return 0;
+ }
- if (sender is not DependencyObject depSender)
- {
- throw new ArgumentException("The sender must be a DependencyObject", nameof(sender));
- }
+ return 6;
+ }
- var type = sender.GetType();
+ ///
+ public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false)
+ {
+ ArgumentNullException.ThrowIfNull(sender);
- if (beforeChanged)
- {
- this.Log().Warn(
- CultureInfo.InvariantCulture,
- "Tried to bind DO {0}.{1}, but DPs can't do beforeChanged. Binding as POCO object",
- type.FullName,
- propertyName);
-
- var ret = new POCOObservableForProperty();
- return ret.GetNotificationForProperty(sender, expression, propertyName, beforeChanged, suppressWarnings);
- }
+ if (sender is not DependencyObject depSender)
+ {
+ throw new ArgumentException("The sender must be a DependencyObject", nameof(sender));
+ }
- var dpFetcher = GetDependencyPropertyFetcher(type, propertyName);
- if (dpFetcher is null)
- {
- this.Log().Warn(
- CultureInfo.InvariantCulture,
- "Tried to bind DO {0}.{1}, but DP doesn't exist. Binding as POCO object",
- type.FullName,
- propertyName);
-
- var ret = new POCOObservableForProperty();
- return ret.GetNotificationForProperty(sender, expression, propertyName, beforeChanged, suppressWarnings);
- }
+ var type = sender.GetType();
- return Observable.Create>(subj =>
- {
- var handler = new DependencyPropertyChangedCallback((_, _) =>
- subj.OnNext(new ObservedChange