另一个关于ReactiveUI的问题。我有一个用于编辑表单的视图模型。模型是响应对象。我想仅在对象发生更改时启用保存命令。我的尝试:
var canSaveCommand =
this.WhenAnyValue(vm => vm.CurrentClient)
.Where(client => client != null)
.Select(client =>
client.Changed
)
.Any();
但是,当窗体出现时,SaveCommand
已启用。我的错误在哪里?
您希望使用"切换而不是选择许多"。SelectMany 不会取消订阅以前的客户端。它将合并来自所有客户端的事件。Switch 在订阅下一个客户端之前取消订阅上一个客户端。
var canSaveCommand =
this.WhenAnyValue(vm => vm.CurrentClient)
.Where(client => client != null)
.Select(client =>
client.Changed
)
.Switch()
.Any();
例如,以下代码使其清晰。假设我们有一个名为AudioChannel
的类 它生成我们可以处理并发送给扬声器的音频帧。
public class IAudioChannel {
public IObservable<AudioFrame> AudioFrameObservable {get;}
}
然后,我们可能会有一个用户可以选择的音频节点列表,但我们只希望将最新的音频发送到扬声器。下面的类将当前选定的音频节点作为可观察对象提供。
public class AudioListViewModel {
public class IObservable<IAudioChannel> CurrentAudioChannelObservable {get;}
}
现在考虑以下代码
AudioListViewModel viewModel;
viewModel
.CurrentAudioChannelObservable
.SelectMany(current=>current.AudioFrameObservable)
.Subscribe(frame=>frame.Play());
与
AudioListViewModel viewModel;
viewModel
.CurrentAudioChannelObservable
.Select(current=>current.AudioFrameObservable)
.Switch()
.Subscribe(frame=>frame.Play());
在第一个版本中,随着我们更改音频节点的选择,我们添加了越来越多的订阅。音频输出很快变成了混合通道的混乱。在第二个版本中,一次只订阅一个通道,音频输出仅播放单个通道的输出。
许多人在开始使用RX时都会犯这个错误。例如,我在ReactiveUI框架中发现了一个使用SelectMany而不是Switch的错误。
然而
ReactiveUI中有一个内置的方法,可以清晰地实现这一目标
实际上还有另一种方法可以实现您想要的,我将把它放在另一个答案中,只是为了向您展示如何使用 ReactiveUI。
var canSaveCommand =
this
.WhenAnyObservable(vm => vm.CurrentClient.Changed)
.StartWith(false);
请注意,null 不必显式处理,但您应该从false开始,以确保在没有可观察值可用时存在值。
何时任何可观察
WhenAnyObservable的行为很像Rx运算符CombineLatest,在 它监视一个或多个可观察量,并允许您定义 基于每个最新值的投影。何时任何可观察 与 CombineLatest 的不同之处在于它的参数是表达式, 而不是直接引用目标可观察量。的影响 这种区别在于,由WhenAnyObservable设置的手表不是 与 订阅。也就是说,表达式指向的可观察量可以 稍后替换,新可观察量的结果仍将是 捕获。这可以派上用场的一个例子是当视图 想要观察视图模型上的可观察量,但视图模型可以 在视图的生存期内被替换。而不是需要 每次更改视图模型后重新订阅目标可观察对象, 您可以使用 WhenAnyObservable 指定要监视的"路径"。这 允许您在视图中使用单个订阅,而不管 目标视图模型的生命周期。
尝试将 Select 更改为 SelectMany。然后,这将为您提供要
传递到"任何"的更改的"可观察",而不是要传递到"任何"的更改的"可观察"的可观察量。