14 KiB
14 KiB
Navisworks WPF插件UI架构重构技术设计方案
文档信息
- 创建日期: 2025-08-16
- 版本: v1.0
- 状态: 技术设计阶段
- 作者: Claude技术架构师 + Claude Code
1. 项目背景
1.1 问题描述
当前Navisworks 2026 WPF插件在路径规划完成后频繁崩溃,根本原因是将2017年的同步事件驱动架构强行迁移到异步WPF/MVVM模式,导致严重的线程安全问题。
1.2 技术现状
- 架构: WPF + MVVM模式
- 数据绑定: ObservableCollection + PropertyChanged
- 线程模型: 后台线程 + Dispatcher异步调用
- 状态管理: 事件驱动 + 多层嵌套调用
1.3 关键问题
- 线程竞争: 后台线程直接修改UI绑定的ObservableCollection
- 事件重入: PropertyChanged事件链导致循环调用
- 调用链过深: 多层Dispatcher.BeginInvoke嵌套
- 状态不一致: UI更新缺乏原子性保证
2. 技术架构设计
2.1 总体架构图
┌─────────────────────────────────────────────────────────────┐
│ UI Layer (WPF) │
├─────────────────────────────────────────────────────────────┤
│ LogisticsControlViewModel (重构) │
│ ├── ThreadSafeObservableCollection<PathRouteViewModel> │
│ ├── UIStateManager.ExecuteUIUpdate() │
│ └── Command Pattern (替代事件驱动) │
├─────────────────────────────────────────────────────────────┤
│ UI State Management │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ UIStateManager │ │ ViewModelBase │ │
│ │ (线程安全) │ │ (防重入机制) │ │
│ └─────────────────┘ └─────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Business Logic Layer │
│ PathPlanningManager (解耦UI) │
│ ├── IPathPlanningService (接口) │
│ ├── PathPlanningResult (数据传输对象) │
│ └── StatusUpdateCommand (命令模式) │
└─────────────────────────────────────────────────────────────┘
2.2 核心组件设计
2.2.1 UIStateManager - 统一UI状态管理器
public class UIStateManager
{
private readonly SynchronizationContext _uiContext;
private readonly object _lock = new object();
private readonly Queue<UIUpdateOperation> _updateQueue;
private volatile bool _isProcessing = false;
// 线程安全的UI更新接口
public Task ExecuteUIUpdateAsync<T>(Func<T> operation) where T : class
{
return Task.Run(() =>
{
lock (_lock)
{
if (_uiContext.CheckAccess())
{
return operation();
}
else
{
T result = default(T);
_uiContext.Send(_ => result = operation(), null);
return result;
}
}
});
}
// 批量UI更新(原子性保证)
public void ExecuteBatchUIUpdate(params Action[] updates)
{
if (_uiContext.CheckAccess())
{
foreach (var update in updates)
update();
}
else
{
_uiContext.Send(_ =>
{
foreach (var update in updates)
update();
}, null);
}
}
}
2.2.2 ThreadSafeObservableCollection - 线程安全集合
public class ThreadSafeObservableCollection<T> : ObservableCollection<T>
{
private readonly object _lock = new object();
private readonly SynchronizationContext _synchronizationContext;
public ThreadSafeObservableCollection()
{
_synchronizationContext = SynchronizationContext.Current;
BindingOperations.EnableCollectionSynchronization(this, _lock);
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (_synchronizationContext != null && _synchronizationContext != SynchronizationContext.Current)
{
_synchronizationContext.Send(_ => base.OnCollectionChanged(e), null);
}
else
{
base.OnCollectionChanged(e);
}
}
// 批量操作接口
public void AddRange(IEnumerable<T> items)
{
lock (_lock)
{
foreach (var item in items)
Items.Add(item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
}
2.2.3 重构的ViewModelBase - 防重入PropertyChanged
public class ViewModelBase : INotifyPropertyChanged
{
private readonly UIStateManager _uiStateManager;
private readonly HashSet<string> _updatingProperties = new HashSet<string>();
private readonly object _propertyLock = new object();
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
lock (_propertyLock)
{
// 防重入检查
if (_updatingProperties.Contains(propertyName))
return;
_updatingProperties.Add(propertyName);
}
try
{
_uiStateManager.ExecuteUIUpdateAsync(() =>
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
});
}
finally
{
lock (_propertyLock)
{
_updatingProperties.Remove(propertyName);
}
}
}
// 批量属性更新
protected void SetProperties(params (string name, object value)[] properties)
{
_uiStateManager.ExecuteBatchUIUpdate(
properties.Select(p => new Action(() => OnPropertyChanged(p.name))).ToArray()
);
}
}
2.2.4 Command Pattern替代事件驱动
public interface IPathPlanningCommand
{
Task<PathPlanningResult> ExecuteAsync();
}
public class AutoPathPlanningCommand : IPathPlanningCommand
{
private readonly Point3D _startPoint;
private readonly Point3D _endPoint;
private readonly PathPlanningManager _manager;
public async Task<PathPlanningResult> ExecuteAsync()
{
// 纯业务逻辑,不涉及UI
var route = await _manager.AutoPlanPathAsync(_startPoint, _endPoint);
return new PathPlanningResult
{
IsSuccess = route != null,
Route = route,
Message = route != null ? "规划成功" : "未找到可行路径"
};
}
}
// UI层处理
public class LogisticsControlViewModel : ViewModelBase
{
private async void ExecuteAutoPlanPath()
{
var command = new AutoPathPlanningCommand(_startPoint, _endPoint, _pathPlanningManager);
var result = await command.ExecuteAsync();
// 使用UIStateManager统一处理UI更新
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
if (result.IsSuccess)
{
// 原子性UI更新
var viewModel = CreatePathRouteViewModel(result.Route);
PathRoutes.Add(viewModel);
SelectedPathRoute = viewModel;
StatusText = result.Message;
}
else
{
StatusText = result.Message;
}
});
}
}
2.3 状态机设计
public enum UIState
{
Idle,
Planning,
Updating,
Error
}
public class UIStateMachine
{
private UIState _currentState = UIState.Idle;
private readonly object _stateLock = new object();
public bool TryTransition(UIState fromState, UIState toState)
{
lock (_stateLock)
{
if (_currentState == fromState)
{
_currentState = toState;
return true;
}
return false;
}
}
public UIState CurrentState
{
get { lock (_stateLock) return _currentState; }
}
}
3. 实施计划
3.1 第一阶段:基础设施搭建(1-2周)
3.1.1 创建核心组件
- 实现UIStateManager类
- 实现ThreadSafeObservableCollection类
- 重构ViewModelBase基类
- 创建UIStateMachine状态机
3.1.2 单元测试
- UIStateManager线程安全测试
- ThreadSafeObservableCollection并发测试
- ViewModelBase防重入测试
3.2 第二阶段:核心组件重构(2-3周)
3.2.1 重构LogisticsControlViewModel
public class LogisticsControlViewModel : ViewModelBase
{
// 使用线程安全集合
public ThreadSafeObservableCollection<PathRouteViewModel> PathRoutes { get; }
// 使用UIStateManager处理所有UI更新
private readonly UIStateManager _uiStateManager;
// 使用状态机管理UI状态
private readonly UIStateMachine _stateMachine;
// 使用Command Pattern替代事件订阅
public ICommand AutoPlanPathCommand { get; }
}
3.2.2 解耦PathPlanningManager
public interface IPathPlanningService
{
Task<PathRoute> AutoPlanPathAsync(Point3D start, Point3D end, double vehicleSize, double safetyMargin);
Task<bool> ValidatePathAsync(PathRoute route);
}
public class PathPlanningManager : IPathPlanningService
{
// 移除所有UI相关事件
// 只保留纯业务逻辑
public async Task<PathRoute> AutoPlanPathAsync(Point3D start, Point3D end, double vehicleSize, double safetyMargin)
{
// 纯后台线程执行,不触发任何UI事件
return await Task.Run(() => {
// 原有的路径规划逻辑
});
}
}
3.2.3 重新设计UI更新流程
旧流程 (有问题):
PathPlanningManager.AutoPlanPath()
→ 后台线程触发StatusChanged事件
→ UI线程订阅事件处理
→ 直接修改ObservableCollection
→ 嵌套Dispatcher调用
→ 崩溃
新流程 (安全):
UI.ExecuteAutoPlanPath()
→ Command.ExecuteAsync()
→ PathPlanningService.AutoPlanPathAsync()
→ 返回PathPlanningResult
→ UIStateManager.ExecuteUIUpdateAsync()
→ 原子性UI更新
→ 完成
3.3 第三阶段:测试验证(1周)
3.3.1 压力测试场景
- 重复规划测试: 连续执行100次"选择起点→选择终点→自动规划"
- 属性修改测试: 重复执行"通道→门→规划→删除→规划"场景
- 并发操作测试: 同时进行多个UI操作
- 内存泄漏测试: 长时间运行检查内存使用
3.3.2 功能验证清单
- 自动路径规划功能正常
- UI响应性能良好
- 无UI线程阻塞
- 无内存泄漏
- 错误处理正确
3.4 第四阶段:性能优化(1周)
3.4.1 UI性能优化
- 减少不必要的PropertyChanged触发
- 优化数据绑定性能
- 实现虚拟化长列表
3.4.2 内存管理优化
- 确保事件订阅正确释放
- 优化大对象内存分配
- 实现对象池模式
4. 风险控制
4.1 技术风险
-
兼容性风险: 新架构可能影响现有功能
- 缓解措施: 分阶段迁移,保持API接口不变
-
性能风险: 线程安全机制可能影响性能
- 缓解措施: 性能基准测试,优化关键路径
-
复杂性风险: 新架构增加代码复杂度
- 缓解措施: 详细文档,代码审查
4.2 进度风险
-
开发进度: 重构工作量可能超出预期
- 缓解措施: 分阶段实施,每阶段可独立交付
-
测试时间: 充分测试需要时间
- 缓解措施: 自动化测试,并行开发和测试
4.3 回滚策略
- 每个阶段都在独立分支开发
- 保持主分支稳定
- 每个阶段完成后合并,确保可回滚
5. 预期效果
5.1 稳定性提升
- 消除UI崩溃: 通过线程安全机制彻底解决崩溃问题
- 提高可靠性: 原子性UI更新确保状态一致性
- 增强健壮性: 防重入机制避免循环调用
5.2 维护性改善
- 代码结构清晰: 分层架构,职责明确
- 可测试性强: 依赖注入,便于单元测试
- 可扩展性好: 接口设计,便于功能扩展
5.3 用户体验优化
- 响应速度快: 异步操作,UI不阻塞
- 操作流畅: 无卡顿,无异常
- 功能稳定: 重复操作不崩溃
6. 总结
本方案通过全面重构UI架构,从根本上解决了WPF版本的稳定性问题。核心思想是:
- 分离关注点: 业务逻辑与UI逻辑完全分离
- 线程安全: 统一的线程安全管理机制
- 原子操作: 确保UI更新的原子性
- 防重入: 避免事件循环调用
- 状态管理: 清晰的UI状态流转
通过这个重构方案,将恢复2017版本的稳定性,同时保持WPF现代UI的优势,为后续功能扩展打下坚实基础。
本文档将根据实施进度持续更新,确保设计方案与实际开发保持同步。