Reputation: 242
I'm using observables to handle keydown events. I'd like to publish an observable, that will always set the key to handled iff the consumer has actually processed it. Is this possible?
Here's an example how I set keys handled now:
KeyDown.Where(e => e.Key == Keys.W)
.Subscribe(e => { Console.WriteLine(e.Key); e.Handled = true; });
Here's an example how I'd like to do it (if the handled property was automatically set afterwards, I could just publish Keys instead of KeyDownEventArgs too):
HandledKeyDown.Where(k => k == Keys.W).Subscribe(k => Console.WriteLine(k));
Here's what I tried:
HandledKeyDown = Observable.Create<Key>(observer => {
var keys = KeyDown.Subscribe(e => { e.Handled = true; observer.OnNext(e.Key); });
return () => keys.Dispose();
});
The issue with this code is that the key will always be set as handled, regardless whether the consumer actually processed it.
Is there a way to know if the code has "reached" the subscribe method?
Upvotes: 0
Views: 214
Reputation: 15618
What you'd like is to overload subscription on KeyEventArgs
so that it makes an event as handled after the subscription. Sounds like an extension:
public static IDisposable SubscribeAndHandle(this IObservable<KeyEventArgs> input, Action<Keys> action)
{
return input.Subscribe(e => { action(e.KeyCode); e.Handled = true; });
}
Then you just use SubscribeAndHandle
instead of Subscribe
, which will set Handled
to true only after the action has been run, and the action can use Keys
not KeyEventArgs
:
var sub = KeyDown
.Where(e => e.KeyCode = Keys.W) // and so on
.SubscribeAndHandle(k => Console.WriteLine(k));
Note that you could use Do
instead in the extension method if you don't mind that Handled
is set to true before the action is performed. This is a cleaner way of running side effects:
public static IDisposable SubscribeAndHandle(this IObservable<KeyEventArgs> input, Action<Keys> action)
{
return input.Do(e => e.Handled = true)
.Select(e => e.KeyCode)
.Subscribe(e => action(e));
}
A helpful comment from @Enigmativity suggested that perhaps you might like to decide whether Handled
is set to true or not from within your subscription. This is simple enough, just change the extension method to accept a Func<Keys,bool>
rather than an Action<Keys>
:
public static IDisposable SubscribeAndHandle(this IObservable<KeyEventArgs> input, Func<Keys,bool> action)
{
return input.Subscribe(e => e.Handled = action(e.KeyCode));
}
Upvotes: 1
Reputation: 117029
I would look at doing something like this:
var HandledKeyDown =
KeyDown
.Do(e => { /* Handling Code Here */ })
.Where(e => e.Handled)
.Where(e => e.Key == Keys.W)
.Select(e => e.Key)
.Subscribe(k => Console.WriteLine(k));
The Do
extension method lets you "intercept" the value of an observable inline.
Let me know if this helps.
Upvotes: 0