Use Reactive Extensions to merge KeyEvents and MouseClicks to close a window

After reading this post, you’ve learned how to convert MouseClick and KeyDown events into observable collections. You’ll see how you can even make these collections more convenient by merging them into one stream that notifies your window to close with or without saving data

window

How would you close a modal dialog with events? Easy. Create the following KeyDown and ButtonClick events.

event action returns
VirtualKey.Enter Close true
VirtualKey.Escape Close false
ButtonOK.Click Close true
ButtonCancel.Click Close false

And that is perfectly OK! But you’re here to learn how to do this with Rx (Reactive Extensions). Let’s see how this would work. Start by creating an Observable that pushes KeyDownEvents:

var keys = Observable.FromEventPattern<TypedEventHandler<CoreWindow, KeyEventArgs>, KeyEventArgs>(
    h => Window.Current.CoreWindow.KeyDown += h,
    h => Window.Current.CoreWindow.KeyDown -= h)
    .Select(pattern => pattern.EventArgs);

Then you create two Observables that pushes ButtonClicks for OK and Cancel:

var okClicks = Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs>(
    h => ButtonOK.Click += h,
    h => ButtonOK.Click -= h);

var cancelClicks = Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs>(
    h => ButtonCancel.Click += h,
    h => ButtonCancel.Click -= h);

Now merge these streams and subscribe to them. For the Enter and OK Button, pass true to the Close method:

keys.Where(x => x.VirtualKey == VirtualKey.Enter)
  .Select(_ => true).Merge(okClicks.Select(_ => true))
  .Subscribe(Close);

And pass false to the Close method for Escape and the Cancel button:

Add(keys.Where(x => x.VirtualKey == VirtualKey.Escape)
  .Select(_ => false).Merge(cancelClicks.Select(_ => false))
  .Subscribe(Close);

The actual Close method is simplified for this example. Usually you’d write some logic to close a window or popup. Here only the intended action is shown:

private void Close(bool result) {
    Info.Text = "Window close action: " + result;
    HiddenTextBoxJustToGetTheFocusAwayFromTheClickedButton.Focus(FocusState.Programmatic);
}

Dispose

Don’t forget to dispose the subscriptions. The OnNavigatingFrom in MainPage.xaml.cs in the demo project will show you how.