如何在 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 或用户拉动刷新。
将 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 或用户拉动刷新。