Как дождаться события QueryCompleted?

Я создал небольшое тестовое приложение, чтобы получить долготу и широту и преобразовать их в фактический адрес:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Device.Location;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Maps.Services;
using Microsoft.Phone.Shell;
using PhoneApp1.Resources;
using Windows.Devices.Geolocation;

namespace PhoneApp1
{
public partial class MainPage : PhoneApplicationPage
    {
    private GeoCoordinate Location;
    public ObservableCollection<string> Addresses { get; set; }

    // Constructor
    public MainPage()
        {
        InitializeComponent();

        // Sample code to localize the ApplicationBar
        //BuildLocalizedApplicationBar();
        }

    protected override async void OnNavigatedTo( NavigationEventArgs e )
        {
        await GetLocation();
        }

    public async Task GetLocation()
        {
        Location = await CoordinateConverter.GetLocation();

        ReverseGeoCoding.StartReverseGeoCoding( Location );

        //ReverseGeoCoding.done.WaitOne();

        string Address = ReverseGeoCoding.Address;
        }
    }

public static class ReverseGeoCoding
    {
    public static ObservableCollection<string> Addresses = new ObservableCollection< string >();
    public static string Address;
    public static bool Completed;
    public static AutoResetEvent done = new AutoResetEvent( true );

    public static void StartReverseGeoCoding( GeoCoordinate Location )
        {
        Completed = false;
        var reverseGeocode = new ReverseGeocodeQuery();
        reverseGeocode.GeoCoordinate = new GeoCoordinate( Location.Latitude, Location.Longitude );
        reverseGeocode.QueryCompleted += ReverseGeocodeQueryCompleted;
        done.Reset(); 
        reverseGeocode.QueryAsync();
        }

    public static void ReverseGeocodeQueryCompleted( object sender, QueryCompletedEventArgs<System.Collections.Generic.IList<MapLocation>> e )
        {
        var reverseGeocode = sender as ReverseGeocodeQuery;
        if ( reverseGeocode != null )
            {
            reverseGeocode.QueryCompleted -= ReverseGeocodeQueryCompleted;
            }

        //Microsoft.Phone.Maps.Services.MapAddress address;                 
        Addresses.Clear();
        if ( !e.Cancelled )
            {
            foreach ( var address in e.Result.Select( adrInfo => adrInfo.Information.Address ) )
                {
                Addresses.Add( string.Format( "{0} {1}, {2} {3} {4}, {5}",
                  address.HouseNumber,
                  address.Street,
                  address.City,
                  address.State,
                  address.PostalCode,
                  address.Country ).Trim() );
                }
            }

        if ( Addresses.Count > 0 )
            {
            Address = Addresses[ 0 ].ToString();
            }
        else
            {
            Address = "";
            }

        done.Set(); 
        Completed = true;
        }
    }

public static class CoordinateConverter
    {
    public static GeoCoordinate ConvertGeocoordinate( Geocoordinate geocoordinate )
        {
        return new GeoCoordinate
            (
            geocoordinate.Latitude,
            geocoordinate.Longitude,
            geocoordinate.Altitude ?? Double.NaN,
            geocoordinate.Accuracy,
            geocoordinate.AltitudeAccuracy ?? Double.NaN,
            geocoordinate.Speed ?? Double.NaN,
            geocoordinate.Heading ?? Double.NaN
            );
        }

    public static async Task<GeoCoordinate> GetLocation()
        {
        // Get current location.
        Geolocator myGeolocator = new Geolocator();
        myGeolocator.DesiredAccuracy = PositionAccuracy.High;
        //myGeolocator.DesiredAccuracyInMeters = 50;

        Geocoordinate myGeocoordinate = null;

        try
            {
            Geoposition myGeoposition = await myGeolocator.GetGeopositionAsync
                    (
                    maximumAge: TimeSpan.FromMinutes( 1 ),
                    timeout: TimeSpan.FromSeconds( 10 )
                    );
            myGeocoordinate = myGeoposition.Coordinate;
            }
        catch ( Exception ex )
            {
            if ( (uint)ex.HResult == 0x80004004 )
                {
                // the application does not have the right capability or the location master switch is off
                MessageBox.Show( "location  is disabled in phone settings" );
                }
            }

        if ( myGeocoordinate == null )
            {
            return GeoCoordinate.Unknown;
            }

        GeoCoordinate myGeoCoordinate = CoordinateConverter.ConvertGeocoordinate( myGeocoordinate );
        return myGeoCoordinate;
        }
    }
}

Код работает нормально, т.е. вызывается ReverseGeocodeQueryCompleted и адрес вычисляется правильно. Однако ReverseGeocodeQueryCompleted возникает после того, как GetLocation() завершена, а адрес, присвоенный Address, имеет значение null.

мой вопрос как сделать

ReverseGeoCoding.StartReverseGeoCoding( Location ); 

дождитесь завершения:

