如何正确订阅ReactiveObject的Changed序列?

时间:2017-07-10 20:32:18

标签: linq system.reactive reactive-programming reactiveui

关于ReactiveUi的另一个问题。我有一个ViewModel用于编辑表单。模型是ReactiveObject。我想只在发生对象更改时启用savecommand。我的尝试:

var canSaveCommand =
        this.WhenAnyValue(vm => vm.CurrentClient)
            .Where(client => client != null)
            .Select(client =>
                client.Changed
            )
            .Any();

但是当表单出现时,SaveCommand已经启用。我的错误在哪里?

2 个答案:

答案 0 :(得分:4)

您想使用Switch而不是SelectMany。 SelectMany不会取消订阅以前的客户。它将合并来自所有客户端的事件。在订阅下一个客户端之前切换取消订阅。

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

vs

AudioListViewModel viewModel;

viewModel
    .CurrentAudioChannelObservable
    .Select(current=>current.AudioFrameObservable)
    .Switch()
    .Subscribe(frame=>frame.Play());

在我们更改音频节点选择的第一个版本中,我们添加了越来越多的订阅。音频输出很快变成混乱频道的混乱。在第二个版本中,一次仅订阅一个频道,音频输出仅播放来自单个频道的输出。

许多人在开始使用RX时犯了这个错误。例如,我发现使用SelectMany而不是Switch的bug in the ReactiveUI framework

然而

ReactiveUI中有一种内置方式可以清晰地实现这一目标

实际上还有另一种方法可以达到你想要的效果,我会把它放在另一个答案中,只是为了向你展示如何使用ReactiveUI。

var canSaveCommand =
        this
          .WhenAnyObservable(vm => vm.CurrentClient.Changed)
          .StartWith(false);

请注意,null不必明确处理,但您应该从 false 开始,以确保在没有observable可用的情况下存在值。

WhenAnyObservable

  

WhenAnyObservable与Rx运算符CombineLatest非常相似   它观察一个或多个可观察量,并允许您定义一个   根据每个人的最新价值进行投影。 WhenAnyObservable   与CombineLatest的不同之处在于它的参数是表达式,   而不是直接引用目标可观察量。的影响   这种差异是由WhenAnyObservable设置的手表不是   与当时存在的特定可观察实例相关联   订阅。也就是说,表达式指向的可观察量可以   稍后被替换,新的observable的结果仍然是   抓获。这可以派上用场的一个例子是视图   想要在viewmodel上观察一个observable,但是viewmodel可以   在视图的生命周期内被替换。而不是需要   每次更改视图模型后重新订阅目标observable,   你可以使用WhenAnyObservable指定&#39;路径&#39;观看。这个   允许您在视图中使用单个订阅,而不管它是什么   目标视图模型的生命周期。

答案 1 :(得分:-1)

尝试将您的选择更改为SelectMany。然后,这将为您提供一个Observable,表示要传递给Any的更改而不是Observable的Observable,以便将更改传递给Any。