Как да изчакам събитието 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, е нула.

Въпросът ми е как да направя

ReverseGeoCoding.StartReverseGeoCoding( Location ); 

изчакайте завършването на:

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

Опитах с AutoResetEvent и WaitOne, но цялата нишка спира и кодът никога не стига до ReverseGeocodeQueryCompleted().

Отворен съм за предложения как да реша този проблем.

EitanB


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;
        }

endif

Като цяло работи добре и отново благодарение на Benoit!

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