ReactiveProperty icon indicating copy to clipboard operation
ReactiveProperty copied to clipboard

ReactiveProperty.MAUI

Open runceel opened this issue 3 years ago • 0 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));
    }
}

runceel avatar Apr 27 '22 23:04 runceel