0

我创建了一个小型测试应用程序来获取经度和纬度并将其转换为实际地址:

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

我尝试使用AutoResetEventand WaitOne,但整个线程停止并且代码永远不会到达ReverseGeocodeQueryCompleted().

我对如何解决这个问题的建议持开放态度。

艾坦B

4

3 回答 3

6

这是一个能够等待 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;
    }
于 2013-09-12T16:25:27.590 回答
0

我修改了一下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!

于 2013-09-12T18:12:05.017 回答
-2

寻找TaskCompletionSource任务同步。

稍后我会写一个更好的回应。同时,查看诺基亚开发者 Wiki上的将 async/await 引入联系人服务

于 2013-09-12T06:44:05.110 回答