Rx Dev Videos

Writing your first Rx Application

Download this episode

Download Video

Description

Today we released Reactive Extensions for .NET on DevLabs.

Rx is a .NET Library that allows programmers to write succinct declarative code to orchestrate and coordinate asynchronous and event-based programs based on familiar .NET idioms and patterns.


In this video, Wes Dyer goes through the steps of writing your first Rx Application. The Hello World equivalent of Rx is Drag-and-Drop.

Embed

Format

Available formats for this video:

Actual format may change based on video formats available and browser capability.

    The Discussion

    • N2Cheval

      Just wow!

       

      One question, how well does this play with WPF triggers?

    • Ian Reed

      Thanks for making this video.  Can you please post the code for this sample app?  My vision is too poor to be able to read any of the code in the video.

    • Judah

      Very cool! Really shows the expressive, declarative nature of Rx. Big improvement over the way we normally do things.

       

      How about a video showing asynchronous programming via Rx? 

    • aL_

      did you check out the wmv high version? text is much clearer there Smiley

    • aL_

      triggers are based on dependency properties, and you could write something that makes a dependecy property into an observable

      im working on something like that infact Smiley

       

      also @wes, Observable.Context is static, what if i want to have one observable use one context and another observable to use another?

    • aL_

      -edit-

      this discussion continued on the rx forums where a neater solution was found

      -end edit-

       

      Hello again, i wrote a little helper class for converting dependency properties to Observable, im not sure its the best way to do it though, what do you guys think? am i missing something? There are some anonymous Observable/disposable in rx that i'd liked to use but they are internal.. i suspect there is something smarter i can do with the Create methods but i coudnt get around making a helper class..

       

      public partial class MainWindow : Window {
        public MainWindow( ) {
        InitializeComponent( );
        Observable.Context = SynchronizationContext.Current;
        this.ActualHeightAsObservable( ).Subscribe( d => label1.Content = d );
        }
      }
      public class DependencyObservable<T> : IDisposable {
        EventHandler eh; DependencyPropertyDescriptor des; object instance;
      
        public DependencyObservable( IObserver<T> observer, DependencyProperty dp, Type ownertype, object ins ) {
          des = DependencyPropertyDescriptor.FromProperty( dp, ownertype );
          instance = ins;
          eh = new EventHandler( ( o, e ) => observer.OnNext( ( T )des.GetValue( instance ) ) );
          des.AddValueChanged( instance, eh ); 
        }
      
        public void Dispose( ) {
          des.RemoveValueChanged( instance, eh ); 
        }
      }
      public static class WindowExtensions {
        public static IObservable<double> ActualHeightAsObservable( this MainWindow w ) {
          return CreateWithDisposable<double>( obs => new DependencyObservable<double>( obs, MainWindow.HeightProperty, 
            w.GetType( ), w ) )
        }
      }
      

       

      The nice thing is that the returned observable respects all the rules of Dependency properties, if the dp is changed by a style, trigger or binding, the Observable should reflect that (ive only tried with bindings though)

    • Flynn0r

      This video makes it very clear what Rx is capable of. Thanx a lot

    • JeffVa_MS

      @aL_, use .SubscribeOn and .ObserveOn expect more work in the future on this area...

    • scyonx

      does RX work with asp.net applications too ?

    • J.Van.Gogh

      Rx is a general purpose library, should work fine in asp.NET, just make sure you don't set the Observable.Context to a UI context (the default context in .NET 3.5 SP1 & .NET 4 Beta 2 is the Reactive EventLoop which should work fine for ASP.NET).

    • aL_

      i tried them but i must have made some mistake because i got the cross thread excepitons anyway.. i tried having them both before and after the assignment to the label but no luck :/

      can you give an example perhaps?

    • pspidey512

      Amazing! Erik Meijer is a programming God!

    • JoshRoss

      What am I doing wrong?  This doesn't do anything.

       

      Public Class Form1
       Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Observable.Context = System.Threading.SynchronizationContext.Current
        Dim xs = Observable.Interval(TimeSpan.FromSeconds(1))
        xs.Subscribe(Function(y) Me.Text = y.ToString)
       End Sub
      End Class

    • J.Van.Gogh

      try setting Observable.Context = System.Threading.SynchronizationContexts.WindowsForms

    • J.Van.Gogh

      If both your observables update the UI, they should both run in the UI context, can you send out a sample of what is going wrong?

    • aL_

      the code i posted before does work Smiley

      however this:

      Observable.Return( 10 ).SubscribeOn( SynchronizationContext.Current ).Subscribe( d => label1.Content = d );

      or this:

      Observable.Return( 10 ).SubscribeOnWindowsForms( ).Subscribe( d => label1.Content = d );

      or this:

      Observable.Return( 10 ).SubscribeOnDispatcher(this.Dispatcher ).Subscribe( d => label1.Content = d );

       

      doesnt work for some reason. they all fail with a cross thread exception in the label1.Content setter, the rest of the program is just an empty wpf app with a label

    • Richard.Hein

      I tried exactly the same thing in VB and in C#, and guess what?  The VB version doesn't work, but the C# version does!  Scared

      The only difference is the way the Form_Load handler is hooked up.

      Ok, I was wrong before, but the real problem is that Me.Text = y.ToString is being treated as a boolean comparison:

       

      Reflector:

       

      <CompilerGenerated> _
      Private Sub _Lambda$__1(ByVal y As Long)
          Dim flag1 As Boolean = (Me.Text = "asdfasdf")
      End Sub
      
    • JoshRoss

      Twas the night before Christmas, when all through the house
      Not a creature was stirring, not even an observable.
      The context was set by the chimney with care,
      In hopes that a subscription would soon would be there.

       

      Sorry, Jeff.  Any other guesses?

    • JoshRoss

      My man Richard! Nice find.  This leave me with another mystery.  When I put the same code inside a button_click event, it also does nothing.  I guess the head in the box does not use vb.net.

    • Wes Dyer

      I tried it out and it seems that the anonymous functions in VB are getting in the way.  If you write it without anonymous functions then it works fine.  The reason is that assignment is a statement and so cannot be an expression and therefore not in an anonymous function.  Instead, the = becomes the equality operator so each time the interval fires it checks to see if the form's text is equal to the current textual representation of the interval's value: obviously wrong.

       

      Here is the fix:

      Imports System.Threading
      Public Class Form1
          Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
              Observable.Context = SynchronizationContexts.WindowsForms
              Dim xs = Observable.Interval(TimeSpan.FromSeconds(1))
              xs.Subscribe(AddressOf Foo)
          End Sub
          Private Sub Foo(ByVal X As Long)
              Me.Text = X.ToString
          End Sub
      End Class
      

    • Richard.Hein

      This does work.

      Private Function SetText(ByVal text As Object)
       Me.Text = text.ToString()
       Return Me.Text
      End Function

      Private Sub Form1_Load_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Load
       Observable.Context = System.Threading.SynchronizationContext.Current
       Dim xs = Observable.Interval(TimeSpan.FromSeconds(1))
       xs.Subscribe(Function(y) SetText(y))
      End Sub

    • Richard.Hein

      Yes, I can definitely verify that's the case.  Bug!

    • JoshRoss

      It gets worse, that is an illegal cross thread call.  So, I have to either disable checking, or have a private field and invoke some update method.  Or, maybe this is a side effect of running the app in x64 in the debugger.  Either way, this is not good.  I thought of overloading the = operator, but that is even more confusing.

    • Wes Dyer

      Did you set the Observable.Context correctly?

       

      What does the code look like?

    • JoshRoss

       

      Public Class Form1
       Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'uncomment this to disable ictc checking
        'Control.CheckForIllegalCrossThreadCalls = False
        Observable.Context = Threading.SynchronizationContexts.WindowsForms
        Dim theAnswer = Observable.Return(42)
        theAnswer.Subscribe(Function(monkeyDo) MonkeySee(monkeyDo))
       End Sub
       Private Function MonkeySee(ByVal monkeyDo As Long) as Boolean
        Me.Text = monkeyDo.ToString
        Return Me.Text = monkeyDo.ToString
       End Function
      End Class
      

       

      EDIT: It looks like I'll have to eat my hat.  That function should be a sub.  Or did I just stumble across something completely different?  That's why I love tomorrow, because tomorrow is another day!  I'm going to bed.

       

      EDIT:  I just ran it on 2010 and noticed that the thread that hits foo is a worker thread.

       

      Public Class Form1
          Private meText As String
          Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
              Observable.Context = Threading.SynchronizationContexts.WindowsForms
              Dim o = Observable.Return(42)
              o.Subscribe(AddressOf foo)
          End Sub
          Public Sub foo(ByVal bar As Long)
              meText = bar.ToString
              Invoke(New MethodInvoker(AddressOf UpdateMeText))
          End Sub
          Public Sub UpdateMeText()
              Me.Text = meText
          End Sub
      End Class

       

      EDIT:  If I use Observable.Context = Threading.SynchronizationContexts.CurrentDispatcher, my threads are in order.  Bug #2.

    • Richard.Hein

      Is this a VB bug or an Rx bug then?  Am I going to get my lunch with Eric?  Smiley

    • Wes Dyer

      Unfortunately, it has nothing to do with Rx.  And if you reported it as a VB bug then the VB team would correctly say that it is by design.  Which probably means that it is a bug in the user code.

       

      Now, understandably this is a rather confusing bug, and I tend to think that overloading "=" to be either relational equality or assignment leads to some rather interesting cases and so I personally think that VB is rather confusing here.

    • tkchen7

      LINQ plus Rx...Cool!!!

    • dbuduev

      Hi guys!

      I've problems with the sample from this video. Current Rx version has no Observable.Context property. Could you please tell me how this code should be rewritten? I played with SubscribeOn....

      var xs = Observable.Interval(TimeSpan.FromSeconds(1)). SubscribeOn(SynchronizationContext.Current);
      xs.Subscribe(value => TextBlock.Text = value.ToString());
      

      , but still have exception 'The calling thread cannot access this object because a different thread owns it'.

       

      Thanks

    • dbuduev

      Manual helped. ObserveOn has to be used instead.

      var xs = Observable.Interval(TimeSpan.FromSeconds(1)); 
      xs.ObserveOn(SynchronizationContext.Current).Subscribe(value => TextBlock.Text = value.ToString());
      

    • Bydia

      How can the same sample be done in Silverlight?
      1. I discovered I do not need the Observable.Context.
      2. the MouseEventArgs do not contain a definition of StartWith.

       

      Is there any site that has samples like:
      The WPF way:
      The Silverlight way:
      The Winform way:
      ????

    • DevJunkie111

      The code as shown in the screen cast does not work as others have commented because Observable.Context has been removed. Would it not make sense to repost the screen cast reflecting the changes to the API to avoid causing pain to all who look at this screen cast and find that their code is broken? 

    • noemata

      For the March 2010 release of Rx, the code needs to be adjusted as follows:

       

              public Window1()
              {
                  InitializeComponent();

                  var xs = Observable.Interval(TimeSpan.FromSeconds(1)).StartWith(-1);

                  var mouseDown = from evt in Observable.FromEvent<MouseButtonEventArgs>(this, "MouseDown")
                                  select evt.EventArgs.GetPosition(this);

                  var mouseUp = from evt in Observable.FromEvent<MouseButtonEventArgs>(this, "MouseUp")
                                  select evt.EventArgs.GetPosition(this);

                  var mouseMove = from evt in Observable.FromEvent<MouseEventArgs>(this, "MouseMove")
                                  select evt.EventArgs.GetPosition(this);

                  var q = from start in mouseDown
                          from delta in mouseMove.StartWith(start).TakeUntil(mouseUp)
                          .Let(mm => mm.Zip(mm.Skip(1), (prev, cur) =>
                              new { X = cur.X - prev.X, Y = cur.Y - prev.Y }))
                          select delta;

                  q.ObserveOnDispatcher().Subscribe(value =>
                      {
                          Canvas.SetLeft(image, Canvas.GetLeft(image) + value.X);
                          Canvas.SetTop(image, Canvas.GetTop(image) + value.Y);
                      });
              }

    • venkateshmc

      Hi Wes,

       

      I suspect there's a problem with the automatic invocation of Dispose when OnCompleted is called, when we use Linq-to-Rx. Details:

       

      The MSDN Library has an example for using IObservable here: http://msdn.microsoft.com/en-us/library/dd783449(v=VS.100).aspx"> http://msdn.microsoft.com/en-us/library/dd783449(v=VS.100).aspx. There the EndTransmission method of the observable (LocationTracker) makes a copy of the observers collection (using ToArray) before calling

      OnCompleted on each observer. It has to create this copy because the OnCompleted method is followed by an automatic call to Dispose.

      [This automatic call happens only when we subscribe to the output of a LINQ method such as Select etc, and not directly subscribe to the

      LocationTracker object (the original observable).] And Dispose removes the observer from the observers collection, so we get the exception “Collection was modified; enumeration operation may not execute.” on the next iteration in the foreach in OnCompleted. To prevent this the MSDN example uses a copy of the observers collection.

       

      The problem with this approach is that an unnecessary copy is created solely to facilitate removal of the observer from the collection member variable. However, notice right there that the last line of EndTransmission is a call to observers.Clear(). So why not leave the work of clearing the collection to the observable class and you (the RX framework) avoid making the call to Dispose internally? That way we wouldn't have to make a copy of the observers collection and at the same time be able to use foreach in EndTransmission.

       

      The observer class here (LocationReporter) has one string member variable instName (apart from the Unsubscriber reference). Making copies of a large number of observers makes that many unnecessary copies of these member variables. Since (from your videos) the sole purpose of Dispose here seems to be unsubscribing, we don't need to call Dispose here because the mass unsubscription is anyway achieved by the observable when it calls observers.Clear() in EndTransmission.

       

      What are your thoughts on this?

       

      -Venkatesh

    • psgivens

      There is no longer an Observable.Context.. I got this working with the following code

       

      var xs = Observable.Interval(TimeSpan.FromSeconds(1));
      xs = xs.ObserveOnDispatcher<long>();
      xs.Subscribe(value => textBlock.Text = value.ToString());
      

    Comments closed

    Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums, or Contact Us and let us know.