使用 RxUI for Xamarin.Forms,您将如何创建一个命令,该命令只自动执行一次(当页面最初出现时(,但该用户可以请求其稍后执行(例如从拉取到刷新类型的事件(?
我已经使用FromEventPattern
将我的命令挂接到Appearing
事件,但是当我导航回页面时,它会再次执行,这是一种不希望的行为。
这是我的方案:我需要在用户打开包含列表的页面时自动填充该列表。然后用户可以选择一个元素并在单独的页面中查看其详细信息(使用NavigationPage
(,但是当用户返回列表页面时,它会被重新填充,这不应该发生。不过,用户应该能够通过按钮请求新数据或拉动以刷新。
谢谢。
使用 Dorus 提示:在您的页面构建器中,您需要从事件创建一个可观察量,然后Take
第一个:
Observable.FromEventPattern(ev => Appearing += ev, ev => Appearing -= ev)
.Select(e => Unit.Default)
.Take(1)
.InvokeCommand(ViewModel.InitialCollectionLoad);
以下是我如何处理这种情况。您需要与此类似的行为,该行为仅在设置特定的已知值时调用您的命令。
// 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 之前不会再次执行。 这对我来说似乎有点麻烦,所以我希望更聪明、更有经验的 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);
这里的缺点是,显然,你有一些重复的逻辑
也许我误解了,但我经常这样做,所以我认为我不是。
我只是使用 VM 创建作为命令初始执行的触发器。然后,我将相同的命令与 XF 中的下拉刷新功能相关联。
所以我的虚拟机看起来像这样:
public class MyVM
{
public MYVM()
{
this.refreshCommand = ...;
this
.refreshCommand
.Execute()
.Subscribe()
_ => {},
_ => {});
}
public ReactiveCommand<...> RefreshCommand => this.refreshCommand;
}
这样,命令将在创建 VM 后立即执行,因此会尽快检索数据。但它不会再次重新执行,除非重新创建 VM 或用户拉取刷新。