This chapter will cover two very important aspects of reactive programming: the ability to configure how time changes within the sequences and their workers (observers/observables) and the ability to interact with all the other elements of the classic CLR, such as events, threads, tasks, and so on. Here's a short index:
An event is the occurrence of something we can handle somehow with our code. More precisely, in .NET, an event is a kind of Delegate
object, an object that represents one or multiple actions to run. The Delegate
object is the .NET implementation of the Observer
pattern with the addition of other features, such as asynchronous execution.
By convention, any event in .NET uses the Delegate
implementation specific to System.EventHandler
or any other childhood according to the inheritance tenet
. This implementation accepts handlers (subscribers) that must accept two parameters, such as the following example:
static void EventHandler1(object o, EventArgs e) { Console.WriteLine("Handling for object {0}", o); }
In place of using the generic EventArgs
type as an event parameter as specified by the EventHandler
delegate, when using the related generic version EventHandler<T>
, we can use any other type as an argument parameter.
By using reactive programming, we can handle events or produce events to interact with classic .NET desktop/mobile applications or server-side workflow/web applications in a simplified way, producing or sourcing events from an observable sequence. In the following section, we will divert all the main operators that help us in this absolutely useful task
By using the FromEventPattern
extension method of the Observable
class, we can produce a sequence of the EventPattern
messages containing the firing event parameters as the sender and the argument. Here's a complete example for console applications:
public static event EventHandler userIsTiredEvent; static void Main(string[] args) { //raise the event in 5 seconds Task.Factory.StartNew(() => { Thread.Sleep(5000); //check event is handled if (userIsTiredEvent != null) userIsTiredEvent("Program.Main", new EventArgs()); }); //classic event handler registration userIsTiredEvent += EventHandler1; //reactive registration var eventSequence = Observable.FromEventPattern(typeof(Program), "userIsTiredEvent"); //some output eventSequence.Materialize().Subscribe(x => Console.WriteLine("From Rx: {0}", x)); Console.ReadLine(); } static void EventHandler1(object o, EventArgs e) { Console.WriteLine("Handling for object {0}", o); }
The FromEventPattern
operator wraps events into EventPattern<T>
that will flow as usual in an observable sequence. In the preceding example, we wrapped the EventPattern
message into the Notification
message (with the Materialize
operator) to receive details about the message's type, as you learned to do in Chapter 5,
Debugging Reactive Extensions
.
With our code, we created two subscribers to userIsTiredEvent
; one outputs the result directly to the console as it is usually done by any event handler, while the second subscriber is totally created and handled by the implementation of the FromEventPattern
method.
In the nonreactive world, we're used to seeing one or more handlers per event because event delegates support multiple subscribers (bear in mind that it's an implementation of the Observable
pattern). However, we rarely see a single handler handling multiple events. On the other hand, in the Rx world, it's absolutely usual to share handlers with a pipeline of sequences that may use the EventPattern
parameters to make needed decisions.
Here's a complete example for a Windows Presentation Foundation (WPF) desktop application. The following code examples focus only on single application portions to explain event bridging to Rx. The full example is available with the other examples.
The UI XAML codes are as follows:
<Grid> <DockPanel> <UniformGrid Rows="1" DockPanel.Dock="Top"> <Button Content="- 10" Command="{Binding ChangeValueCommand}" CommandParameter="-10" /> <Button Content="- 1" Command="{Binding ChangeValueCommand}" CommandParameter="-1" /> <Button Content="+ 1" Command="{Binding ChangeValueCommand}" CommandParameter="+1" /> <Button Content="+ 10" Command="{Binding ChangeValueCommand}" CommandParameter="+10" /> </UniformGrid> <Grid> <Viewbox> <TextBlock Text="{Binding Result}" /> </Viewbox> </Grid> </DockPanel> </Grid>
This XAML will produce a view similar to the following screenshot:
The WPF application has a specific command: CommandBinding
pattern for implementing events that support natively the Model View ViewModel (MVVM) pattern. Different from the classic .NET event pattern, the command-binding pattern supports the N-N association between command raisers and handlers. Differently, in the .NET event pattern, only the event owner can raise the event, while multiple subscribers may exist.
In the following code, we will register a single command (the event definition) with multiple button subscribers (the event raisers) and a single command-binding (the event handler):
public MainWindow() { InitializeComponent(); DataContext = this; //command definition ChangeValueCommand = new RoutedCommand(Guid.NewGuid().ToString(), typeof(MainWindow)); //command binding registration CommandBindings.Add(new CommandBinding(ChangeValueCommand, OnChangeValueCommand)); } public event PropertyChangedEventHandler PropertyChanged; //classic WPF implementation private void OnChangeValueCommand(object sender, ExecutedRoutedEventArgs e) { Result += Convert.ToInt32(e.Parameter); //notify value update Notify("Result"); }
The preceding example is pretty simple. We passed a numeric value as a command parameter from the view (the XAML) and we added/subtracted such a value parameter to/from the Result
property that is visible as a label in the middle of the view.
The preceding code uses the MVVM pattern. To keep it short, we flattened the ViewModel
and the View
classes into a single class. Because this book focuses on reactive programming, if something is not clear here, kindly read more about MVVM by referring to a more specific book or the Internet as follows:
https://msdn.microsoft.com/en-us/library/hh848246.aspx
.
The same example is available by handling the command wrapped into an event with the EventCommand
class and then wrapped into an EventPattern
message for reactive usage. Here's the code:
public MainWindow() { InitializeComponent(); DataContext = this; //command definition var command = new EventCommand(); ChangeValueCommand = command; //sequence initialization //register to the event from the command Observable.FromEventPattern(command, "ExecuteRaised") //subscribe to messages from the sequence .Subscribe(eventDetail => { //EventArgs contains the Parameter of the command Result += Convert.ToInt32(eventDetail.EventArgs); //notify the value update Notify("Result"); }); }
Here's the EventCommand
class:
//a simplified CommandToEvent command public class EventCommand : ICommand { public event EventHandler<object> ExecuteRaised; public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) { return true; } public void Execute(object parameter) { if (ExecuteRaised != null) ExecuteRaised(this, parameter); } }
The first difference that exists between the previous example and the one with ConsoleApplication
in the preceding example is that, here, we injected into FromEventPattern
a specific object (instance
) that raises its event, while in the ConsoleApplication
example, we specified a Type
parameter because that event is static.
Other than this difference, the example is identical (in its design), because the WPF command-binding pattern is somehow already changing the event design into the Rx direction. What if we use a classic window/web form design? Here's an example:
//sequence initialization //register to buttons var button1Sequence = Observable.FromEventPattern(button1, "Click") //create the message to specify right numeric value .Select(x => -10); var button2Sequence = Observable.FromEventPattern(button2, "Click") //create the message to specify right numeric value .Select(x => -1); var button3Sequence = Observable.FromEventPattern(button3, "Click") //create the message to specify right numeric value .Select(x => +1); var button4Sequence = Observable.FromEventPattern(button4, "Click") //create the message to specify right numeric value .Select(x => +10); //create a single merged sequence button1Sequence.Merge(button2Sequence).Merge(button3Sequence).Merge(button4Sequence) //flatten values into a single .Scan((previous, actual) => previous + actual) //subscribe to handle value change .Subscribe(x => { //notify the value update textBox1.Text = x.ToString(); });
The preceding example is within the constructor of a form. We have 4
buttons
and TextBox
for displaying the result. The example is identical to the WPF one, but here, we have a completely different Rx sequence. In Windows Forms, each button raises its Click
event; this means that we need to create 4
sequences from the event pattern and merge these sequences into a single sequence. Once we have flattened the changing values, we need to find the result by using the Scan
operator that gives us a rolling result. At the end of the sequence, a simple subscription sets the result to the textbox1.Text
property.
The FromEvent
method, similar to FromEventPattern
, returns a sequence of the messages that represent event occurrences. The difference is that FromEvent
gives us the ability to interact more deeply with the internals of the FromEvent
sequence generation.
Instead of asking for a CLR event, as was the case with FromEventPattern
, the FromEvent
method asks for two actions. The first (registration) action gives us the internal Action<T>
method that FromEvent
uses to generate messages. The second (unregister) action give us again the same inner action to inform us to stop using that inner action to produce messages.
Here's a short example:
//the action from the FromEvent Action<string> fromEventAction = null; //setup the FromEvent sequence Observable.FromEvent<string>( //register the inner action innerAction => { fromEventAction = innerAction; }, //unregister the inner action innerAction => { fromEventAction = null; } ) .Subscribe(x => Console.WriteLine("-> {0}", x)); while (true) { //invoke the inner action fromEventAction(DateTime.Now.ToString()); Thread.Sleep(1000); }
This is one of the most powerful implementations within Rx between all the message generator operators available throughout the Observable
class, because here, we have the ability to produce an arbitrary amount of messages simply with the delegate method available with Action<T>
, a generic delegate.
Another way of using the FromEvent
method is by using its inner actions to handle an external event. In a few words, we will receive a couple of delegates to ensure the registration/deregistration of our sequence. Then, we will append these delegates (as an event handler) to the external event we want to intercept to create messages. The message flow will start by intercepting the event.
Here's an example:
public static event Action MyStaticEvent; static void Main(string[] args) { //event sequence var sequence = Observable.FromEvent( //register the inner action as handler of the static event x => MyStaticEvent += x, //unregister the inner action from the static event x => MyStaticEvent -= x); //observer sequence.Subscribe(unit => Console.WriteLine(unit)); //manually raise the event MyStaticEvent(); Console.ReadLine(); }
This usage of the FromEvent
method is a bit useless because we could use the FromEventPattern
instead. The difference is always that, with the FromEvent
method we can register to the event (or to multiple events) by ourselves and later unregister in the same way.
The opposite of handling events as messages in the observable
sequence is exposing a reactive message as events. This is a useful way of interacting with the existing (usually desktop) applications, because this choice makes available intercepting OnNext
, OnCompleted
, and OnError
messages as events.
Here's a short example:
//an infinite sequence var sequence = Observable.Interval(TimeSpan.FromSeconds(1)).Select(x => DateTime.Now); //the event wrapper var eventWrapper = sequence.ToEvent(); //register the event handler eventWrapper.OnNext += x => Console.WriteLine("{0}", x); Console.ReadLine();
3.141.38.121