如何在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
相关产品推荐
相关产品推荐

