views:

134

answers:

2

As a proof of concept, I want to write "Done" in a text box after a check box has been checked and a key has been pressed in a text box (in either order). I would expect this code to handle this, but it writes Done as soon as either event happens. Thanks for your help.

var seq = Observable.FromEvent<EventArgs>(this.checkBox, "CheckedChanged")
   .Merge(Observable.FromEvent<KeyPressEventArgs>(this.textBox, "KeyPress"));

seq.Subscribe((unused) => this.resultTextBox.Text = "Done");
A: 

if you need both events to happen - consider using Zip. Merge will trigger the event if any of merged sources raise it.

desco
What if there were 10 different events, instead of two?
Marcel Lamothe
Zip returns Observable that will trigger events when both sources raise it (like rendezvous). You can Zip this result Observable with another one to obtain Observable connected to 3 sources etc...
desco
Would that be a reasonable approach? What about using Observable.Join with Ands?
Marcel Lamothe
+4  A: 

You can use Observable.Join like so (I've added an additional CheckBox for this example):

var checkChanged  = Observable.FromEvent<EventArgs>(this.checkBox, "CheckedChanged");
var check1Changed = Observable.FromEvent<EventArgs>(this.checkBox1, "CheckedChanged");
var keyPress      = Observable.FromEvent<KeyPressEventArgs>(this.textBox, "KeyPress");

var plan1 = checkChanged
            .And(check1Changed).And(keyPress)
            .Then((cc, cc1, kp) => "Done.");

var join = Observable.Join(plan1);

join.Subscribe((result) => this.resultTextBox.Text = result);

Also, if you can join other plans together, for example if things can be "done" when either one set of events fire, or also when another set of events fire:

var checkChanged  = Observable.FromEvent<EventArgs>(this.checkBox, "CheckedChanged");
var check1Changed = Observable.FromEvent<EventArgs>(this.checkBox1, "CheckedChanged");
var keyPress      = Observable.FromEvent<KeyPressEventArgs>(this.textBox, "KeyPress");
var keyPress1     = Observable.FromEvent<KeyPressEventArgs>(this.textBox1, "KeyPress");

var plan1 = checkChanged.And(check1Changed).And(keyPress).Then((cc, cc1, kp) => "Done.");
var plan2 = keyPress.And(keyPress1).Then((kp, kp1) => "Alternate done.");

var join = Observable.Join(plan1, plan2);
Richard Hein
Thanks - I toyed with this approach as well. If these events were async. network calls, would you prefer this style over the Zip approach?
Marcel Lamothe
I think this would be the recommended approach since I worked it out from an example dealing with async network calls. ;) The Rx Forums have more information about various ways of doing this kind of thing, but I think most of them end up using Join.
Richard Hein
I like that. Is handling an error condition in one of these events a trivial matter?
Marcel Lamothe
I haven't tried doing this with Zip, btw. Maybe I'll try it later to compare.
Richard Hein
Handle errors via the Subscribe overload that takes an OnError Action. e.g.: join.Subscribe((result) => this.resultTextBox.Text = result, ex => Console.WriteLine("Error: {0}", ex.Message));
Richard Hein