2015-05-20 11 views
6

Ho un osservabile che uso per la visualizzazione di una finestra di conferma, più o meno della firma:Mostrando una conferma sulla finestra stretta in un modo reattivo

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

Questo mostra all'utente la finestra di dialogo, con un sì/no pulsante combo.

Nella mia finestra principale, che sto l'accesso alla manifestazione di chiusura in quanto tale:

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

Voglio essere in grado di:

  1. visualizzare la finestra di conferma quando la chiusura incendi osservabili
  2. Impostare la proprietà CancelEventArgs.Cancel su true se l'utente fa clic sul pulsante "no".

Ho provato straight-up sottoscrizione:

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

Ma che pende a causa della chiamata Wait(), ho provato anche una variante asincrona:

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

Che c'è bene, perché ritorna subito.

Quello che voglio veramente fare è qualcosa di simile:

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

Ma che non esiste, so che la chiave qui è nel modo in cui mi unisco i due osservabili, ma non ho idea come farlo, e la documentazione è un po 'di luce su esempi pratici.

risposta

2

È necessità per bloccare il gestore di eventi di chiusura, quindi asincrona (o manipolazione Rx) non vi aiuterà molto qui.

Ma è anche necessario bloccarlo in modo che gli eventi dell'interfaccia utente vengano ancora elaborati, quindi l'interfaccia utente non si blocca.

La soluzione più comune per questo è di utilizzare Window.ShowDialog al posto di spettacolo, e questo codice funziona:

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

Ma usando che nel vostro metodo ShowDialog Rx farà il suo blocco di sottoscrizione di chiamata, ed è improbabile quello che vuoi (per altri casi, in questo caso è è quello che ti serve).

In alternativa è possibile eseguire un ciclo dispatcher interno, in questo modo:

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

Che sarà funzionano solo se lo stesso Dispatcher viene utilizzato da entrambe le finestre.

EDIT:

In alternativa, si può evitare il blocco annullando sempre stretta, e chiudere il modulo di te più tardi, che è forse più Rx-way, cioè .:

 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());