Getting Started with Rx for Windows Phone

,

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.

LISTING 17.8. GeoLocatorSampler Class (excerpt)


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).

Image

FIGURE 17.7 A Slider control is used to set the sample interval.

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.144.69