Rx types reside in the Microsoft.Phone.Reactive namespace.
The key to using Rx is to understand the two principal interfaces: IObserver
and IObservable
. Rx’s IObservable
and IObserver
are analogous to IEnumerable
and IEnumerator
, except for one key difference: They do not block.
Rx allows you to create an IObservable
object from an event. In the following excerpt, an IObservable
is created using the IGeoLocator
instance’s PositionChanged
event:
IObservable<IEvent<PositionChangedProxyEventArgs>> observable
= Observable.FromEvent<GeoPositionChangedProxyEventArgs>(
ev => geoLocator.PositionChanged += ev,
ev => geoLocator.PositionChanged -= ev);
By supplying both an add handler and a remove handler, the PositionChanged
event is automatically unsubscribed after the observable
instance is disposed.
When an IObservable
has been acquired, you can limit the frequency of event notifications by using the Sample
extension method for the IObservable<T>
type, as shown in the following excerpt:
IObservable<IEvent<PositionChangedProxyEventArgs<T>>> sampled
= observable.Sample(TimeSpan.FromMilliseconds(sampleIntervalMs));
The final step is to subscribe to the data stream. This is done by calling the Subscribe
extension method, passing it a delegate. The following excerpt calls the Subscribe
method and passes a lambda expression for the PositionChanged
event handler:
sampled.Subscribe(
args =>
{
GeopositionWrapper position = args.EventArgs.Position;
Location = position.GeoCoordinate;
});
If the sampleIntervalMs
value is equal to 1000, the PostionChanged
event handler is called, at most, once each second.
Caution
Using the Subscribe
method results in an object that implements IDisposable
. It is critical that the Dispose
method be called on the object when it is no longer needed, because not doing so can lead to a memory leak. The event source (in this case the IGeoLocator
) may keep the target (GeoPositionSampler
) alive.
The downloadable sample code contains a class called GeoLocatorSampler
that encapsulates this sampling functionality into a reusable class (see Listing 17.8).
GeoLocatorSampler
wraps an IGeoLocator
instance and allows you to sample the IGeoLocator.PositionChanged
event. It implements INotifyPropertyChanged
so that a UI element can bind to its Location
property if needed.
GeoPositionSampler
implements IDisposable
so that when it is disposed, it disposes its Rx subscription object and unsubscribes from the watcher’s StatusChanged
event.
public class GeoLocatorSampler : NotifyPropertyChangeBase, IDisposable
{
readonly IDisposable subscription;
IGeoLocator geoLocator;
public GeoLocatorSampler(IGeoLocator geoLocator, int sampleIntervalMs)
{
this.geoLocator = ArgumentValidator.AssertNotNull(geoLocator, "geoLocator");
ArgumentValidator.AssertGreaterThanOrEqualTo(
0, sampleIntervalMs, "sampleIntervalMs");
IObservable<IEvent<PositionChangedProxyEventArgs>> sampled
= Observable.FromEvent<PositionChangedProxyEventArgs>(
ev => geoLocator.PositionChanged += ev,
ev => geoLocator.PositionChanged -= ev)
.Sample(TimeSpan.FromMilliseconds(sampleIntervalMs));
subscription = sampled.Subscribe(
args =>
{
GeopositionWrapper position = args.EventArgs.Position;
Location = position.GeoCoordinate;
});
geoLocator.StatusChanged += HandleStatusChanged;
}
void HandleStatusChanged(
object o, StatusChangedProxyEventArgs args)
{
Status = args.Status;
}
GeoCoordinate location;
public GeoCoordinate Location
{
get
{
return location;
}
private set
{
Assign(ref location, value);
}
}
PositionStatus status;
public PositionStatus Status
{
get
{
return status;
}
set
{
Assign(ref status, value);
}
}
...
}
The GeoLocatorSampler
initialization has been incorporated into the GeoLocationViewModel.Start
method, as shown:
if (sampleIntervalMs > 0)
{
sampler = new GeoLocatorSampler(
geoLocator, sampleIntervalMs);
sampler.PropertyChanged += (o, args) =>
{
ResolveAddress(sampler.Location);
GeoCoordinate = sampler.Location;
PositionStatus = sampler.Status;
};
}
The GeoLocatorSampler
constructor accepts an IGeoLocator
(either a GeolocatorProxy
or a MockGeoLocator
) and a sample interval in milliseconds.
The GeoLocationView
page uses a Slider
control to set the sample interval of the GeoLocatorSampler
(see Figure 17.7).
Increasing the sample interval causes the latitude and longitude display values to be updated less frequently.
Sampling the PositionChanged
event allows your app to respond periodically to location changes and can improve the behavior of your app when the GeoLocator
object’s movement threshold is set too low for the current conditions.
18.118.16.229