我正在将ReactiveUI用于UWP应用,并且有两个命令CommandA
和CommandB
。 CommandA
在被调用时尝试对硬件进行更改。 CommandB
在被调用时读取硬件并提供最新值。
CommandA
(以参数作为CombBox值)。 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]。
答案 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方法正常工作,但是此编辑后的解决方案更简洁且更不易出错。如果您有任何问题,请告诉我。