I'm currently writing a location-aware MVVM app using UWP and Template10. As part of this app, I'm displaying a map page which uses the MapControl and this needs to show the user's current location.
I'm trying to leverage the LocationService which is part of the Template10 framework (although this service is not currently included in the NuGet package). The problem I have is that I cannot call into this service to get the user's current location in the OnNavigatedToAsync method of the page, because of the constraint mentioned in this MSDN article, i.e.:
Call the RequestAccessAsync before accessing the user’s location. At that time, your app must be in the foreground and RequestAccessAsync must be called from the UI thread. Until the user grants your app permission to their location, your app can't access location data.
This is because the user is presented with a dialog box asking permission to use the device's location - but as we are not on the UI thread, the app just hangs.
Is there a way to call another method (which would call into the LocationService) once the page has completed loading and is ready to go? I've tried hooking in a command call from the page's Loaded event, but the same problem occurs there too.
I'm trying to leverage the LocationService which is part of the Template10 framework (although this service is not currently included in the NuGet package).
What you mean by this LocationService
? I couldn't find any document about this. I just used Geolocator in the doc your provided to test your problem, and I can't reproduce it.
This is because the user is presented with a dialog box asking permission to use the device's location - but as we are not on the UI thread, the app just hangs.
No, it is said the RequestAccessAsync
method must be called before accessing the user’s location and must be called from the UI thread, if you call this RequestAccessAsync
method in the OnNavigatedToAsync
, it is called from UI thread, the app will not hang.
Here is my sample, I created an empty hamburger template 10 project, and in the Detail page:
Firstly adding 4 TextBlock
to show the status and position in 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>
Part of code in the 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;
}
}
Other codes you can refer to the official Geolocation sample.
Is there a way to call another method (which would call into the LocationService) once the page has completed loading and is ready to go?
I think the problem is you need to check out what exactly cause your app hang. If it is the problem with LocationService
, could you please post the document of this service in the comment?
Update:
If you read the source code of this LocationService
, you can find it packaged the Geolocation
APIs into one service, if you use code var locationService = new LocationService();var pos = locationService.Position;
, then most of the code in the StartTracking()
method are useless, they just create another Geolocator
, and try to get position, get notification when position changed again as the service does. But RequestAccessAsync
is still needed, and it must be called before you new a instance of LocationService
, so you can code like this:
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;
}
Just tested, it works fine.