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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 12:40:17