我目前正在使用 UWP 和 Template10 编写一个位置感知的MVVM应用。作为此应用程序的一部分,我正在显示一个使用 MapControl 的地图页面,这需要显示用户的当前位置。
我正在尝试利用 LocationService ,它是Template10框架的一部分(尽管此服务目前不包含在NuGet包中)。我遇到的问题是我无法调用此服务来获取用户在页面的 OnNavigatedToAsync 方法中的当前位置,因为此MSDN article中提到了约束,即:
在访问用户的位置之前调用RequestAccessAsync。在 那个时候,你的应用必须在前台和RequestAccessAsync 必须从UI线程调用。在用户授予您的应用之前 对其位置的权限,您的应用无法访问位置数据。
这是因为会向用户显示一个对话框,询问是否允许使用设备的位置 - 但由于我们不在UI线程上,因此应用程序只会挂起。
有没有办法在页面完成加载后调用另一个方法(可以调用 LocationService )并准备好了?我试过从页面的Loaded事件中调用一个命令调用,但同样的问题也出现了。
答案 0 :(得分:2)
我试图利用作为Template10框架一部分的LocationService(虽然此服务目前不包含在NuGet包中)。
您对此LocationService
的意思是什么?我无法找到任何关于此的文件。我刚刚在您提供的文档中使用了Geolocator来测试您的问题,但我无法重现它。
这是因为向用户显示了一个对话框,要求允许使用设备的位置 - 但由于我们不在UI线程上,因此该应用程序只会挂起。
不,据说在访问用户位置之前必须调用RequestAccessAsync
方法,并且如果您在RequestAccessAsync
中调用此OnNavigatedToAsync
方法,则必须从UI线程调用此方法,它是从UI线程调用的,应用程序不会挂起。
这是我的样本,我创建了一个空的汉堡包模板10项目,并在详细信息页面中:
首先添加4 TextBlock
以显示xaml中的状态和位置:
<!-- content -->
<ScrollViewer Padding="12,8,0,0" RelativePanel.AlignBottomWithPanel="True"
RelativePanel.AlignLeftWithPanel="True"
RelativePanel.AlignRightWithPanel="True"
RelativePanel.Below="pageHeader"
VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource TitleTextBlockStyle}" Text="You passed:" />
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind ViewModel.Value, Mode=OneWay, FallbackValue=DesigntimeValue}" />
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind ViewModel.Status,Mode=OneWay}" />
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind ViewModel.Latitude, Mode=OneWay}" />
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind ViewModel.Longitude, Mode=OneWay}" />
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind ViewModel.Accuracy, Mode=OneWay}" />
</StackPanel>
</ScrollViewer>
ViewModel中的部分代码:
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
{
Value = (suspensionState.ContainsKey(nameof(Value))) ? suspensionState[nameof(Value)]?.ToString() : parameter?.ToString();
StartTracking();
await Task.CompletedTask;
}
private async void StartTracking()
{
// Request permission to access location
var accessStatus = await Geolocator.RequestAccessAsync();
switch (accessStatus)
{
case GeolocationAccessStatus.Allowed:
_cts = new CancellationTokenSource();
CancellationToken token = _cts.Token;
_geolocator = new Geolocator { DesiredAccuracyInMeters = _desireAccuracyInMetersValue };
Geoposition pos = await _geolocator.GetGeopositionAsync().AsTask(token);
UpdateLocationData(pos);
// Subscribe to PositionChanged event to get updated tracking positions
_geolocator.PositionChanged += OnPositionChanged;
// Subscribe to StatusChanged event to get updates of location status changes
_geolocator.StatusChanged += OnStatusChanged;
break;
case GeolocationAccessStatus.Denied:
break;
case GeolocationAccessStatus.Unspecified:
break;
}
}
您可以参考官方Geolocation sample的其他代码。
有一种方法可以在页面完成加载并准备好之后调用另一个方法(可以调用LocationService)吗?
我认为问题是您需要查看应用程序挂起的确切原因。如果LocationService
出现问题,您可以在评论中发布此服务的文档吗?
<强>更新强>
如果你阅读了这个LocationService
的源代码,你可以发现它将Geolocation
API打包到一个服务中,如果你使用代码var locationService = new LocationService();var pos = locationService.Position;
,那么大多数代码都在StartTracking()
方法是无用的,它们只是创建另一个Geolocator
,并尝试获取位置,当服务的位置再次更改位置时获取通知。 但 RequestAccessAsync
仍然需要,并且必须在新建LocationService
实例之前调用它,因此您可以这样编码:
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
{
Value = (suspensionState.ContainsKey(nameof(Value))) ? suspensionState[nameof(Value)]?.ToString() : parameter?.ToString();
var accessStatus = await Geolocator.RequestAccessAsync();
var locationService = new LocationService();
var pos = locationService.Position;
//StartTracking();
await Task.CompletedTask;
}
刚刚测试过,它运行正常。