WPF中Mapsui MapControl多边形框选多线实现问询
在WPF中基于Mapsui实现拖拽多边形框选连线功能
你已实现点击选中单条线的功能,要添加拖拽绘制多边形框选的功能,可按以下步骤实现:
实现步骤
- 新增临时图层,用于实时显示拖拽绘制的框选多边形
- 监听Mapsui控件的鼠标事件,记录拖拽过程中的坐标点,动态更新临时图层的多边形
- 鼠标释放时完成多边形绘制,遍历所有连线,判断哪些连线与多边形相交或被包含,标记为选中状态
- 刷新图层样式,更新选中效果
修改后的完整代码
首先在地图类中添加以下字段:
private bool _isDragging; private List<Coordinate> _selectionPoints = new(); private Layer _selectionLayer; private MemoryProvider _selectionProvider;
修改CreateMapAsync方法,添加临时图层和鼠标事件绑定:
public Task<Map> CreateMapAsync(Map map) { map.Layers.Add(Mapsui.Tiling.OpenStreetMap.CreateTileLayer()); var polylineLayer = CreatePolylineLayer(); map.Layers.Add(polylineLayer); // 初始化框选临时图层 _selectionProvider = new MemoryProvider(); _selectionLayer = new Layer("Selection") { DataSource = _selectionProvider, Style = new VectorStyle { Fill = new Brush { Color = new Mapsui.Styles.Color(0, 0, 255, 50) }, Outline = new Pen { Color = Mapsui.Styles.Color.Blue, Width = 2 } } }; map.Layers.Add(_selectionLayer); map.Navigator.CenterOnAndZoomTo(polylineLayer.Extent!.Centroid, map.Navigator.Resolutions[6]); map.Info += (s, a) => ToggleSelected(a.MapInfo?.Feature); map.Widgets.Add(new MapInfoWidget(map)); // 绑定鼠标事件(sender需为MapControl实例,根据实际场景调整) var mapControl = (MapControl)sender; mapControl.MouseDown += MapControl_MouseDown; mapControl.MouseMove += MapControl_MouseMove; mapControl.MouseUp += MapControl_MouseUp; return Task.FromResult(map); }
实现鼠标事件处理方法:
private void MapControl_MouseDown(object sender, MouseButtonEventArgs e) { if (e.LeftButton != MouseButtonState.Pressed) return; _isDragging = true; _selectionPoints.Clear(); var mapControl = (MapControl)sender; var screenPosition = e.GetPosition(mapControl); var worldPosition = mapControl.Map.Navigator.Viewport.ScreenToWorld(screenPosition); _selectionPoints.Add(worldPosition); } private void MapControl_MouseMove(object sender, MouseEventArgs e) { if (!_isDragging) return; var mapControl = (MapControl)sender; var screenPosition = e.GetPosition(mapControl); var worldPosition = mapControl.Map.Navigator.Viewport.ScreenToWorld(screenPosition); // 更新临时多边形 if (_selectionPoints.Count > 0) { _selectionPoints.RemoveAt(_selectionPoints.Count - 1); } _selectionPoints.Add(worldPosition); if (_selectionPoints.Count >= 2) { var polygon = new Polygon(new LinearRing(_selectionPoints.ToArray())); _selectionProvider.Clear(); _selectionProvider.Add(new GeometryFeature { Geometry = polygon }); _selectionLayer.DataSourceChanged(); } } private void MapControl_MouseUp(object sender, MouseButtonEventArgs e) { if (!_isDragging) return; _isDragging = false; if (_selectionPoints.Count < 3) // 至少3个点才能形成有效多边形 { _selectionProvider.Clear(); _selectionLayer.DataSourceChanged(); return; } // 闭合多边形 _selectionPoints.Add(_selectionPoints[0]); var selectionPolygon = new Polygon(new LinearRing(_selectionPoints.ToArray())); // 获取所有连线要素 var polylineLayer = map.Layers.First(l => l.Name == "Polyline"); var features = ((MemoryProvider)polylineLayer.DataSource).GetFeatures().ToList(); // 判断连线是否被框选覆盖 var selectedFeatures = features.Where(f => f.Geometry.Intersects(selectionPolygon) || f.Geometry.Within(selectionPolygon)).ToList(); // 批量设置选中状态 BatchToggleSelected(selectedFeatures); // 清空临时框选图层 _selectionProvider.Clear(); _selectionLayer.DataSourceChanged(); }
添加批量选中方法,修改原有ToggleSelected:
private void ToggleSelected(IFeature? feature) { if (feature == null) return; var isSelected = feature["selected"]?.ToString() == "true"; feature["selected"] = isSelected ? "false" : "true"; ((Layer)map.Layers.First(l => l.Name == "Polyline")).DataSourceChanged(); } private void BatchToggleSelected(List<IFeature> features) { // 先清空所有选中状态,再设置新选中项 var allFeatures = ((MemoryProvider)map.Layers.First(l => l.Name == "Polyline").DataSource).GetFeatures().ToList(); foreach (var f in allFeatures) { f["selected"] = "false"; } foreach (var f in features) { f["selected"] = "true"; } ((Layer)map.Layers.First(l => l.Name == "Polyline")).DataSourceChanged(); }
原有CreatePolylineLayer、CreatePoinst、CreatePolylines、CreateLineStringStyle方法保持不变即可。
关键说明
- 临时图层使用半透明填充色,避免遮挡底层地图与连线
- 鼠标拖拽时实时更新多边形终点,实现动态绘制效果
- 借助NetTopologySuite的空间关系方法判断连线是否被框选覆盖
- 批量更新选中状态后调用
DataSourceChanged()触发图层重绘,更新样式
内容的提问来源于stack exchange,提问作者dnlzrv
相关产品推荐
相关产品推荐

