516 lines
13 KiB
Markdown
516 lines
13 KiB
Markdown
# Design Document
|
||
|
||
## Overview
|
||
|
||
本设计文档详细描述了将Navisworks物流路径规划插件从AddInPlugin架构迁移到DockPanePlugin架构的技术实现方案。基于Navisworks 2026 API文档中的DockPanePlugin示例代码,我们将重新设计插件架构以提供可停靠的用户界面面板,并利用WPF技术栈实现更现代化的用户体验。
|
||
|
||
## Architecture
|
||
|
||
### 插件架构变更
|
||
|
||
#### 当前架构 (AddInPlugin)
|
||
```csharp
|
||
[PluginAttribute("Basic", "Tian", ToolTip = "Transport Plugin", DisplayName = "Transport Plugin")]
|
||
[AddInPlugin(AddInLocation.AddIn)]
|
||
public class Main : AddInPlugin
|
||
{
|
||
public override int Execute(params string[] parameters)
|
||
{
|
||
// 显示模态对话框
|
||
ShowDockPane();
|
||
return 0;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 目标架构 (DockPanePlugin)
|
||
```csharp
|
||
[Plugin("NavisworksTransport.Main", "Tian",
|
||
DisplayName = "物流路径规划",
|
||
ToolTip = "物流路径规划和动画仿真插件")]
|
||
[DockPanePlugin(420, 700)] // 宽度420, 高度700
|
||
[Strings("NavisworksTransport.Tian.name")]
|
||
public class Main : DockPanePlugin
|
||
{
|
||
public override Control CreateControlPane()
|
||
{
|
||
// 创建WPF用户控件并用ElementHost托管
|
||
var wpfControl = new LogisticsControlPanel();
|
||
var elementHost = new ElementHost
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
Child = wpfControl
|
||
};
|
||
return elementHost;
|
||
}
|
||
|
||
public override void DestroyControlPane(Control pane)
|
||
{
|
||
// 清理资源和事件订阅
|
||
if (pane is ElementHost host && host.Child is LogisticsControlPanel wpfControl)
|
||
{
|
||
wpfControl.Cleanup();
|
||
}
|
||
pane.Dispose();
|
||
}
|
||
}
|
||
```
|
||
|
||
### WPF用户控件架构
|
||
|
||
#### 主控件结构
|
||
```
|
||
LogisticsControlPanel (UserControl)
|
||
├── MainTabControl (TabControl)
|
||
│ ├── ModelSettingsTab (TabItem)
|
||
│ │ └── ModelSettingsView (UserControl)
|
||
│ ├── PathEditingTab (TabItem)
|
||
│ │ └── PathEditingView (UserControl)
|
||
│ ├── AnimationControlTab (TabItem)
|
||
│ │ └── AnimationControlView (UserControl)
|
||
│ └── SystemManagementTab (TabItem)
|
||
│ └── SystemManagementView (UserControl)
|
||
└── BottomPanel (StackPanel)
|
||
├── HelpButton (Button)
|
||
├── AboutButton (Button)
|
||
└── CloseButton (Button)
|
||
```
|
||
|
||
## Components and Interfaces
|
||
|
||
### 1. 主插件类 (Main)
|
||
|
||
**职责:**
|
||
- 实现DockPanePlugin接口
|
||
- 管理插件生命周期
|
||
- 创建和销毁控件面板
|
||
|
||
**关键方法:**
|
||
- `CreateControlPane()`: 创建ElementHost并托管WPF控件
|
||
- `DestroyControlPane(Control pane)`: 清理资源和释放控件
|
||
|
||
### 2. WPF主控件 (LogisticsControlPanel)
|
||
|
||
**职责:**
|
||
- 作为所有功能的容器
|
||
- 管理Tab页面切换
|
||
- 处理全局事件和状态
|
||
|
||
**关键属性和方法:**
|
||
```csharp
|
||
public partial class LogisticsControlPanel : UserControl
|
||
{
|
||
// 子视图引用
|
||
private ModelSettingsView _modelSettingsView;
|
||
private PathEditingView _pathEditingView;
|
||
private AnimationControlView _animationControlView;
|
||
private SystemManagementView _systemManagementView;
|
||
|
||
// 管理器实例
|
||
private PathPlanningManager _pathPlanningManager;
|
||
private static bool _isSessionInitialized = false;
|
||
|
||
public LogisticsControlPanel()
|
||
{
|
||
InitializeComponent();
|
||
InitializeSession();
|
||
InitializeViews();
|
||
SubscribeToEvents();
|
||
}
|
||
|
||
public void Cleanup()
|
||
{
|
||
UnsubscribeFromEvents();
|
||
CleanupManagers();
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. 子视图控件
|
||
|
||
#### ModelSettingsView
|
||
- 类别属性设置
|
||
- 物流模型列表
|
||
- 可见性控制
|
||
- 统计信息显示
|
||
|
||
#### PathEditingView
|
||
- 路径列表管理
|
||
- 当前路径编辑
|
||
- 路径文件管理
|
||
|
||
#### AnimationControlView
|
||
- 动画参数设置
|
||
- 播放控制
|
||
- 碰撞检测
|
||
|
||
#### SystemManagementView
|
||
- 模型分层拆分
|
||
- 日志管理
|
||
- 插件设置
|
||
- 系统信息
|
||
|
||
### 4. MVVM架构支持
|
||
|
||
#### ViewModel基类
|
||
```csharp
|
||
public abstract class ViewModelBase : INotifyPropertyChanged
|
||
{
|
||
public event PropertyChangedEventHandler PropertyChanged;
|
||
|
||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||
{
|
||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||
}
|
||
|
||
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||
{
|
||
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
|
||
field = value;
|
||
OnPropertyChanged(propertyName);
|
||
return true;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 主ViewModel
|
||
```csharp
|
||
public class LogisticsControlViewModel : ViewModelBase
|
||
{
|
||
private string _selectedModelsText;
|
||
private string _instructionText;
|
||
private ObservableCollection<LogisticsModel> _logisticsModels;
|
||
|
||
public string SelectedModelsText
|
||
{
|
||
get => _selectedModelsText;
|
||
set => SetProperty(ref _selectedModelsText, value);
|
||
}
|
||
|
||
public string InstructionText
|
||
{
|
||
get => _instructionText;
|
||
set => SetProperty(ref _instructionText, value);
|
||
}
|
||
|
||
public ObservableCollection<LogisticsModel> LogisticsModels
|
||
{
|
||
get => _logisticsModels;
|
||
set => SetProperty(ref _logisticsModels, value);
|
||
}
|
||
|
||
// Commands
|
||
public ICommand RefreshCommand { get; }
|
||
public ICommand ClearSelectionCommand { get; }
|
||
public ICommand ShowAllCommand { get; }
|
||
public ICommand HideAllCommand { get; }
|
||
}
|
||
```
|
||
|
||
## Data Models
|
||
|
||
### 1. 数据传输对象
|
||
|
||
#### LogisticsModel
|
||
```csharp
|
||
public class LogisticsModel : INotifyPropertyChanged
|
||
{
|
||
private string _name;
|
||
private string _category;
|
||
private string _attributes;
|
||
private bool _isVisible;
|
||
|
||
public string Name
|
||
{
|
||
get => _name;
|
||
set => SetProperty(ref _name, value);
|
||
}
|
||
|
||
public string Category
|
||
{
|
||
get => _category;
|
||
set => SetProperty(ref _category, value);
|
||
}
|
||
|
||
public string Attributes
|
||
{
|
||
get => _attributes;
|
||
set => SetProperty(ref _attributes, value);
|
||
}
|
||
|
||
public bool IsVisible
|
||
{
|
||
get => _isVisible;
|
||
set => SetProperty(ref _isVisible, value);
|
||
}
|
||
|
||
public ModelItem NavisworksItem { get; set; }
|
||
|
||
// INotifyPropertyChanged implementation
|
||
public event PropertyChangedEventHandler PropertyChanged;
|
||
|
||
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||
{
|
||
if (EqualityComparer<T>.Default.Equals(field, value)) return;
|
||
field = value;
|
||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||
}
|
||
}
|
||
```
|
||
|
||
#### PathRoute
|
||
```csharp
|
||
public class PathRoute : INotifyPropertyChanged
|
||
{
|
||
private string _name;
|
||
private ObservableCollection<PathPoint> _points;
|
||
private bool _isActive;
|
||
|
||
public string Name
|
||
{
|
||
get => _name;
|
||
set => SetProperty(ref _name, value);
|
||
}
|
||
|
||
public ObservableCollection<PathPoint> Points
|
||
{
|
||
get => _points;
|
||
set => SetProperty(ref _points, value);
|
||
}
|
||
|
||
public bool IsActive
|
||
{
|
||
get => _isActive;
|
||
set => SetProperty(ref _isActive, value);
|
||
}
|
||
|
||
// INotifyPropertyChanged implementation
|
||
public event PropertyChangedEventHandler PropertyChanged;
|
||
|
||
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||
{
|
||
if (EqualityComparer<T>.Default.Equals(field, value)) return;
|
||
field = value;
|
||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 业务逻辑管理器
|
||
|
||
现有的管理器类保持不变,但需要适配WPF的事件模型:
|
||
- `PathPlanningManager`
|
||
- `PathDataManager`
|
||
- `VisibilityManager`
|
||
- `ModelSplitterManager`
|
||
- `LogisticsAnimationManager`
|
||
|
||
## Error Handling
|
||
|
||
### 1. WPF异常处理
|
||
|
||
```csharp
|
||
public partial class LogisticsControlPanel : UserControl
|
||
{
|
||
public LogisticsControlPanel()
|
||
{
|
||
InitializeComponent();
|
||
|
||
// 订阅WPF异常事件
|
||
Dispatcher.UnhandledException += OnDispatcherUnhandledException;
|
||
}
|
||
|
||
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||
{
|
||
LogManager.Error($"[WPF异常] {e.Exception.Message}");
|
||
LogManager.Error($"[WPF异常] 堆栈信息: {e.Exception.StackTrace}");
|
||
|
||
// 标记异常已处理,避免程序崩溃
|
||
e.Handled = true;
|
||
|
||
// 显示用户友好的错误信息
|
||
MessageBox.Show($"界面操作发生错误: {e.Exception.Message}",
|
||
"错误", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 线程安全处理
|
||
|
||
```csharp
|
||
public class ThreadSafeHelper
|
||
{
|
||
public static void InvokeOnUIThread(Action action)
|
||
{
|
||
if (Application.Current?.Dispatcher?.CheckAccess() == true)
|
||
{
|
||
action();
|
||
}
|
||
else
|
||
{
|
||
Application.Current?.Dispatcher?.Invoke(action);
|
||
}
|
||
}
|
||
|
||
public static T InvokeOnUIThread<T>(Func<T> func)
|
||
{
|
||
if (Application.Current?.Dispatcher?.CheckAccess() == true)
|
||
{
|
||
return func();
|
||
}
|
||
else
|
||
{
|
||
return Application.Current.Dispatcher.Invoke(func);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## Testing Strategy
|
||
|
||
### 1. 单元测试
|
||
|
||
#### 插件生命周期测试
|
||
```csharp
|
||
[TestClass]
|
||
public class MainPluginTests
|
||
{
|
||
[TestMethod]
|
||
public void CreateControlPane_ShouldReturnElementHost()
|
||
{
|
||
// Arrange
|
||
var plugin = new Main();
|
||
|
||
// Act
|
||
var control = plugin.CreateControlPane();
|
||
|
||
// Assert
|
||
Assert.IsInstanceOfType(control, typeof(ElementHost));
|
||
Assert.IsNotNull(((ElementHost)control).Child);
|
||
Assert.IsInstanceOfType(((ElementHost)control).Child, typeof(LogisticsControlPanel));
|
||
}
|
||
|
||
[TestMethod]
|
||
public void DestroyControlPane_ShouldDisposeControl()
|
||
{
|
||
// Arrange
|
||
var plugin = new Main();
|
||
var control = plugin.CreateControlPane();
|
||
|
||
// Act
|
||
plugin.DestroyControlPane(control);
|
||
|
||
// Assert
|
||
Assert.IsTrue(control.IsDisposed);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### WPF控件测试
|
||
```csharp
|
||
[TestClass]
|
||
public class LogisticsControlPanelTests
|
||
{
|
||
[TestMethod]
|
||
public void Constructor_ShouldInitializeAllViews()
|
||
{
|
||
// Arrange & Act
|
||
var control = new LogisticsControlPanel();
|
||
|
||
// Assert
|
||
Assert.IsNotNull(control.FindName("MainTabControl"));
|
||
Assert.IsNotNull(control.FindName("ModelSettingsTab"));
|
||
Assert.IsNotNull(control.FindName("PathEditingTab"));
|
||
Assert.IsNotNull(control.FindName("AnimationControlTab"));
|
||
Assert.IsNotNull(control.FindName("SystemManagementTab"));
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 集成测试
|
||
|
||
#### Navisworks API集成测试
|
||
```csharp
|
||
[TestClass]
|
||
public class NavisworksIntegrationTests
|
||
{
|
||
[TestMethod]
|
||
public void SelectionChanged_ShouldUpdateViewModel()
|
||
{
|
||
// 需要在Navisworks环境中运行
|
||
// 测试选择变更事件是否正确更新WPF界面
|
||
}
|
||
|
||
[TestMethod]
|
||
public void PathPlanning_ShouldCreateVisualPath()
|
||
{
|
||
// 测试路径规划功能是否在WPF界面中正确显示
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. 用户界面测试
|
||
|
||
#### WPF自动化测试
|
||
```csharp
|
||
[TestClass]
|
||
public class UIAutomationTests
|
||
{
|
||
[TestMethod]
|
||
public void TabSwitching_ShouldWorkCorrectly()
|
||
{
|
||
// 使用WPF测试框架测试Tab切换功能
|
||
}
|
||
|
||
[TestMethod]
|
||
public void DataBinding_ShouldUpdateUI()
|
||
{
|
||
// 测试数据绑定是否正确更新界面
|
||
}
|
||
}
|
||
```
|
||
|
||
## Performance Considerations
|
||
|
||
### 1. WPF渲染优化
|
||
|
||
- 使用虚拟化ListView和TreeView处理大量数据
|
||
- 实现延迟加载避免界面卡顿
|
||
- 使用异步操作处理耗时任务
|
||
|
||
### 2. 内存管理
|
||
|
||
- 正确实现IDisposable接口
|
||
- 及时取消事件订阅避免内存泄漏
|
||
- 使用WeakReference处理长期引用
|
||
|
||
### 3. 线程优化
|
||
|
||
- 将Navisworks API调用保持在主线程
|
||
- 使用BackgroundWorker处理后台任务
|
||
- 合理使用Dispatcher.BeginInvoke避免界面阻塞
|
||
|
||
## Migration Strategy
|
||
|
||
### 阶段1:基础架构迁移
|
||
1. 创建DockPanePlugin基础结构
|
||
2. 实现ElementHost托管机制
|
||
3. 创建基本的WPF用户控件框架
|
||
|
||
### 阶段2:功能模块迁移
|
||
1. 迁移模型设置功能到WPF
|
||
2. 迁移路径编辑功能到WPF
|
||
3. 迁移动画控制功能到WPF
|
||
4. 迁移系统管理功能到WPF
|
||
|
||
### 阶段3:优化和测试
|
||
1. 实现MVVM模式和数据绑定
|
||
2. 优化性能和用户体验
|
||
3. 完善错误处理和异常管理
|
||
4. 进行全面测试和调试
|
||
|
||
### 阶段4:部署和验证
|
||
1. 更新部署配置
|
||
2. 验证插件注册和加载
|
||
3. 进行用户验收测试
|
||
4. 文档更新和培训 |