Översikt över kartor och platser

Windows App SDK och WinUI 3 tillhandahåller API:er och kontroller för att visa kartor, identifiera användarens plats och konfigurera geofences. Använd de här funktionerna för att skapa appar som visar interaktiva kartor med stift, spårar användarens position och utlöser åtgärder när användaren går in i eller lämnar ett geografiskt område.

Den här artikeln introducerar varje funktion och innehåller ett komplett exempel som kombinerar MapControl, Geolocatoroch GeofenceMonitor i en enda fungerande app.

Visa kartor med MapControl

MapControl visar en interaktiv karta som drivs av Azure Maps. Du kan lägga till stift, lager och svara på användarinteraktioner som panorering, zooma och klicka.

MapControl kräver ett Azure Maps konto. Se Hantera ditt Azure Maps-konto för att skapa ett konto och hämta en tjänsttoken.

Detaljerade användningsinstruktioner finns i MapControl.

<MapControl x:Name="myMap"
            MapServiceToken="YOUR_AZURE_MAPS_TOKEN"
            Height="400" />

Note

UWP MapControl och Windows. Services.Maps-API:er är inaktuella och kanske inte är tillgängliga i framtida versioner av Windows. WinUI 3-appar bör använda den nya MapControl som beskrivs ovan. Mer information finns i Resurser för inaktuella funktioner.

Identifiera användarens plats

Windows.Devices.Geolocation-API:erna gör att du kan hämta enhetens geografiska position. Dessa API:er fungerar i både UWP- och Windows App SDK-appar (WinUI 3). Du kan:

En stegvis guide finns i Hämta användarens plats.

Konfigurera geofences

En geofence definierar en geografisk gräns. Appen får meddelanden när användaren går in i eller avslutar gränsen. Geofences är användbara för platsbaserade påminnelser, aviseringar eller innehållsleverans.

Anvisningar om hur du skapar och övervakar geofences finns i Konfigurera en geofence.

Platskapacitet och sekretess

Alla plats-API:er kräver den platsfunktion som deklareras i appens paketmanifest. Du måste också anropa Geolocator.RequestAccessAsync under körning innan du får åtkomst till platsdata.

Windows ger användarna kontroll över vilka appar som kan komma åt deras plats via Inställningar > Sekretess och säkerhetsplats>. Din app ska hantera det fall där användaren nekar eller återkallar platsåtkomst.

Fullständigt exempel

I följande exempel sammanförs MapControl, Geolocatoroch GeofenceMonitor i ett enda WinUI 3-fönster. Om ingen Azure Maps-nyckel har konfigurerats fungerar kartan fortfarande, men med begränsad funktionalitet, medan geolokalisering och geofencing fortsätter att fungera.

Förutsättningar

  • Windows App SDK 2.2 eller senare
  • En Azure Maps nyckel – krävs för att visa kartpaneler. Utan en giltig nyckel renderas MapControl men visar en tom karta.
  • Funktionen Platsenhet som deklareras i Package.appxmanifest:
<DeviceCapability Name="location" />

Ange din Azure Maps nyckel som en miljövariabel innan du kör appen:

$env:AZURE_MAPS_KEY = "your-key-here"

MainWindow.xaml

En kontrollpanel på 300 bildpunkter till vänster med knappar och statustext och en MapControl till höger. Ett överlägg visas när Azure Maps-nyckeln saknas.

<?xml version="1.0" encoding="utf-8" ?>
<Window
    x:Class="MapLocationDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Map Location Demo">
    <Window.SystemBackdrop>
        <MicaBackdrop />
    </Window.SystemBackdrop>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <TitleBar Title="Map Location Demo" />

        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="300" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>

            <Grid Grid.Column="0" Margin="16" RowSpacing="12">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>

                <TextBlock Text="Location Demo" FontSize="20" FontWeight="Bold"/>
                <StackPanel Grid.Row="1" Spacing="8">
                    <Button x:Name="FindMeButton" Content="Find My Location"
                            Click="FindMeButton_Click" HorizontalAlignment="Stretch"/>
                    <Button x:Name="AddGeofenceButton" Content="Add Geofence Here"
                            Click="AddGeofenceButton_Click" HorizontalAlignment="Stretch"/>
                </StackPanel>
                <TextBlock x:Name="StatusText" Grid.Row="2"
                           Text="Click 'Find My Location' to begin." TextWrapping="Wrap"/>
                <ListView x:Name="EventLog" Grid.Row="3" Header="Event Log"/>
            </Grid>

            <Grid Grid.Column="1">
                <MapControl x:Name="MyMap" />
                <StackPanel x:Name="MapKeyMissing" Visibility="Collapsed"
                            HorizontalAlignment="Center" VerticalAlignment="Center"
                            Spacing="8">
                    <FontIcon Glyph="&#xE783;" FontSize="48"
                              HorizontalAlignment="Center"
                              Foreground="{ThemeResource SystemFillColorCautionBrush}" />
                    <TextBlock Text="Azure Maps key not configured"
                               FontSize="18" FontWeight="SemiBold"
                               HorizontalAlignment="Center" />
                    <TextBlock x:Name="MapKeyHint" TextWrapping="Wrap" MaxWidth="400"
                               HorizontalAlignment="Center" TextAlignment="Center"
                               Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
                </StackPanel>
            </Grid>
        </Grid>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.Devices.Geolocation;
