My solutions to the Reactive Framework Tripple-click puzzle
Here are two solutions I wrote to the puzzle I posted earlier this week.
The first version is very imperative and uses the Do operator to side effect, it then uses variables captured in the closure to track its state. This one you have to be careful with to make sure to add the ToSubject operator to not have the side effects happen multiple times:
public static IObservable<RoutedEventArgs> GetMultiClickImperative(this ButtonBase button, int count, int multiClickSpeedInMilliSeconds)
{
var click = Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(i => i.Invoke, i => button.Click += i, i => button.Click -= i)
.Select(e => e.EventArgs);
var slidingWindow = new DateTime[count];
var offset = 0;
return click.Do(e =>
{
offset = (offset + 1) % count;
slidingWindow[offset] = DateTime.Now;
})
.Where(f => (slidingWindow[offset] - slidingWindow[(offset + 1) % count]) .TotalMilliseconds < multiClickSpeedInMilliSeconds)
.Do(e =>
{
slidingWindow = new DateTime[count];
})
.ToSubject();
}
The second version I wrote, uses Scan and would look much nicer if it weren't for C# not supporting mutable anonymous types:
public static IObservable<RoutedEventArgs> GetMultiClickScan(this ButtonBase button, int count, int multiClickSpeedInMilliSeconds)
{
var click = Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(i => i.Invoke, i => button.Click += i, i => button.Click -= i)
.Select(e => e.EventArgs);
return click.Scan(new
{
SlidingWindow = new DateTime[count],
Offset = 0,
Hit = false,
Event = new RoutedEventArgs()
}, (oldState, e) =>
{
var slidingWindow = oldState.SlidingWindow;
var offset = (oldState.Offset + 1) % count;
slidingWindow[offset] = DateTime.Now;
var hit = (slidingWindow[offset] - slidingWindow[(offset + 1) % count]).TotalMilliseconds < multiClickSpeedInMilliSeconds;
if (hit)
slidingWindow = new DateTime[count];
return new
{
SlidingWindow = slidingWindow,
Offset = offset,
Hit = hit,
Event = e
};
})
.Where(state => state.Hit)
.Select(state => state.Event);
}
I'll ask my team mates to post their solutions as well.