runceel / ReactiveProperty

ReactiveProperty provides MVVM and asynchronous support features under Reactive Extensions. Target frameworks are .NET 6+, .NET Framework 4.7.2 and .NET Standard 2.0.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ReactiveProperty.MAUI

runceel opened this issue · comments

  • Because SynchronizationContextScheduler that is default scheduler for ReactiveProperty always executes action later even if it is run on UI thread. need ReactivePropertyMauiScheduler like below:
using Microsoft.Maui.Dispatching;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;

namespace Reactive.Bindings.Schedulers;

/// <summary>
/// A scheduler for ReactiveProperty and ReactiveCollection on MAUI.
/// </summary>
public class ReactivePropertyMauiScheduler : LocalScheduler
{
    private readonly IDispatcher _dispatcher;

    /// <summary>
    /// Construct a scheduler from Dispatcher.
    /// </summary>
    /// <param name="dispatcher">An application's dispatcher</param>
    public ReactivePropertyMauiScheduler(IDispatcher dispatcher)
    {
        _dispatcher = dispatcher;
    }

    /// <summary>
    /// Schedules an action to be executed.
    /// </summary>
    /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
    /// <param name="state">State passed to the action to be executed.</param>
    /// <param name="action">Action to be executed.</param>
    /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
    public override IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
    {
        if (action == null)
        {
            throw new ArgumentNullException(nameof(action));
        }

        if (!_dispatcher.IsDispatchRequired)
        {
            return action(this, state);
        }

        var d = new SingleAssignmentDisposable();

        _dispatcher.Dispatch(() =>
        {
            if (!d.IsDisposed)
            {
                d.Disposable = action(this, state);
            }
        });

        return d;
    }

    /// <summary>
    /// Schedules an action to be executed after dueTime.
    /// </summary>
    /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
    /// <param name="state">State passed to the action to be executed.</param>
    /// <param name="action">Action to be executed.</param>
    /// <param name="dueTime">Relative time after which to execute the action.</param>
    /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
    public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
    {
        if (action == null)
        {
            throw new ArgumentNullException(nameof(action));
        }

        var dt = Scheduler.Normalize(dueTime);
        if (dt.Ticks == 0)
        {
            return Schedule(state, action);
        }

        // Note that avoiding closure allocation here would introduce infinite generic recursion over the TState argument
        return DefaultScheduler.Instance.Schedule(state, dt, (_, state1) => Schedule(state1, action));
    }
}

Close this issue. Because custom dispatcher is NOT needed for ReactivePropertySlim<T> and ValidatableReactiveProperty<T>.