using Windows.Devices.Geolocation.Geofencing;

namespace MapLocationDemo;

public sealed partial class MainWindow : Window
{
    private Geolocator? _geolocator;
    private BasicGeoposition _lastPosition;
    private bool _mapAvailable;

    public MainWindow()
    {
        InitializeComponent();
        _mapAvailable = TryConfigureMap();
        GeofenceMonitor.Current.GeofenceStateChanged += OnGeofenceStateChanged;
    }

    // Read the Azure Maps key from an environment variable.
    // If missing, collapse the map and show an informational overlay.
    private bool TryConfigureMap()
    {
        var key = Environment.GetEnvironmentVariable("AZURE_MAPS_KEY");
        if (string.IsNullOrWhiteSpace(key))
        {
            MyMap.Visibility = Visibility.Collapsed;
            MapKeyMissing.Visibility = Visibility.Visible;
            MapKeyHint.Text = "Set the AZURE_MAPS_KEY environment variable "
                + "and restart.\nGeolocation and geofencing still work "
                + "without the map.";
            Log("Azure Maps key not found — map disabled");
            return false;
        }
        MyMap.MapServiceToken = key;
        return true;
    }

    private async void FindMeButton_Click(object sender, RoutedEventArgs e)
    {
        FindMeButton.IsEnabled = false;
        StatusText.Text = "Requesting location access...";

        var access = await Geolocator.RequestAccessAsync();
        if (access != GeolocationAccessStatus.Allowed)
        {
            StatusText.Text =
                "Location access denied. Check Settings > Privacy > Location.";
            FindMeButton.IsEnabled = true;
            return;
        }

        _geolocator = new Geolocator { DesiredAccuracyInMeters = 100 };
        try
        {
            var pos = await _geolocator.GetGeopositionAsync();
            var lat = pos.Coordinate.Point.Position.Latitude;
            var lon = pos.Coordinate.Point.Position.Longitude;
            _lastPosition = new BasicGeoposition
            {
                Latitude = lat, Longitude = lon
            };

            StatusText.Text =
                $"Location: {lat:F5}, {lon:F5}  ({pos.Coordinate.Accuracy:F0} m)";
            Log($"Position: {lat:F5}, {lon:F5}");

            if (_mapAvailable)
            {
                var pt = new Geopoint(_lastPosition);
                MyMap.Center = pt;
                MyMap.ZoomLevel = 15;

                var layer = new MapElementsLayer();
                layer.MapElements = new List<MapElement>
                {
                    new MapIcon { Location = pt }
                };
                MyMap.Layers.Clear();
                MyMap.Layers.Add(layer);
            }
        }
        catch (Exception ex) { StatusText.Text = $"Error: {ex.Message}"; }
        finally { FindMeButton.IsEnabled = true; }
    }

    private void AddGeofenceButton_Click(object sender, RoutedEventArgs e)
    {
        if (_lastPosition.Latitude == 0 && _lastPosition.Longitude == 0)
        {
            StatusText.Text = "Get your location first.";
            return;
        }

        var fence = new Geofence("MyGeofence",
            new Geocircle(_lastPosition, 200),
            MonitoredGeofenceStates.Entered | MonitoredGeofenceStates.Exited,
            false, TimeSpan.FromSeconds(5));
        GeofenceMonitor.Current.Geofences.Add(fence);

        StatusText.Text = $"Geofence added at "
            + $"{_lastPosition.Latitude:F5}, {_lastPosition.Longitude:F5}";
        Log("Geofence registered");
    }

    private void OnGeofenceStateChanged(
        GeofenceMonitor sender, object args)
    {
        var reports = sender.ReadReports();
        DispatcherQueue.TryEnqueue(() =>
        {
            foreach (var r in reports)
            {
                var msg = r.NewState switch
                {
                    GeofenceState.Entered => $"Entered: {r.Geofence.Id}",
                    GeofenceState.Exited  => $"Exited: {r.Geofence.Id}",
                    GeofenceState.Removed => $"Removed: {r.Geofence.Id}",
                    _ => null
                };
                if (msg != null) { StatusText.Text = msg; Log(msg); }
            }
        });
    }

    private void Log(string msg) =>
        EventLog.Items.Insert(0, $"[{DateTime.Now:HH:mm:ss}] {msg}");
}

Nyckelmönster

  • MapControlär inbyggd i Windows App SDK 1.6 och senare. Ange MapServiceToken till din Azure Maps-nyckel.
  • TryConfigureMap AZURE_MAPS_KEY kontrollerar miljövariabeln vid start. Om variabeln är tom komprimeras kartan och ett överlägg förklarar hur du åtgärdar det – ingen krasch, ingen tom karta.
  • DeviceCapability Name="location" i Package.appxmanifest krävs eller Geolocator.RequestAccessAsync returnerar Denied.
  • GeofenceMonitor.GeofenceStateChanged utlöses i en bakgrundstråd, så använd DispatcherQueue.TryEnqueue för att uppdatera användargränssnittet.