I have three observables:
var itemsManipulationStarted =
Observable.FromEvent<ManipulationStartedEventArgs>(Items, "ManipulationStarted");
var pageManipulationDelta = Observable.FromEvent<ManipulationDeltaEventArgs>(this, "ManipulationDelta");
var pageManipulationCompleted =
Observable.FromEvent<ManipulationCompletedEventArgs>(this, "ManipulationCompleted");
They are always called in that sequense: itemsManipulationStarted -> pageManipulationDelta -> pageManipulationCompleted
After every call to any observable I need to make some actions:
itemsManipulationStarted.Subscribe(e => Items.IsHitTestVisible = false);
pageManipulationDelta.SkipUntil(itemsManipulationStarted)
.Subscribe(e => e.EventArgs.Complete());
pageManipulationCompleted.SkipUntil(pageManipulationDelta)
.Subscribe(e => Items.IsHitTestVisible = true);
Is it clear or is there another way to write some "fluent" subscribing?
-
\$\begingroup\$ To be clear.. do you rely on these events working in this order? \$\endgroup\$Dan– Dan2014年10月01日 10:10:03 +00:00Commented Oct 1, 2014 at 10:10
-
\$\begingroup\$ @DanPantry, yes \$\endgroup\$Veikedo– Veikedo2014年10月01日 10:12:52 +00:00Commented Oct 1, 2014 at 10:12
2 Answers 2
If you're on C# 5.0, you can use await
with IObservable
s to write code like this:
await itemsManipulationStarted.FirstAsync();
Items.IsHitTestVisible = false;
var e = await pageManipulationDelta.FirstAsync();
e.EventArgs.Complete();
await pageManipulationCompleted.FirstAsync();
Items.IsHitTestVisible = true;
Though I'm not completely sure this will do what you want, because of timing.
-
\$\begingroup\$ Actually I'm under windows phone 7 :\ Thanks! \$\endgroup\$Veikedo– Veikedo2014年10月03日 04:12:41 +00:00Commented Oct 3, 2014 at 4:12
-
\$\begingroup\$ wow. Actually I can use
async\await
. I'll try your answer. Thanks again! \$\endgroup\$Veikedo– Veikedo2014年10月03日 04:31:59 +00:00Commented Oct 3, 2014 at 4:31 -
\$\begingroup\$ If when I'm awaiting
xxx.FirstAsync()
and theIObservable
becomes disposed, I would got an exception? \$\endgroup\$Veikedo– Veikedo2014年10月03日 04:37:43 +00:00Commented Oct 3, 2014 at 4:37 -
\$\begingroup\$ Well, yes. The observable becomes disposed so the element you are awaiting becomes unreachable. It might be worth instead re-writing your code to not use
.FirstAsync()
(although you can do this) and instead utilize the various methods in Rx to combine sequences. All of this could be feasibly combined into a single sequence. \$\endgroup\$Dan– Dan2014年10月03日 07:38:12 +00:00Commented Oct 3, 2014 at 7:38
As per your comment, it appears that you rely on events working in this order. This seems like a bit of an anti-pattern to me to give the end-user the freedom to use all of these events, but rely on them working in the order that they do.
As a result, I would recommend removing the "ManipulationStarted" and "ManipulationCompleted" events. Instead, I would suggest something more along the lines of this:
pageManipulationDelta.Subscribe(e => e.EventArgs.Complete());
And then only fire this event - the "ManipulationDelta" event during the period of Manipulation being started or ended (whatever manipulation is). There is almost certainly a better way to approach your issue. There is no need for you to have these 3 observables 'flow' into each other. Instead, you should only expose one observable - let the event publisher decide on any security restrictions... such as state mutation, which is against the nature of Rx itself anyway.
Revised code:
var pageManipulationDelta = Observable.FromEvent<ManipulationDeltaEventArgs>(this, "ManipulationDelta");
....
// When we start manipulation
IsManipulationStarted = true;
ManipulationDelta(args);
IsManipulationStarted = false;
Although the fact there is the mutation of a boolean state variable confuses me - why is this necessary? To give a more indepth review of your code I will need to see the reason why you have approached it the way you have.
Note that this is not tested:
var combinedObservable = Observable.Defer(() =>
{
var allowPublications = false;
var subscription = startObservable.Select(_ => true)
.Merge(endObservable.Select(_ => false))
.Throttle(TimeSpan.FromMilliseconds(500))
.Subscribe(state => allowPublications = state);
return Observable.Using(() => subscription, _ => deltaObservable.DoWhile(() => allowPublications));
});
This is the simplest way I see of approaching your problem without exposing state. This allows you to completely encapsulate the state of your enabled/disabled delta publications. Replace startObservable
, endObservable
and deltaObservable
as required. You may want to consider reducing the Throttle
, but I figure it's pretty necessary since you are dealing with UI input to debounce your events.
You can expose combinedObservable
and subscribe to that in place of your ManipulationDeltaEvent
- the start/end capabilities will be handled automagically. You will need to add sequence repeating yourself.
-
\$\begingroup\$ Actually events are raised by my WP7 controls. The goal of my code is to block swipe gesture in the pivot control. \$\endgroup\$Veikedo– Veikedo2014年10月03日 04:15:32 +00:00Commented Oct 3, 2014 at 4:15
-
\$\begingroup\$ The goal of my code is to block a swipe gesture in the pivot control. The pivot (its name is Items in the code) is a child of page. To block swipe gesture I need to catch the
ManipulationStarted
event on Items and setItems.IsHitTestVisible = false
to prevent handling swipe gesture. AfterItems
, the event bubbled to the parent control (it is page). In theManipulationDelta
of page I complete "manipulation" (to prevent animation). Next I setItems.IsHitTestVisible = true
to allow up\down (scrolling) gesture. So I'm not able to manually firing the events. Thank you for your reply! \$\endgroup\$Veikedo– Veikedo2014年10月03日 04:27:30 +00:00Commented Oct 3, 2014 at 4:27 -
1\$\begingroup\$ See my edit for a proposed solution. \$\endgroup\$Dan– Dan2014年10月03日 08:16:49 +00:00Commented Oct 3, 2014 at 8:16