You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Xamarin Forms的UWP平台通过鼠标在地图上绘制可选中图形?

Xamarin Forms UWP 地图图形绘制与选中Demo实现

以下是基于Xamarin Forms UWP平台的地图图形绘制解决方案,包含图形选择工具栏、手动绘制(点/折线/多边形)及图形选中功能:

1. 共享项目核心代码

MainPage.xaml(UI布局)

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MapDrawingDemo.MainPage">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <!-- 图形选择工具栏 -->
        <StackLayout Grid.Row="0" Orientation="Horizontal" Padding="10" BackgroundColor="#f0f0f0">
            <Label Text="选择图形类型:" VerticalOptions="Center"/>
            <Picker x:Name="ShapePicker" VerticalOptions="Center" WidthRequest="150">
                <Picker.Items>
                    <x:String>点</x:String>
                    <x:String>折线</x:String>
                    <x:String>多边形</x:String>
                </Picker.Items>
                <Picker.SelectedIndex>0</Picker.SelectedIndex>
            </Picker>
            <Button Text="清除所有图形" Clicked="ClearShapes_Clicked" Margin="10,0"/>
        </StackLayout>
        <!-- 地图容器 -->
        <ContentView x:Name="MapContainer" Grid.Row="1"/>
    </Grid>
</ContentPage>

MainPage.xaml.cs(逻辑交互)

using Xamarin.Forms;

namespace MapDrawingDemo
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            MapContainer.Content = new Map();
        }

        private void ClearShapes_Clicked(object sender, EventArgs e)
        {
            MessagingCenter.Send(this, "ClearShapes");
        }

        public string SelectedShapeType => ShapePicker.SelectedItem.ToString();

        protected override void OnPropertyChanged(string propertyName = null)
        {
            base.OnPropertyChanged(propertyName);
            if (propertyName == nameof(SelectedShapeType))
            {
                MessagingCenter.Send(this, "ShapeChanged");
            }
        }
    }
}

2. UWP平台自定义渲染器

创建MapRenderer.cs文件,实现原生MapControl的绘制与选中逻辑:

using System.Collections.Generic;
using Windows.Devices.Geolocation;
using Windows.UI.Xaml.Controls.Maps;
using Windows.UI.Xaml.Input;
using Xamarin.Forms.Platform.UWP;
using MapDrawingDemo;
using MapDrawingDemo.UWP;
using Xamarin.Forms;

[assembly: ExportRenderer(typeof(Map), typeof(MapRenderer))]
namespace MapDrawingDemo.UWP
{
    public class MapRenderer : ViewRenderer<Map, MapControl>
    {
        private MapControl _mapControl;
        private string _currentShapeType;
        private List<Geopoint> _currentPoints = new List<Geopoint>();
        private List<MapElement> _drawnShapes = new List<MapElement>();
        private MapElement _selectedElement;

        protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
        {
            base.OnElementChanged(e);

            if (Control == null)
            {
                _mapControl = new MapControl();
                // 替换为你的Bing地图服务令牌
                _mapControl.MapServiceToken = "YOUR_BING_MAP_TOKEN";
                // 默认定位到北京
                _mapControl.Center = new Geopoint(new BasicGeoposition { Latitude = 39.9042, Longitude = 116.4074 });
                _mapControl.ZoomLevel = 12;
                _mapControl.PointerPressed += MapControl_PointerPressed;
                SetNativeControl(_mapControl);
            }

            if (e.NewElement != null)
            {
                MessagingCenter.Subscribe<MainPage>(this, "ShapeChanged", sender =>
                {
                    _currentShapeType = sender.SelectedShapeType;
                    _currentPoints.Clear();
                });

                MessagingCenter.Subscribe<MainPage>(this, "ClearShapes", _ =>
                {
                    _mapControl.MapElements.Clear();
                    _drawnShapes.Clear();
                    _selectedElement = null;
                });
            }
        }

        private void MapControl_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(_currentShapeType)) return;

