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

如何解决C# Mapsui.WPF中两个EditingWidget切换绘图的异常问题?

Mapsui.WPF 绘图模式切换异常问题

我使用C# Mapsui.WPF库在地图上绘制多边形和线,需求如下:

  • 点击「DrawPolygon」按钮可绘制多边形,点击「DrawLines」按钮可绘制线
  • 激活一种绘图模式时,另一种自动禁用(通过单选按钮关联Widget的Enabled状态)

目前功能框架已实现,但存在异常,尝试了两种方案均有缺陷:


方案1:XAML加载时初始化EditManager和Widget

初始化时禁用Widget避免误操作,但后续点击按钮重新启用后无法进行绘图。相关代码:

public void LoadModelData(object sender, RoutedEventArgs e)
{
    DataContext = ViewModel;

    // mapsui load new MapControl to XAML-Tag and add OpenStreetMap
    // then add it to Content of ContentControl-Tag
    mapControl = new Mapsui.UI.Wpf.MapControl();
    mapControl.Map?.Layers.Add(Mapsui.Tiling.OpenStreetMap.CreateTileLayer());
    MapContainer.Content = mapControl;
    mapControl.Map.Widgets.Add(new MapInfoWidget(mapControl.Map)); // should always be first widget
    mapControl.Map.Layers.Add(ViewModel.PolygonLayer, ViewModel.LineLayer);

    mapControl.Info += MapControlOnInfo;
    mapControl.MouseMove += MapControlOnMouseMove;

    
    ViewModel.LineEditManager = new EditManager
    {
        Layer = (WritableLayer)mapControl.Map.Layers.First(l => l.Name == "Line"),
        EditMode = EditMode.AddLine,
    };
    IWidget widget = new EditingWidget(mapControl, ViewModel.LineEditManager, new EditManipulation());
    mapControl.Map.Widgets.Add(widget);
    ViewModel.LineWidgetIndex = mapControl.Map.Widgets.ToList().IndexOf(widget);
// Especially here seems to be the problem
    mapControl.Map.Widgets.ElementAt(ViewModel.LineWidgetIndex).Enabled = false;

    ViewModel.BBoxEditManager = new EditManager
    {
        Layer = (WritableLayer)mapControl.Map.Layers.First(l => l.Name == "Polygon"),
        EditMode = EditMode.AddPolygon,
    };
    widget = new EditingWidget(mapControl, ViewModel.BBoxEditManager, new EditManipulation());
    mapControl.Map.Widgets.Add(widget);
    ViewModel.BBoxWidgetIndex = mapControl.Map.Widgets.ToList().IndexOf(widget);
// and also here 
    mapControl.Map.Widgets.ElementAt(ViewModel.BBoxWidgetIndex).Enabled = false;
}

方案2:按钮点击时初始化Widget(不存在时)

首次绘制多边形或线功能正常,但切换到另一种模式后,无法再次启用之前的绘图功能。相关代码:

/// <summary>
/// enables or disables Widget for LineLayer
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void DrawLine(object sender, RoutedEventArgs e)
{
    if (ViewModel.LineEditManager.Layer != ViewModel.LineLayer)
    {
        ViewModel.LineEditManager = new EditManager
        {
            Layer = (WritableLayer)mapControl.Map.Layers.First(l => l.Name == "Line"),
            EditMode = EditMode.AddLine,
        };
        IWidget widget = new EditingWidget(mapControl, ViewModel.LineEditManager, new EditManipulation());
        mapControl.Map.Widgets.Add(widget);
        ViewModel.LineWidgetIndex = mapControl.Map.Widgets.ToList().IndexOf(widget);
    }

    mapControl.Map.Widgets.ElementAt(ViewModel.LineWidgetIndex).Enabled = drawSection.IsChecked.Value; 
}

