3711 lines
147 KiB
C#
3711 lines
147 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Collections.ObjectModel;
|
||
using System.ComponentModel;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using System.Windows;
|
||
using System.Windows.Input;
|
||
using System.Windows.Media;
|
||
using NavisworksTransport.UI.WPF.Collections;
|
||
using NavisworksTransport.UI.WPF.Models;
|
||
using NavisworksTransport.UI.WPF.Commands;
|
||
using Autodesk.Navisworks.Api;
|
||
using NavisApplication = Autodesk.Navisworks.Api.Application;
|
||
using NavisworksTransport.Utils;
|
||
using NavisworksTransport.Core;
|
||
|
||
namespace NavisworksTransport.UI.WPF.ViewModels
|
||
{
|
||
/// <summary>
|
||
/// 分层管理页签的ViewModel - UI架构重构完成版本
|
||
///
|
||
/// 重构要点:
|
||
/// 1. 移除了ExecuteWithUIStateManagerAsync包装器,该方法导致UIStateManager嵌套调用和死锁
|
||
/// 2. 实现正确的业务逻辑与UI分离模式:
|
||
/// - 初始UI状态更新
|
||
/// - 纯业务逻辑执行(后台线程,不使用UIStateManager)
|
||
/// - 结果UI更新
|
||
/// - 异常处理和UI更新
|
||
/// - 清理UI状态
|
||
/// 3. 确保Command的ExecuteAsync在Task.Run中执行,避免嵌套UIStateManager调用
|
||
/// 4. 所有业务方法都遵循统一的四步骤模式,确保线程安全和避免死锁
|
||
/// 5. 特别针对PreviewSplitAsync方法进行了重点重构,解决了死锁问题的根源
|
||
/// </summary>
|
||
public class LayerManagementViewModel : ViewModelBase, IDisposable
|
||
{
|
||
#region 私有字段和依赖注入
|
||
|
||
private readonly FloorDetector _floorDetector;
|
||
private readonly FloorAttributeManager _floorAttributeManager = new FloorAttributeManager();
|
||
private readonly ModelSplitterManager _modelSplitterManager;
|
||
private readonly AttributeGrouper _attributeGrouper;
|
||
private readonly UIStateManager _uiStateManager;
|
||
private CancellationTokenSource _cancellationTokenSource;
|
||
|
||
// 选择事件订阅管理器
|
||
private SelectionEventSubscription _selectionEventSubscription;
|
||
|
||
// 资源释放状态标志
|
||
private bool _disposed;
|
||
|
||
#endregion
|
||
|
||
#region 状态字段
|
||
|
||
private bool _isProcessing;
|
||
private bool _needsManualFloorSetup;
|
||
private bool _showPreviewResults;
|
||
private bool _showPreviewPrompt = true;
|
||
private string _floorAnalysisResult = "点击[分析楼层]开始检测";
|
||
private Brush _floorAnalysisResultColor = Brushes.Gray;
|
||
private string _selectedFloorAttribute;
|
||
private string _selectedNodesText = "未选择节点";
|
||
private string _floorAttributeStatus = "";
|
||
private string _outputDirectory = "";
|
||
private string _currentSelectionText = "未选择项目";
|
||
private string _currentOperationText = "";
|
||
private double _progressPercentage;
|
||
private string _selectedSplitStrategy = "智能检测";
|
||
private string _selectedCustomLayerOption = "按楼层";
|
||
private bool _showCustomLayerOptions = false;
|
||
|
||
private bool _includeChildNodes = true;
|
||
private bool _preserveMaterials = true;
|
||
private bool _generatePreview = false;
|
||
|
||
// 深度控制相关字段
|
||
private string _selectedDepth = "1级";
|
||
|
||
// 导出选项相关字段
|
||
private bool _embedXrefs = false;
|
||
private bool _preventObjectPropertyExport = false;
|
||
|
||
// 楼层属性相关字段
|
||
private string _selectedModelsText = "请在主界面中选择需要设置的模型";
|
||
private string _selectedFloorLevel = "F1";
|
||
private string _selectedZone = "";
|
||
private string _selectedSubSystem = "";
|
||
// 新的分层参数系统相关字段
|
||
private string _selectedLayerParameter = "楼层";
|
||
private string _selectedParameterValue = "F1";
|
||
private bool _showLayerAttributeInfo = false;
|
||
private string _currentLayerAttributeInfo = string.Empty;
|
||
private bool _showFloorAttributeInfo = false;
|
||
private string _currentFloorAttributeInfo = "";
|
||
private bool _showCancelButton = false;
|
||
|
||
#endregion
|
||
|
||
#region 公共属性 - 使用线程安全的SetProperty方法
|
||
|
||
/// <summary>
|
||
/// 是否正在处理中
|
||
/// </summary>
|
||
public bool IsProcessing
|
||
{
|
||
get => _isProcessing;
|
||
set
|
||
{
|
||
if (SetPropertyThreadSafe(ref _isProcessing, value))
|
||
{
|
||
OnPropertyChanged(nameof(IsNotProcessing));
|
||
RefreshAllCommands();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否未在处理中
|
||
/// </summary>
|
||
public bool IsNotProcessing => !IsProcessing;
|
||
|
||
/// <summary>
|
||
/// 楼层分析结果文本
|
||
/// </summary>
|
||
public string FloorAnalysisResult
|
||
{
|
||
get => _floorAnalysisResult;
|
||
set => SetPropertyThreadSafe(ref _floorAnalysisResult, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 楼层分析结果颜色
|
||
/// </summary>
|
||
public Brush FloorAnalysisResultColor
|
||
{
|
||
get => _floorAnalysisResultColor;
|
||
set => SetPropertyThreadSafe(ref _floorAnalysisResultColor, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否需要手动设置楼层
|
||
/// </summary>
|
||
public bool NeedsManualFloorSetup
|
||
{
|
||
get => _needsManualFloorSetup;
|
||
set => SetPropertyThreadSafe(ref _needsManualFloorSetup, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 可用属性列表 - 使用线程安全集合
|
||
/// </summary>
|
||
public ThreadSafeObservableCollection<string> AvailableAttributes { get; } =
|
||
new ThreadSafeObservableCollection<string>();
|
||
|
||
/// <summary>
|
||
/// 选中的楼层属性
|
||
/// </summary>
|
||
public string SelectedFloorAttribute
|
||
{
|
||
get => _selectedFloorAttribute;
|
||
set
|
||
{
|
||
if (SetPropertyThreadSafe(ref _selectedFloorAttribute, value))
|
||
{
|
||
OnPropertyChanged(nameof(CanApplyFloorAttributes));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选中节点文本
|
||
/// </summary>
|
||
public string SelectedNodesText
|
||
{
|
||
get => _selectedNodesText;
|
||
set => SetPropertyThreadSafe(ref _selectedNodesText, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 楼层属性设置状态
|
||
/// </summary>
|
||
public string FloorAttributeStatus
|
||
{
|
||
get => _floorAttributeStatus;
|
||
set => SetPropertyThreadSafe(ref _floorAttributeStatus, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可以应用楼层属性
|
||
/// </summary>
|
||
public bool CanApplyFloorAttributes =>
|
||
!string.IsNullOrEmpty(SelectedFloorAttribute) &&
|
||
!IsProcessing &&
|
||
SelectedNodesText != "未选择节点";
|
||
|
||
/// <summary>
|
||
/// 深度选项列表 - 使用线程安全集合
|
||
/// </summary>
|
||
public ThreadSafeObservableCollection<string> DepthOptions { get; } =
|
||
new ThreadSafeObservableCollection<string>
|
||
{
|
||
"1级",
|
||
"2级",
|
||
"3级",
|
||
"4级",
|
||
"5级",
|
||
"全部"
|
||
};
|
||
|
||
/// <summary>
|
||
/// 选中的遍历深度
|
||
/// </summary>
|
||
public string SelectedDepth
|
||
{
|
||
get => _selectedDepth;
|
||
set => SetPropertyThreadSafe(ref _selectedDepth, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前选择的深度对应的数值
|
||
/// </summary>
|
||
public int CurrentDepthValue
|
||
{
|
||
get
|
||
{
|
||
switch (SelectedDepth)
|
||
{
|
||
case "1级":
|
||
return 1;
|
||
case "2级":
|
||
return 2;
|
||
case "3级":
|
||
return 3;
|
||
case "4级":
|
||
return 4;
|
||
case "5级":
|
||
return 5;
|
||
case "全部":
|
||
return 1000;
|
||
default:
|
||
return 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分层策略列表 - 使用线程安全集合
|
||
/// </summary>
|
||
public ThreadSafeObservableCollection<string> SplitStrategies { get; } =
|
||
new ThreadSafeObservableCollection<string>
|
||
{
|
||
"智能检测",
|
||
"自定义"
|
||
};
|
||
|
||
/// <summary>
|
||
/// 选中的分层策略
|
||
/// </summary>
|
||
public string SelectedSplitStrategy
|
||
{
|
||
get => _selectedSplitStrategy;
|
||
set
|
||
{
|
||
if (SetPropertyThreadSafe(ref _selectedSplitStrategy, value))
|
||
{
|
||
// 当选择"自定义"时显示二级选项
|
||
ShowCustomLayerOptions = value == "自定义";
|
||
|
||
OnPropertyChanged(nameof(CanPreviewSplit));
|
||
OnPropertyChanged(nameof(CanExecuteSplit));
|
||
OnPropertyChanged(nameof(CanIsolateSelectedLayer));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自定义分层选项集合
|
||
/// </summary>
|
||
public ThreadSafeObservableCollection<string> CustomLayerOptions { get; } =
|
||
new ThreadSafeObservableCollection<string>
|
||
{
|
||
"按楼层",
|
||
"按区域",
|
||
"按子系统"
|
||
};
|
||
|
||
/// <summary>
|
||
/// 选中的自定义分层选项
|
||
/// </summary>
|
||
public string SelectedCustomLayerOption
|
||
{
|
||
get => _selectedCustomLayerOption;
|
||
set
|
||
{
|
||
if (SetPropertyThreadSafe(ref _selectedCustomLayerOption, value))
|
||
{
|
||
OnPropertyChanged(nameof(CanPreviewSplit));
|
||
OnPropertyChanged(nameof(CanExecuteSplit));
|
||
OnPropertyChanged(nameof(CanIsolateSelectedLayer));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否显示自定义分层选项
|
||
/// </summary>
|
||
public bool ShowCustomLayerOptions
|
||
{
|
||
get => _showCustomLayerOptions;
|
||
set => SetPropertyThreadSafe(ref _showCustomLayerOptions, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 输出目录
|
||
/// </summary>
|
||
public string OutputDirectory
|
||
{
|
||
get => _outputDirectory;
|
||
set
|
||
{
|
||
if (SetPropertyThreadSafe(ref _outputDirectory, value))
|
||
{
|
||
OnPropertyChanged(nameof(CanPreviewSplit));
|
||
OnPropertyChanged(nameof(CanExecuteSplit));
|
||
OnPropertyChanged(nameof(CanIsolateSelectedLayer));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可以预览分层
|
||
/// </summary>
|
||
public bool CanPreviewSplit =>
|
||
!string.IsNullOrEmpty(SelectedSplitStrategy) &&
|
||
!IsProcessing;
|
||
|
||
/// <summary>
|
||
/// 是否可以执行分层(不再依赖OutputDirectory,改为使用文件对话框)
|
||
/// </summary>
|
||
public bool CanExecuteSplit =>
|
||
SplitPreviewResults.Count > 0 &&
|
||
!IsProcessing;
|
||
|
||
/// <summary>
|
||
/// 是否可以单独显示选中的分层
|
||
/// </summary>
|
||
public bool CanIsolateSelectedLayer =>
|
||
SelectedPreviewResult != null &&
|
||
SplitPreviewResults.Count > 0 &&
|
||
!IsProcessing;
|
||
|
||
/// <summary>
|
||
/// 分层预览结果 - 使用线程安全集合
|
||
/// </summary>
|
||
public ThreadSafeObservableCollection<SplitPreviewItem> SplitPreviewResults { get; } =
|
||
new ThreadSafeObservableCollection<SplitPreviewItem>();
|
||
|
||
/// <summary>
|
||
/// 选中的预览结果
|
||
/// </summary>
|
||
private SplitPreviewItem _selectedPreviewResult;
|
||
public SplitPreviewItem SelectedPreviewResult
|
||
{
|
||
get => _selectedPreviewResult;
|
||
set
|
||
{
|
||
if (SetPropertyThreadSafe(ref _selectedPreviewResult, value))
|
||
{
|
||
OnPropertyChanged(nameof(CanIsolateSelectedLayer));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否显示预览结果
|
||
/// </summary>
|
||
public bool ShowPreviewResults
|
||
{
|
||
get => _showPreviewResults;
|
||
set => SetPropertyThreadSafe(ref _showPreviewResults, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否显示预览提示
|
||
/// </summary>
|
||
public bool ShowPreviewPrompt
|
||
{
|
||
get => _showPreviewPrompt;
|
||
set => SetPropertyThreadSafe(ref _showPreviewPrompt, value);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 当前选择文本
|
||
/// </summary>
|
||
public string CurrentSelectionText
|
||
{
|
||
get => _currentSelectionText;
|
||
set
|
||
{
|
||
if (SetPropertyThreadSafe(ref _currentSelectionText, value))
|
||
{
|
||
OnPropertyChanged(nameof(HasSelectedItems));
|
||
OnPropertyChanged(nameof(CanSetLayerAttribute));
|
||
OnPropertyChanged(nameof(CanClearLayerAttribute));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否有选中项目
|
||
/// </summary>
|
||
public bool HasSelectedItems => CurrentSelectionText != "未选择项目" && !IsProcessing;
|
||
|
||
/// <summary>
|
||
/// 包含子节点
|
||
/// </summary>
|
||
public bool IncludeChildNodes
|
||
{
|
||
get => _includeChildNodes;
|
||
set => SetPropertyThreadSafe(ref _includeChildNodes, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保留材质贴图
|
||
/// </summary>
|
||
public bool PreserveMaterials
|
||
{
|
||
get => _preserveMaterials;
|
||
set => SetPropertyThreadSafe(ref _preserveMaterials, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成预览图
|
||
/// </summary>
|
||
public bool GeneratePreview
|
||
{
|
||
get => _generatePreview;
|
||
set => SetPropertyThreadSafe(ref _generatePreview, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 嵌入 ReCap 和纹理数据
|
||
/// </summary>
|
||
public bool EmbedXrefs
|
||
{
|
||
get => _embedXrefs;
|
||
set => SetPropertyThreadSafe(ref _embedXrefs, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 阻止导出对象特性
|
||
/// </summary>
|
||
public bool PreventObjectPropertyExport
|
||
{
|
||
get => _preventObjectPropertyExport;
|
||
set => SetPropertyThreadSafe(ref _preventObjectPropertyExport, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前操作文本
|
||
/// </summary>
|
||
public string CurrentOperationText
|
||
{
|
||
get => _currentOperationText;
|
||
set => SetPropertyThreadSafe(ref _currentOperationText, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 进度百分比
|
||
/// </summary>
|
||
public double ProgressPercentage
|
||
{
|
||
get => _progressPercentage;
|
||
set => SetPropertyThreadSafe(ref _progressPercentage, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选中模型文本
|
||
/// </summary>
|
||
public string SelectedModelsText
|
||
{
|
||
get => _selectedModelsText;
|
||
set => SetPropertyThreadSafe(ref _selectedModelsText, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选中的楼层标识
|
||
/// </summary>
|
||
public string SelectedFloorLevel
|
||
{
|
||
get => _selectedFloorLevel;
|
||
set => SetPropertyThreadSafe(ref _selectedFloorLevel, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选中的区域标识
|
||
/// </summary>
|
||
public string SelectedZone
|
||
{
|
||
get => _selectedZone;
|
||
set => SetPropertyThreadSafe(ref _selectedZone, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选中的子系统标识
|
||
/// </summary>
|
||
public string SelectedSubSystem
|
||
{
|
||
get => _selectedSubSystem;
|
||
set => SetPropertyThreadSafe(ref _selectedSubSystem, value);
|
||
}
|
||
/// <summary>
|
||
/// 选中的分层参数类型(楼层、区域、子系统)
|
||
/// </summary>
|
||
public string SelectedLayerParameter
|
||
{
|
||
get => _selectedLayerParameter;
|
||
set
|
||
{
|
||
if (SetPropertyThreadSafe(ref _selectedLayerParameter, value))
|
||
{
|
||
// 当分层参数类型改变时,自动更新参数值为合适的默认值
|
||
switch (value)
|
||
{
|
||
case "楼层":
|
||
SelectedParameterValue = "F1";
|
||
break;
|
||
case "区域":
|
||
SelectedParameterValue = "北区";
|
||
break;
|
||
case "子系统":
|
||
SelectedParameterValue = "消防系统";
|
||
break;
|
||
default:
|
||
SelectedParameterValue = "F1";
|
||
break;
|
||
}
|
||
|
||
// 通知相关属性更新
|
||
OnPropertyChanged(nameof(CanSetLayerAttribute));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分层参数值
|
||
/// </summary>
|
||
public string SelectedParameterValue
|
||
{
|
||
get => _selectedParameterValue;
|
||
set
|
||
{
|
||
if (SetPropertyThreadSafe(ref _selectedParameterValue, value))
|
||
{
|
||
// 通知相关属性更新
|
||
OnPropertyChanged(nameof(CanSetLayerAttribute));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否显示分层属性信息
|
||
/// </summary>
|
||
public bool ShowLayerAttributeInfo
|
||
{
|
||
get => _showLayerAttributeInfo;
|
||
set => SetPropertyThreadSafe(ref _showLayerAttributeInfo, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前分层属性信息
|
||
/// </summary>
|
||
public string CurrentLayerAttributeInfo
|
||
{
|
||
get => _currentLayerAttributeInfo;
|
||
set => SetPropertyThreadSafe(ref _currentLayerAttributeInfo, value);
|
||
}
|
||
/// <summary>
|
||
/// 分层参数选项集合
|
||
/// </summary>
|
||
public ObservableCollection<string> LayerParameterOptions { get; } = new ObservableCollection<string>
|
||
{
|
||
"楼层",
|
||
"区域",
|
||
"子系统"
|
||
};
|
||
|
||
/// <summary>
|
||
/// 是否显示楼层属性信息
|
||
/// </summary>
|
||
public bool ShowFloorAttributeInfo
|
||
{
|
||
get => _showFloorAttributeInfo;
|
||
set => SetPropertyThreadSafe(ref _showFloorAttributeInfo, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前楼层属性信息
|
||
/// </summary>
|
||
public string CurrentFloorAttributeInfo
|
||
{
|
||
get => _currentFloorAttributeInfo;
|
||
set => SetPropertyThreadSafe(ref _currentFloorAttributeInfo, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否有选中的模型(用于楼层属性设置)
|
||
/// </summary>
|
||
public bool HasSelectedModels
|
||
{
|
||
get
|
||
{
|
||
try
|
||
{
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
return document?.CurrentSelection?.SelectedItems?.Count > 0;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可以设置楼层属性
|
||
/// </summary>
|
||
public bool CanSetFloorAttribute
|
||
{
|
||
get => HasSelectedModels && !string.IsNullOrEmpty(SelectedFloorLevel) && IsNotProcessing;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可以设置分层属性
|
||
/// </summary>
|
||
public bool CanSetLayerAttribute => IsNotProcessing && !string.IsNullOrWhiteSpace(SelectedParameterValue) && HasSelectedItems;
|
||
|
||
/// <summary>
|
||
/// 是否可以清除分层属性
|
||
/// </summary>
|
||
public bool CanClearLayerAttribute
|
||
{
|
||
get
|
||
{
|
||
try
|
||
{
|
||
if (IsProcessing || !HasSelectedItems)
|
||
return false;
|
||
|
||
// 检查选中的模型是否有任何分层属性
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document?.CurrentSelection?.SelectedItems?.Count > 0)
|
||
{
|
||
var selection = document.CurrentSelection.SelectedItems;
|
||
|
||
foreach (var item in selection)
|
||
{
|
||
// 使用新的快速检查方法
|
||
if (_floorAttributeManager.HasFloorAttributes(item))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 检查清除分层属性条件异常: {ex.Message}", ex);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可以清除楼层属性
|
||
/// </summary>
|
||
public bool CanClearFloorAttribute
|
||
{
|
||
get
|
||
{
|
||
try
|
||
{
|
||
if (IsProcessing || !HasSelectedItems)
|
||
return false;
|
||
|
||
// 检查选中的模型是否有楼层属性
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document?.CurrentSelection?.SelectedItems?.Count > 0)
|
||
{
|
||
var selection = document.CurrentSelection.SelectedItems;
|
||
|
||
foreach (var item in selection)
|
||
{
|
||
// 使用新的 Native API 方法
|
||
var floorLevel = _floorAttributeManager.GetFloorProperty(item, "楼层");
|
||
if (!string.IsNullOrEmpty(floorLevel))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 检查清除楼层属性条件异常: {ex.Message}", ex);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否显示取消按钮
|
||
/// </summary>
|
||
public bool ShowCancelButton
|
||
{
|
||
get => _showCancelButton;
|
||
set => SetPropertyThreadSafe(ref _showCancelButton, value);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 命令 - 使用统一的Command Pattern
|
||
|
||
public ICommand AnalyzeFloorsCommand { get; private set; }
|
||
public ICommand RefreshAttributesCommand { get; private set; }
|
||
public ICommand SelectNodesCommand { get; private set; }
|
||
public ICommand ApplyFloorAttributesCommand { get; private set; }
|
||
public ICommand PreviewSplitCommand { get; private set; }
|
||
public ICommand ExecuteSplitCommand { get; private set; }
|
||
public ICommand BrowseOutputDirectoryCommand { get; private set; }
|
||
public ICommand SaveSelectedItemsCommand { get; private set; }
|
||
public ICommand CancelOperationCommand { get; private set; }
|
||
public ICommand DiagnosticCommand { get; private set; }
|
||
public ICommand TestExportToNwdCommand { get; private set; }
|
||
|
||
// 楼层属性相关命令
|
||
public ICommand RefreshSelectionCommand { get; private set; }
|
||
public ICommand SetFloorAttributeCommand { get; private set; }
|
||
public ICommand ClearFloorAttributeCommand { get; private set; }
|
||
public ICommand ViewFloorAttributeCommand { get; private set; }
|
||
|
||
// 新的分层属性命令
|
||
public ICommand SetLayerAttributeCommand { get; private set; }
|
||
public ICommand ClearLayerAttributeCommand { get; private set; }
|
||
public ICommand ViewLayerAttributeCommand { get; private set; }
|
||
|
||
// 单独显示相关命令
|
||
public ICommand IsolateSelectedLayerCommand { get; private set; }
|
||
public ICommand ShowAllLayersCommand { get; private set; }
|
||
|
||
#endregion
|
||
|
||
#region 构造函数 - 使用依赖注入和统一架构
|
||
|
||
public LayerManagementViewModel() : base()
|
||
{
|
||
try
|
||
{
|
||
// 获取UI状态管理器实例
|
||
_uiStateManager = UIStateManager.Instance;
|
||
// 验证关键组件是否正常初始化
|
||
if (_uiStateManager == null)
|
||
{
|
||
LogManager.Error("UIStateManager初始化失败");
|
||
throw new InvalidOperationException("UIStateManager初始化失败");
|
||
}
|
||
|
||
// 初始化业务逻辑组件
|
||
_floorDetector = new FloorDetector();
|
||
_modelSplitterManager = new ModelSplitterManager();
|
||
_attributeGrouper = new AttributeGrouper();
|
||
|
||
// 初始化命令 - 使用异步Command Pattern
|
||
InitializeCommands();
|
||
|
||
// 订阅事件
|
||
_modelSplitterManager.ProgressChanged += OnProgressChanged;
|
||
_modelSplitterManager.StatusChanged += OnStatusChanged;
|
||
|
||
// 订阅Navisworks选择变化事件 - 使用新的选择管理服务
|
||
SubscribeToSelectionEvents();
|
||
|
||
// 异步初始化
|
||
InitializeAsync();
|
||
|
||
LogManager.Info("LayerManagementViewModel构造函数执行完成 - 使用统一UI架构");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"LayerManagementViewModel构造函数异常: {ex.Message}", ex);
|
||
|
||
// 在构造函数中尽量保证对象处于可用状态
|
||
CurrentOperationText = "初始化失败,请检查日志";
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 构造函数 - 支持统一状态栏
|
||
/// </summary>
|
||
/// <param name="mainViewModel">主ViewModel,用于统一状态栏</param>
|
||
public LayerManagementViewModel(LogisticsControlViewModel mainViewModel) : base()
|
||
{
|
||
try
|
||
{
|
||
// 设置主ViewModel引用到基类
|
||
SetMainViewModel(mainViewModel);
|
||
|
||
// 获取UI状态管理器实例
|
||
_uiStateManager = UIStateManager.Instance;
|
||
// 验证关键组件是否正常初始化
|
||
if (_uiStateManager == null)
|
||
{
|
||
LogManager.Error("UIStateManager初始化失败");
|
||
throw new InvalidOperationException("UIStateManager初始化失败");
|
||
}
|
||
|
||
// 初始化业务逻辑组件
|
||
_floorDetector = new FloorDetector();
|
||
_modelSplitterManager = new ModelSplitterManager();
|
||
_attributeGrouper = new AttributeGrouper();
|
||
|
||
// 初始化命令 - 使用异步Command Pattern
|
||
InitializeCommands();
|
||
|
||
// 订阅事件
|
||
_modelSplitterManager.ProgressChanged += OnProgressChanged;
|
||
_modelSplitterManager.StatusChanged += OnStatusChanged;
|
||
|
||
// 订阅Navisworks选择变化事件 - 使用新的选择管理服务
|
||
SubscribeToSelectionEvents();
|
||
|
||
// 异步初始化
|
||
InitializeAsync();
|
||
|
||
LogManager.Info("LayerManagementViewModel构造函数执行完成 - 支持统一状态栏");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"LayerManagementViewModel构造函数异常: {ex.Message}", ex);
|
||
|
||
// 在构造函数中尽量保证对象处于可用状态
|
||
CurrentOperationText = "初始化失败,请检查日志";
|
||
throw;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 命令初始化 - 统一的Command Pattern
|
||
|
||
/// <summary>
|
||
/// 初始化命令(使用Command Pattern框架)
|
||
/// </summary>
|
||
private void InitializeCommands()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
// 使用异步RelayCommand,采用正确的业务逻辑与UI分离模式
|
||
AnalyzeFloorsCommand = new RelayCommand(
|
||
async () => await AnalyzeFloorsAsync(),
|
||
() => IsNotProcessing);
|
||
|
||
RefreshAttributesCommand = new RelayCommand(
|
||
async () => await RefreshAttributesAsync(),
|
||
() => IsNotProcessing);
|
||
|
||
SelectNodesCommand = new RelayCommand(
|
||
async () => await SelectNodesAsync(),
|
||
() => IsNotProcessing);
|
||
|
||
ApplyFloorAttributesCommand = new RelayCommand(
|
||
async () => await ApplyFloorAttributesAsync(),
|
||
() => CanApplyFloorAttributes);
|
||
|
||
PreviewSplitCommand = new RelayCommand(
|
||
async () => await PreviewSplitAsync(),
|
||
() => CanPreviewSplit);
|
||
|
||
ExecuteSplitCommand = new RelayCommand(
|
||
async () => await ExecuteSplitAsync(),
|
||
() => CanExecuteSplit);
|
||
|
||
BrowseOutputDirectoryCommand = new RelayCommand(
|
||
async () => await BrowseOutputDirectoryAsync(),
|
||
() => IsNotProcessing);
|
||
|
||
SaveSelectedItemsCommand = new RelayCommand(
|
||
async () => await SaveSelectedItemsAsync(),
|
||
() => HasSelectedItems);
|
||
|
||
CancelOperationCommand = new RelayCommand(CancelOperation, () => IsProcessing);
|
||
|
||
DiagnosticCommand = new RelayCommand(
|
||
async () => await RunDiagnosticAsync(),
|
||
() => IsNotProcessing);
|
||
|
||
TestExportToNwdCommand = new RelayCommand(
|
||
async () => await TestExportToNwdAsync(),
|
||
() => IsNotProcessing);
|
||
|
||
// 楼层属性相关命令初始化
|
||
RefreshSelectionCommand = new RelayCommand(
|
||
async () => await RefreshSelectionAsync(),
|
||
() => IsNotProcessing);
|
||
|
||
SetFloorAttributeCommand = new RelayCommand(
|
||
async () => await SetFloorAttributeAsync(),
|
||
() => CanSetFloorAttribute);
|
||
|
||
ClearFloorAttributeCommand = new RelayCommand(
|
||
async () => await ClearFloorAttributeAsync(),
|
||
() => CanClearFloorAttribute);
|
||
|
||
ViewFloorAttributeCommand = new RelayCommand(
|
||
async () => await ViewFloorAttributeAsync(),
|
||
() => HasSelectedItems);
|
||
|
||
// 新的分层属性命令初始化
|
||
SetLayerAttributeCommand = new RelayCommand(
|
||
async () => await SetLayerAttributeAsync(),
|
||
() => CanSetLayerAttribute);
|
||
|
||
ClearLayerAttributeCommand = new RelayCommand(
|
||
async () => await ClearLayerAttributeAsync(),
|
||
() => CanClearLayerAttribute);
|
||
|
||
ViewLayerAttributeCommand = new RelayCommand(
|
||
async () => await ViewLayerAttributeAsync(),
|
||
() => HasSelectedItems);
|
||
|
||
// 单独显示相关命令
|
||
IsolateSelectedLayerCommand = new RelayCommand(
|
||
async () => await IsolateSelectedLayerAsync(),
|
||
() => CanIsolateSelectedLayer);
|
||
|
||
ShowAllLayersCommand = new RelayCommand(
|
||
async () => await ShowAllLayersAsync(),
|
||
() => IsNotProcessing);
|
||
|
||
LogManager.Info("分层管理命令初始化完成");
|
||
}, "初始化命令");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 业务逻辑方法 - 使用统一的UIStateManager
|
||
|
||
// ExecuteWithUIStateManagerAsync包装器已移除 - 该方法导致UIStateManager嵌套调用和死锁
|
||
// 现在采用正确的业务逻辑与UI分离模式
|
||
|
||
/// <summary>
|
||
/// 分析楼层 - 实现正确的业务逻辑与UI分离模式
|
||
/// </summary>
|
||
private async Task AnalyzeFloorsAsync()
|
||
{
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
ShowCancelButton = false; // 分析楼层操作不支持取消
|
||
CurrentOperationText = "正在分析楼层...";
|
||
UpdateMainStatus("检测模型中的楼层属性", -1, true);
|
||
});
|
||
|
||
try
|
||
{
|
||
// 2. 纯业务逻辑执行(后台线程,不使用UIStateManager)
|
||
var command = new FloorAnalysisCommand(_floorDetector, CurrentDepthValue);
|
||
var result = await command.ExecuteAsync();
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
if (result.IsSuccess)
|
||
{
|
||
SetFloorAnalysisResult(result.Message, result.StatusColor);
|
||
NeedsManualFloorSetup = result.NeedsManualSetup;
|
||
UpdateMainStatus("楼层分析完成");
|
||
}
|
||
else
|
||
{
|
||
SetFloorAnalysisResult(result.ErrorMessage, Brushes.Red);
|
||
UpdateMainStatus("楼层分析失败");
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 分析楼层异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
SetFloorAnalysisResult($"分析失败: {ex.Message}", Brushes.Red);
|
||
UpdateMainStatus("操作异常");
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
ShowCancelButton = false;
|
||
CurrentOperationText = "";
|
||
ProgressPercentage = 0;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新属性列表 - 实现正确的业务逻辑与UI分离模式
|
||
/// </summary>
|
||
private async Task RefreshAttributesAsync()
|
||
{
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
CurrentOperationText = "正在刷新属性列表...";
|
||
UpdateMainStatus("获取模型属性");
|
||
});
|
||
|
||
try
|
||
{
|
||
// 2. 纯业务逻辑执行(后台线程,不使用UIStateManager)
|
||
var command = new RefreshAttributesCommand(_floorDetector, CurrentDepthValue);
|
||
var result = await command.ExecuteAsync();
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
AvailableAttributes.Clear();
|
||
if (result.IsSuccess && result.Attributes != null)
|
||
{
|
||
foreach (var attr in result.Attributes)
|
||
{
|
||
AvailableAttributes.Add(attr);
|
||
}
|
||
UpdateMainStatus($"成功获取 {result.Attributes.Count} 个属性");
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus(result.ErrorMessage ?? "获取属性失败");
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 刷新属性异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
AvailableAttributes.Clear();
|
||
UpdateMainStatus($"刷新失败: {ex.Message}");
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
CurrentOperationText = "";
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选择节点 - 实现正确的业务逻辑与UI分离模式
|
||
/// </summary>
|
||
private async Task SelectNodesAsync()
|
||
{
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
CurrentOperationText = "正在获取选中节点...";
|
||
UpdateMainStatus("检查当前选择");
|
||
});
|
||
|
||
try
|
||
{
|
||
// 2. 纯业务逻辑执行(后台线程,不使用UIStateManager)
|
||
var command = new SelectNodesCommand();
|
||
var result = await command.ExecuteAsync();
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
if (result.IsSuccess)
|
||
{
|
||
SelectedNodesText = NavisworksSelectionHelper.FormatSelectionText(result.Count, result.SelectedItems, "个节点");
|
||
UpdateMainStatus(result.Count > 0
|
||
? $"获取到 {result.Count} 个选中节点"
|
||
: "当前没有选中的节点");
|
||
}
|
||
else
|
||
{
|
||
SelectedNodesText = "获取节点失败";
|
||
UpdateMainStatus(result.ErrorMessage ?? "选择节点操作失败");
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 选择节点异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
SelectedNodesText = "获取节点异常";
|
||
UpdateMainStatus($"操作失败: {ex.Message}");
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
CurrentOperationText = "";
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 应用楼层属性 - 实现正确的业务逻辑与UI分离模式
|
||
/// </summary>
|
||
private async Task ApplyFloorAttributesAsync()
|
||
{
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
CurrentOperationText = "正在应用楼层属性...";
|
||
FloorAttributeStatus = "处理中...";
|
||
UpdateMainStatus("为选中节点设置楼层属性");
|
||
});
|
||
|
||
try
|
||
{
|
||
// 2. 纯业务逻辑执行(后台线程,不使用UIStateManager)
|
||
var command = new ApplyFloorAttributesCommand(SelectedFloorAttribute);
|
||
var result = await command.ExecuteAsync();
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
if (result.IsSuccess)
|
||
{
|
||
FloorAttributeStatus = $"成功为 {result.Count} 个节点设置楼层属性";
|
||
UpdateMainStatus($"属性设置完成,影响 {result.Count} 个节点");
|
||
}
|
||
else
|
||
{
|
||
FloorAttributeStatus = result.ErrorMessage ?? "楼层属性设置失败";
|
||
UpdateMainStatus("属性设置操作失败");
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 应用楼层属性异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
FloorAttributeStatus = $"设置失败: {ex.Message}";
|
||
UpdateMainStatus("属性设置异常");
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
CurrentOperationText = "";
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 预览分层 - 实现正确的业务逻辑与UI分离模式(解决死锁问题的关键方法)
|
||
/// </summary>
|
||
private async Task PreviewSplitAsync()
|
||
{
|
||
// 1. 初始UI状态更新 - 清空旧结果并显示加载状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
ShowCancelButton = false; // 预览操作不支持取消
|
||
CurrentOperationText = "正在生成分层预览...";
|
||
ProgressPercentage = 0;
|
||
UpdateMainStatus("准备分层配置");
|
||
|
||
// 清空旧的预览结果
|
||
SplitPreviewResults.Clear();
|
||
|
||
// 显示加载状态
|
||
ShowPreviewResults = false;
|
||
ShowPreviewPrompt = false;
|
||
|
||
// 添加加载提示项
|
||
var loadingItem = new SplitPreviewItem
|
||
{
|
||
LayerName = "正在分析...",
|
||
LayerAttribute = "分析中"
|
||
};
|
||
SplitPreviewResults.Add(loadingItem);
|
||
ShowPreviewResults = true;
|
||
});
|
||
|
||
try
|
||
{
|
||
// 2. 纯业务逻辑执行(后台线程,不使用UIStateManager)
|
||
var strategy = GetSplitStrategyFromString(SelectedSplitStrategy);
|
||
var config = new ModelSplitterManager.SplitConfiguration
|
||
{
|
||
Strategy = strategy,
|
||
OutputDirectory = OutputDirectory,
|
||
MaxDepth = CurrentDepthValue, // 传递深度参数
|
||
ExportOptions = new ModelSplitterManager.NwdExportUserOptions
|
||
{
|
||
EmbedXrefs = EmbedXrefs,
|
||
PreventObjectPropertyExport = PreventObjectPropertyExport,
|
||
FileVersion = "2026"
|
||
}
|
||
};
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 开始预览分层,策略: {SelectedSplitStrategy}");
|
||
|
||
var command = new PreviewSplitCommand(_modelSplitterManager, config);
|
||
var result = await command.ExecuteAsync();
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 预览分层完成,成功: {result.IsSuccess}, 项目数: {result.PreviewItems?.Count ?? 0}");
|
||
|
||
// 3. 结果UI更新 - 清空加载提示并显示实际结果
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
// 清空加载提示
|
||
SplitPreviewResults.Clear();
|
||
|
||
if (result.IsSuccess && result.PreviewItems?.Count > 0)
|
||
{
|
||
// 添加实际预览结果
|
||
foreach (var item in result.PreviewItems)
|
||
{
|
||
SplitPreviewResults.Add(item);
|
||
}
|
||
|
||
ShowPreviewResults = true;
|
||
ShowPreviewPrompt = false;
|
||
UpdateMainStatus($"预览完成:共检测到 {SplitPreviewResults.Count} 个分层(智能遍历仅显示关键节点)");
|
||
}
|
||
else
|
||
{
|
||
// 预览失败或无结果,不在列表中显示错误项,而是隐藏结果列表并显示提示
|
||
ShowPreviewResults = false;
|
||
ShowPreviewPrompt = true; // 显示提示信息
|
||
UpdateMainStatus(result.ErrorMessage ?? "未找到任何分层属性,请先设置模型的分层属性(楼层、区域或子系统)");
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 预览分层异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
// 清空加载提示,隐藏结果列表并显示异常提示
|
||
SplitPreviewResults.Clear();
|
||
|
||
ShowPreviewResults = false;
|
||
ShowPreviewPrompt = true; // 显示提示信息
|
||
UpdateMainStatus($"预览异常: {ex.Message}");
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
ShowCancelButton = false;
|
||
// 只有在预览成功时才清空操作文本,失败时保留提示信息
|
||
if (SplitPreviewResults.Count > 0)
|
||
{
|
||
CurrentOperationText = "";
|
||
}
|
||
ProgressPercentage = 0;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行分层 - 使用文件对话框选择保存目录,实现正确的业务逻辑与UI分离模式
|
||
/// </summary>
|
||
private async Task ExecuteSplitAsync()
|
||
{
|
||
try
|
||
{
|
||
// 首先在UI线程上使用文件对话框让用户选择保存目录
|
||
LogManager.Info("[LayerManagementViewModel] 准备显示文件选择对话框");
|
||
var browseCommand = new BrowseDirectoryCommand("");
|
||
var browseResult = await browseCommand.ExecuteAsync();
|
||
LogManager.Info($"[LayerManagementViewModel] 文件对话框结果: IsSuccess={browseResult?.IsSuccess}, Path={browseResult?.SelectedPath}");
|
||
|
||
if (browseResult == null || !browseResult.IsSuccess || string.IsNullOrEmpty(browseResult.SelectedPath))
|
||
{
|
||
// 用户取消了对话框或选择失败
|
||
LogManager.Info("[LayerManagementViewModel] 用户取消了分层保存操作");
|
||
return;
|
||
}
|
||
|
||
var selectedDirectory = browseResult.SelectedPath;
|
||
LogManager.Info($"[LayerManagementViewModel] 用户选择保存目录: {selectedDirectory}");
|
||
|
||
// 过滤出需要保存的分层
|
||
var layersToSave = SplitPreviewResults.Where(p => p.IsSelectedForSave).ToList();
|
||
if (layersToSave.Count == 0)
|
||
{
|
||
LogManager.Warning("[LayerManagementViewModel] 没有选中任何分层进行保存");
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentOperationText = "没有选中任何分层进行保存";
|
||
UpdateMainStatus("请勾选需要保存的分层");
|
||
});
|
||
return;
|
||
}
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 将保存 {layersToSave.Count}/{SplitPreviewResults.Count} 个选中的分层");
|
||
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
ShowCancelButton = true; // 分层保存支持取消
|
||
CurrentOperationText = "正在执行分层保存...";
|
||
UpdateMainStatus($"准备保存到: {selectedDirectory}");
|
||
});
|
||
|
||
_cancellationTokenSource = new CancellationTokenSource();
|
||
|
||
// 2. 纯业务逻辑执行(后台线程,不使用UIStateManager)
|
||
var strategy = GetSplitStrategyFromString(SelectedSplitStrategy);
|
||
var config = new ModelSplitterManager.SplitConfiguration
|
||
{
|
||
Strategy = strategy,
|
||
OutputDirectory = selectedDirectory,
|
||
MaxDepth = CurrentDepthValue, // 传递深度参数
|
||
ExportOptions = new ModelSplitterManager.NwdExportUserOptions
|
||
{
|
||
EmbedXrefs = EmbedXrefs,
|
||
PreventObjectPropertyExport = PreventObjectPropertyExport,
|
||
FileVersion = "2026"
|
||
}
|
||
};
|
||
|
||
// 将选中的 SplitPreviewItem 转换为 SplitPreviewResult 以便传递给 ModelSplitterManager
|
||
var previewResultsForSave = layersToSave.Select(item => new ModelSplitterManager.SplitPreviewResult
|
||
{
|
||
LayerName = item.LayerName,
|
||
LayerAttribute = item.LayerAttribute,
|
||
Items = item.Items,
|
||
IsSelectedForSave = item.IsSelectedForSave,
|
||
Metadata = item.Metadata // 重要:复制元数据,包含RootNodes等优化信息
|
||
}).ToList();
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 开始执行分层,输出目录: {selectedDirectory}");
|
||
|
||
// 直接调用 ModelSplitterManager 的方法处理选中的分层
|
||
await ExecuteSelectedLayersAsync(config, previewResultsForSave, _cancellationTokenSource.Token);
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 分层执行完成");
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentOperationText = "分层保存完成";
|
||
UpdateMainStatus($"文件已保存到: {selectedDirectory}");
|
||
// 更新OutputDirectory显示(可选)
|
||
OutputDirectory = selectedDirectory;
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 执行分层异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentOperationText = "分层保存异常";
|
||
UpdateMainStatus($"保存失败: {ex.Message}");
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
ShowCancelButton = false;
|
||
_cancellationTokenSource = null;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行选中的分层保存
|
||
/// </summary>
|
||
private async Task ExecuteSelectedLayersAsync(
|
||
ModelSplitterManager.SplitConfiguration config,
|
||
List<ModelSplitterManager.SplitPreviewResult> previewResultsForSave,
|
||
CancellationToken cancellationToken)
|
||
{
|
||
Progress progress = null;
|
||
try
|
||
{
|
||
// 确保输出目录存在
|
||
if (!Directory.Exists(config.OutputDirectory))
|
||
{
|
||
Directory.CreateDirectory(config.OutputDirectory);
|
||
}
|
||
|
||
// 逐个处理选中的分层
|
||
int successCount = 0;
|
||
var failedLayers = new List<string>();
|
||
|
||
// 🎯 创建批量导出进度条(在主线程中)
|
||
await System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
|
||
{
|
||
progress = NavisApplication.BeginProgress("批量导出分层",
|
||
$"准备导出 {previewResultsForSave.Count} 个分层...");
|
||
});
|
||
|
||
for (int i = 0; i < previewResultsForSave.Count; i++)
|
||
{
|
||
var preview = previewResultsForSave[i];
|
||
|
||
// 🎯 更新进度条描述和百分比(在主线程中)
|
||
bool userCanceled = false;
|
||
await System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
|
||
{
|
||
if (progress != null && !progress.IsCanceled)
|
||
{
|
||
// 计算总体进度百分比
|
||
double currentProgress = (double)i / previewResultsForSave.Count;
|
||
progress.Update(currentProgress);
|
||
|
||
// 更新进度条描述
|
||
NavisApplication.EndProgress();
|
||
progress = NavisApplication.BeginProgress("批量导出分层",
|
||
$"正在导出第 {i + 1}/{previewResultsForSave.Count} 个分层:{preview.LayerName}");
|
||
}
|
||
else if (progress != null && progress.IsCanceled)
|
||
{
|
||
userCanceled = true;
|
||
}
|
||
});
|
||
|
||
// 检查用户是否取消或请求取消
|
||
if (userCanceled || cancellationToken.IsCancellationRequested)
|
||
{
|
||
LogManager.Info($"[LayerManagementViewModel] 用户取消分层操作,已处理 {successCount}/{previewResultsForSave.Count} 个分层");
|
||
break;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 处理单个分层
|
||
await ProcessSingleLayerAsync(preview, config, cancellationToken);
|
||
successCount++;
|
||
LogManager.Info($"[LayerManagementViewModel] 分层 {preview.LayerName} 处理成功 ({successCount}/{previewResultsForSave.Count})");
|
||
}
|
||
catch (Exception layerEx)
|
||
{
|
||
string errorMsg = $"分层 {preview.LayerName} 处理失败: {layerEx.Message}";
|
||
LogManager.Error($"[LayerManagementViewModel] {errorMsg}");
|
||
failedLayers.Add(preview.LayerName);
|
||
}
|
||
}
|
||
|
||
// 生成最终统计信息
|
||
string finalMessage = $"分层处理完成:成功 {successCount} 个,失败 {failedLayers.Count} 个";
|
||
if (failedLayers.Count > 0)
|
||
{
|
||
finalMessage += $"\n失败列表: {string.Join(", ", failedLayers)}";
|
||
LogManager.Warning($"[LayerManagementViewModel] {finalMessage}");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Info($"[LayerManagementViewModel] {finalMessage}");
|
||
}
|
||
|
||
// 只更新状态栏文字,不更新进度百分比
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
UpdateMainStatus(finalMessage);
|
||
});
|
||
|
||
// 如果所有分层都失败,抛出异常
|
||
if (successCount == 0 && previewResultsForSave.Count > 0)
|
||
{
|
||
throw new InvalidOperationException($"所有选中的分层处理都失败了,共 {previewResultsForSave.Count} 个分层");
|
||
}
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 分层执行完成:成功 {successCount}/{previewResultsForSave.Count}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 分层执行失败: {ex.Message}");
|
||
throw;
|
||
}
|
||
finally
|
||
{
|
||
// 🎯 确保进度条被关闭(在主线程中)
|
||
if (progress != null)
|
||
{
|
||
await System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
|
||
{
|
||
try
|
||
{
|
||
NavisApplication.EndProgress();
|
||
LogManager.Info("[LayerManagementViewModel] 批量导出进度条已关闭");
|
||
}
|
||
catch (Exception progressEx)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 关闭进度条失败: {progressEx.Message}");
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理单个分层(异步版本)
|
||
/// </summary>
|
||
private async Task ProcessSingleLayerAsync(ModelSplitterManager.SplitPreviewResult preview, ModelSplitterManager.SplitConfiguration config, CancellationToken cancellationToken)
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info($"[LayerManagementViewModel] 开始处理分层: {preview.LayerName}, 类型: {preview.LayerAttribute}");
|
||
|
||
// 检查取消请求
|
||
cancellationToken.ThrowIfCancellationRequested();
|
||
|
||
if (preview.Items == null || preview.Items.Count == 0)
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] 分层 {preview.LayerName} 没有包含模型元素,跳过导出");
|
||
return;
|
||
}
|
||
|
||
// 生成输出文件路径(使用新的智能文件名格式)
|
||
string fileName = GenerateFileName(preview.LayerName, config.Strategy, config);
|
||
string outputPath = Path.Combine(config.OutputDirectory, fileName);
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 分层文件路径: {outputPath}");
|
||
|
||
// 检查模型项数量,对大量模型项进行特殊处理
|
||
if (preview.Items != null && preview.Items.Count > 100)
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] 大量模型项检测:{preview.Items.Count}个,可能需要更多内存和时间");
|
||
|
||
// 强制垃圾回收,释放内存
|
||
GC.Collect();
|
||
GC.WaitForPendingFinalizers();
|
||
GC.Collect();
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 垃圾回收完成,当前内存: {GC.GetTotalMemory(false) / 1024 / 1024} MB");
|
||
}
|
||
|
||
// 使用TryExportToNwd API导出分层
|
||
bool success = ExportLayerToNwd(preview, outputPath, config);
|
||
|
||
if (success)
|
||
{
|
||
LogManager.Info($"[LayerManagementViewModel] 分层 {preview.LayerName} 导出成功: {outputPath}");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 分层 {preview.LayerName} 导出失败");
|
||
throw new InvalidOperationException($"分层 {preview.LayerName} 导出失败");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 处理分层失败 {preview.LayerName}: {ex.Message}");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取根节点名称 - 新的智能文件名格式使用
|
||
/// 逻辑:文档文件名 → 第一个模型根项DisplayName → "NavisworksModel"
|
||
/// </summary>
|
||
/// <returns>根节点名称</returns>
|
||
private string GetRootNodeName()
|
||
{
|
||
try
|
||
{
|
||
var document = NavisApplication.ActiveDocument;
|
||
|
||
// 优先使用文档文件名(去除扩展名)
|
||
if (document != null && !string.IsNullOrEmpty(document.FileName))
|
||
{
|
||
string fileName = Path.GetFileNameWithoutExtension(document.FileName);
|
||
if (!string.IsNullOrEmpty(fileName))
|
||
{
|
||
return SanitizeFileName(fileName);
|
||
}
|
||
}
|
||
|
||
// 其次尝试使用第一个模型根项的DisplayName
|
||
if (document?.Models != null && document.Models.Count > 0)
|
||
{
|
||
foreach (Model model in document.Models)
|
||
{
|
||
if (model.RootItem != null && !string.IsNullOrEmpty(model.RootItem.DisplayName))
|
||
{
|
||
return SanitizeFileName(model.RootItem.DisplayName);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 最后使用默认名称
|
||
return "NavisworksModel";
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] 获取根节点名称失败: {ex.Message}");
|
||
return "NavisworksModel";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取分层属性类型名称
|
||
/// </summary>
|
||
/// <param name="strategy">分层策略</param>
|
||
/// <returns>属性类型名称</returns>
|
||
private string GetAttributeTypeName(ModelSplitterManager.SplitStrategy strategy)
|
||
{
|
||
switch (strategy)
|
||
{
|
||
case ModelSplitterManager.SplitStrategy.BySmartFloorDetection:
|
||
return "楼层";
|
||
case ModelSplitterManager.SplitStrategy.ByFloorAttribute:
|
||
return "楼层";
|
||
case ModelSplitterManager.SplitStrategy.ByZoneAttribute:
|
||
return "区域";
|
||
case ModelSplitterManager.SplitStrategy.BySubSystemAttribute:
|
||
return "子系统";
|
||
default:
|
||
return "分层";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清理文件名中的非法字符
|
||
/// </summary>
|
||
/// <param name="fileName">原始文件名</param>
|
||
/// <returns>清理后的文件名</returns>
|
||
private string SanitizeFileName(string fileName)
|
||
{
|
||
if (string.IsNullOrEmpty(fileName))
|
||
return "未命名";
|
||
|
||
// 移除文件名中的非法字符,保留中文字符
|
||
var invalidChars = Path.GetInvalidFileNameChars();
|
||
string sanitized = fileName;
|
||
|
||
foreach (char c in invalidChars)
|
||
{
|
||
sanitized = sanitized.Replace(c, '_');
|
||
}
|
||
|
||
// 移除其他特殊字符,保留字母、数字、下划线和中文字符
|
||
sanitized = System.Text.RegularExpressions.Regex.Replace(sanitized, @"[^\w\u4e00-\u9fa5\-\.]", "_");
|
||
|
||
// 移除多余的下划线
|
||
sanitized = System.Text.RegularExpressions.Regex.Replace(sanitized, @"_{2,}", "_");
|
||
|
||
// 移除首尾下划线
|
||
sanitized = sanitized.Trim('_', ' ');
|
||
|
||
// 限制长度
|
||
if (sanitized.Length > 50)
|
||
{
|
||
sanitized = sanitized.Substring(0, 50).TrimEnd('_');
|
||
}
|
||
|
||
return string.IsNullOrEmpty(sanitized) ? "未命名" : sanitized;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成文件名 - 新的智能格式:根节点名称_分层属性_属性值_时间戳.nwd
|
||
/// </summary>
|
||
/// <param name="attributeValue">属性值(分层名称)</param>
|
||
/// <param name="strategy">分层策略</param>
|
||
/// <param name="config">配置信息</param>
|
||
/// <returns>文件名(不包括路径)</returns>
|
||
private string GenerateFileName(string attributeValue, ModelSplitterManager.SplitStrategy strategy, ModelSplitterManager.SplitConfiguration config)
|
||
{
|
||
try
|
||
{
|
||
// 获取根节点名称
|
||
string rootNodeName = GetRootNodeName();
|
||
|
||
// 获取分层属性类型名称
|
||
string attributeTypeName = GetAttributeTypeName(strategy);
|
||
|
||
// 清理属性值(原分层名称)
|
||
string sanitizedAttributeValue = SanitizeFileName(attributeValue);
|
||
|
||
// 生成时间戳
|
||
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||
|
||
// 新的统一格式:根节点名称_分层属性_属性值_时间戳.nwd
|
||
string fileName = $"{rootNodeName}_{attributeTypeName}_{sanitizedAttributeValue}_{timestamp}.nwd";
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 生成智能文件名: {fileName}");
|
||
LogManager.Info($"[LayerManagementViewModel] 文件名组成: 根节点='{rootNodeName}', 属性类型='{attributeTypeName}', 属性值='{sanitizedAttributeValue}', 时间戳='{timestamp}'");
|
||
|
||
return fileName;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 生成智能文件名失败: {ex.Message}");
|
||
// 失败时使用后备命名方式
|
||
string fallbackName = $"NavisworksModel_分层_{SanitizeFileName(attributeValue)}_{DateTime.Now:yyyyMMdd_HHmmss}.nwd";
|
||
LogManager.Warning($"[LayerManagementViewModel] 使用后备文件名: {fallbackName}");
|
||
return fallbackName;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 导出分层到NWD文件
|
||
/// </summary>
|
||
private bool ExportLayerToNwd(ModelSplitterManager.SplitPreviewResult preview, string outputPath, ModelSplitterManager.SplitConfiguration config)
|
||
{
|
||
try
|
||
{
|
||
// 委托给SimplifiedModelSplitterManager处理实际的导出逻辑
|
||
return _modelSplitterManager.ExportLayerToNwd(preview, outputPath, config);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 导出分层失败: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 浏览输出目录 - 实现正确的业务逻辑与UI分离模式
|
||
/// </summary>
|
||
private async Task BrowseOutputDirectoryAsync()
|
||
{
|
||
try
|
||
{
|
||
// 浏览目录操作需要在UI线程执行,但不需要显示Processing状态
|
||
var command = new BrowseDirectoryCommand(OutputDirectory);
|
||
var result = await command.ExecuteAsync();
|
||
|
||
// 结果UI更新
|
||
if (result.IsSuccess && !string.IsNullOrEmpty(result.SelectedPath))
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
OutputDirectory = result.SelectedPath;
|
||
});
|
||
|
||
LogManager.Info($"[分层管理] 用户选择目录: {result.SelectedPath}");
|
||
}
|
||
else if (!result.IsSuccess && !string.IsNullOrEmpty(result.ErrorMessage))
|
||
{
|
||
LogManager.Warning($"[分层管理] 目录选择失败: {result.ErrorMessage}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 浏览目录异常: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存选中项目
|
||
/// </summary>
|
||
private async Task SaveSelectedItemsAsync()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("[LayerManagementViewModel] 开始保存当前选择项");
|
||
|
||
IsProcessing = true;
|
||
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document?.Models?.Count == 0 || document.CurrentSelection.SelectedItems.Count == 0)
|
||
{
|
||
LogManager.Warning("[LayerManagementViewModel] 没有活动文档或未选择节点");
|
||
MessageBox.Show("请先选择要保存的项目", "保存提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||
return;
|
||
}
|
||
|
||
// 保存当前选择状态
|
||
var originalSelection = new List<ModelItem>(document.CurrentSelection.SelectedItems);
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 用户选中了 {originalSelection.Count} 个节点");
|
||
|
||
// 生成默认文件名
|
||
string defaultFileName = GenerateDefaultFileNameForMultipleItems(document, originalSelection);
|
||
|
||
// 获取保存路径
|
||
string saveFilePath = null;
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
var saveDialog = new Microsoft.Win32.SaveFileDialog
|
||
{
|
||
Title = "保存当前选择项",
|
||
Filter = "Navisworks文件 (*.nwd)|*.nwd",
|
||
DefaultExt = "nwd",
|
||
FileName = defaultFileName
|
||
};
|
||
|
||
if (saveDialog.ShowDialog() == true)
|
||
{
|
||
saveFilePath = saveDialog.FileName;
|
||
}
|
||
});
|
||
|
||
if (string.IsNullOrEmpty(saveFilePath))
|
||
{
|
||
return;
|
||
}
|
||
|
||
bool exportResult = false;
|
||
string errorMessage = "";
|
||
|
||
// 在主线程执行导出
|
||
await Task.Run(() =>
|
||
{
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
try
|
||
{
|
||
// 收集要导出的项目 (仅根节点)
|
||
var itemsToExport = new ModelItemCollection();
|
||
itemsToExport.AddRange(originalSelection);
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 准备导出 {itemsToExport.Count} 个节点");
|
||
|
||
// 保存当前可见性状态
|
||
var originalVisibilityState = SaveCurrentVisibilityState(document);
|
||
|
||
try
|
||
{
|
||
// 隔离显示
|
||
bool isolateSuccess = VisibilityHelper.IsolateSpecificItems(itemsToExport);
|
||
if (!isolateSuccess) throw new InvalidOperationException("隔离显示失败");
|
||
|
||
// 导出选项
|
||
var exportOptions = new Autodesk.Navisworks.Api.NwdExportOptions
|
||
{
|
||
ExcludeHiddenItems = true,
|
||
EmbedXrefs = EmbedXrefs,
|
||
PreventObjectPropertyExport = PreventObjectPropertyExport
|
||
};
|
||
|
||
document.ExportToNwd(saveFilePath, exportOptions);
|
||
exportResult = true;
|
||
LogManager.Info("[LayerManagementViewModel] ExportToNwd API调用完成");
|
||
}
|
||
finally
|
||
{
|
||
// 恢复可见性
|
||
RestoreVisibilityState(document, originalVisibilityState);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 导出异常: {ex.Message}", ex);
|
||
errorMessage = ex.Message;
|
||
exportResult = false;
|
||
}
|
||
});
|
||
});
|
||
|
||
// 恢复原始选择 (需要在主线程)
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
try
|
||
{
|
||
document.CurrentSelection.Clear();
|
||
document.CurrentSelection.AddRange(originalSelection);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] 恢复选择失败: {ex.Message}");
|
||
}
|
||
});
|
||
|
||
// 显示结果
|
||
if (exportResult)
|
||
{
|
||
if (File.Exists(saveFilePath))
|
||
{
|
||
var fileInfo = new FileInfo(saveFilePath);
|
||
MessageBox.Show(
|
||
$"保存成功!\n\n文件: {saveFilePath}\n大小: {fileInfo.Length / 1024} KB",
|
||
"保存结果", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show($"保存失败: {errorMessage}", "保存错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 保存选中项目过程异常: {ex.Message}", ex);
|
||
MessageBox.Show($"保存过程异常: {ex.Message}", "保存错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
finally
|
||
{
|
||
IsProcessing = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存当前可见性状态
|
||
/// </summary>
|
||
private Dictionary<ModelItem, bool> SaveCurrentVisibilityState(Document document)
|
||
{
|
||
var visibilityState = new Dictionary<ModelItem, bool>();
|
||
|
||
try
|
||
{
|
||
// 获取所有顶级项目并记录它们的可见性状态
|
||
foreach (Model model in document.Models)
|
||
{
|
||
foreach (ModelItem topLevelItem in model.RootItem.Children)
|
||
{
|
||
try
|
||
{
|
||
// 记录是否隐藏(IsHidden为true表示隐藏,我们存储可见性所以取反)
|
||
visibilityState[topLevelItem] = !topLevelItem.IsHidden;
|
||
}
|
||
catch
|
||
{
|
||
// 如果无法获取状态,默认为可见
|
||
visibilityState[topLevelItem] = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 保存可见性状态完成,记录了 {visibilityState.Count} 个顶级项目");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 保存可见性状态失败: {ex.Message}");
|
||
}
|
||
|
||
return visibilityState;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 恢复可见性状态
|
||
/// </summary>
|
||
private void RestoreVisibilityState(Document document, Dictionary<ModelItem, bool> visibilityState)
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("[LayerManagementViewModel] 恢复可见性状态");
|
||
|
||
if (visibilityState == null || visibilityState.Count == 0)
|
||
{
|
||
LogManager.Warning("[LayerManagementViewModel] 没有可见性状态需要恢复,重置为全部可见");
|
||
document.Models.ResetAllHidden();
|
||
return;
|
||
}
|
||
|
||
// 使用成熟的可见性控制模式 - 分别收集要显示和隐藏的项目
|
||
var itemsToShow = new ModelItemCollection();
|
||
var itemsToHide = new ModelItemCollection();
|
||
|
||
foreach (var kvp in visibilityState)
|
||
{
|
||
try
|
||
{
|
||
if (kvp.Value) // 原来是可见的
|
||
{
|
||
itemsToShow.Add(kvp.Key);
|
||
}
|
||
else // 原来是隐藏的
|
||
{
|
||
itemsToHide.Add(kvp.Key);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] 处理项目可见性状态时出错: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
// 首先重置所有项目为可见状态
|
||
document.Models.ResetAllHidden();
|
||
|
||
// 然后隐藏原来应该隐藏的项目
|
||
if (itemsToHide.Count > 0)
|
||
{
|
||
document.Models.SetHidden(itemsToHide, true);
|
||
LogManager.Info($"[LayerManagementViewModel] 恢复隐藏 {itemsToHide.Count} 个项目");
|
||
}
|
||
|
||
LogManager.Info("[LayerManagementViewModel] 可见性状态恢复完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 恢复可见性状态失败: {ex.Message}");
|
||
// 失败时至少确保模型处于可见状态
|
||
try
|
||
{
|
||
document.Models.ResetAllHidden();
|
||
LogManager.Info("[LayerManagementViewModel] 已重置为全部可见状态");
|
||
}
|
||
catch (Exception resetEx)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 重置可见性也失败: {resetEx.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 运行环境诊断 - 检查Navisworks API环境
|
||
/// </summary>
|
||
private async Task RunDiagnosticAsync()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("[LayerManagementViewModel] 开始环境诊断");
|
||
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
CurrentOperationText = "正在进行环境诊断...";
|
||
UpdateMainStatus("检查Navisworks API环境和线程状态");
|
||
});
|
||
|
||
// 2. 纯业务逻辑执行(后台线程)
|
||
string diagnosticReport = await Task.Run(() =>
|
||
{
|
||
return CreateDiagnosticReport();
|
||
});
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentOperationText = "环境诊断完成";
|
||
UpdateMainStatus("环境诊断报告已生成");
|
||
});
|
||
|
||
// 显示诊断结果
|
||
ShowDiagnosticResult(diagnosticReport);
|
||
|
||
LogManager.Info("[LayerManagementViewModel] 环境诊断完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 环境诊断异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentOperationText = "环境诊断异常";
|
||
UpdateMainStatus($"诊断失败: {ex.Message}");
|
||
});
|
||
|
||
ShowDiagnosticResult($"诊断过程中发生异常: {ex.Message}");
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建环境诊断报告
|
||
/// </summary>
|
||
/// <returns>诊断报告</returns>
|
||
private string CreateDiagnosticReport()
|
||
{
|
||
var report = new System.Text.StringBuilder();
|
||
report.AppendLine("=== Navisworks API环境诊断报告 ===");
|
||
|
||
try
|
||
{
|
||
// 1. 线程状态
|
||
var apartmentState = System.Threading.Thread.CurrentThread.GetApartmentState();
|
||
report.AppendLine($"线程状态: {apartmentState} {(apartmentState == System.Threading.ApartmentState.STA ? "✓" : "✗")}");
|
||
|
||
// 2. API可用性
|
||
try
|
||
{
|
||
var app = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
report.AppendLine("API可用性: 可用 ✓");
|
||
}
|
||
catch (Exception apiEx)
|
||
{
|
||
report.AppendLine($"API可用性: 异常 - {apiEx.Message} ✗");
|
||
}
|
||
|
||
// 3. 文档状态
|
||
try
|
||
{
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document != null)
|
||
{
|
||
report.AppendLine($"活动文档: {document.FileName ?? "未命名"} ✓");
|
||
report.AppendLine($"模型数量: {document.Models?.Count ?? 0}");
|
||
}
|
||
else
|
||
{
|
||
report.AppendLine("活动文档: 无 ✗");
|
||
}
|
||
}
|
||
catch (Exception docEx)
|
||
{
|
||
report.AppendLine($"活动文档: 异常 - {docEx.Message} ✗");
|
||
}
|
||
|
||
// 4. 内存状态
|
||
long memoryMB = GC.GetTotalMemory(false) / 1024 / 1024;
|
||
report.AppendLine($"当前内存: {memoryMB} MB");
|
||
|
||
report.AppendLine("=== 诊断完成 ===");
|
||
|
||
string result = report.ToString();
|
||
LogManager.Info($"[LayerManagement] 环境诊断报告:\n{result}");
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
string error = $"诊断过程出错: {ex.Message}";
|
||
report.AppendLine(error);
|
||
LogManager.Error($"[LayerManagement] {error}");
|
||
return report.ToString();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示环境诊断结果对话框
|
||
/// </summary>
|
||
private void ShowDiagnosticResult(string report)
|
||
{
|
||
try
|
||
{
|
||
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||
{
|
||
var diagWindow = new Window
|
||
{
|
||
Title = "Navisworks API环境诊断",
|
||
Width = 600,
|
||
Height = 500,
|
||
WindowStartupLocation = WindowStartupLocation.CenterScreen,
|
||
ResizeMode = ResizeMode.CanResize
|
||
};
|
||
|
||
var textBox = new System.Windows.Controls.TextBox
|
||
{
|
||
Text = report,
|
||
IsReadOnly = true,
|
||
VerticalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Auto,
|
||
HorizontalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Auto,
|
||
FontFamily = new System.Windows.Media.FontFamily("Consolas"),
|
||
FontSize = 12,
|
||
Margin = new Thickness(10),
|
||
TextWrapping = TextWrapping.Wrap
|
||
};
|
||
|
||
var panel = new System.Windows.Controls.DockPanel();
|
||
|
||
var buttonPanel = new System.Windows.Controls.StackPanel
|
||
{
|
||
Orientation = System.Windows.Controls.Orientation.Horizontal,
|
||
HorizontalAlignment = HorizontalAlignment.Right,
|
||
Margin = new Thickness(10)
|
||
};
|
||
System.Windows.Controls.DockPanel.SetDock(buttonPanel, System.Windows.Controls.Dock.Bottom);
|
||
|
||
var copyButton = new System.Windows.Controls.Button
|
||
{
|
||
Content = "复制报告",
|
||
Width = 80,
|
||
Height = 25,
|
||
Margin = new Thickness(0, 0, 10, 0)
|
||
};
|
||
copyButton.Click += (s, e) =>
|
||
{
|
||
try
|
||
{
|
||
System.Windows.Clipboard.SetText(report);
|
||
MessageBox.Show("诊断报告已复制到剪贴板", "复制成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"复制失败: {ex.Message}", "复制错误", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||
}
|
||
};
|
||
|
||
var closeButton = new System.Windows.Controls.Button
|
||
{
|
||
Content = "关闭",
|
||
Width = 80,
|
||
Height = 25,
|
||
IsDefault = true
|
||
};
|
||
closeButton.Click += (s, e) => diagWindow.Close();
|
||
|
||
buttonPanel.Children.Add(copyButton);
|
||
buttonPanel.Children.Add(closeButton);
|
||
panel.Children.Add(buttonPanel);
|
||
panel.Children.Add(textBox);
|
||
|
||
diagWindow.Content = panel;
|
||
diagWindow.ShowDialog();
|
||
}));
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 显示诊断结果失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 测试ExportToNwd API - 专门的导出API
|
||
/// </summary>
|
||
private async Task TestExportToNwdAsync()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("[LayerManagementViewModel] 开始测试ExportToNwd API");
|
||
|
||
// 1. 简单状态更新
|
||
IsProcessing = true;
|
||
|
||
// 2. 获取保存路径
|
||
string saveFilePath = null;
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
var saveDialog = new Microsoft.Win32.SaveFileDialog
|
||
{
|
||
Title = "复杂导出测试 - 选择保存位置",
|
||
Filter = "Navisworks文件 (*.nwd)|*.nwd",
|
||
DefaultExt = "nwd",
|
||
FileName = $"复杂导出测试_{DateTime.Now:yyyyMMdd_HHmmss}.nwd"
|
||
};
|
||
|
||
if (saveDialog.ShowDialog() == true)
|
||
{
|
||
saveFilePath = saveDialog.FileName;
|
||
}
|
||
});
|
||
|
||
if (string.IsNullOrEmpty(saveFilePath))
|
||
{
|
||
LogManager.Info("[LayerManagementViewModel] 用户取消了ExportToNwd测试");
|
||
return;
|
||
}
|
||
|
||
// 3. 使用ExportToNwd API测试
|
||
bool exportResult = false;
|
||
string errorMessage = "";
|
||
|
||
try
|
||
{
|
||
LogManager.Info($"[LayerManagementViewModel] 开始ExportToNwd测试到: {saveFilePath}");
|
||
|
||
// 获取当前文档
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document?.Models?.Count == 0)
|
||
{
|
||
errorMessage = "没有活动文档或模型";
|
||
exportResult = false;
|
||
}
|
||
else
|
||
{
|
||
// 保存当前选择状态
|
||
var originalSelection = new List<ModelItem>();
|
||
foreach (ModelItem item in document.CurrentSelection.SelectedItems)
|
||
{
|
||
originalSelection.Add(item);
|
||
}
|
||
|
||
try
|
||
{
|
||
// 复杂测试:基于用户当前选择的节点进行导出
|
||
ModelItem targetItem = null;
|
||
var allVisibilityItems = new List<ModelItem>();
|
||
|
||
// 第一步:检查用户当前是否有选中的节点
|
||
if (document.CurrentSelection.SelectedItems.Count > 0)
|
||
{
|
||
// 使用用户选中的第一个节点作为目标
|
||
targetItem = document.CurrentSelection.SelectedItems.First();
|
||
LogManager.Info($"[LayerManagementViewModel] 使用用户选中的节点: {targetItem.DisplayName}");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Info("[LayerManagementViewModel] 用户没有选中节点,将自动选择一个二级节点");
|
||
}
|
||
|
||
// 第二步:收集所有可见的模型项(用于可见性控制)
|
||
foreach (Model model in document.Models)
|
||
{
|
||
var rootItem = model.RootItem;
|
||
LogManager.Info($"[LayerManagementViewModel] 处理模型根项: {rootItem.DisplayName},子项数量: {rootItem.Children.Count()}");
|
||
|
||
// 遍历一级节点
|
||
foreach (ModelItem firstLevel in rootItem.Children)
|
||
{
|
||
allVisibilityItems.Add(firstLevel); // 收集用于可见性控制
|
||
LogManager.Info($"[LayerManagementViewModel] 一级节点: {firstLevel.DisplayName},子项数量: {firstLevel.Children.Count()}");
|
||
|
||
// 如果用户没有选中节点,则选择第一个二级节点作为fallback
|
||
if (targetItem == null && firstLevel.Children.Count() > 0)
|
||
{
|
||
foreach (ModelItem secondLevel in firstLevel.Children)
|
||
{
|
||
targetItem = secondLevel;
|
||
LogManager.Info($"[LayerManagementViewModel] 选中二级目标节点: {secondLevel.DisplayName}");
|
||
break; // 只要第一个二级节点
|
||
}
|
||
}
|
||
|
||
// 收集其他二级节点用于可见性控制
|
||
foreach (ModelItem secondLevel in firstLevel.Children)
|
||
{
|
||
allVisibilityItems.Add(secondLevel);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (targetItem == null)
|
||
{
|
||
LogManager.Error("[LayerManagementViewModel] 没有找到可用的二级节点");
|
||
errorMessage = "没有找到可用的二级节点";
|
||
exportResult = false;
|
||
return;
|
||
}
|
||
|
||
// 第二步:确保目标节点被选中
|
||
if (!originalSelection.Contains(targetItem))
|
||
{
|
||
// 如果目标节点不在原始选择中,则清空并重新选择
|
||
document.CurrentSelection.Clear();
|
||
document.CurrentSelection.Add(targetItem);
|
||
LogManager.Info($"[LayerManagementViewModel] 已选择目标节点: {targetItem.DisplayName}");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Info($"[LayerManagementViewModel] 目标节点已在选择中: {targetItem.DisplayName}");
|
||
}
|
||
|
||
// 第三步:隐藏其他节点,只保留目标节点可见
|
||
LogManager.Info($"[LayerManagementViewModel] 准备导出选中节点及其所有子项: {targetItem.DisplayName}");
|
||
LogManager.Info($"[LayerManagementViewModel] 目标节点子项数量: {targetItem.Children.Count()}");
|
||
|
||
// 创建要隐藏的项目集合 - 隐藏除了目标节点之外的所有一级节点
|
||
var itemsToHide = new ModelItemCollection();
|
||
int hiddenCount = 0;
|
||
|
||
foreach (ModelItem item in allVisibilityItems)
|
||
{
|
||
// 隐藏除了目标节点之外的所有一级节点
|
||
if (item != targetItem && item.Parent?.Parent == null) // 确保是一级节点
|
||
{
|
||
try
|
||
{
|
||
itemsToHide.Add(item);
|
||
hiddenCount++;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] 添加隐藏项目失败: {item.DisplayName} - {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
// 执行隐藏操作
|
||
if (itemsToHide.Count > 0)
|
||
{
|
||
try
|
||
{
|
||
document.Models.SetHidden(itemsToHide, true);
|
||
LogManager.Info($"[LayerManagementViewModel] 成功隐藏 {hiddenCount} 个一级节点,只保留目标节点可见");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] 隐藏操作失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
// 确保选中目标节点
|
||
document.CurrentSelection.Clear();
|
||
document.CurrentSelection.Add(targetItem);
|
||
LogManager.Info($"[LayerManagementViewModel] 已选择目标节点: {targetItem.DisplayName}");
|
||
|
||
// 创建导出选项 - 使用用户配置的参数
|
||
var exportOptions = new Autodesk.Navisworks.Api.NwdExportOptions();
|
||
exportOptions.ExcludeHiddenItems = true; // 只导出可见项目(目标节点及其子项)
|
||
exportOptions.EmbedXrefs = EmbedXrefs;
|
||
exportOptions.PreventObjectPropertyExport = PreventObjectPropertyExport;
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] TestExportToNwd导出选项: EmbedXrefs={exportOptions.EmbedXrefs}, PreventObjectPropertyExport={exportOptions.PreventObjectPropertyExport}");
|
||
|
||
LogManager.Info("[LayerManagementViewModel] 开始调用ExportToNwd API");
|
||
|
||
// 使用ExportToNwd API
|
||
document.ExportToNwd(saveFilePath, exportOptions);
|
||
|
||
exportResult = true;
|
||
LogManager.Info("[LayerManagementViewModel] ExportToNwd API调用完成");
|
||
}
|
||
finally
|
||
{
|
||
// 恢复所有隐藏项目的可见性
|
||
try
|
||
{
|
||
if (document?.Models != null)
|
||
{
|
||
// 获取所有模型项并恢复可见性
|
||
var allItems = new ModelItemCollection();
|
||
foreach (Model model in document.Models)
|
||
{
|
||
foreach (ModelItem item in model.RootItem.Children)
|
||
{
|
||
allItems.Add(item);
|
||
}
|
||
}
|
||
|
||
if (allItems.Count > 0)
|
||
{
|
||
document.Models.SetHidden(allItems, false);
|
||
LogManager.Info("[LayerManagementViewModel] 已恢复所有项目可见性");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] 恢复可见性失败: {ex.Message}");
|
||
}
|
||
|
||
// 恢复原始选择
|
||
try
|
||
{
|
||
document.CurrentSelection.Clear();
|
||
foreach (ModelItem item in originalSelection)
|
||
{
|
||
try
|
||
{
|
||
document.CurrentSelection.Add(item);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] ExportToNwd恢复选择项时出错: {ex.Message}");
|
||
}
|
||
}
|
||
LogManager.Info("[LayerManagementViewModel] 已恢复原始选择");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] 恢复选择失败: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] ExportToNwd API异常: {ex.Message}", ex);
|
||
errorMessage = ex.Message;
|
||
exportResult = false;
|
||
}
|
||
|
||
// 4. 显示结果
|
||
if (exportResult)
|
||
{
|
||
if (File.Exists(saveFilePath))
|
||
{
|
||
var fileInfo = new FileInfo(saveFilePath);
|
||
MessageBox.Show(
|
||
$"ExportToNwd API测试成功!\n\n文件路径: {saveFilePath}\n文件大小: {fileInfo.Length / 1024} KB\n创建时间: {fileInfo.CreationTime}\n\n这证明ExportToNwd API工作正常!",
|
||
"ExportToNwd测试结果", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show("ExportToNwd操作完成,但文件不存在。", "ExportToNwd测试结果", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show($"ExportToNwd API测试失败!\n\n错误信息: {errorMessage}\n\n这可能说明ExportToNwd API有问题。",
|
||
"ExportToNwd测试结果", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
|
||
LogManager.Info("[LayerManagementViewModel] ExportToNwd API测试完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] ExportToNwd测试过程异常: {ex.Message}", ex);
|
||
MessageBox.Show($"ExportToNwd测试过程异常: {ex.Message}", "ExportToNwd测试错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
finally
|
||
{
|
||
// 5. 简单的状态清理
|
||
IsProcessing = false;
|
||
}
|
||
}
|
||
|
||
|
||
private void CancelOperation()
|
||
{
|
||
_cancellationTokenSource?.Cancel();
|
||
}
|
||
|
||
#endregion
|
||
|
||
// 选择状态格式化方法已移至NavisworksSelectionHelper中
|
||
|
||
#region 事件处理和辅助方法
|
||
|
||
/// <summary>
|
||
/// 更新当前选择状态 - 使用新的选择管理服务
|
||
/// </summary>
|
||
public async Task UpdateCurrentSelectionAsync()
|
||
{
|
||
try
|
||
{
|
||
// 使用新的选择管理服务获取选择状态(后台线程)
|
||
var selectionResult = await NavisworksSelectionHelper.GetCurrentSelectionStateAsync();
|
||
|
||
// 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
if (selectionResult.Success)
|
||
{
|
||
CurrentSelectionText = NavisworksSelectionHelper.FormatSelectionText(selectionResult, "个项目");
|
||
}
|
||
else
|
||
{
|
||
CurrentSelectionText = selectionResult.ErrorMessage ?? "获取选择状态失败";
|
||
LogManager.Warning($"[LayerManagementViewModel] 更新选择状态失败: {selectionResult.ErrorMessage}");
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 更新选择状态异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentSelectionText = "选择状态异常";
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 异步初始化
|
||
/// </summary>
|
||
private async void InitializeAsync()
|
||
{
|
||
try
|
||
{
|
||
// 设置默认输出目录
|
||
var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
OutputDirectory = Path.Combine(documentsPath, "NavisworksTransport", "分层输出");
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 初始化失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
|
||
{
|
||
// 使用UIStateManager安全更新进度
|
||
_uiStateManager.QueueUIUpdate(() =>
|
||
{
|
||
ProgressPercentage = e.ProgressPercentage;
|
||
UpdateMainStatus(e.UserState?.ToString() ?? "");
|
||
}, UIUpdatePriority.Normal);
|
||
}
|
||
|
||
private void OnStatusChanged(object sender, string status)
|
||
{
|
||
_uiStateManager.QueueUIUpdate(() =>
|
||
{
|
||
CurrentOperationText = status ?? "处理中...";
|
||
}, UIUpdatePriority.Normal);
|
||
}
|
||
|
||
private void SetFloorAnalysisResult(string text, Brush color)
|
||
{
|
||
FloorAnalysisResult = text;
|
||
FloorAnalysisResultColor = color;
|
||
}
|
||
|
||
private ModelSplitterManager.SplitStrategy GetSplitStrategyFromString(string strategy)
|
||
{
|
||
switch (strategy)
|
||
{
|
||
case "智能检测":
|
||
return ModelSplitterManager.SplitStrategy.BySmartFloorDetection;
|
||
case "自定义":
|
||
// 根据二级选项决定具体的自定义分层策略
|
||
switch (SelectedCustomLayerOption)
|
||
{
|
||
case "按楼层":
|
||
return ModelSplitterManager.SplitStrategy.ByFloorAttribute;
|
||
case "按区域":
|
||
return ModelSplitterManager.SplitStrategy.ByZoneAttribute;
|
||
case "按子系统":
|
||
return ModelSplitterManager.SplitStrategy.BySubSystemAttribute;
|
||
default:
|
||
return ModelSplitterManager.SplitStrategy.ByFloorAttribute;
|
||
}
|
||
default:
|
||
return ModelSplitterManager.SplitStrategy.BySmartFloorDetection;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前选择的完整分层策略描述(包含二级选项)
|
||
/// </summary>
|
||
public string GetFullStrategyDescription()
|
||
{
|
||
if (SelectedSplitStrategy == "自定义" && !string.IsNullOrEmpty(SelectedCustomLayerOption))
|
||
{
|
||
return $"{SelectedSplitStrategy} - {SelectedCustomLayerOption}";
|
||
}
|
||
return SelectedSplitStrategy;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 生成针对多个选中项目的默认文件名
|
||
/// </summary>
|
||
private string GenerateDefaultFileNameForMultipleItems(Document document, List<ModelItem> selectedItems)
|
||
{
|
||
try
|
||
{
|
||
// 获取根节点名(第一个模型的名称,去掉扩展名)
|
||
string rootName = "Unknown";
|
||
if (document.Models.Count > 0)
|
||
{
|
||
var firstModel = document.Models.First();
|
||
if (!string.IsNullOrEmpty(firstModel.FileName))
|
||
{
|
||
rootName = Path.GetFileNameWithoutExtension(firstModel.FileName);
|
||
}
|
||
else if (!string.IsNullOrEmpty(firstModel.RootItem.DisplayName))
|
||
{
|
||
rootName = firstModel.RootItem.DisplayName;
|
||
}
|
||
}
|
||
|
||
// 生成选中项目描述
|
||
string selectedDescription;
|
||
if (selectedItems.Count == 1)
|
||
{
|
||
selectedDescription = selectedItems[0].DisplayName ?? "Unknown";
|
||
}
|
||
else
|
||
{
|
||
selectedDescription = $"{selectedItems.Count}个节点";
|
||
}
|
||
|
||
// 清理文件名中的非法字符
|
||
string cleanRootName = CleanFileName(rootName);
|
||
string cleanSelectedDescription = CleanFileName(selectedDescription);
|
||
|
||
// 生成时间戳
|
||
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||
|
||
// 组合文件名
|
||
return $"{cleanRootName}_{cleanSelectedDescription}_{timestamp}.nwd";
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] 生成多选文件名失败: {ex.Message}");
|
||
return $"多选集_{DateTime.Now:yyyyMMdd_HHmmss}.nwd";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成默认文件名:根节点名_选中节点名_时间戳
|
||
/// </summary>
|
||
private string GenerateDefaultFileName(Document document, ModelItem selectedItem)
|
||
{
|
||
try
|
||
{
|
||
// 获取根节点名(第一个模型的名称,去掉扩展名)
|
||
string rootName = "Unknown";
|
||
if (document.Models.Count > 0)
|
||
{
|
||
var firstModel = document.Models.First();
|
||
if (!string.IsNullOrEmpty(firstModel.FileName))
|
||
{
|
||
rootName = Path.GetFileNameWithoutExtension(firstModel.FileName);
|
||
}
|
||
else if (!string.IsNullOrEmpty(firstModel.RootItem.DisplayName))
|
||
{
|
||
rootName = firstModel.RootItem.DisplayName;
|
||
}
|
||
}
|
||
|
||
// 获取选中节点名,清理特殊字符
|
||
string selectedName = "Unknown";
|
||
if (selectedItem != null && !string.IsNullOrEmpty(selectedItem.DisplayName))
|
||
{
|
||
selectedName = selectedItem.DisplayName;
|
||
}
|
||
|
||
// 清理文件名中的非法字符
|
||
string cleanRootName = CleanFileName(rootName);
|
||
string cleanSelectedName = CleanFileName(selectedName);
|
||
|
||
// 生成时间戳
|
||
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||
|
||
// 组合文件名
|
||
return $"{cleanRootName}_{cleanSelectedName}_{timestamp}.nwd";
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] 生成默认文件名失败: {ex.Message}");
|
||
return $"选择集_{DateTime.Now:yyyyMMdd_HHmmss}.nwd";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清理文件名中的非法字符
|
||
/// </summary>
|
||
private string CleanFileName(string fileName)
|
||
{
|
||
if (string.IsNullOrEmpty(fileName))
|
||
return "Unknown";
|
||
|
||
// Windows文件名非法字符
|
||
char[] invalidChars = Path.GetInvalidFileNameChars();
|
||
string cleanName = fileName;
|
||
|
||
foreach (char c in invalidChars)
|
||
{
|
||
cleanName = cleanName.Replace(c, '_');
|
||
}
|
||
|
||
// 替换一些常见的特殊字符
|
||
cleanName = cleanName.Replace(" ", "_")
|
||
.Replace(".", "_")
|
||
.Replace(":", "_")
|
||
.Replace("/", "_")
|
||
.Replace("\\", "_");
|
||
|
||
// 限制长度
|
||
if (cleanName.Length > 50)
|
||
{
|
||
cleanName = cleanName.Substring(0, 50);
|
||
}
|
||
|
||
return cleanName;
|
||
}
|
||
|
||
|
||
private void RefreshAllCommands()
|
||
{
|
||
OnPropertyChanged(nameof(CanApplyFloorAttributes));
|
||
OnPropertyChanged(nameof(CanPreviewSplit));
|
||
OnPropertyChanged(nameof(CanExecuteSplit));
|
||
OnPropertyChanged(nameof(CanIsolateSelectedLayer));
|
||
OnPropertyChanged(nameof(HasSelectedItems));
|
||
OnPropertyChanged(nameof(CanSetFloorAttribute));
|
||
OnPropertyChanged(nameof(CanClearFloorAttribute));
|
||
OnPropertyChanged(nameof(CanSetLayerAttribute));
|
||
OnPropertyChanged(nameof(CanClearLayerAttribute));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放资源
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
Dispose(true);
|
||
GC.SuppressFinalize(this);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放资源的具体实现
|
||
/// </summary>
|
||
/// <param name="disposing">是否正在释放托管资源</param>
|
||
protected virtual void Dispose(bool disposing)
|
||
{
|
||
if (!_disposed)
|
||
{
|
||
if (disposing)
|
||
{
|
||
try
|
||
{
|
||
// 取消正在进行的操作
|
||
_cancellationTokenSource?.Cancel();
|
||
_cancellationTokenSource?.Dispose();
|
||
|
||
// 取消事件订阅
|
||
if (_modelSplitterManager != null)
|
||
{
|
||
_modelSplitterManager.ProgressChanged -= OnProgressChanged;
|
||
_modelSplitterManager.StatusChanged -= OnStatusChanged;
|
||
}
|
||
|
||
// 取消Navisworks选择变化事件订阅
|
||
UnsubscribeFromSelectionEvents();
|
||
|
||
LogManager.Info("[LayerManagementViewModel] 资源清理完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 资源清理失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
_disposed = true;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 向后兼容性接口
|
||
|
||
/// <summary>
|
||
/// 向后兼容:提供UIStateManager访问接口
|
||
/// </summary>
|
||
public UIStateManager UIStateManager => _uiStateManager;
|
||
|
||
/// <summary>
|
||
/// 验证ViewModel状态是否正常
|
||
/// </summary>
|
||
public bool IsValidState()
|
||
{
|
||
return _uiStateManager != null &&
|
||
AvailableAttributes != null &&
|
||
SplitPreviewResults != null &&
|
||
SplitStrategies != null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取ViewModel状态信息
|
||
/// </summary>
|
||
public string GetStateInfo()
|
||
{
|
||
return $"UIStateManager: {(_uiStateManager != null ? "已初始化" : "未初始化")}, " +
|
||
$"可用属性数量: {AvailableAttributes?.Count ?? 0}, " +
|
||
$"预览结果数量: {SplitPreviewResults?.Count ?? 0}";
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 选择事件处理
|
||
|
||
/// <summary>
|
||
/// 订阅Navisworks选择变化事件 - 使用新的选择管理服务
|
||
/// </summary>
|
||
private void SubscribeToSelectionEvents()
|
||
{
|
||
try
|
||
{
|
||
// 使用新的选择管理服务订阅选择变化事件
|
||
_selectionEventSubscription = NavisworksSelectionHelper.SubscribeToSelectionChanges(
|
||
OnSelectionChangedAsync, _uiStateManager);
|
||
|
||
LogManager.Info("[LayerManagementViewModel] 已通过NavisworksSelectionHelper订阅选择变化事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 订阅选择事件失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消订阅Navisworks选择变化事件 - 使用新的选择管理服务
|
||
/// </summary>
|
||
private void UnsubscribeFromSelectionEvents()
|
||
{
|
||
try
|
||
{
|
||
// 通过Dispose方法取消订阅
|
||
_selectionEventSubscription?.Dispose();
|
||
_selectionEventSubscription = null;
|
||
|
||
LogManager.Info("[LayerManagementViewModel] 已通过NavisworksSelectionHelper取消订阅选择变化事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 取消选择事件订阅失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选择变化事件处理器 - 使用新的选择管理服务
|
||
/// </summary>
|
||
private async Task OnSelectionChangedAsync(SelectionStateResult selectionResult)
|
||
{
|
||
// 如果已经释放,直接返回
|
||
if (_disposed) return;
|
||
|
||
try
|
||
{
|
||
// 更新楼层属性相关的选择状态(使用新的选择结果)
|
||
await UpdateFloorAttributeSelectionStateAsync(selectionResult);
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 选择状态已更新: {selectionResult.Count}个项目");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 处理选择变化事件异常: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新楼层属性相关的选择状态 - 使用新的选择管理服务
|
||
/// </summary>
|
||
private async Task UpdateFloorAttributeSelectionStateAsync(SelectionStateResult selectionResult = null)
|
||
{
|
||
// 如果已经释放,直接返回
|
||
if (_disposed) return;
|
||
|
||
try
|
||
{
|
||
// 如果没有提供选择结果,则获取当前选择状态
|
||
if (selectionResult == null)
|
||
{
|
||
selectionResult = await NavisworksSelectionHelper.GetCurrentSelectionStateAsync();
|
||
}
|
||
|
||
// UI更新 - 使用新的选择管理服务格式化选择文本
|
||
if (selectionResult.Success)
|
||
{
|
||
SelectedModelsText = NavisworksSelectionHelper.FormatSelectionText(selectionResult, "个模型");
|
||
}
|
||
else
|
||
{
|
||
SelectedModelsText = selectionResult.ErrorMessage ?? "检查选择状态异常";
|
||
}
|
||
|
||
// 刷新命令状态
|
||
OnPropertyChanged(nameof(HasSelectedItems));
|
||
OnPropertyChanged(nameof(HasSelectedModels));
|
||
OnPropertyChanged(nameof(CanSetFloorAttribute));
|
||
OnPropertyChanged(nameof(CanClearFloorAttribute));
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 楼层属性选择状态已更新: 成功={selectionResult.Success}, 数量={selectionResult.Count}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 更新楼层属性选择状态异常: {ex.Message}", ex);
|
||
SelectedModelsText = "检查选择状态异常";
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 楼层属性管理方法
|
||
|
||
/// <summary>
|
||
/// 刷新选择状态 - 使用新的选择管理服务
|
||
/// </summary>
|
||
private async Task RefreshSelectionAsync()
|
||
{
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
CurrentOperationText = "正在检查模型选择...";
|
||
});
|
||
|
||
try
|
||
{
|
||
// 2. 使用新的选择管理服务获取选择状态(后台线程)
|
||
var selectionResult = await NavisworksSelectionHelper.GetCurrentSelectionStateAsync();
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
if (selectionResult.Success)
|
||
{
|
||
SelectedModelsText = NavisworksSelectionHelper.FormatSelectionText(selectionResult, "个模型");
|
||
CurrentOperationText = "检查完成";
|
||
}
|
||
else
|
||
{
|
||
SelectedModelsText = selectionResult.ErrorMessage ?? "检查选择状态失败";
|
||
CurrentOperationText = "检查失败";
|
||
}
|
||
|
||
// 刷新命令状态
|
||
OnPropertyChanged(nameof(HasSelectedItems));
|
||
OnPropertyChanged(nameof(HasSelectedModels));
|
||
OnPropertyChanged(nameof(CanSetFloorAttribute));
|
||
OnPropertyChanged(nameof(CanClearFloorAttribute));
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 刷新选择异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
SelectedModelsText = "检查选择状态异常";
|
||
CurrentOperationText = $"检查失败: {ex.Message}";
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置楼层属性
|
||
/// </summary>
|
||
private async Task SetFloorAttributeAsync()
|
||
{
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
IsProcessing = true;
|
||
ShowCancelButton = false; // 设置楼层属性不支持取消
|
||
CurrentOperationText = "正在设置楼层属性...";
|
||
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document?.CurrentSelection?.SelectedItems == null || document.CurrentSelection.SelectedItems.Count == 0)
|
||
{
|
||
CurrentOperationText = "未选择任何模型项";
|
||
return;
|
||
}
|
||
|
||
// 使用成员变量 _floorAttributeManager
|
||
var selectedItems = document.CurrentSelection.SelectedItems.ToList();
|
||
var successCount = 0;
|
||
|
||
foreach (var item in selectedItems)
|
||
{
|
||
try
|
||
{
|
||
bool result = _floorAttributeManager.SetFloorAttribute(
|
||
item,
|
||
SelectedFloorLevel,
|
||
string.IsNullOrWhiteSpace(SelectedZone) ? null : SelectedZone,
|
||
string.IsNullOrWhiteSpace(SelectedSubSystem) ? null : SelectedSubSystem);
|
||
|
||
if (result)
|
||
{
|
||
successCount++;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"设置模型 {item.DisplayName} 的楼层属性失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
CurrentOperationText = $"楼层属性设置完成,成功设置 {successCount}/{selectedItems.Count} 个模型项";
|
||
LogManager.Info($"[LayerManagementViewModel] 楼层属性设置完成,成功 {successCount}/{selectedItems.Count} 个");
|
||
|
||
// 刷新选中模型信息
|
||
await RefreshSelectionAsync();
|
||
}, "设置楼层属性");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除楼层属性 - 实现正确的业务逻辑与UI分离模式(参考ModelSettingsViewModel)
|
||
/// </summary>
|
||
private async Task ClearFloorAttributeAsync()
|
||
{
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
ShowCancelButton = false; // 清除楼层属性不支持取消
|
||
CurrentOperationText = "正在清除楼层属性...";
|
||
UpdateMainStatus("正在清除选中模型的楼层属性");
|
||
});
|
||
|
||
try
|
||
{
|
||
// 2. 纯业务逻辑执行(后台线程,不使用UIStateManager)
|
||
var result = await Task.Run<dynamic>(() =>
|
||
{
|
||
try
|
||
{
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document?.CurrentSelection?.SelectedItems == null || document.CurrentSelection.SelectedItems.Count == 0)
|
||
{
|
||
return new { Success = false, Count = 0, Message = "请先选择模型元素" };
|
||
}
|
||
|
||
// 使用成员变量 _floorAttributeManager
|
||
var selectedItems = document.CurrentSelection.SelectedItems.ToList();
|
||
var successCount = 0;
|
||
|
||
foreach (var item in selectedItems)
|
||
{
|
||
try
|
||
{
|
||
bool itemResult = _floorAttributeManager.ClearFloorAttribute(item);
|
||
if (itemResult)
|
||
{
|
||
successCount++;
|
||
}
|
||
}
|
||
catch (Exception itemEx)
|
||
{
|
||
LogManager.Warning($"清除模型项 {item.DisplayName} 的楼层属性失败:{itemEx.Message}");
|
||
}
|
||
}
|
||
|
||
return new
|
||
{
|
||
Success = successCount > 0,
|
||
Count = successCount,
|
||
TotalCount = selectedItems.Count,
|
||
Message = successCount > 0 ?
|
||
$"已清除 {successCount} 个模型项的楼层属性" :
|
||
"没有找到可清除的楼层属性"
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new { Success = false, Count = 0, TotalCount = 0, Message = $"清除属性失败: {ex.Message}" };
|
||
}
|
||
});
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentOperationText = result.Message;
|
||
UpdateMainStatus(result.Success ?
|
||
$"楼层属性清除完成,成功清除 {result.Count}/{result.TotalCount} 个模型项" :
|
||
"楼层属性清除失败");
|
||
|
||
if (result.Success)
|
||
{
|
||
// 清除成功,刷新选择状态
|
||
LogManager.Info($"[LayerManagementViewModel] 清除楼层属性成功,{result.Count}/{result.TotalCount} 个模型项");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] 清除楼层属性失败:{result.Message}");
|
||
}
|
||
});
|
||
|
||
// 4. 刷新选择状态
|
||
if (result.Success)
|
||
{
|
||
await RefreshSelectionAsync();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 清除楼层属性异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentOperationText = "清除楼层属性异常";
|
||
UpdateMainStatus($"清除失败: {ex.Message}");
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
// 5. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
ShowCancelButton = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查看楼専属性 - 不显示进度条的即时操作
|
||
/// </summary>
|
||
private async Task ViewFloorAttributeAsync()
|
||
{
|
||
try
|
||
{
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document?.CurrentSelection?.SelectedItems == null || document.CurrentSelection.SelectedItems.Count == 0)
|
||
{
|
||
// 使用UI线程更新状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
ShowFloorAttributeInfo = true;
|
||
CurrentFloorAttributeInfo = "未选择任何模型项,请先选择要查看的模型";
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 在后台线程执行业务逻辑
|
||
var result = await Task.Run<dynamic>(() =>
|
||
{
|
||
try
|
||
{
|
||
var selectedItems = document.CurrentSelection.SelectedItems.ToList();
|
||
var floorAttributeInfo = new List<string>();
|
||
|
||
foreach (var item in selectedItems.Take(10)) // 限制显示前10个,避免信息过多
|
||
{
|
||
try
|
||
{
|
||
string floorLevel = _floorAttributeManager.GetFloorProperty(item, "楼层");
|
||
string itemName = item.DisplayName ?? "未命名";
|
||
|
||
if (!string.IsNullOrEmpty(floorLevel))
|
||
{
|
||
floorAttributeInfo.Add($"✅ {itemName}: {floorLevel}");
|
||
}
|
||
else
|
||
{
|
||
floorAttributeInfo.Add($"❌ {itemName}: 未设置楼层属性");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
floorAttributeInfo.Add($"⚠️ {item.DisplayName}: 查询失败 - {ex.Message}");
|
||
}
|
||
}
|
||
|
||
if (selectedItems.Count > 10)
|
||
{
|
||
floorAttributeInfo.Add($"... 还有 {selectedItems.Count - 10} 个模型项未显示");
|
||
}
|
||
|
||
return new { Success = true, Info = string.Join("\n", floorAttributeInfo), Count = selectedItems.Count };
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new { Success = false, Info = $"查询失败: {ex.Message}", Count = 0 };
|
||
}
|
||
});
|
||
|
||
// 在UI线程更新结果
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentFloorAttributeInfo = result.Info;
|
||
ShowFloorAttributeInfo = true;
|
||
});
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 楼层属性查看完成,共 {result.Count} 个模型项");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 查看楼层属性异常: {ex.Message}", ex);
|
||
|
||
// 在UI线程显示错误信息
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentFloorAttributeInfo = $"查看操作异常: {ex.Message}";
|
||
ShowFloorAttributeInfo = true;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置分层属性
|
||
/// </summary>
|
||
private async Task SetLayerAttributeAsync()
|
||
{
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
if (string.IsNullOrWhiteSpace(SelectedParameterValue))
|
||
{
|
||
CurrentOperationText = "请输入参数值";
|
||
return;
|
||
}
|
||
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document?.CurrentSelection?.SelectedItems?.Count == 0)
|
||
{
|
||
CurrentOperationText = "请先选择要设置属性的模型对象";
|
||
return;
|
||
}
|
||
|
||
IsProcessing = true;
|
||
ShowCancelButton = false;
|
||
CurrentOperationText = "正在设置分层属性...";
|
||
var totalItems = document.CurrentSelection.SelectedItems.Count;
|
||
var processedItems = 0;
|
||
|
||
try
|
||
{
|
||
// 使用成员变量 _floorAttributeManager
|
||
var selectedItems = document.CurrentSelection.SelectedItems.ToList();
|
||
|
||
foreach (var item in selectedItems)
|
||
{
|
||
// 使用新的SetSingleLayerAttribute方法,只设置指定的属性类型
|
||
bool result = _floorAttributeManager.SetSingleLayerAttribute(item, SelectedLayerParameter, SelectedParameterValue);
|
||
|
||
if (result)
|
||
{
|
||
processedItems++;
|
||
}
|
||
|
||
ProgressPercentage = (double)processedItems / totalItems * 100;
|
||
}
|
||
|
||
CurrentOperationText = $"分层属性设置完成,成功设置 {processedItems}/{totalItems} 个对象的{SelectedLayerParameter}属性:{SelectedParameterValue}";
|
||
LogManager.Info($"[LayerManagementViewModel] 成功设置分层属性: {SelectedLayerParameter}={SelectedParameterValue},影响对象数:{processedItems}");
|
||
|
||
// 刷新选中模型信息
|
||
await RefreshSelectionAsync();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 设置分层属性异常: {ex.Message}", ex);
|
||
CurrentOperationText = $"设置分层属性时发生错误:{ex.Message}";
|
||
}
|
||
finally
|
||
{
|
||
IsProcessing = false;
|
||
ProgressPercentage = 0;
|
||
}
|
||
}, "设置分层属性");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除分层属性
|
||
/// </summary>
|
||
private async Task ClearLayerAttributeAsync()
|
||
{
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document?.CurrentSelection?.SelectedItems?.Count == 0)
|
||
{
|
||
CurrentOperationText = "请先选择要清除属性的模型对象";
|
||
return;
|
||
}
|
||
|
||
IsProcessing = true;
|
||
ShowCancelButton = false;
|
||
CurrentOperationText = "正在清除分层属性...";
|
||
var totalItems = document.CurrentSelection.SelectedItems.Count;
|
||
var processedItems = 0;
|
||
|
||
try
|
||
{
|
||
// 使用成员变量 _floorAttributeManager
|
||
var selectedItems = document.CurrentSelection.SelectedItems.ToList();
|
||
|
||
foreach (var item in selectedItems)
|
||
{
|
||
try
|
||
{
|
||
bool result = _floorAttributeManager.ClearFloorAttribute(item);
|
||
if (result)
|
||
{
|
||
processedItems++;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"清除模型 {item.DisplayName} 的分层属性失败:{ex.Message}");
|
||
}
|
||
|
||
ProgressPercentage = (double)(processedItems + 1) / totalItems * 100;
|
||
}
|
||
|
||
CurrentOperationText = $"分层属性清除完成,成功清除 {processedItems}/{totalItems} 个对象的分层属性";
|
||
LogManager.Info($"[LayerManagementViewModel] 成功清除分层属性,影响对象数:{processedItems}");
|
||
|
||
// 刷新选中模型信息
|
||
await RefreshSelectionAsync();
|
||
ShowLayerAttributeInfo = false;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 清除分层属性异常: {ex.Message}", ex);
|
||
CurrentOperationText = $"清除分层属性时发生错误:{ex.Message}";
|
||
}
|
||
finally
|
||
{
|
||
IsProcessing = false;
|
||
ProgressPercentage = 0;
|
||
}
|
||
}, "清除分层属性");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查看分层属性
|
||
/// </summary>
|
||
private async Task ViewLayerAttributeAsync()
|
||
{
|
||
try
|
||
{
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document?.CurrentSelection?.SelectedItems == null || document.CurrentSelection.SelectedItems.Count == 0)
|
||
{
|
||
// 使用UI线程更新状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
ShowLayerAttributeInfo = true;
|
||
CurrentLayerAttributeInfo = "未选择任何模型项,请先选择要查看的模型";
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 在后台线程执行业务逻辑
|
||
var result = await Task.Run<dynamic>(() =>
|
||
{
|
||
try
|
||
{
|
||
var selectedItems = document.CurrentSelection.SelectedItems.ToList();
|
||
var totalItems = selectedItems.Count;
|
||
var attributeInfo = new System.Text.StringBuilder();
|
||
|
||
attributeInfo.AppendLine($"选中对象总数:{totalItems}");
|
||
attributeInfo.AppendLine();
|
||
|
||
var hasAttributeCount = 0;
|
||
|
||
foreach (var item in selectedItems.Take(30)) // 增加显示数量到30个
|
||
{
|
||
try
|
||
{
|
||
string itemName = item.DisplayName ?? "未命名";
|
||
var itemAttributes = new List<string>();
|
||
|
||
// 获取楼层属性
|
||
var floorLevel = _floorAttributeManager.GetFloorProperty(item, "楼层");
|
||
if (!string.IsNullOrEmpty(floorLevel))
|
||
{
|
||
itemAttributes.Add($"楼层={floorLevel}");
|
||
}
|
||
|
||
// 获取区域属性
|
||
var zone = _floorAttributeManager.GetFloorProperty(item, "区域");
|
||
if (!string.IsNullOrEmpty(zone))
|
||
{
|
||
itemAttributes.Add($"区域={zone}");
|
||
}
|
||
|
||
// 获取子系统属性
|
||
var subsystem = _floorAttributeManager.GetFloorProperty(item, "子系统");
|
||
if (!string.IsNullOrEmpty(subsystem))
|
||
{
|
||
itemAttributes.Add($"子系统={subsystem}");
|
||
}
|
||
|
||
if (itemAttributes.Count > 0)
|
||
{
|
||
hasAttributeCount++;
|
||
attributeInfo.AppendLine($"✅ {itemName}: {string.Join(", ", itemAttributes)}");
|
||
}
|
||
else
|
||
{
|
||
attributeInfo.AppendLine($"❌ {itemName}: 无分层属性");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
attributeInfo.AppendLine($"⚠️ {item.DisplayName}: 查询失败 - {ex.Message}");
|
||
}
|
||
}
|
||
|
||
if (selectedItems.Count > 30)
|
||
{
|
||
attributeInfo.AppendLine($"... 还有 {selectedItems.Count - 30} 个模型项未显示");
|
||
}
|
||
|
||
return new { Success = true, Info = attributeInfo.ToString(), Count = totalItems, HasAttributeCount = hasAttributeCount };
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new { Success = false, Info = $"查询失败: {ex.Message}", Count = 0, HasAttributeCount = 0 };
|
||
}
|
||
});
|
||
|
||
// 在UI线程更新结果
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentLayerAttributeInfo = result.Info;
|
||
ShowLayerAttributeInfo = true;
|
||
});
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 分层属性查看完成,共 {result.Count} 个模型项,其中 {result.HasAttributeCount} 个有分层属性");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 查看分层属性异常: {ex.Message}", ex);
|
||
|
||
// 在UI线程显示错误信息
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentLayerAttributeInfo = $"查看操作异常: {ex.Message}";
|
||
ShowLayerAttributeInfo = true;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单独显示选中的分层
|
||
/// </summary>
|
||
private async Task IsolateSelectedLayerAsync()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("[LayerManagementViewModel] 开始执行单独显示分层");
|
||
|
||
if (SelectedPreviewResult == null)
|
||
{
|
||
LogManager.Warning("[LayerManagementViewModel] 未选择任何预览结果");
|
||
return;
|
||
}
|
||
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
CurrentOperationText = "正在准备单独显示...";
|
||
UpdateMainStatus($"隐藏除'{SelectedPreviewResult.LayerName}'之外的其他分层");
|
||
});
|
||
|
||
// 2. 在UI线程执行Navisworks API调用
|
||
bool isolateResult = false;
|
||
string resultMessage = "";
|
||
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
try
|
||
{
|
||
// 获取选中分层的模型项
|
||
if (SelectedPreviewResult.Items == null || SelectedPreviewResult.Items.Count == 0)
|
||
{
|
||
LogManager.Warning($"[LayerManagementViewModel] 选中分层 '{SelectedPreviewResult.LayerName}' 没有模型项");
|
||
isolateResult = false;
|
||
resultMessage = "选中的分层没有模型项";
|
||
return;
|
||
}
|
||
|
||
// 调用可见性管理器的公共工具函数
|
||
bool success = VisibilityHelper.IsolateSpecificItems(SelectedPreviewResult.Items);
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 单独显示执行结果: {success}");
|
||
isolateResult = success;
|
||
resultMessage = success ? "已隔离显示项目" : "隔离显示失败";
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 单独显示执行异常: {ex.Message}", ex);
|
||
isolateResult = false;
|
||
resultMessage = $"执行异常: {ex.Message}";
|
||
}
|
||
});
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
if (isolateResult)
|
||
{
|
||
CurrentOperationText = $"已单独显示: {SelectedPreviewResult.LayerName}";
|
||
UpdateMainStatus("其他分层已隐藏,您可以预览选中的分层内容");
|
||
}
|
||
else
|
||
{
|
||
CurrentOperationText = "单独显示失败";
|
||
UpdateMainStatus(resultMessage ?? "请检查日志了解详细信息");
|
||
}
|
||
});
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 单独显示分层完成: {isolateResult}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 单独显示分层异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentOperationText = "单独显示异常";
|
||
UpdateMainStatus($"操作失败: {ex.Message}");
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示所有分层(恢复可见性)
|
||
/// </summary>
|
||
private async Task ShowAllLayersAsync()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("[LayerManagementViewModel] 开始恢复所有分层显示");
|
||
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
CurrentOperationText = "正在恢复显示...";
|
||
UpdateMainStatus("恢复所有分层的可见性");
|
||
});
|
||
|
||
// 2. 在UI线程执行Navisworks API调用
|
||
bool restoreResult = false;
|
||
string resultMessage = "";
|
||
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
try
|
||
{
|
||
// 调用可见性管理器的公共工具函数
|
||
bool success = VisibilityHelper.ShowAllItems();
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 恢复显示执行结果: {success}");
|
||
restoreResult = success;
|
||
resultMessage = success ? "所有项目已显示" : "显示失败";
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 恢复显示执行异常: {ex.Message}", ex);
|
||
restoreResult = false;
|
||
resultMessage = $"执行异常: {ex.Message}";
|
||
}
|
||
});
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
if (restoreResult)
|
||
{
|
||
CurrentOperationText = "已恢复所有分层显示";
|
||
UpdateMainStatus("所有分层内容都已可见");
|
||
}
|
||
else
|
||
{
|
||
CurrentOperationText = "恢复显示失败";
|
||
UpdateMainStatus(resultMessage ?? "请检查日志了解详细信息");
|
||
}
|
||
});
|
||
|
||
LogManager.Info($"[LayerManagementViewModel] 恢复所有分层显示完成: {restoreResult}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[LayerManagementViewModel] 恢复所有分层显示异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CurrentOperationText = "恢复显示异常";
|
||
UpdateMainStatus($"操作失败: {ex.Message}");
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
}
|
||
} |