如何解决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的修复
- 直接保存Widget实例:初始化时将
EditingWidget实例保存到ViewModel的属性(如LineEditingWidget、PolygonEditingWidget),避免通过索引操作可能出现的偏移问题。 - 同步EditManager状态:启用Widget时调用
EditManager.Start()激活编辑流程,禁用时调用EditManager.Stop()终止当前编辑,避免状态冲突。 - 互斥模式切换:激活一种模式前,自动禁用另一种模式的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
相关产品推荐
相关产品推荐

