Reactive Extension (Rx) Observables

Resides in the namespace FasterWPF.Observables in the ObservableHelper static class.

Note: you must add a reference to the Reactive Framework (Rx) .dlls

As of Version 5.0, all events subscribed through WPF-CPS are now Rx Observables under-the-hood. Typically, you will still subscribe during BeginComposite . . . EndComposite and unsubscribe via FasterWPF.Selectors.Select.DisposeEventsOnAllParents( root FrameworkElement ). This method will recurse down from the starting root element, calling DisposeEvents on each Parent down its chain of children.

However, the fact that events are now Rx Observables enables a few new scenarios (such as GetObservables, GetSubjects, FireObservables, and SubscribeObservables) and there are new methods related to these. Such methods reside in the new namespace FasterWPF.Observables, within the new ObservableHelper class.

Currently, these new methods mostly support working with Parents and Children in the WPF-CPS world, but do not support working with Borders and Composite Containers directly. (The regular event-related methods in CommonExt do support Borders and Composite Containers so it would make sense to support these consistently; perhaps, at some time in the future, I will add support for these, but it is yet to be seen whether and how much these new Rx Observable-related methods will be used?)

These new methods fall into four categories:
1. GetObservables - get the Observable from an event tied to a Parent or Child
2. GetSubjects - get the Subject from an event tied to a Parent or Child
3. FireObservables - fire an event tied to a Parent or Child
4. SubscribeObservables - subscribe to an event tied to a Parent or Child

In general, you could look at the GetObservables and GetSubjects methods as supporting the Fire and Subscribe methods, and these may likely only be used in more advanced scenarios by users already familiar with Rx. Therefore, I will only cover the latter two categories: FireObservables and SubscribeObservables, as well as mentioning how to leverage the Rx Operators.

Compared to jQuery

I believe it may be easiest to explain firing and subscribing Observables by comparing them to jQuery's similar functionality. In jQuery, you may subscribe a click event to an element based on its class name:
$(".myClassName" ).on( "click", function() {
  alert( $( this ).text() );
});
Then, you may fire the event tied to that element later
$( ".myClassName" ).trigger( "click" );
The concept is the same in WPF-CPS. The main approach that I took is to subscribe and fire based on class names that you assigned to your elements earlier. To get this to work, tag your Parent and your Child element with class names.

Add a Selector Class name to a Parent
myParent.SetSelectorClass("myParentClassName");

Add a Selector Class name to a Child at XY within BeginComposite . . . EndComposite
myParent..BeginComposite<DataGrid>(rowGuid7 + "2")
                .AddText(0, 0, "First Name")
                .AddText(1, 0, "Last Name")
                .AddText(2, 0, "Magic Inc.")
                .SetSelectorClassAtXY<TextBlock, DataGrid>(2, 0, FasterWPF.SettingsManager.SelectorPosition.One, 
"myChildClassName")
                .EndComposite(new DataGridArgs(rowGuid7, 7, 1));

FireObservables

Now that we have assigned class names to both Parent and Child, we may reach them either from a random starting FrameworkElement or from a starting Window. (For instance, in a Single Page Application (SPA), you may run the entire WPF app from within a single CPSWindow.) Once the selectors engine traverses to the Parent and the Child, it will retrieve the designated Rx Subject (based on event name) and Fire it.
// Fire an Observable from a Child with eventName and eventArgs from a Parent. Find the Parent and Child (First matching) via Selector Class names (traversing down from Window)

FasterWPF.Observables.ObservableHelper.FireObservableByClassName<MouseButtonEventArgs>(mainWindow, "myParentClassName", "myChildClassName", "PreviewMouseDown", Helpers.GetMouseClickArgs());

Note #1: GetMouseClickArgs() is a helper method that will simulate the MouseButtonEventArgs for a simple mouse left click.
Note #2: Only the first child found with a matching class name is retrieved.

SubscribeObservables

The same applies to subscribing. Both Parent and Child are located by class, then the new Event is subscribed to (in this case, on the Child, but you also may subscribe on a Parent.)
//  Subscribe a Dependency Event Observable to a Child with eventName and eventArgs from a Parent. Find the Parent and Child (First matching) via Selector Class names (traversing down from Window)

