我创建了一个小型测试应用程序以获取经度和纬度并将其转换为实际地址:
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并正确计算地址。但是,在GetLocation()完成并且分配给Address的地址为null之后,会发生ReverseGeocodeQueryCompleted。
我的问题是如何制作
ReverseGeoCoding.StartReverseGeoCoding( Location );
等待完成:
ReverseGeocodeQueryCompleted( object sender, QueryCompletedEventArgs<System.Collections.Generic.IList<MapLocation>> e )
{
....
}
我尝试过使用AutoResetEvent
和WaitOne
,但是整个线程都停止了,并且代码从未到达ReverseGeocodeQueryCompleted()
。
我愿意接受有关如何解决此问题的建议。
EitanB
这里是一种扩展方法,可以等待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;
}
我修改了Benoit的答案,看起来像这样:
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;
}
整体效果很好,再次感谢Benoit!
寻找TaskCompletionSource
以进行任务同步。
稍后我会写出更好的答复。同时,请查看Bringing async/await to the Contacts service上的Nokia Developer Wiki。