Confirmation window closes reactively

I have an observable that I use to display a confirmation dialog, roughly the signature:

IObservable<DialogResult> ShowDialog(string title, string message); 

This shows the user a dialog with a yes / no combination.

In my main window, I access the closing event as such:

 this.Events().Closing.[Do something here] 

I want to be able to:

  • Show confirmation dialog when closing observable
  • Set the CancelEventArgs.Cancel property to true if the user clicks the No button.

I tried a direct subscription:

 this.Events().Closing.Subscribe(e => { var res = Dialogs.ShowDialog("Close?", "Really Close?").Wait(); e.Cancel = res == DialogResult.Ok; }); 

But this freezes due to the Wait() call, I also tried the asynchronous option:

 this.Events().Closing.Subscribe(async e => { var res = await Dialogs.ShowDialog("Close?", "Really Close?"); e.Cancel = res == DialogResult.Ok; }); 

This is not good, because he returns immediately.

What I really want to do is something like:

 this.Events().Closing.ThenDo(_ => Dialogs.ShowDialog("Close?", "Really Close?")) .Subscribe((cancelEventArgs, dialogResult) => { cancelEventArgs.Cancel == dialogResult == DialogResult.Ok; }); 

But this does not exist, I know that the key here is how I combine the two observables, but I have no idea how to do this, and the documentation is a little illuminated with practical examples.

+6
source share
2 answers

You need to block the close event handler, so asynchronous (or Rx manipulation) will not help you here.

But you also need to lock it so that the user interface events are still being processed, so the user interface does not hang.

The most common solution for this is to use Window.ShowDialog instead of Show, and this code works:

  this.Events().Closing.Subscribe(e => { var ret = new Window().ShowDialog(); e.Cancel = true; }); 

But using this ShowDialog Rx method will make it a block block subscription, and this is unlikely what you want (for other cases, in this case, this is what you need).

Alternatively, you can start the dispatcher's inner loop, for example:

  this.Events().Closing.Subscribe(e => { var dialog = Dialogs.ShowDialog("Close?", "Really Close?"); var dispatcherFrame = new DispatcherFrame(); dialog.Take(1).Subscribe(res => { e.Cancel = res == DialogResult.Ok; dispatcherFrame.Continue = false; }); // this will process UI events till the above fires Dispatcher.PushFrame(dispatcherFrame); }); 

This will only work if the same Dispatcher is used by both windows.

EDIT:

Alternatively, you can avoid locking by always undoing the closure and closing the form yourself later, which may be more than the Rx-way, i.e.:

  var forceClose = default(bool); this.Events().Closing .Where(_ => !forceClose) .Do(e => e.Cancel = true) .SelectMany(_ => Dialogs.ShowDialog("Close?", "Really Close?")) .Where(res => res == DialogResult.Ok) .Do(_ => forceClose = true) .Subscribe(_ => this.Close()); 
+2
source

The best way to combine with observable streams is CombineLatest. This will fire when one of them is updated with the last value in each thread.

Channel 9 videos on Combine Latest

-1
source

Source: https://habr.com/ru/post/987595/


All Articles