13 KiB
13 KiB
Design Document
Overview
本设计文档详细描述了将Navisworks物流路径规划插件从AddInPlugin架构迁移到DockPanePlugin架构的技术实现方案。基于Navisworks 2026 API文档中的DockPanePlugin示例代码,我们将重新设计插件架构以提供可停靠的用户界面面板,并利用WPF技术栈实现更现代化的用户体验。
Architecture
插件架构变更
当前架构 (AddInPlugin)
[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)
[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页面切换
- 处理全局事件和状态
关键属性和方法:
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基类
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
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
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
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的事件模型:
PathPlanningManagerPathDataManagerVisibilityManagerModelSplitterManagerLogisticsAnimationManager
Error Handling
1. WPF异常处理
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. 线程安全处理
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. 单元测试
插件生命周期测试
[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控件测试
[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集成测试
[TestClass]
public class NavisworksIntegrationTests
{
[TestMethod]
public void SelectionChanged_ShouldUpdateViewModel()
{
// 需要在Navisworks环境中运行
// 测试选择变更事件是否正确更新WPF界面
}
[TestMethod]
public void PathPlanning_ShouldCreateVisualPath()
{
// 测试路径规划功能是否在WPF界面中正确显示
}
}
3. 用户界面测试
WPF自动化测试
[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:基础架构迁移
- 创建DockPanePlugin基础结构
- 实现ElementHost托管机制
- 创建基本的WPF用户控件框架
阶段2:功能模块迁移
- 迁移模型设置功能到WPF
- 迁移路径编辑功能到WPF
- 迁移动画控制功能到WPF
- 迁移系统管理功能到WPF
阶段3:优化和测试
- 实现MVVM模式和数据绑定
- 优化性能和用户体验
- 完善错误处理和异常管理
- 进行全面测试和调试
阶段4:部署和验证
- 更新部署配置
- 验证插件注册和加载
- 进行用户验收测试
- 文档更新和培训