NavisworksTransport/doc/working/UI架构重构技术设计方案_20250816.md

464 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 关键问题
1. **线程竞争**: 后台线程直接修改UI绑定的ObservableCollection
2. **事件重入**: PropertyChanged事件链导致循环调用
3. **调用链过深**: 多层Dispatcher.BeginInvoke嵌套
4. **状态不一致**: 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状态管理器
```csharp
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 - 线程安全集合
```csharp
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
```csharp
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替代事件驱动
```csharp
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 状态机设计
```csharp
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
```csharp
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
```csharp
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 压力测试场景
1. **重复规划测试**: 连续执行100次"选择起点→选择终点→自动规划"
2. **属性修改测试**: 重复执行"通道→门→规划→删除→规划"场景
3. **并发操作测试**: 同时进行多个UI操作
4. **内存泄漏测试**: 长时间运行检查内存使用
#### 3.3.2 功能验证清单
- [ ] 自动路径规划功能正常
- [ ] UI响应性能良好
- [ ] 无UI线程阻塞
- [ ] 无内存泄漏
- [ ] 错误处理正确
### 3.4 第四阶段性能优化1周
#### 3.4.1 UI性能优化
- [ ] 减少不必要的PropertyChanged触发
- [ ] 优化数据绑定性能
- [ ] 实现虚拟化长列表
#### 3.4.2 内存管理优化
- [ ] 确保事件订阅正确释放
- [ ] 优化大对象内存分配
- [ ] 实现对象池模式
## 4. 风险控制
### 4.1 技术风险
1. **兼容性风险**: 新架构可能影响现有功能
- **缓解措施**: 分阶段迁移保持API接口不变
2. **性能风险**: 线程安全机制可能影响性能
- **缓解措施**: 性能基准测试,优化关键路径
3. **复杂性风险**: 新架构增加代码复杂度
- **缓解措施**: 详细文档,代码审查
### 4.2 进度风险
1. **开发进度**: 重构工作量可能超出预期
- **缓解措施**: 分阶段实施,每阶段可独立交付
2. **测试时间**: 充分测试需要时间
- **缓解措施**: 自动化测试,并行开发和测试
### 4.3 回滚策略
- 每个阶段都在独立分支开发
- 保持主分支稳定
- 每个阶段完成后合并,确保可回滚
## 5. 预期效果
### 5.1 稳定性提升
- **消除UI崩溃**: 通过线程安全机制彻底解决崩溃问题
- **提高可靠性**: 原子性UI更新确保状态一致性
- **增强健壮性**: 防重入机制避免循环调用
### 5.2 维护性改善
- **代码结构清晰**: 分层架构,职责明确
- **可测试性强**: 依赖注入,便于单元测试
- **可扩展性好**: 接口设计,便于功能扩展
### 5.3 用户体验优化
- **响应速度快**: 异步操作UI不阻塞
- **操作流畅**: 无卡顿,无异常
- **功能稳定**: 重复操作不崩溃
## 6. 总结
本方案通过全面重构UI架构从根本上解决了WPF版本的稳定性问题。核心思想是
1. **分离关注点**: 业务逻辑与UI逻辑完全分离
2. **线程安全**: 统一的线程安全管理机制
3. **原子操作**: 确保UI更新的原子性
4. **防重入**: 避免事件循环调用
5. **状态管理**: 清晰的UI状态流转
通过这个重构方案将恢复2017版本的稳定性同时保持WPF现代UI的优势为后续功能扩展打下坚实基础。
---
*本文档将根据实施进度持续更新,确保设计方案与实际开发保持同步。*