            if (_mapControl.TryPixelToLocation(e.GetCurrentPoint(_mapControl).Position, out var location))
            {
                _currentPoints.Add(location);

                switch (_currentShapeType)
                {
                    case "点":
                        DrawPoint(location);
                        _currentPoints.Clear();
                        break;
                    case "折线":
                        HandlePolylineDrawing();
                        break;
                    case "多边形":
                        HandlePolygonDrawing();
                        break;
                }
            }
        }

        private void DrawPoint(Geopoint location)
        {
            var pointIcon = new MapIcon
            {
                Location = location,
                NormalizedAnchorPoint = new Windows.Foundation.Point(0.5, 0.5),
                Image = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/pin.png")),
                Tag = "Point"
            };
            AddInteractiveShape(pointIcon);
        }

        private void HandlePolylineDrawing()
        {
            // 移除临时预览折线
            RemoveTempShape("TempPolyline");

            if (_currentPoints.Count == 2)
            {
                var polyline = new MapPolyline
                {
                    Path = new Geopath(_currentPoints),
                    StrokeColor = Windows.UI.Colors.Blue,
                    StrokeThickness = 3,
                    Tag = "Polyline"
                };
                AddInteractiveShape(polyline);
                _currentPoints.Clear();
            }
            else if (_currentPoints.Count > 2)
            {
                // 添加临时预览折线
                var tempLine = new MapPolyline
                {
                    Path = new Geopath(_currentPoints),
                    StrokeColor = Windows.UI.Colors.LightBlue,
                    StrokeThickness = 3,
                    Tag = "TempPolyline"
                };
                AddInteractiveShape(tempLine);
            }
        }

        private void HandlePolygonDrawing()
        {
            // 移除临时预览多边形
            RemoveTempShape("TempPolygon");

            if (_currentPoints.Count == 3)
            {
                var polygon = new MapPolygon
                {
                    Path = new Geopath(_currentPoints),
                    FillColor = Windows.UI.Color.FromArgb(100, 0, 255, 0),
                    StrokeColor = Windows.UI.Colors.Green,
                    StrokeThickness = 2,
                    Tag = "Polygon"
                };
                AddInteractiveShape(polygon);
                _currentPoints.Clear();
            }
            else if (_currentPoints.Count > 3)
            {
                // 添加临时预览多边形
                var tempPoly = new MapPolygon
                {
                    Path = new Geopath(_currentPoints),
                    FillColor = Windows.UI.Color.FromArgb(50, 0, 255, 0),
                    StrokeColor = Windows.UI.Colors.LightGreen,
                    StrokeThickness = 2,
                    Tag = "TempPolygon"
                };
                AddInteractiveShape(tempPoly);
            }
        }

        private void AddInteractiveShape(MapElement shape)
        {
            _mapControl.MapElements.Add(shape);
            _drawnShapes.Add(shape);
            shape.Tapped += Shape_Tapped;
        }

        private void RemoveTempShape(string tag)
        {
            var tempShape = _drawnShapes.LastOrDefault(s => s.Tag.ToString() == tag);
            if (tempShape != null)
            {
                _mapControl.MapElements.Remove(tempShape);
                _drawnShapes.Remove(tempShape);
            }
        }

        private void Shape_Tapped(MapElement sender, MapElementTappedEventArgs args)
        {
            // 重置上一个选中图形的样式
            if (_selectedElement != null)
            {
                ResetShapeStyle(_selectedElement);
            }

            // 设置当前选中图形的样式
            _selectedElement = sender;
            SetSelectedStyle(_selectedElement);
        }

        private void SetSelectedStyle(MapElement shape)
        {
            switch (shape.Tag.ToString())
            {
                case "Point":
                    ((MapIcon)shape).Image = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/pin_selected.png"));
                    break;
                case "Polyline":
                    var line = (MapPolyline)shape;
                    line.StrokeColor = Windows.UI.Colors.Red;
                    line.StrokeThickness = 4;
                    break;
                case "Polygon":
                    var poly = (MapPolygon)shape;
                    poly.StrokeColor = Windows.UI.Colors.Red;
                    poly.StrokeThickness = 3;
                    poly.FillColor = Windows.UI.Color.FromArgb(150, 255, 0, 0);
                    break;
            }
        }

        private void ResetShapeStyle(MapElement shape)
        {
            switch (shape.Tag.ToString())
            {
                case "Point":
                    ((MapIcon)shape).Image = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/pin.png"));
                    break;
                case "Polyline":
                    var line = (MapPolyline)shape;
                    line.StrokeColor = Windows.UI.Colors.Blue;
                    line.StrokeThickness = 3;
                    break;
                case "Polygon":
                    var poly = (MapPolygon)shape;
                    poly.StrokeColor = Windows.UI.Colors.Green;
                    poly.StrokeThickness = 2;
                    poly.FillColor = Windows.UI.Color.FromArgb(100, 0, 255, 0);
                    break;
            }
        }
    }
}

3. 配置说明

  • Bing地图令牌:需要在Azure门户申请Bing Maps服务,获取令牌后替换代码中的YOUR_BING_MAP_TOKEN。
  • 图标资源:准备两个PNG图标(正常点图标pin.png、选中状态pin_selected.png),放置在UWP项目的Assets文件夹中。
  • 功能逻辑:
    • 通过顶部Picker选择要绘制的图形类型,点击地图完成绘制:点为单次点击,折线/多边形为多次点击完成(3次点击完成多边形,2次完成折线)。
    • 点击已绘制的图形可切换选中状态,选中后样式会变化。
    • 点击"清除所有图形"按钮可清空地图上的所有绘制内容。

内容的提问来源于stack exchange,提问作者kajal pokale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 08:53:00