2012-05-20 3 views
2

Итак, я могу поэкспериментировать с Reactive Extensions, я бы хотел создать IObservable ключей, нажатых пользователем. Как я могу это сделать?IObservable из нажатых клавиш

Это для C# консольного приложения

ответ

4

Попробуйте это, чтобы получить наблюдаемую последовательность клавиш чтения:

 IObservable<System.ConsoleKeyInfo> keys = 
      Observable 
       .Defer(() => 
        Observable 
         .Start(() => 
          Console.ReadKey())) 
       .Repeat(); 

Я испытал это, и она работала, как лечить.

+0

Если я подпишу два обработчика, только один из них обработает каждое событие (случайным образом, для каждого события). Это не то, что я ожидал, я ожидал, что оба обработчика будут получать каждое событие –

+0

'keys.Subscribe (x => Console.Write (" 1 ", x.Key)); keys.Subscribe (x => Console.Write («0», x.Key)); Thread.Sleep (-1); 'then type 'abc' Я вижу« a1b1c0 », а не« a1a0b1b0c0c1 » –

+0

исправление: вместо a10b10c01 –

0

Я не вижу способа, как читать нажатия асинхронны, так что я думаю, что вы должны использовать синхронный Console.ReadKey() в отдельном потоке, наряду с Subject<T>. Что-то вроде:

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; 
} 
0

Альтернативой методу от @svick является то, что петля ReadKey является Enumerable и преобразуется в Observable. Это ставит его в фоновом режиме.

static IEnumerable<ConsoleKeyInfo> KeyPresses() 
    { 
     ConsoleKeyInfo key; 
     do 
     { 
      key = Console.ReadKey(); 
      yield return key; 
     } while (key.Key != ConsoleKey.Escape); 
    } 

Мы можем генерировать наблюдаемые на пул потоков:

 var keys = KeyPresses().ToObservable(System.Reactive.Concurrency.Scheduler.ThreadPool); 
     keys.Subscribe(key => Console.WriteLine("Pressed: {0}", key.Key)); 

И ждать на основной поток для ключа Escape:

 keys.Where(key => key.Key == ConsoleKey.Escape).First(); 
3

Блокирующий версии ReadKey() есть проблема в том, что если вы распоряжаетесь подпиской, она по-прежнему предлагает вам нажать клавишу.

Если вы хотите иметь чистую отмену подписки, то есть сможете отменить приглашение, это (к сожалению) необходимо пройти с помощью опроса.

Observable.Interval(TimeSpan.FromMilliseconds(100)) 
      .Where(_ => Console.KeyAvailable) 
      .Select(_ => (char)Console.ReadKey(false).Key) 

Теперь вы можете делать классные вещи, как Amb этот поток с Observable.Timer, чтобы установить тайм-аут для нажатий.

Смежные вопросы