FasterWPF.Observables.ObservableHelper.SubscribeDepEventByClassName<TextBlock, DataGrid, MouseButtonEventArgs>(mainWindow, "myParentClassName", "myChildClassName", "IsMouseDirectlyOverChanged", eventPattern =>
            {
                //do something . . .
            });

Note #1: Why are both the parent and the child class names required? This is likely due to laziness on my part. The child is grabbed to get the child key-row-column and the events themselves are stored on the parent in the DisposablesDictionary. In a future release, I may be able to streamline further.
Note #2: "IsMouseDirectlyOverChanged" is a DependencyPropertyChanged event. WPF-CPS supports these types of special WPF events where a Dependency Property value notifies that it has changed. (Typically, these are referred to in WPF-CPS as DependencyEvents.)

Connectable Observables

Rx supports the concept of a Connectable Observable that you may publish and then compose on further at a later time. I leverage this concept in overloaded versions of the Subscribe methods. For instance, in the example below when I subscribe to the "IsMouseDirectlyOverChanged" dependency property changed event, I connect on the Rx Operator Skip( . . . ) passing in 3 as a parameter. This will cause the event subscription not to fire the first three times, but will fire as normal subsequently.
//  Subscribe a Dependency Event Observable to a Child with eventName and eventArgs from a Parent. Find the Parent and Child (First matching) via Selector Class names (traversing down from Window). Signature for connectable is: (connectableObservable, actionDelegate)=>{  . . .  }.            

FasterWPF.Observables.ObservableHelper.SubscribeDepEventByClassName<TextBlock, DataGrid, MouseButtonEventArgs>(mainWindow, "MagicParent", "magic", "IsMouseDirectlyOverChanged", eventPattern =>
            {
                 //do something . . .
            }, (connectableObservable, actionDelegate) => { connectableObservable.Skip(3).Subscribe(actionDelegate); });
Note #1: This isn't a very practical example, but feel free to explore the Rx Operators here: http://rxwiki.wikidot.com/101samples
Note #2: You may wonder why I didn't just let you "dot" into adding Rx Operators? Something like SubscribeDepEventByClassName( . . . ).Skip( . . . ). This could likely be very doable, but again, I am just holding off for the moment to wait and see how useful these scenarios may prove to be in WPF-CPS. For now, I have made the functionality available. If it proves beneficial, we may streamline it further?

Other Scenarios

I also support a handful of other scenarios, such as checking whether an Observable is available by class name before attempting to Fire it:
if (FasterWPF.Observables.ObservableHelper.IsObservableAvailableByClassName<MouseButtonEventArgs>(startingelement, 100, "CPSWindow", "myParentClassName", "myChildClassName", "PreviewMouseDown"))
{
              FasterWPF.Observables.ObservableHelper.FireObservableByClassName<MouseButtonEventArgs>(startingelement, 100, "CPSWindow", "myParentClassName", "myChildClassName", "PreviewMouseDown", Helpers.GetMouseClickArgs());

}

Plus, it's a little simpler to Subscribe and Fire an event just tied to the Parent (no Child involved):
this.SubscribeObservableOnParent<DataGrid, MouseButtonEventArgs>("myParentClassName", "PreviewMouseDown", evPtrn => { 
System.Windows.MessageBox.Show("Fire away!"); }, 
(connectableObservable, actionDelegate) => { connectableObservable.Skip(2).Subscribe(actionDelegate); });

Also, if you already have a handle to the Parent, you may fire an event on the Child by Key-Row-Column:
myParent.FireObservable("myKey", 0, 0, "PreviewMouseDown", Helpers.GetMouseClickArgs());

Finally, you may sidestep classes and the Selectors engine altogether if you already have a handle to the Composite (Border). Just fire by Row-Column:
myParent.FireObservable(myBrder, 0, 0, "PreviewMouseDown", Helpers.GetMouseClickArgs());


Last edited Apr 15, 2014 at 5:34 AM by stagathome0069, version 18

Comments

No comments yet.