/// <summary>
/// enables or disables Widget for PolygonLayer
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DrawBBox(object sender, RoutedEventArgs e)
{
    if (ViewModel.BBoxEditManager.Layer != ViewModel.PolygonLayer)
    {
        ViewModel.BBoxEditManager = new EditManager
        {
            Layer = (WritableLayer)mapControl.Map.Layers.First(l => l.Name == "Polygon"),
            EditMode = EditMode.AddPolygon,
        };
        IWidget widget = new EditingWidget(mapControl, ViewModel.BBoxEditManager, new EditManipulation());
        mapControl.Map.Widgets.Add(widget);
        ViewModel.BBoxWidgetIndex = mapControl.Map.Widgets.ToList().IndexOf(widget);
    }

    mapControl.Map.Widgets.ElementAt(ViewModel.BBoxWidgetIndex).Enabled = drawBBox.IsChecked.Value; 
}

解决思路

针对方案1的修复

  1. 直接保存Widget实例:初始化时将EditingWidget实例保存到ViewModel的属性(如LineEditingWidget、PolygonEditingWidget),避免通过索引操作可能出现的偏移问题。
  2. 同步EditManager状态:启用Widget时调用EditManager.Start()激活编辑流程,禁用时调用EditManager.Stop()终止当前编辑,避免状态冲突。
  3. 互斥模式切换:激活一种模式前,自动禁用另一种模式的Widget并停止其EditManager。

修改后的初始化代码片段:

// 初始化线编辑组件
ViewModel.LineEditManager = new EditManager
{
    Layer = (WritableLayer)mapControl.Map.Layers.First(l => l.Name == "Line"),
    EditMode = EditMode.AddLine,
};
ViewModel.LineEditingWidget = new EditingWidget(mapControl, ViewModel.LineEditManager, new EditManipulation());
mapControl.Map.Widgets.Add(ViewModel.LineEditingWidget);
ViewModel.LineEditingWidget.Enabled = false;

// 初始化多边形编辑组件
ViewModel.BBoxEditManager = new EditManager
{
    Layer = (WritableLayer)mapControl.Map.Layers.First(l => l.Name == "Polygon"),
    EditMode = EditMode.AddPolygon,
};
ViewModel.PolygonEditingWidget = new EditingWidget(mapControl, ViewModel.BBoxEditManager, new EditManipulation());
mapControl.Map.Widgets.Add(ViewModel.PolygonEditingWidget);
ViewModel.PolygonEditingWidget.Enabled = false;

按钮点击事件修改:

public void DrawLine(object sender, RoutedEventArgs e)
{
    bool isEnabled = drawSection.IsChecked.Value;
    ViewModel.LineEditingWidget.Enabled = isEnabled;
    ViewModel.PolygonEditingWidget.Enabled = !isEnabled;

    if (isEnabled)
    {
        ViewModel.LineEditManager.Start();
        ViewModel.BBoxEditManager.Stop();
    }
    else
    {
        ViewModel.LineEditManager.Stop();
    }
}

private void DrawBBox(object sender, RoutedEventArgs e)
{
    bool isEnabled = drawBBox.IsChecked.Value;
    ViewModel.PolygonEditingWidget.Enabled = isEnabled;
    ViewModel.LineEditingWidget.Enabled = !isEnabled;

    if (isEnabled)
    {
        ViewModel.BBoxEditManager.Start();
        ViewModel.LineEditManager.Stop();
    }
    else
    {
        ViewModel.BBoxEditManager.Stop();
    }
}

通用注意事项

  • 避免重复添加Widget:确保每个编辑模式的Widget只被添加一次到Map的Widgets集合,重复添加会引发输入监听冲突。
  • 绑定单选按钮状态:将单选按钮的IsChecked属性直接绑定到ViewModel中对应Widget的Enabled属性,结合命令模式减少代码耦合。
  • 监听EditManager状态:订阅EditManager的EditModeChanged事件,确保UI状态与编辑状态同步。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 11:44:56