Как ждать события 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
3 ответа
Вот метод расширения, который можно ожидать QuerryAsync:
public static Task<IList<MapLocation>> QuerryTaskAsync(this ReverseGeocodeQuery reverseGeocode)
{
TaskCompletionSource<IList<MapLocation> > tcs=new TaskCompletionSource<IList<MapLocation>>();
EventHandler<QueryCompletedEventArgs<IList<MapLocation>>> queryCompleted = null;
queryCompleted=(send, arg) =>
{
//Unregister event so that QuerryTaskAsync 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;
}
Я немного изменил ответ Бенуа, чтобы он выглядел так:
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
В целом работает отлично, и снова спасибо Бенуа!
Ищу TaskCompletionSource
для синхронизации задач.
Я напишу лучший ответ позже. В то же время взгляните на раздел "Асинхронизация / ожидание" в службе контактов на вики- сайте Nokia для разработчиков.