Übersicht über Karten und Position

Windows App SDK und WinUI 3 stellen APIs und Steuerelemente zum Anzeigen von Karten bereit, erkennen den Standort des Benutzers und das Einrichten von Geofences. Verwenden Sie diese Funktionen, um Apps zu erstellen, die interaktive Karten mit Pins anzeigen, die Position des Benutzers nachverfolgen und Aktionen auslösen, wenn der Benutzer einen geografischen Bereich eingibt oder verlässt.

Dieser Artikel stellt die einzelnen Funktionen vor und enthält ein vollständiges Beispiel, das MapControl, Geolocator und GeofenceMonitor in einer einzigen funktionsfähigen App kombiniert.

Anzeigen von Karten mit MapControl

MapControl zeigt eine interaktive Karte an, die von Azure Maps unterstützt wird. Sie können Pins, Ebenen hinzufügen und auf Benutzerinteraktionen wie Schwenken, Zoomen und Klicken reagieren.

MapControl erfordert ein Azure Maps Konto. Siehe Verwalten Ihres Azure Maps Kontos zum Erstellen eines Kontos und Abrufen eines Diensttokens.

Ausführliche Verwendungsanweisungen finden Sie unter MapControl.

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

Note

UWP MapControl und Windows. Services.Maps-APIs sind veraltet und sind in zukünftigen Versionen von Windows möglicherweise nicht verfügbar. WinUI 3-Apps sollten das oben beschriebene neue MapControl verwenden. Weitere Informationen finden Sie unter Ressourcen für veraltete Funktionen.

Erkennen des Standorts des Benutzers

Die Windows.Devices.Geolocation-APIs ermöglichen Ihnen, die geografische Position des Geräts abzurufen. Diese APIs funktionieren sowohl in UWP- als auch in Windows App SDK-Apps (WinUI 3). Sie haben folgende Möglichkeiten:

Eine Schritt-für-Schritt-Anleitung finden Sie unter Den Standort des Benutzers abrufen.

Geofences einrichten

Ein Geofence definiert eine geografische Grenze. Ihre App empfängt Benachrichtigungen, wenn der Benutzer die Grenze eingibt oder verlässt. Geofences sind nützlich für standortbasierte Erinnerungen, Warnungen oder die Bereitstellung von Inhalten.

Anweisungen zum Erstellen und Überwachen von Geofences finden Sie unter Einrichten eines Geofence-Raums.

Standortfunktion und Datenschutz

Alle Standort-APIs erfordern die Standortfunktion , die im Paketmanifest Ihrer App deklariert ist. Sie müssen auch Geolocator.RequestAccessAsync zur Laufzeit aufrufen, bevor Sie auf Standortdaten zugreifen.

Windows gibt Benutzern über Einstellungen > Datenschutz und Sicherheit > Standort die Kontrolle darüber, welche Apps auf ihren Standort zugreifen dürfen. Ihre App sollte den Fall behandeln, in dem der Benutzer den Standortzugriff verweigert oder widerruft.

Vollständiges Beispiel

Das folgende Beispiel führt MapControl, Geolocator und GeofenceMonitor in einem einzelnen WinUI 3-Fenster zusammen. Wenn kein Azure Maps-Schlüssel konfiguriert ist, wird die Kartenfunktionalität kontrolliert eingeschränkt, während Geolokalisierung und Geofencing weiterhin funktionieren.

Voraussetzungen

  • Windows App SDK 2.2 oder höher
  • Ein Azure Maps Schlüssel – erforderlich zum Anzeigen von Kartenkacheln. Ohne einen gültigen Schlüssel rendert das MapControl-Objekt, zeigt jedoch eine leere Karte an.
  • Die in Package.appxmanifest deklarierte Gerätefunktion Standort:
<DeviceCapability Name="location" />

Legen Sie ihren Azure Maps Schlüssel als Umgebungsvariable fest, bevor Sie die App ausführen:

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

MainWindow.xaml

Ein 300-Pixel-Bedienfeld auf der linken Seite mit Schaltflächen und Statustext sowie MapControl auf der rechten Seite. Eine Überlagerung wird angezeigt, wenn der Azure Maps-Schlüssel fehlt.

<?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}");
}

Wichtige Muster

  • MapControlist in Windows App SDK 1.6 und höher integriert. Legen Sie für MapServiceToken Ihren Azure Maps-Schlüssel fest.
  • TryConfigureMap überprüft die Umgebungsvariable AZURE_MAPS_KEY beim Start. Ist die Variable leer, wird die Karte eingeklappt, und ein Overlay erläutert, wie sich das Problem beheben lässt – kein Absturz, keine leere Karte.
  • DeviceCapability Name="location" in Package.appxmanifest ist erforderlich oder Geolocator.RequestAccessAsync gibt zurück Denied.
  • GeofenceMonitor.GeofenceStateChanged wird auf einem Hintergrundthread ausgelöst. Verwenden Sie daher DispatcherQueue.TryEnqueue, um die Benutzeroberfläche zu aktualisieren.