2017-08-18 67 views
4

对Xamarin.Forms使用RxUI,你将如何创建一个只能自动执行一次的命令(当页面初始出现时),但是te用户可以稍后请求它的执行(比如从pull到refresh类型事件)?如何在Xamarin.Forms中只使用ReactiveUI执行一次命令?

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

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

谢谢。

+3

你试过'FromEventPattern(...出现......)。拿(1)'? – Dorus

+0

为了让它也听按钮的点击,你可以用这个可观察的元素“合并”。 – Dorus

+0

是的@Dorus,它没有工作。不过,这是一个好主意。 – fferegrino

回答

1

以下是我处理这种情况的方法。你需要一个类似于这个的行为,只有当设置了一个特定的已知值时,它才会调用你的命令。当您的视图模型它发生 -

// 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被丢弃并重新创建之后才会被再次执行。 对我来说这似乎有些黑客,所以我希望更聪明,更经验丰富的RxUI奇才会加入,但它可以完成工作。

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

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); 

这里的缺点是,很明显,你有一些逻辑复制

+0

谢谢,@Joe,你的解决方案也可以,我可以用它来实现ViewModel的任务。 – fferegrino

+0

@fferegrino - 关心提供Dorus提出的FromEventPattern解决方案作为答案吗?这可能对其他人有用,我自己也很好奇 – Joe

2

使用多鲁什提示:在你的页面consturctor你需要从事件创建一个可观察到的,然后Take只是第一个:

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

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

我只是使用VM创建作为命令初始执行的触发器。然后,我将这个命令与XF中的pull-to-refresh功能关联起来。

所以我的虚拟机看起来像这样:

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

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

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

这种方式,只要在创建虚拟机执行的命令,所以数据被检索尽快。但是除非重新创建虚拟机或用户进行刷新,否则不会重新执行。