反复调用反应性命令直到满足条件

时间:2018-06-20 01:12:58

标签: system.reactive reactiveui rx.net

我正在将ReactiveUI用于UWP应用,并且有两个命令CommandACommandBCommandA在被调用时尝试对硬件进行更改。 CommandB在被调用时读取硬件并提供最新值。

  1. 当组合框值更改时,我想调用CommandA(以参数作为CombBox值)。
  2. 在执行CommandA之后,我想重复调用CommandB,直到获得与ComboBox中选择的值相同或发生超时的值为止。超时后应显示错误。

要检查CommandA是否已完成执行,我为[1]编写了以下代码

this.WhenAnyValue(x => x.ComboBoxAValue)
    .InvokeCommand(CommandA);

CommandA.IsExecuting
    .Buffer(2,1)
    .Where(t => t[0] == true && t[1] == false)
    .Select( x=> Unit.Default)
    .InvokeCommand(CommandB) // This statement would attempt to invoke CommandB only once

我不确定该怎么做[2]。

2 个答案:

答案 0 :(得分:0)

我认为这是一个更好的解决方案,但是您可以采用以下方法:

public class MyCoolViewModel : ReactiveObject
{
    private readonly Subject<Unit> _ticksToInvokeB;

    private readonly ObservableAsPropertyHelper<bool> _commandAExecutedCorrectly;
    public bool CommandAExecutedCorrectly => _commandAExecutedCorrectly.Value;

    public ReactiveCommand<Unit, bool> CommandA { get; set; }
    public ReactiveCommand<Unit, bool> CommandB { get; set; }

    private string _comboBoxValue;
    public string ComboBoxValue
    {
        get => _comboBoxValue;
        set => this.RaiseAndSetIfChanged(ref _comboBoxValue, value);
    }

    public MyCoolViewModel()
    {
        //Subject implements IObservable<T> and IObserver<T>, also alow us to tick values to its observable
        _ticksToInvokeB = new Subject<Unit>(); 

        CommandA = ReactiveCommand.Create<Unit,bool>( _ =>
        {
            Console.WriteLine("Command A");
            return true;
        });

        CommandB = ReactiveCommand.CreateFromTask<Unit,bool>( async _ =>
        {
            await Task.Delay(3000);
            var isTheSame = DateTime.Now.Second % 2 == 0;
            Console.WriteLine($"CommandB: is the same: {isTheSame}");
            if(!isTheSame)//if the value is not the same, tick a new unit, since we ticked a new value, CommandA will be executed
                _ticksToInvokeB.OnNext(Unit.Default);


            return isTheSame;
        });

        CommandA//We just send commandA execution to an OAPH
            .ToProperty(this, x => x.CommandAExecutedCorrectly, out _commandAExecutedCorrectly);

        this.WhenAnyValue(x => x.ComboBoxValue)
        .Skip(1) //this is because ComboBoxValue has a initial value (null) so we ignore it 
        .Select(_ => Unit.Default) //When it changes simply project an Unit
        .InvokeCommand(CommandA);//Inke CommandA

        this.WhenAnyValue(x => x.CommandAExecutedCorrectly)//When changes maded CommandA will set ChangesMaded to true
        .Where(b => b) // only when Command A gets executed correctly
        .Do(b => TickUnit()) // Tick a new unit
        .Subscribe();

        _ticksToInvokeB
            .Throttle(TimeSpan.FromMilliseconds(200))//delay a little bit the next value
            .InvokeCommand(CommandB);//invokes CommandB

    }

    private void TickUnit()
    {
        Console.WriteLine("Ticking new value");
        _ticksToInvokeB.OnNext(Unit.Default);
    }

}

让我知道它是否对您有帮助。

关于。

答案 1 :(得分:-1)

首先,我将执行类似this的操作,而不是使用用于检测已完成命令的缓冲技术。而且,我不必担心为CommandB创建命令,而只是将其作为方法执行。如果需要,您仍然可以使用命令,但是在此示例中,我将仅使用异步调用。我正在使用 ExecuteUntilItYieldsTheSelectedComboBoxValue 在循环中连续执行CommandB逻辑,直到找到匹配的值。它利用了Observable.Create,因此您可以控制何时触发OnNext。您可以在 Timeout 上为其添加标签,并在“订阅”扩展中对其进行处理。

CommandA.IsExecuting
    // IsExecuting has an initial value of false.  We can skip that first value
    .Skip(1)
    // Filter until the executing state becomes false.
    .Where(isExecuting => !isExecuting)
    // Start an inner observable for your "CommandB" logic.
    .Select(_ => ExecuteUntilItYieldsTheSelectedComboBoxValue())
    // Whenever CommandA is invoked, dispose of the last inner observable subscription and resubscribe.
    .Switch()
    .Subscribe(
        _ => Console.WriteLine("OnNext"),
        ex => [display error message]);

...

private IObservable<Unit> ExecuteUntilItYieldsTheSelectedComboBoxValue()
{
    return Observable
        .Create<Unit>(
            async o =>
            {
                int randNum = -1;
                do
                {
                    randNum = await GetRandomNumberAsync();
                } while(randNum != ComboBoxValue);

                o.OnNext(Unit.Default);
                o.OnCompleted();

                return Disposable.Empty;
            })
        .Timeout(TimeSpan.FromSeconds(3));
}

更新

基于谜题指出的内容,关于需要返回比Disposable.Empty更好的东西(以取消正在进行的异步任务并退出循环),我将ExecuteUntilItYieldsTheSelectedComboBoxValue方法更改为以下内容:< / p>

private IObservable<Unit> ExecuteUntilItYieldsTheSelectedComboBoxValue()
{
    return Observable
        // Call our async method and pass in a cancellation token.
        .FromAsync(ct => GetRandomNumberAsync(ct))
        // Keep generating random numbers.
        .Repeat()
        // Until we find one that matches the ComboBoxValue.
        .Where(x => x == ComboBoxValue)
        // Just take the first one so this inner observable will complete.
        .Take(1)
        .Select(_ => Unit.Default)
        .Timeout(TimeSpan.FromSeconds(3));
}

请注意,仍然可以使Observable.Create方法正常工作,但是此编辑后的解决方案更简洁且更不易出错。如果您有任何问题,请告诉我。