ReverseGeocodeQueryCompleted( object sender, QueryCompletedEventArgs<System.Collections.Generic.IList<MapLocation>> e )
{
....
}

Я пробовал с AutoResetEvent и WaitOne, но весь поток останавливается, и код так и не доходит до ReverseGeocodeQueryCompleted().

Я открыт для предложений, как решить эту проблему.

ЭйтанБ


person eitan barazani    schedule 12.09.2013    source источник


Ответы (3)


Вот метод расширения, позволяющий ожидать QueryAsync:

public static Task<IList<MapLocation>> QueryTaskAsync(this ReverseGeocodeQuery reverseGeocode)
{
    TaskCompletionSource<IList<MapLocation> > tcs = new TaskCompletionSource<IList<MapLocation>>();
    EventHandler<QueryCompletedEventArgs<IList<MapLocation>>> queryCompleted = null;

    queryCompleted = (send, arg) =>
        {
            // Unregister event so that QueryTaskAsync can be called several time on same object
            reverseGeocode.QueryCompleted -= queryCompleted;

            if (arg.Error != null)
            {
                tcs.SetException(arg.Error);
            }
            else if (arg.Cancelled)
            {
                tcs.SetCanceled();
            }
            else
            {
                tcs.SetResult(arg.Result);
            }
        };

        reverseGeocode.QueryCompleted += queryCompleted;

        reverseGeocode.QueryAsync();

        return tcs.Task;
    }
person Benoit Catherinet    schedule 12.09.2013

Я немного изменил ответ Бенуа, чтобы он выглядел так:

        public static Task<string> StartReverseGeoCodingAsync( System.Device.Location.GeoCoordinate Location )
        {
        var reverseGeocode = new ReverseGeocodeQuery();
        reverseGeocode.GeoCoordinate = new System.Device.Location.GeoCoordinate( Location.Latitude, Location.Longitude );

        var tcs = new TaskCompletionSource<string>();
        EventHandler<QueryCompletedEventArgs<System.Collections.Generic.IList<MapLocation>>> handler = null;
        handler = ( sender, args ) =>
        {
            if ( args.Error != null )
                {
                tcs.SetException( args.Error );
                }
            else if ( args.Cancelled )
                {
                tcs.SetCanceled();
                }
            else
                {
                Addresses.Clear();
                foreach ( var address in args.Result.Select( adrInfo => adrInfo.Information.Address ) )
                    {
                    Addresses.Add(
                        string.Format( "{0} {1}, {2} {3} {4}, {5}",
                                       address.HouseNumber,
                                       address.Street,
                                       address.City,
                                       address.State,
                                       address.PostalCode,
                                       address.Country ).Trim() );
                    }
                string Address = Addresses.Count > 0 ? Address = Addresses[ 0 ].ToString() : string.Empty;
                reverseGeocode.QueryCompleted -= handler;
                tcs.SetResult( Address );
                }
        };

        reverseGeocode.QueryCompleted += handler;
        reverseGeocode.QueryAsync();
        return tcs.Task;
        }

это заменит следующие две функции в моем коде:

#if never
    public static void StartReverseGeoCoding( GeoCoordinate Location )
        {
        var reverseGeocode = new ReverseGeocodeQuery();
        reverseGeocode.GeoCoordinate = new GeoCoordinate( Location.Latitude, Location.Longitude );
        reverseGeocode.QueryCompleted += ReverseGeocodeQueryCompleted;
        reverseGeocode.QueryAsync();
        }

    public static void ReverseGeocodeQueryCompleted( object sender, QueryCompletedEventArgs<System.Collections.Generic.IList<MapLocation>> e )
        {
        var reverseGeocode = sender as ReverseGeocodeQuery;
        if ( reverseGeocode != null )
            {
            reverseGeocode.QueryCompleted -= ReverseGeocodeQueryCompleted;
            }

        // Microsoft.Phone.Maps.Services.MapAddress address;                
        Addresses.Clear();
        if ( !e.Cancelled )
            {
            foreach ( var address in e.Result.Select( adrInfo => adrInfo.Information.Address ) )
                {
                Addresses.Add( string.Format( "{0} {1}, {2} {3} {4}, {5}",
                  address.HouseNumber,
                  address.Street,
                  address.City,
                  address.State,
                  address.PostalCode,
                  address.Country ).Trim() );
                }
            }

        Address = ( Addresses.Count > 0 ) ? Addresses[ 0 ].ToString() : string.Empty;
        }

конец

В целом работает отлично, и снова спасибо Бенуа!

person eitan barazani    schedule 12.09.2013

Найдите TaskCompletionSource для синхронизации задач.

Я напишу лучший ответ позже. Тем временем взгляните на Добавление async/await в службу контактов на странице Вики для разработчиков Nokia.

person Paulo Morgado    schedule 12.09.2013
comment
Я искал там, не уверен, что пример, который они там приводят, делает его более понятным для меня. Можете ли вы уточнить, как преобразовать его в TaskCompletionSource? - person eitan barazani; 12.09.2013