I have created GeoCoordinateReactiveService using Rx in Windows Phone 8.
The problem is that I need to start Geocoordinatewatcher before I Subscribe for Observable which is observing over PositionChange event.
So if position change event is fired before I subscribe for the first time I won't be getting the last data. How can I change current implementation to do so.
below is my current code:
this.StatusObservable = Observable
.FromEventPattern<GeoPositionStatusChangedEventArgs>(
handler => geoCoordinateWatcher.StatusChanged += handler,
handler => geoCoordinateWatcher.StatusChanged -= handler)
.Select(ep => ep.EventArgs.Status);
this.PositionObservable = Observable
.FromEventPattern<GeoPositionChangedEventArgs<GeoCoordinate>>(
handler => geoCoordinateWatcher.PositionChanged += handler,
handler => geoCoordinateWatcher.PositionChanged -= handler)
.Select(ep => ep.EventArgs.Position);
geoCoordinateWatcher.Start();
geoCoordinateService.StatusObservable
.ObserveOnDispatcher()
.Subscribe(this.OnStatusChanged);
geoCoordinateService.PositionObservable
.ObserveOnDispatcher()
.Subscribe(this.OnPositionChanged);
Option 1
Subscribe before starting your watcher:
geoCoordinateService.StatusObservable
.ObserveOnDispatcher()
.Subscribe(this.OnStatusChanged);
geoCoordinateService.PositionObservable
.ObserveOnDispatcher()
.Subscribe(this.OnPositionChanged);
geoCoordinateWatcher.Start();
Since you have given limited information, I have no reason to believe this is insufficient.
Option 2
Use Replay to define an IConnectableObservable<T>, then Connect prior to starting your watcher:
var status = geoCoordinateService.StatusObservable.Replay(1);
var position = geoCoordinateService.PositionObservable.Replay(1);
var statusConnection = status.Connect();
var positionConnection = position.Connect();
geoCoordinateWatcher.Start();
status.ObserveOnDispatcher().Subscribe(this.OnStatusChanged);
position.ObserveOnDispatcher().Subscribe(this.OnPositionChanged);
This second option is necessary if you really do need to perform your subscription at a later time than you start your watcher.
Related
There is a way to wrap an event as observable using Observable.FromEvent. E.g. this class:
class Generator<T>
{
event Action<T> onPush;
public IObservable<T> Items =>
Observable.FromEvent<T>(d => onPush += d, d => onPush -= d);
public void Push(T item) => onPush?.Invoke(item);
}
However, I haven't found a way to complete the observable also by an event - how can I do that?
Update:
To clarify what I mean, the class above produces IObservable<T> which is "endless" and never completes. I want to make it completed by another event, not to make another observable. So the question can be reduces to this:
How to make an arbitrary IObservable<T> completed prematurely, i.e. the OnCompleted notification to be called?
An observable represents a stream of notifications, or events. When an observable sources from an event, they are inherently endless. The observable connects to the event, referencing the object, so the object backing the event will never go out of scope. .NET/C# doesn't provide a way to indicate that an event will never be called again, so the observable directly connecting to the event is endless.
This is not uncommon; most event-based observables never have OnCompleted called explicitly, modelling the real world where it is quite hard to say definitively that something will never happen again.
However, this isn't a problem: Observables are meant to run infinitely, and cause no damage. An unsubscribed observable doesn't take up much resources. If you're not interested in an event-sourced observable, unsubscribe all subscriptions and you're fine.
One way to do this is with one of the Take operators, like the TakeUntil operator (as mentioned below). Try the following code (using your Generator class):
var g = new Generator<int>();
g.Items
.TakeUntil(i => i > 3)
.Subscribe(
i => Console.WriteLine($"OnNext: {i}"),
e => Console.WriteLine($"OnError: Message: {e.Message}"),
() => Console.WriteLine("OnCompleted")
);
g.Push(1);
g.Push(2);
g.Push(3);
g.Push(4);
g.Push(5);
g.Push(6);
Output:
OnNext: 1
OnNext: 2
OnNext: 3
OnNext: 4
OnCompleted
TakeUntil unsubscribes from the Items observable after there's a message with an integer larger than 3. This is why there's an OnCompleted, and no 5, 6 messages.
Also, as Enigmativity mentioned, your Generator<T> class is basically the same as Subject<T>, I suggest you use that.
Original answer:
Make another observable from the event, then use .TakeUntil:
class Generator<T>
{
event Action<T> onPush;
event Action<Unit> onCompleted;
public IObservable<T> Items =>
Observable.FromEvent<T>(d => onPush += d, d => onPush -= d)
.TakeUntil(Completion);
public IObservable<Unit> Completion =>
Observable.FromEvent<Unit>(d => onCompleted += d, d => onCompleted -= d);
public void Push(T item) => onPush?.Invoke(item);
public void Complete() => onCompleted?.Invoke(Unit.Default);
}
I have WPF program which listens what income data from Pushbullet through websocket (onMessage event)
The process of data is done in a background worker (in DoWork event) in order to not freeze the UI.
But might happen the data are more than one and seems that results are broken.
WS.onMessage += (s, ev) => {
var wk = new BackgroundWorker();
wk.DoWork += (se, evt) => {
Process1();
Dispatcher.Invoke(()=>{ print to ui });
}
wk.RunWorkerAsync();
}
How to process item one by one when onMessage is triggered? I mean I want to process an item and the process another after first is done even onMessage is already triggered few times, depends how many data comes ...
I'd suggest looking at Microsoft's Reactive Framework (NuGet "System.Reactive"). Then you can do this:
IDisposable subscription =
Observable
.FromEventPattern<EventHandler, EventArgs>(
h => WS.onMessage += h, h => WS.onMessage -= h)
.SelectMany(ep => Observable.Start(() => Process1()))
.ObserveOnDispatcher()
.Subscribe(x =>
{
/* Print to UI */
});
It'll process on a background thread and then marshall back to the dispatcher. It'll produce zero or more values in the .Subscribe method and will only produce one value at a time.
If you want to close down the observable just call .Dispose() on the subscription.
You might need to adjust the EventHandler & EventArgs types to suit your source event.
I needed a small function that will wait for the left mous button to be released, and will not be based on the MouseUp event.
In many cases when we need this, we simply write an event handler for the MouseUp event.
It's simple, and it works.
There are however cases, where using the MouseUp event will not be useful,
such as when we are already in another (different) event handler,
and the left mouse button might be pressed when this event handler is called, and we need to wait for it to be released.
(the goal is to have a single flow of code, and not have to split it between several places which might already be occupied with another code)
I implemented it this way:
public void WaitForMouseUp()
{
while( (Control.MouseButtons&MouseButtons.Left)!=0 )
Application.DoEvents();
}
It works,
you can use it for example when you are in the event handler for the Control.Enter event,
and if the control was entered via the mouse, then this function will block until the mouse button is released.
I only worry about one thing:
I am using Application.DoEvents() there, and I wonder if there another way instead of using Application.DoEvents().
(Application.DoEvents(); has disadvantages of possible reentrancy, and so, so for this reason I try to minimize using it, whenever possible)
Anyone has an idea with what I can substitute the Application.DoEvents() part?
Here's an awesome way to do what you're asking. Use Microsoft's Reactive Extensions to make a single line of code do everything you want.
The reactive extensions provide a whole lot of operators that can be applied to events.
So first some basic observables that directly relate to normal control events:
var mouseEnters =
Observable
.FromEventPattern(
h => button1.MouseEnter += h,
h => button1.MouseEnter -= h);
var mouseLeaves =
Observable
.FromEventPattern(
h => button1.MouseLeave += h,
h => button1.MouseLeave -= h);
var mouseUps =
Observable
.FromEventPattern<MouseEventHandler, MouseEventArgs>(
h => button1.MouseUp += h,
h => button1.MouseUp -= h);
Now we need a query that will fire only once when the mouse up occurs, but only if the mouse has entered the button1, but only before it leaves.
var query =
mouseEnters
.Select(me => mouseUps.Take(1).TakeUntil(mouseLeaves))
.Switch();
Now to subscribe to the event to be able to handle it:
var subscription =
query
.Subscribe(ep =>
{
/*
this code runs for the first mouse up only
after each mouse enter on `button1`
unless the mouse leaves `button1`
*/
});
It now because very simple to unsubscribe as the type of subscription is IDisposable. So you simply call subscription.Dispose();.
Just NuGet "Rx-WinForms" to get the bits for your project.
In fact what #Kai Brummund is suggesting is a variation of my answer to Force loop to wait for an event. Adjusting the code from there for MouseUp is simple as
public static class Utils
{
public static Task WhenMouseUp(this Control control)
{
var tcs = new TaskCompletionSource<object>();
MouseEventHandler onMouseUp = null;
onMouseUp = (sender, e) =>
{
control.MouseUp -= onMouseUp;
tcs.TrySetResult(null);
};
control.MouseUp += onMouseUp;
return tcs.Task;
}
}
and the usage is
Control c = ...;
await c.WhenMouseUp();
The same technique can be used for any event.
If You wan't to write a flow within a single method, you can make an awaitable using a TaskCompletionSource.
Your flow:
await MouseUp();
...
private Task MouseUp() {
_tcs = new TaskCompletionSource();
return _tcs.Task;
}
public ... OnMouseUpEvent() {
_tcs?.SetResult(true);
}
Sorry for Pseudo code, will update this once I get something other than a mobile.
OT: Commenters: Think outside of the Box!
I needed a small function that will wait for the mouse's left button to be released.
No you don't. WinForms GUI programming is event driven, asynchronous. You should use the MouseUp event to detect the mouse button's release. This does mean that you need to implement your logic using state based asynchronous techniques, rather than the synchronous model that you crave.
I have a class Foo with an event that publishes a FooState enum. I want to turn this event into an observable that replays the last value for new subscribers.
Even if there are no subscribers, any new subscriber should get the last value.
public enum FooState
{
Stopped = 0,
Starting = 1,
Running = 2,
}
public delegate void FooEventHandler(object sender, FooEventArgs e);
public class FooEventArgs : EventArgs
{
public FooEventArgs(FooState fooState)
{
this.State = fooState;
}
public FooState State {get; private set;}
}
public class Foo
{
public event FooEventHandler FooEvent;
public void OnFooEvent(FooState state)
{
var fooEvent = FooEvent;
if(fooEvent != null)
{
fooEvent(this, new FooEventArgs(state));
}
}
}
My attempts so far revolved around using Publish, RefCount and Replay. But none of the combinations I tried work if I subscribe to the observable after I fire the event.
Replay(1).RefCount() works as long there is already at least one subscription but I need to work for the first late subscription as well.
var foo = new Foo();
var obs = Observable.FromEventPattern<FooEventHandler, FooEventArgs>(
h => foo.FooEvent += h,
h => foo.FooEvent -= h)
.DistinctUntilChanged()
.Replay(1)
.RefCount();
// Works if this line is uncomented.
//obs.Subscribe(x => Console.WriteLine("Early Subscriber = " + x.EventArgs.State));
foo.OnFooEvent(FooState.Running);
obs.Subscribe(x => Console.WriteLine("Late Subscriber = " + x.EventArgs.State));
Does anyone know how to do this with Rx?
RefCount connects only after the first subscription. If you want to have fine grained control of when the connection occurs you should use Replay + Connect.
So do instead:
var publishedSource = eventSource.DistinctUntilChanged().Replay(1);
var connection = publishedSource.Connect();
//Subscribe to publishedSource to receive events and dispose of
connection when you are done.
Posted from my phone so apologies for any syntax errors in advance.
Rx is doing the right thing converting your event notifications to your stream and replaying them, but what you are asking is:
"Why when I subscribe to the event, don't I get the initial state".
Events don't work like that. If I do a += on foo.FooEvent, I don't get an immediate trigger with the current value. I only get notified when it changes.
As you have noticed, 'Replay' will replay subsequent events, but not provide the state at the time of subscription.
To solve your problem, you'll need to ensure that the current value is put onto the stream before you hook up the stream for change notifications.
Check out Observable.StartWith().
i.e. Do ".StartWith(foo.State)" before the.DistinctUntilChanged() call (immediately after the .FromEventPattern).
Consider the situation in which you want to subscribe to an event for one and only one notification. Once the first notification lands, you unsubscribe from all future events. Would the following pattern present any memory issues? It works, but I wasn't sure if the self-referencing closure could keeps things around in memory longer than desired.
public class Entity
{
public event EventHandler NotifyEvent;
}
// And then, elsewhere, for a listen-once handler, we might do this:
Entity entity = new Entity();
Action<object, EventArgs> listener = null;
listener = (sender, args) =>
{
// do something interesting
// unsubscribe, so we only get 1 event notification
entity.NotifyEvent -= new EventHandler(listener);
};
entity.NotifyEvent += new EventHandler(listener);
Note that you have to declare 'listener' and assign a value (null). Otherwise the compiler complains about 'Use of unassigned local variable listener'
There is nothing wrong with this pattern. It's the very same pattern I and many others use for assigning and removing a lambda expression to an event handler.
While I think the general pattern is fine, I wouldn't go through Action<object, EventArgs>. I'd use:
EventHandler listener = null;
listener = (sender, args) =>
{
// do something interesting
// unsubscribe, so we only get 1 event notification
entity.NotifyEvent -= listener;
};
entity.NotifyEvent += listener;