Reputation: 137594
So I can experiment with Reactive Extensions, I'd like to create an IObservable of keys pressed by the user. How can I do this?
This is for a C# console application
Upvotes: 3
Views: 777
Reputation: 12667
The blocking versions of ReadKey() have a problem, in that if you dispose the subscription, it still prompts you to press a key.
If you want to have a clean unsubscription, i.e, be able to cancel the prompt, it's (unfortunately) necessary to go with a polling approach.
Observable.Interval(TimeSpan.FromMilliseconds(100))
.Where(_ => Console.KeyAvailable)
.Select(_ => (char)Console.ReadKey(false).Key)
You can now do cool things like Amb
this stream with Observable.Timer
to set up a timeout for keypresses.
Upvotes: 3
Reputation: 15618
An alternative to the method from @svick is to have the ReadKey
loop as an Enumerable
, and convert to an Observable
. This puts it in the background.
static IEnumerable<ConsoleKeyInfo> KeyPresses()
{
ConsoleKeyInfo key;
do
{
key = Console.ReadKey();
yield return key;
} while (key.Key != ConsoleKey.Escape);
}
We can generate the observables on the thread pool:
var keys = KeyPresses().ToObservable(System.Reactive.Concurrency.Scheduler.ThreadPool);
keys.Subscribe(key => Console.WriteLine("Pressed: {0}", key.Key));
And wait on the main thread for the Escape
key:
keys.Where(key => key.Key == ConsoleKey.Escape).First();
Upvotes: 0
Reputation: 117064
Try this to get an observable sequence of read keys:
IObservable<System.ConsoleKeyInfo> keys =
Observable
.Defer(() =>
Observable
.Start(() =>
Console.ReadKey()))
.Repeat();
I tested this and it worked like a treat.
Upvotes: 4
Reputation: 244827
I don't see any way how to read key presses asynchronously, so I think you have to use the synchronous Console.ReadKey()
on a separate thread, along with Subject<T>
. Something like:
IObservable<ConsoleKeyInfo> ObserveKeysUntilEscape()
{
var keys = new Subject<ConsoleKeyInfo>();
Task.Run(
() =>
{
ConsoleKeyInfo key;
do
{
key = Console.ReadKey();
keys.OnNext(key);
} while (key.Key != ConsoleKey.Escape);
keys.OnCompleted();
});
return keys;
}
Upvotes: 0