如何在 Xamarin.Forms 中使用 ReactiveUI 仅执行一次命令?

How to execute a command only once using ReactiveUI in Xamarin.Forms?

将 RxUI 用于 Xamarin.Forms,您将如何创建一个仅自动执行一次(当页面最初出现时)但用户可以稍后请求执行的命令(例如从拉动刷新事件)?

我已经使用 FromEventPattern 将我的命令挂钩到 Appearing 事件,但是当我导航回该页面时,它会再次执行,这是一种不受欢迎的行为。

这是我的场景:当用户打开包含它的页面时,我需要一个自动填充的列表。然后用户可以 select 一个元素并在单独的页面中查看其详细信息(使用 NavigationPage),但是当用户 returns 到列表页面时它会被重新填充,这是不应该发生的。不过,用户应该能够通过按钮或拉动刷新来请求新数据。

谢谢。

以下是我处理这种情况的方式。您将需要一种与此类似的行为,它只会在设置特定的已知值时调用您的命令。

// Only invoke the command when the property matches a known value that can't happen through normal execution
this.WhenAnyValue(vm => vm.SomeProperty)
                .Where(sp => sp == null)
                .Throttle(TimeSpan.FromSeconds(.25), TaskPoolScheduler.Default)
                .Do(_ => Debug.WriteLine($"Refresh the List"))
                .InvokeCommand(GetList)
                .DisposeWith(SubscriptionDisposables);

在构造函数的末尾,设置 SomeProperty 以匹配已知值

this.SomeProperty = null; // or some value that makes sense

在这种情况下,您不需要在 OnAppearing 中手动触发命令 - 它会在您的 ViewModel 首次构建时发生,并且在处理并重新创建 ViewModel 之前不会再次执行。 这对我来说似乎有点 hack-ey,所以我希望更聪明、更有经验的 RxUI 向导会插话,但它完成了工作。

如果您希望保留 OnAppearing 调用,您也可以通过设置 ReactiveCommand 的 canExecute 属性 并为您的 PullToRefresh 操作使用完全不同的 ReactiveCommand 来处理此问题(即使这两个命令都会否则表现相同)。在这种情况下,您希望在最初填充列表后使 canExecute 始终为 false,因此即使用户 return 访问页面也不会触发初始填充。

var isInitialized = this.WhenAnyValue(vm => vm.IsInit).Select( _ => _ == false).DistinctUntilChanged();

InitList = ReactiveCommand.CreateFromTask( _ => 
{ 
    // get list 
}, isInitialized);

RefreshList = ReactiveCommand.CreateFromTask( _ =>
{
    // essentially the same as InitList, but with different/no canExecute parameters
});

InitList.ObserveOn(RxApp.MainThreadScheduler).Subscribe(result =>
{
    this.IsInit = false
}).DisposeWith(SubscriptionDisposables);

这里的缺点显然是重复了一些逻辑

使用 Doris hunt:在您的页面构造函数中,您需要从事件创建一个可观察对象,然后 Take 只是第一个:

Observable.FromEventPattern(ev => Appearing += ev, ev => Appearing -= ev)
            .Select(e => Unit.Default)
            .Take(1)
            .InvokeCommand(ViewModel.InitialCollectionLoad);

也许我误会了,但我经常这样做,所以我不认为我是。

我只是将 VM 创建用作命令初始执行的触发器。然后我将同一命令与 XF 中的下拉刷新功能相关联。

所以我的 VM 看起来像这样:

public class MyVM
{
    public MYVM()
    {
        this.refreshCommand = ...;

        this
            .refreshCommand
            .Execute()
            .Subscribe()
                _ => {},
                _ => {});
    }

    public ReactiveCommand<...> RefreshCommand => this.refreshCommand;
}

这样一创建虚拟机就执行命令,所以尽快检索数据。但它不会再次重新执行,除非重新创建 VM 或用户拉动刷新。