问题
specifically I am trying to simulate performing an action if the key is held down for a duration greater than Threshold (T).
I am trying to do this using Reactive Extensions .NET (The stable 1.0 version) without state variables.
Here's the marble diagram of my inputs and what I need:
let T = 3 (so 4 dddd without a key up event make up a "key held down")
keyDown: --dddd---dd--d-dddddddddd----
keyUp: -----------u-----u--u---------------u--
desired: --------a---------------a----------
Here's some example code I came up with that works but uses a state variable.
var keyDownStream = Observable.FromEventPattern<KeyEventArgs>(this, "KeyDown").Where(args => args.EventArgs.Key == Key.OemPeriod);
var keyUpStream = Observable.FromEventPattern<KeyEventArgs>(this, "KeyUp").Where(args => args.EventArgs.Key == Key.OemPeriod);
var repeatGuard = false;
keyUpStream.Subscribe(x => repeatGuard = false);
keyDownStream
.DelayOrCancel(TimeSpan.FromSeconds(2.0), keyUpStream)
.Where(_ => repeatGuard == false)
.Do(_ =>
{
repeatGuard = true;
})
.Subscribe(
result =>
{
Console.WriteLine("KeyHold");
}
);
public static class JustinsRx
{
public static IObservable<T> DelayOrCancel<T, TCancel>(this IObservable<T> source,
TimeSpan delay,
IObservable<TCancel> cancel)
{
//argument checking skipped
return from s in source
from i in Observable.Timer(delay).TakeUntil(cancel)
select s;
}
}
回答1:
This works, but I feel that it could be made shorter.
var firstKeyDowns = Observable
.Merge(keyDownStream.Select(_ => 'd'), keyUpStream.Select(_ => 'u'))
.DistinctUntilChanged()
.Where(c => c == 'd');
var query = from s in firstKeyDowns
from i in Observable.Timer(delay).TakeUntil(keyUpStream)
select s;
EDIT: Here's a different version that I think is a bit nicer:
var noRepeats = Observable
.Merge(keyDownStream.Select(_ => 'd'), keyUpStream.Select(_ => 'u'))
.DistinctUntilChanged();
var query = noRepeats
.Throttle(delay)
.Where(c => c == 'd');
回答2:
This worked for me:
var timer = Observable.Timer(TimeSpan.FromSeconds(1.0));
var query =
keyDownStream
.Select(_ =>
keyUpStream
.Select(_ => 'u')
.Amb(timer.Select(_ => 't'))
.Take(1)
.Where(x => x == 'u')
.Select(_ => Unit.Default))
.Switch();
来源:https://stackoverflow.com/questions/10013409/using-rx-to-execute-delayed-action-once-with-cancellation-for-each-window