1688 lines
64 KiB
C#
1688 lines
64 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using System.Windows.Input;
|
||
using Autodesk.Navisworks.Api;
|
||
using NavisApplication = Autodesk.Navisworks.Api.Application;
|
||
using NavisworksTransport.Core;
|
||
using NavisworksTransport.Core.Config;
|
||
using NavisworksTransport.UI.WPF.Collections;
|
||
using NavisworksTransport.UI.WPF.Models;
|
||
using NavisworksTransport.Utils;
|
||
|
||
namespace NavisworksTransport.UI.WPF.ViewModels
|
||
{
|
||
/// <summary>
|
||
/// 类别设置页签的ViewModel - 基于分层管理页签的设计风格
|
||
///
|
||
/// 功能特点:
|
||
/// 1. 参考分层管理页签的布局风格和UI组织方式
|
||
/// 2. 将模型选择提示移动到类别属性区域中
|
||
/// 3. 提供完整的物流属性设置功能
|
||
/// 4. 支持批量属性设置和单个模型编辑
|
||
/// 5. 使用MVVM架构和线程安全的UI更新
|
||
/// </summary>
|
||
public class ModelSettingsViewModel : ViewModelBase, IDisposable
|
||
{
|
||
#region 私有字段和依赖注入
|
||
|
||
private readonly UIStateManager _uiStateManager;
|
||
|
||
// 选择事件订阅管理器
|
||
private SelectionEventSubscription _selectionEventSubscription;
|
||
|
||
// 文档模型集合变化事件订阅标志
|
||
private bool _modelsCollectionEventSubscribed;
|
||
|
||
// 资源释放状态标志
|
||
private bool _disposed;
|
||
|
||
// 上次模型数量,用于检测模型加载完成
|
||
private int _lastModelsCount = -1;
|
||
|
||
#endregion
|
||
|
||
#region 状态字段
|
||
|
||
private bool _isProcessing;
|
||
private string _selectedModelsText;
|
||
private string _selectedCategory;
|
||
private bool _isTraversable;
|
||
private int _priority;
|
||
private double _widthLimit;
|
||
private double _heightLimit;
|
||
private double _speedLimit;
|
||
private bool _isLogisticsOnlyMode;
|
||
private LogisticsModel _selectedLogisticsModel;
|
||
private string _selectedCategoryFilter = "全部";
|
||
|
||
|
||
#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 SelectedModelsText
|
||
{
|
||
get => _selectedModelsText;
|
||
set => SetPropertyThreadSafe(ref _selectedModelsText, value);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 可用类别集合 - 使用线程安全集合
|
||
/// </summary>
|
||
public ThreadSafeObservableCollection<string> AvailableCategories { get; } =
|
||
new ThreadSafeObservableCollection<string>();
|
||
|
||
/// <summary>
|
||
/// 选中的类别
|
||
/// </summary>
|
||
public string SelectedCategory
|
||
{
|
||
get => _selectedCategory;
|
||
set
|
||
{
|
||
if (SetPropertyThreadSafe(ref _selectedCategory, value))
|
||
{
|
||
OnPropertyChanged(nameof(CanSetLogisticsAttribute));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可通行
|
||
/// </summary>
|
||
public bool IsTraversable
|
||
{
|
||
get => _isTraversable;
|
||
set => SetPropertyThreadSafe(ref _isTraversable, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 优先级列表 - 使用线程安全集合
|
||
/// </summary>
|
||
public ThreadSafeObservableCollection<int> PriorityLevels { get; } =
|
||
new ThreadSafeObservableCollection<int> { 1, 2, 3, 4, 5 };
|
||
|
||
/// <summary>
|
||
/// 优先级(1-5级,5为最高)
|
||
/// </summary>
|
||
public int Priority
|
||
{
|
||
get => _priority;
|
||
set => SetPropertyThreadSafe(ref _priority, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 宽度限制(米)
|
||
/// </summary>
|
||
public double WidthLimit
|
||
{
|
||
get => _widthLimit;
|
||
set => SetPropertyThreadSafe(ref _widthLimit, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 高度限制(米)
|
||
/// </summary>
|
||
public double HeightLimit
|
||
{
|
||
get => _heightLimit;
|
||
set => SetPropertyThreadSafe(ref _heightLimit, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 限速(米/秒)
|
||
/// </summary>
|
||
public double SpeedLimit
|
||
{
|
||
get => _speedLimit;
|
||
set => SetPropertyThreadSafe(ref _speedLimit, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可以设置物流属性
|
||
/// </summary>
|
||
public bool CanSetLogisticsAttribute =>
|
||
!string.IsNullOrEmpty(SelectedCategory) &&
|
||
!IsProcessing &&
|
||
HasSelectedModels;
|
||
/// <summary>
|
||
/// 是否可以清除物流属性
|
||
/// </summary>
|
||
public bool CanClearLogisticsAttribute =>
|
||
!IsProcessing &&
|
||
HasSelectedModels &&
|
||
HasSelectedModelsWithLogisticsAttributes;
|
||
|
||
/// <summary>
|
||
/// 是否有选中的模型
|
||
/// </summary>
|
||
public bool HasSelectedModels
|
||
{
|
||
get
|
||
{
|
||
try
|
||
{
|
||
var document = NavisApplication.ActiveDocument;
|
||
return document?.CurrentSelection?.SelectedItems?.Count > 0;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 选中的模型是否包含物流属性
|
||
/// </summary>
|
||
public bool HasSelectedModelsWithLogisticsAttributes
|
||
{
|
||
get
|
||
{
|
||
try
|
||
{
|
||
var document = NavisApplication.ActiveDocument;
|
||
var selectedItems = document?.CurrentSelection?.SelectedItems;
|
||
if (selectedItems == null || selectedItems.Count == 0)
|
||
return false;
|
||
|
||
// 检查是否至少有一个选中的模型具有物流属性
|
||
foreach (var item in selectedItems)
|
||
{
|
||
if (CategoryAttributeManager.HasLogisticsAttributes(item))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 物流模型集合 - 使用线程安全集合
|
||
/// </summary>
|
||
public ThreadSafeObservableCollection<LogisticsModel> LogisticsModels { get; } =
|
||
new ThreadSafeObservableCollection<LogisticsModel>();
|
||
|
||
/// <summary>
|
||
/// 选中的物流模型
|
||
/// </summary>
|
||
/// <summary>
|
||
/// 选中的物流模型
|
||
/// </summary>
|
||
public LogisticsModel SelectedLogisticsModel
|
||
{
|
||
get => _selectedLogisticsModel;
|
||
set
|
||
{
|
||
if (SetPropertyThreadSafe(ref _selectedLogisticsModel, value))
|
||
{
|
||
// 选中Navisworks中对应的模型
|
||
SelectModelInNavisworks(value);
|
||
|
||
// 回填属性值到UI
|
||
LoadModelAttributes(value);
|
||
|
||
// 高亮并聚焦到选中的模型
|
||
if (value?.NavisworksItem != null)
|
||
{
|
||
FocusOnLogisticsModel(value);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 可用类别筛选选项 - 从所有可用类别动态获取
|
||
/// 注意:不包含"全部","全部"是UI层面的特殊选项
|
||
/// </summary>
|
||
public ThreadSafeObservableCollection<string> AvailableCategoryFilters { get; } =
|
||
new ThreadSafeObservableCollection<string>();
|
||
|
||
/// <summary>
|
||
/// 当前选中的类别筛选条件
|
||
/// </summary>
|
||
public string SelectedCategoryFilter
|
||
{
|
||
get => _selectedCategoryFilter;
|
||
set
|
||
{
|
||
if (SetPropertyThreadSafe(ref _selectedCategoryFilter, value))
|
||
{
|
||
// 筛选条件变化时,触发筛选列表刷新
|
||
OnPropertyChanged(nameof(FilteredLogisticsModels));
|
||
LogManager.Info($"[ModelSettingsViewModel] 物流模型筛选条件已切换为: {value}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 筛选后的物流模型列表
|
||
/// </summary>
|
||
public System.Collections.Generic.List<LogisticsModel> FilteredLogisticsModels
|
||
{
|
||
get
|
||
{
|
||
if (SelectedCategoryFilter == "全部" || string.IsNullOrEmpty(SelectedCategoryFilter))
|
||
{
|
||
return new System.Collections.Generic.List<LogisticsModel>(LogisticsModels);
|
||
}
|
||
|
||
return System.Linq.Enumerable.ToList(
|
||
System.Linq.Enumerable.Where(LogisticsModels, m => m.Category == SelectedCategoryFilter));
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 加载模型属性并回填到UI
|
||
/// </summary>
|
||
/// <param name="logisticsModel">选中的物流模型</param>
|
||
/// <summary>
|
||
/// 加载模型属性并回填到UI
|
||
/// </summary>
|
||
/// <param name="logisticsModel">选中的物流模型</param>
|
||
private void LoadModelAttributes(LogisticsModel logisticsModel)
|
||
{
|
||
if (logisticsModel?.NavisworksItem == null)
|
||
{
|
||
// 正常情况:清除选择时传入null,无需处理
|
||
return;
|
||
}
|
||
|
||
// 从Navisworks项中获取物流属性信息
|
||
var info = CategoryAttributeManager.GetLogisticsAttributeInfo(logisticsModel.NavisworksItem);
|
||
if (info == null)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 异常:列表中的模型 {logisticsModel.Name} 没有物流属性信息!");
|
||
return;
|
||
}
|
||
|
||
// 验证物流类型
|
||
if (string.IsNullOrEmpty(info.ElementType))
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 异常:模型 {logisticsModel.Name} 的ElementType为空!");
|
||
return;
|
||
}
|
||
|
||
// 将类别ID转换为显示名称
|
||
string displayName = GetDisplayNameByCategoryId(info.ElementType);
|
||
if (string.IsNullOrEmpty(displayName))
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 异常:模型 {logisticsModel.Name} 的ElementType '{info.ElementType}' 无法找到对应的类别!");
|
||
return;
|
||
}
|
||
|
||
if (!AvailableCategories.Contains(displayName))
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 异常:模型 {logisticsModel.Name} 的类别显示名称 '{displayName}' 不在可用类别中!");
|
||
return;
|
||
}
|
||
|
||
// 验证优先级
|
||
if (info.Priority < 1 || info.Priority > 5)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 异常:模型 {logisticsModel.Name} 的优先级 {info.Priority} 超出有效范围(1-5)!");
|
||
return;
|
||
}
|
||
|
||
// 验证高度限制
|
||
if (info.HeightLimit <= 0)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 异常:模型 {logisticsModel.Name} 的高度限制 {info.HeightLimit} 无效!");
|
||
return;
|
||
}
|
||
|
||
// 验证速度限制
|
||
if (info.SpeedLimit <= 0)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 异常:模型 {logisticsModel.Name} 的速度限制 {info.SpeedLimit} 无效!");
|
||
return;
|
||
}
|
||
|
||
// 验证宽度限制
|
||
if (info.WidthLimit <= 0)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 异常:模型 {logisticsModel.Name} 的宽度限制 {info.WidthLimit} 无效!");
|
||
return;
|
||
}
|
||
|
||
// 所有验证通过,回填属性(使用显示名称)
|
||
SelectedCategory = displayName;
|
||
IsTraversable = info.IsTraversable;
|
||
Priority = info.Priority;
|
||
HeightLimit = info.HeightLimit;
|
||
SpeedLimit = info.SpeedLimit;
|
||
WidthLimit = info.WidthLimit;
|
||
|
||
LogManager.Info($"[ModelSettingsViewModel] 已回填模型属性: {logisticsModel.Name}, 类型: {info.ElementType}, 可通行: {info.IsTraversable}, 优先级: {info.Priority}, 高度: {info.HeightLimit}m, 速度: {info.SpeedLimit}m/s, 宽度: {info.WidthLimit}m");
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 是否处于仅显示物流模式
|
||
/// </summary>
|
||
public bool IsLogisticsOnlyMode
|
||
{
|
||
get => _isLogisticsOnlyMode;
|
||
set
|
||
{
|
||
if (SetPropertyThreadSafe(ref _isLogisticsOnlyMode, value))
|
||
{
|
||
// 当开关状态改变时,自动应用可见性设置
|
||
ApplyVisibilityMode();
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
#endregion
|
||
|
||
#region 命令 - 使用统一的Command Pattern
|
||
|
||
public ICommand RefreshSelectionCommand { get; private set; }
|
||
public ICommand SetLogisticsAttributeCommand { get; private set; }
|
||
public ICommand ClearLogisticsAttributeCommand { get; private set; }
|
||
public ICommand RefreshLogisticsModelsCommand { get; private set; }
|
||
public ICommand ResetToDefaultsCommand { get; private set; }
|
||
public ICommand ToggleModelVisibilityCommand { get; private set; }
|
||
|
||
#endregion
|
||
|
||
#region 构造函数 - 使用依赖注入和统一架构
|
||
|
||
public ModelSettingsViewModel(LogisticsControlViewModel mainViewModel = null) : base()
|
||
{
|
||
try
|
||
{
|
||
// 从配置文件初始化默认值(必须在UI绑定之前完成)
|
||
var config = ConfigManager.Instance.Current;
|
||
_selectedModelsText = "请在主界面中选择需要设置的模型";
|
||
_selectedCategory = "通道";
|
||
_isTraversable = config.Logistics.Traversable;
|
||
_priority = config.Logistics.Priority;
|
||
_widthLimit = config.Logistics.WidthLimitMeters;
|
||
_heightLimit = config.Logistics.HeightLimitMeters;
|
||
_speedLimit = config.Logistics.SpeedLimitMetersPerSecond;
|
||
_isLogisticsOnlyMode = false;
|
||
|
||
// 获取UI状态管理器实例和设置主ViewModel引用到基类
|
||
_uiStateManager = UIStateManager.Instance;
|
||
SetMainViewModel(mainViewModel);
|
||
|
||
// 验证关键组件是否正常初始化
|
||
if (_uiStateManager == null)
|
||
{
|
||
LogManager.Error("UIStateManager初始化失败");
|
||
throw new InvalidOperationException("UIStateManager初始化失败");
|
||
}
|
||
|
||
// 初始化命令
|
||
InitializeCommands();
|
||
|
||
// 订阅配置变更事件
|
||
SubscribeToConfigChanges();
|
||
|
||
// 订阅Navisworks选择变化事件 - 使用新的选择管理服务
|
||
SubscribeToSelectionEvents();
|
||
|
||
// 订阅文档模型集合变化事件
|
||
SubscribeToDocumentModelsEvents();
|
||
|
||
// 异步初始化
|
||
InitializeAsync();
|
||
|
||
LogManager.Info("ModelSettingsViewModel构造函数执行完成 - 使用统一UI架构");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"ModelSettingsViewModel构造函数异常: {ex.Message}", ex);
|
||
|
||
UpdateMainStatus("初始化失败,请检查日志");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 配置变更处理
|
||
|
||
/// <summary>
|
||
/// 订阅配置变更事件
|
||
/// </summary>
|
||
private void SubscribeToConfigChanges()
|
||
{
|
||
try
|
||
{
|
||
ConfigManager.Instance.CategoryConfigurationChanged += OnCategoryConfigurationChanged;
|
||
LogManager.Info("ModelSettingsViewModel 已订阅配置变更事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"订阅配置变更事件失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消订阅配置变更事件
|
||
/// </summary>
|
||
private void UnsubscribeFromConfigChanges()
|
||
{
|
||
try
|
||
{
|
||
ConfigManager.Instance.CategoryConfigurationChanged -= OnCategoryConfigurationChanged;
|
||
LogManager.Info("ModelSettingsViewModel 已取消订阅配置变更事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"取消订阅配置变更事件时发生异常: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置变更事件处理
|
||
/// </summary>
|
||
private void OnCategoryConfigurationChanged(object sender, Tuple<ConfigCategory, ConfigurationChangedEventArgs> args)
|
||
{
|
||
try
|
||
{
|
||
var category = args.Item1;
|
||
var eventArgs = args.Item2;
|
||
|
||
// 处理 Logistics 类别变更
|
||
if (category == ConfigCategory.Logistics || category == ConfigCategory.All)
|
||
{
|
||
LogManager.Info("收到 Logistics 配置变更通知,正在更新默认属性值...");
|
||
|
||
var config = ConfigManager.Instance.Current;
|
||
|
||
// 更新默认属性值(仅当用户未选择模型时)
|
||
SafeExecute(() =>
|
||
{
|
||
// 如果当前没有选择模型,更新默认值
|
||
if (!HasSelectedModels)
|
||
{
|
||
IsTraversable = config.Logistics.Traversable;
|
||
Priority = config.Logistics.Priority;
|
||
WidthLimit = config.Logistics.WidthLimitMeters;
|
||
HeightLimit = config.Logistics.HeightLimitMeters;
|
||
SpeedLimit = config.Logistics.SpeedLimitMetersPerSecond;
|
||
|
||
string source = eventArgs.IsExternalChange ? "外部修改" : "内部更新";
|
||
UpdateMainStatus($"默认属性已更新 ({source})");
|
||
LogManager.Info($"默认物流属性已更新 - 来源: {source}");
|
||
}
|
||
});
|
||
}
|
||
|
||
// 处理自定义类别变更 - 刷新可用类别列表
|
||
if (category == ConfigCategory.All)
|
||
{
|
||
LogManager.Info("收到配置变更通知,正在更新可用类别列表...");
|
||
|
||
SafeExecute(() =>
|
||
{
|
||
// 重新加载自定义类别
|
||
CategoryAttributeManager.ReloadCustomCategories();
|
||
|
||
// 刷新可用类别列表
|
||
RefreshAvailableCategories();
|
||
|
||
string source = eventArgs.IsExternalChange ? "外部修改" : "内部更新";
|
||
UpdateMainStatus($"类别列表已更新 ({source})");
|
||
LogManager.Info($"可用类别列表已更新 - 来源: {source}, 当前类别数: {AvailableCategories.Count}");
|
||
});
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"处理配置变更事件失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新可用类别列表
|
||
/// </summary>
|
||
private void RefreshAvailableCategories()
|
||
{
|
||
_uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
// 保存当前选中的类别
|
||
string previousSelectedCategory = SelectedCategory;
|
||
string previousSelectedFilter = SelectedCategoryFilter;
|
||
|
||
// 清空集合
|
||
AvailableCategories.Clear();
|
||
AvailableCategoryFilters.Clear();
|
||
|
||
// 先添加"全部"选项到筛选器
|
||
AvailableCategoryFilters.Add("全部");
|
||
|
||
// 从 CategoryAttributeManager 获取所有可用类别(内置 + 自定义)
|
||
var allCategories = CategoryAttributeManager.GetAllAvailableCategories().ToList();
|
||
|
||
foreach (var cat in allCategories)
|
||
{
|
||
AvailableCategories.Add(cat.DisplayName);
|
||
AvailableCategoryFilters.Add(cat.DisplayName);
|
||
}
|
||
|
||
// 恢复之前的选择(如果仍然存在)
|
||
if (!string.IsNullOrEmpty(previousSelectedCategory) &&
|
||
AvailableCategories.Contains(previousSelectedCategory))
|
||
{
|
||
SelectedCategory = previousSelectedCategory;
|
||
}
|
||
else if (AvailableCategories.Count > 0)
|
||
{
|
||
// 默认选择"通道"或第一个类别
|
||
SelectedCategory = AvailableCategories.Contains("通道") ? "通道" : AvailableCategories[0];
|
||
}
|
||
|
||
// 恢复筛选器选择
|
||
if (!string.IsNullOrEmpty(previousSelectedFilter) &&
|
||
AvailableCategoryFilters.Contains(previousSelectedFilter))
|
||
{
|
||
SelectedCategoryFilter = previousSelectedFilter;
|
||
}
|
||
else
|
||
{
|
||
SelectedCategoryFilter = "全部";
|
||
}
|
||
}).ConfigureAwait(false);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 命令初始化 - 统一的Command Pattern
|
||
|
||
/// <summary>
|
||
/// 初始化命令(使用Command Pattern框架)
|
||
/// </summary>
|
||
private void InitializeCommands()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
RefreshSelectionCommand = new RelayCommand(
|
||
async () => await RefreshSelectionAsync(),
|
||
() => IsNotProcessing);
|
||
|
||
SetLogisticsAttributeCommand = new RelayCommand(
|
||
async () => await SetLogisticsAttributeAsync(),
|
||
() => CanSetLogisticsAttribute);
|
||
|
||
ClearLogisticsAttributeCommand = new RelayCommand(
|
||
async () => await ClearLogisticsAttributeAsync(),
|
||
() => CanClearLogisticsAttribute);
|
||
|
||
RefreshLogisticsModelsCommand = new RelayCommand(
|
||
async () => await RefreshLogisticsModelsAsync(),
|
||
() => IsNotProcessing);
|
||
|
||
ResetToDefaultsCommand = new RelayCommand(
|
||
async () => await ResetToDefaultsAsync(),
|
||
() => IsNotProcessing);
|
||
|
||
ToggleModelVisibilityCommand = new RelayCommand<LogisticsModel>(
|
||
async (model) => await ToggleModelVisibilityAsync(model),
|
||
(model) => model != null && IsNotProcessing);
|
||
|
||
LogManager.Info("类别设置命令初始化完成 - 使用统一Command Pattern框架");
|
||
}, "初始化命令");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 业务逻辑方法 - 使用统一的UIStateManager
|
||
|
||
/// <summary>
|
||
/// 刷新选择状态 - 使用新的选择管理服务
|
||
/// </summary>
|
||
private async Task RefreshSelectionAsync()
|
||
{
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
UpdateMainStatus("正在检查模型选择...", -1, true);
|
||
});
|
||
|
||
try
|
||
{
|
||
// 2. 使用新的选择管理服务获取选择状态(后台线程)
|
||
var selectionResult = await NavisworksSelectionHelper.GetCurrentSelectionStateAsync();
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
if (selectionResult.Success)
|
||
{
|
||
SelectedModelsText = NavisworksSelectionHelper.FormatSelectionText(selectionResult, "个模型");
|
||
UpdateMainStatus("检查完成");
|
||
}
|
||
else
|
||
{
|
||
SelectedModelsText = selectionResult.ErrorMessage ?? "检查选择状态失败";
|
||
UpdateMainStatus("检查失败");
|
||
}
|
||
|
||
// 🔧 修复:刷新所有与选择相关的命令状态(包括清除属性按钮)
|
||
OnPropertyChanged(nameof(HasSelectedModels));
|
||
OnPropertyChanged(nameof(HasSelectedModelsWithLogisticsAttributes));
|
||
OnPropertyChanged(nameof(CanSetLogisticsAttribute));
|
||
OnPropertyChanged(nameof(CanClearLogisticsAttribute));
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 刷新选择异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
SelectedModelsText = "检查选择状态异常";
|
||
UpdateMainStatus($"检查失败: {ex.Message}");
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置物流属性 - 实现正确的业务逻辑与UI分离模式
|
||
/// </summary>
|
||
private async Task SetLogisticsAttributeAsync()
|
||
{
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
UpdateMainStatus("正在设置物流属性...", -1, true);
|
||
});
|
||
|
||
|
||
try
|
||
{
|
||
// 2. 业务逻辑执行(必须在主线程上执行,因为包含 Navisworks COM API 调用)
|
||
var result = NavisworksApiHelper.ExecuteOnUIThread(() =>
|
||
{
|
||
try
|
||
{
|
||
var selectedItems = NavisApplication.ActiveDocument?.CurrentSelection?.SelectedItems;
|
||
if (selectedItems == null || selectedItems.Count == 0)
|
||
{
|
||
return new { Success = false, Count = 0, Message = "请先选择模型元素" };
|
||
}
|
||
|
||
// 根据显示名称查找类别ID
|
||
string categoryId = GetCategoryIdByDisplayName(SelectedCategory);
|
||
if (string.IsNullOrEmpty(categoryId))
|
||
{
|
||
return new { Success = false, Count = 0, Message = $"无效的物流类别: {SelectedCategory}" };
|
||
}
|
||
|
||
int successCount = CategoryAttributeManager.AddLogisticsAttributes(
|
||
selectedItems,
|
||
categoryId,
|
||
isTraversable: IsTraversable,
|
||
priority: Priority,
|
||
heightLimit: HeightLimit,
|
||
speedLimit: SpeedLimit,
|
||
widthLimit: WidthLimit);
|
||
|
||
return new {
|
||
Success = true,
|
||
Count = successCount,
|
||
Message = $"已为 {successCount} 个元素设置物流属性: {SelectedCategory}"
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new { Success = false, Count = 0, Message = $"设置属性失败: {ex.Message}" };
|
||
}
|
||
});
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
UpdateMainStatus(result.Message);
|
||
|
||
if (result.Success)
|
||
{
|
||
LogManager.Info($"设置物流属性成功: {result.Message}");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"设置物流属性失败: {result.Message}");
|
||
}
|
||
});
|
||
|
||
|
||
// 如果设置成功,异步刷新物流模型列表
|
||
if (result.Success)
|
||
{
|
||
await RefreshLogisticsModelsAsync();
|
||
|
||
// 如果当前处于仅显示物流模式,重新应用可见性设置
|
||
if (IsLogisticsOnlyMode)
|
||
{
|
||
// 重要:ApplyVisibilityMode包含Navisworks API调用,必须在主线程中执行
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() => ApplyVisibilityMode());
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 设置物流属性异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
UpdateMainStatus($"设置属性出错: {ex.Message}");
|
||
});
|
||
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
});
|
||
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除物流属性 - 实现正确的业务逻辑与UI分离模式
|
||
/// </summary>
|
||
private async Task ClearLogisticsAttributeAsync()
|
||
{
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
UpdateMainStatus("正在清除物流属性...", -1, true);
|
||
});
|
||
|
||
try
|
||
{
|
||
// 2. 业务逻辑执行(必须在主线程上执行,因为包含 Navisworks COM API 调用)
|
||
var result = await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
try
|
||
{
|
||
var selectedItems = NavisApplication.ActiveDocument?.CurrentSelection?.SelectedItems;
|
||
if (selectedItems == null || selectedItems.Count == 0)
|
||
{
|
||
return new { Success = false, Count = 0, Message = "请先选择模型元素" };
|
||
}
|
||
|
||
// 使用CategoryAttributeManager的RemoveLogisticsAttributes方法
|
||
int successCount = CategoryAttributeManager.RemoveLogisticsAttributes(selectedItems);
|
||
|
||
return new {
|
||
Success = successCount > 0,
|
||
Count = successCount,
|
||
Message = successCount > 0 ?
|
||
$"已清除 {successCount} 个元素的物流属性" :
|
||
"没有找到可清除的物流属性"
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new { Success = false, Count = 0, Message = $"清除属性失败: {ex.Message}" };
|
||
}
|
||
});
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
UpdateMainStatus(result.Message);
|
||
|
||
if (result.Success)
|
||
{
|
||
LogManager.Info($"清除物流属性成功: {result.Message}");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"清除物流属性失败: {result.Message}");
|
||
}
|
||
});
|
||
|
||
// 如果清除成功,异步刷新物流模型列表
|
||
if (result.Success)
|
||
{
|
||
await RefreshLogisticsModelsAsync();
|
||
|
||
// 如果当前处于仅显示物流模式,重新应用可见性设置
|
||
if (IsLogisticsOnlyMode)
|
||
{
|
||
// 重要:ApplyVisibilityMode包含Navisworks API调用,必须在主线程中执行
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() => ApplyVisibilityMode());
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 清除物流属性异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
UpdateMainStatus($"清除属性出错: {ex.Message}");
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新物流模型列表 - 实现正确的业务逻辑与UI分离模式
|
||
/// </summary>
|
||
private async Task RefreshLogisticsModelsAsync()
|
||
{
|
||
// 1. 初始UI状态更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
UpdateMainStatus("正在刷新物流模型列表...", -1, true);
|
||
});
|
||
|
||
try
|
||
{
|
||
// 2. 业务逻辑执行(必须在主线程上执行,因为包含 Navisworks COM API 调用)
|
||
var result = await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
try
|
||
{
|
||
// 直接使用 CategoryAttributeManager 的 API
|
||
var document = NavisApplication.ActiveDocument;
|
||
var logisticsItems = CategoryAttributeManager.GetAllLogisticsItems();
|
||
var models = new List<LogisticsModel>();
|
||
|
||
foreach (var item in logisticsItems)
|
||
{
|
||
// 直接使用 CategoryAttributeManager 的 API
|
||
var info = CategoryAttributeManager.GetLogisticsAttributeInfo(item);
|
||
|
||
// 将类别ID转换为显示名称
|
||
string categoryId = info?.ElementType ?? "未分类";
|
||
string displayName = categoryId;
|
||
if (categoryId != "未分类")
|
||
{
|
||
var categoryDef = CategoryAttributeManager.GetCategoryDefinition(categoryId);
|
||
if (categoryDef != null)
|
||
{
|
||
displayName = categoryDef.DisplayName;
|
||
}
|
||
}
|
||
|
||
var model = new LogisticsModel
|
||
{
|
||
Name = item.DisplayName ?? "未命名模型",
|
||
Category = displayName,
|
||
Attributes = FormatLogisticsAttributes(info),
|
||
IsVisible = !item.IsHidden,
|
||
NavisworksItem = item
|
||
};
|
||
models.Add(model);
|
||
}
|
||
|
||
return new { Success = true, Models = models, Count = models.Count };
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"刷新物流模型列表异常: {ex.Message}", ex);
|
||
return new { Success = false, Models = new List<LogisticsModel>(), Count = 0 };
|
||
}
|
||
});
|
||
|
||
// 3. 结果UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
LogisticsModels.Clear();
|
||
|
||
if (result.Success)
|
||
{
|
||
foreach (var model in result.Models)
|
||
{
|
||
LogisticsModels.Add(model);
|
||
}
|
||
// 触发筛选列表刷新
|
||
OnPropertyChanged(nameof(FilteredLogisticsModels));
|
||
UpdateMainStatus($"已刷新物流模型列表,共 {result.Count} 个模型");
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus("刷新物流模型列表失败");
|
||
}
|
||
});
|
||
|
||
LogManager.Info($"已刷新所有物流模型列表,共 {result.Count} 个模型");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 刷新物流模型异常: {ex.Message}", ex);
|
||
|
||
// 异常UI更新
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
LogisticsModels.Clear();
|
||
OnPropertyChanged(nameof(FilteredLogisticsModels));
|
||
UpdateMainStatus($"刷新列表出错: {ex.Message}");
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
// 4. 清理UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重置为默认值 - 从配置文件读取默认值
|
||
/// </summary>
|
||
private async Task ResetToDefaultsAsync()
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
var config = ConfigManager.Instance.Current;
|
||
|
||
SelectedCategory = "通道";
|
||
IsTraversable = config.Logistics.Traversable;
|
||
Priority = config.Logistics.Priority;
|
||
WidthLimit = config.Logistics.WidthLimitMeters;
|
||
HeightLimit = config.Logistics.HeightLimitMeters;
|
||
SpeedLimit = config.Logistics.SpeedLimitMetersPerSecond;
|
||
|
||
UpdateMainStatus("已重置为默认值(来自配置)");
|
||
LogManager.Info($"已重置类别设置为默认值 - 可通行:{IsTraversable}, 优先级:{Priority}, 宽度:{WidthLimit:F1}m, 高度:{HeightLimit:F1}m, 速度:{SpeedLimit:F1}m/s");
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 切换模型可见性
|
||
/// </summary>
|
||
private async Task ToggleModelVisibilityAsync(LogisticsModel model)
|
||
{
|
||
if (model?.NavisworksItem == null) return;
|
||
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = true;
|
||
});
|
||
|
||
try
|
||
{
|
||
// 重要:Navisworks API 操作必须在主线程中执行,不能在Task.Run中
|
||
var newVisibility = !model.IsVisible;
|
||
|
||
// 使用VisibilityHelper来切换单个模型的可见性
|
||
var itemCollection = new ModelItemCollection { model.NavisworksItem };
|
||
bool success = VisibilityHelper.SetItemsVisibility(itemCollection, !newVisibility);
|
||
|
||
// 更新UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
if (success)
|
||
{
|
||
model.IsVisible = newVisibility;
|
||
var action = newVisibility ? "显示" : "隐藏";
|
||
UpdateMainStatus($"已{action}模型: {model.Name}");
|
||
LogManager.Info($"成功{action}模型: {model.Name}");
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus($"切换模型可见性失败: {model.Name}");
|
||
LogManager.Error($"切换模型可见性失败: {model.Name}");
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"ToggleModelVisibilityAsync异常: {ex.Message}", ex);
|
||
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
UpdateMainStatus($"切换可见性异常: {ex.Message}");
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsProcessing = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 在Navisworks中选择对应的模型
|
||
/// </summary>
|
||
private void SelectModelInNavisworks(LogisticsModel model)
|
||
{
|
||
// 重要:根据示例代码,选择操作必须在主线程中执行,不能在Task.Run中
|
||
try
|
||
{
|
||
if (model?.NavisworksItem == null)
|
||
{
|
||
// 如果没有选中模型,清除Navisworks选择
|
||
bool success = NavisworksSelectionHelper.SetModelSelection((ModelItem)null);
|
||
if (success)
|
||
{
|
||
LogManager.Info("已清除Navisworks选择");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning("清除Navisworks选择失败");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 选择指定的模型项
|
||
bool success = NavisworksSelectionHelper.SetModelSelection(model.NavisworksItem);
|
||
if (success)
|
||
{
|
||
LogManager.Info($"已在Navisworks中选择模型: {model.Name}");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"在Navisworks中选择模型失败: {model.Name}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"在Navisworks中选择模型失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
// 选择状态格式化方法已移至NavisworksSelectionHelper中
|
||
|
||
#region 场景加载完成事件处理
|
||
|
||
/// <summary>
|
||
/// 订阅场景加载完成事件
|
||
/// </summary>
|
||
private void SubscribeToDocumentModelsEvents()
|
||
{
|
||
try
|
||
{
|
||
var document = NavisApplication.ActiveDocument;
|
||
if (document?.Models != null && !_modelsCollectionEventSubscribed)
|
||
{
|
||
document.Models.SceneLoaded += OnSceneLoaded;
|
||
document.Models.CollectionChanged += OnModelsCollectionChanged;
|
||
_modelsCollectionEventSubscribed = true;
|
||
LogManager.Info("[ModelSettingsViewModel] 已订阅SceneLoaded和CollectionChanged事件");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 订阅SceneLoaded事件失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消场景加载完成事件
|
||
/// </summary>
|
||
private void UnsubscribeFromDocumentModelsEvents()
|
||
{
|
||
try
|
||
{
|
||
if (_modelsCollectionEventSubscribed)
|
||
{
|
||
var document = NavisApplication.ActiveDocument;
|
||
if (document?.Models != null)
|
||
{
|
||
document.Models.SceneLoaded -= OnSceneLoaded;
|
||
document.Models.CollectionChanged -= OnModelsCollectionChanged;
|
||
}
|
||
_modelsCollectionEventSubscribed = false;
|
||
LogManager.Info("[ModelSettingsViewModel] 已取消订阅SceneLoaded和CollectionChanged事件");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 取消订阅SceneLoaded事件失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 场景加载完成事件处理器
|
||
/// </summary>
|
||
private async void OnSceneLoaded(object sender, EventArgs e)
|
||
{
|
||
// 如果已经释放,直接返回
|
||
if (_disposed) return;
|
||
|
||
try
|
||
{
|
||
var document = NavisApplication.ActiveDocument;
|
||
var modelsCount = document?.Models?.Count ?? 0;
|
||
|
||
LogManager.Info($"[ModelSettingsViewModel] 场景加载完成,当前模型数量: {modelsCount}");
|
||
|
||
// 更新上次模型数量
|
||
_lastModelsCount = modelsCount;
|
||
|
||
// 注意:不在 SceneLoaded 事件中直接刷新物流模型列表
|
||
// 因为 SceneLoaded 事件可能在模型完全加载之前触发
|
||
// 刷新逻辑移到 OnModelsCollectionChanged 中处理
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 处理场景加载完成事件异常: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 模型集合变化事件处理器 - 用于检测文档加载完成
|
||
/// </summary>
|
||
private async void OnModelsCollectionChanged(object sender, EventArgs e)
|
||
{
|
||
// 如果已经释放,直接返回
|
||
if (_disposed) return;
|
||
|
||
try
|
||
{
|
||
var document = NavisApplication.ActiveDocument;
|
||
var currentModelsCount = document?.Models?.Count ?? 0;
|
||
|
||
// 检测模型数量从 0 变为 >0,表示文档已加载完成
|
||
if (_lastModelsCount == 0 && currentModelsCount > 0)
|
||
{
|
||
LogManager.Info($"[ModelSettingsViewModel] 检测到文档加载完成(模型数量: {_lastModelsCount} -> {currentModelsCount})");
|
||
|
||
// 刷新物流模型列表
|
||
await RefreshLogisticsModelsAsync();
|
||
}
|
||
|
||
// 更新上次模型数量
|
||
_lastModelsCount = currentModelsCount;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 处理模型集合变化事件异常: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 选择事件处理
|
||
|
||
/// <summary>
|
||
/// 订阅Navisworks选择变化事件 - 使用新的选择管理服务
|
||
/// </summary>
|
||
private void SubscribeToSelectionEvents()
|
||
{
|
||
try
|
||
{
|
||
// 使用新的选择管理服务订阅选择变化事件
|
||
_selectionEventSubscription = NavisworksSelectionHelper.SubscribeToSelectionChanges(
|
||
OnSelectionChangedAsync, _uiStateManager);
|
||
|
||
LogManager.Info("[ModelSettingsViewModel] 已通过NavisworksSelectionHelper订阅选择变化事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 订阅选择事件失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消订阅Navisworks选择变化事件 - 使用新的选择管理服务
|
||
/// </summary>
|
||
private void UnsubscribeFromSelectionEvents()
|
||
{
|
||
try
|
||
{
|
||
// 通过Dispose方法取消订阅
|
||
_selectionEventSubscription?.Dispose();
|
||
_selectionEventSubscription = null;
|
||
|
||
LogManager.Info("[ModelSettingsViewModel] 已通过NavisworksSelectionHelper取消订阅选择变化事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 取消选择事件订阅失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选择变化事件处理器 - 使用新的选择管理服务
|
||
/// </summary>
|
||
private async Task OnSelectionChangedAsync(SelectionStateResult selectionResult)
|
||
{
|
||
// 如果已经释放,直接返回
|
||
if (_disposed) return;
|
||
|
||
try
|
||
{
|
||
// 更新物流属性相关的选择状态(使用新的选择结果)
|
||
await UpdateModelSelectionStateAsync(selectionResult);
|
||
|
||
LogManager.Debug($"[ModelSettingsViewModel] 选择状态已更新: {selectionResult.Count}个项目");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 处理选择变化事件异常: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新模型选择状态 - 使用新的选择管理服务
|
||
/// </summary>
|
||
private async Task UpdateModelSelectionStateAsync(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(HasSelectedModels));
|
||
OnPropertyChanged(nameof(HasSelectedModelsWithLogisticsAttributes));
|
||
OnPropertyChanged(nameof(CanSetLogisticsAttribute));
|
||
OnPropertyChanged(nameof(CanClearLogisticsAttribute));
|
||
|
||
LogManager.Debug($"[ModelSettingsViewModel] 模型选择状态已更新: 成功={selectionResult.Success}, 数量={selectionResult.Count}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 更新模型选择状态异常: {ex.Message}", ex);
|
||
SelectedModelsText = "检查选择状态异常";
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 辅助方法
|
||
|
||
/// <summary>
|
||
/// 异步初始化
|
||
/// </summary>
|
||
private async void InitializeAsync()
|
||
{
|
||
try
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
// 初始化物流类别(包括内置和自定义类别)
|
||
AvailableCategories.Clear();
|
||
AvailableCategoryFilters.Clear();
|
||
|
||
// 先添加"全部"选项到筛选器
|
||
AvailableCategoryFilters.Add("全部");
|
||
|
||
// 从 CategoryAttributeManager 获取所有可用类别(内置 + 自定义)
|
||
var allCategories = CategoryAttributeManager.GetAllAvailableCategories();
|
||
foreach (var category in allCategories)
|
||
{
|
||
AvailableCategories.Add(category.DisplayName);
|
||
AvailableCategoryFilters.Add(category.DisplayName);
|
||
}
|
||
|
||
// 设置默认选择
|
||
if (AvailableCategories.Count > 0 && AvailableCategories.Contains("通道"))
|
||
{
|
||
SelectedCategory = "通道";
|
||
}
|
||
else if (AvailableCategories.Count > 0)
|
||
{
|
||
SelectedCategory = AvailableCategories[0];
|
||
}
|
||
|
||
// 设置默认筛选器为"全部"
|
||
SelectedCategoryFilter = "全部";
|
||
});
|
||
|
||
// 注意:不在初始化时刷新选择状态和物流模型列表
|
||
// 刷新会在以下情况自动触发:
|
||
// 1. 选择状态 - 通过 NavisworksSelectionHelper 的选择变化事件自动刷新
|
||
// 2. 物流模型列表 - 通过 Models.CollectionChanged 事件在文档加载后自动刷新
|
||
|
||
// 🔧 确保初始化完成后所有Command状态正确
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
RefreshAllCommands();
|
||
UpdateMainStatus("类别设置已就绪");
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 初始化失败: {ex.Message}");
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
UpdateMainStatus("初始化失败,请检查日志");
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 应用可见性模式
|
||
/// </summary>
|
||
private void ApplyVisibilityMode()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
if (IsLogisticsOnlyMode)
|
||
{
|
||
ShowLogisticsOnlyInternal();
|
||
}
|
||
else
|
||
{
|
||
// 取消"仅显示物流元素",退出隔离模式
|
||
if (VisibilityHelper.ExitIsolation())
|
||
{
|
||
// 更新物流模型列表中的可见性状态
|
||
foreach (var model in LogisticsModels)
|
||
{
|
||
model.IsVisible = true;
|
||
}
|
||
|
||
UpdateMainStatus("已恢复可见性状态");
|
||
LogManager.Info("[UI-ModelSettings] 退出隔离模式,恢复可见性状态");
|
||
}
|
||
else
|
||
{
|
||
// 没有隔离状态,显示全部
|
||
VisibilityHelper.ShowAllItems();
|
||
UpdateMainStatus("已显示全部元素");
|
||
LogManager.Info("[UI-ModelSettings] 没有隔离状态,显示全部");
|
||
}
|
||
}
|
||
}, "应用可见性模式", runOnUIThread: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 内部方法:仅显示物流元素
|
||
/// </summary>
|
||
private void ShowLogisticsOnlyInternal()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("[UI-ModelSettings] 开始仅显示物流元素");
|
||
|
||
var document = NavisApplication.ActiveDocument;
|
||
if (document == null || document.Models == null)
|
||
{
|
||
LogManager.Warning("[UI-ModelSettings] 没有活动文档");
|
||
UpdateMainStatus("没有活动文档");
|
||
return;
|
||
}
|
||
|
||
var logisticsItems = CategoryAttributeManager.GetAllLogisticsItems();
|
||
|
||
if (logisticsItems == null || logisticsItems.Count == 0)
|
||
{
|
||
LogManager.Info("[UI-ModelSettings] 没有找到物流元素");
|
||
UpdateMainStatus("未找到物流元素");
|
||
return;
|
||
}
|
||
|
||
LogManager.Info($"[UI-ModelSettings] 找到 {logisticsItems.Count} 个物流元素");
|
||
|
||
// 进入隔离模式(自动保存当前状态)
|
||
bool success = VisibilityHelper.EnterIsolation(logisticsItems);
|
||
|
||
if (success)
|
||
{
|
||
// 更新物流模型列表中的可见性状态
|
||
foreach (var model in LogisticsModels)
|
||
{
|
||
model.IsVisible = true;
|
||
}
|
||
|
||
LogManager.Info($"[UI-ModelSettings] 成功隔离显示物流元素");
|
||
UpdateMainStatus($"仅显示物流元素 ({logisticsItems.Count} 个物流节点)");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Error("[UI-ModelSettings] 进入隔离模式失败");
|
||
UpdateMainStatus("操作失败");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[UI-ModelSettings] 仅显示物流元素失败:{ex.Message}");
|
||
UpdateMainStatus($"操作失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 根据显示名称获取类别ID
|
||
/// </summary>
|
||
/// <param name="displayName">类别显示名称</param>
|
||
/// <returns>类别ID,如果未找到返回null</returns>
|
||
private string GetCategoryIdByDisplayName(string displayName)
|
||
{
|
||
if (string.IsNullOrEmpty(displayName))
|
||
return null;
|
||
|
||
// 从所有可用类别中查找
|
||
var allCategories = CategoryAttributeManager.GetAllAvailableCategories();
|
||
foreach (var category in allCategories)
|
||
{
|
||
if (category.DisplayName == displayName)
|
||
{
|
||
return category.Id;
|
||
}
|
||
}
|
||
|
||
// 如果没找到匹配,直接返回显示名称(可能是旧数据或其他情况)
|
||
return displayName;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据类别ID获取显示名称
|
||
/// </summary>
|
||
/// <param name="categoryId">类别ID</param>
|
||
/// <returns>类别显示名称,如果未找到返回null</returns>
|
||
private string GetDisplayNameByCategoryId(string categoryId)
|
||
{
|
||
if (string.IsNullOrEmpty(categoryId))
|
||
return null;
|
||
|
||
// 从所有可用类别中查找
|
||
var allCategories = CategoryAttributeManager.GetAllAvailableCategories();
|
||
foreach (var category in allCategories)
|
||
{
|
||
if (category.Id.Equals(categoryId, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
return category.DisplayName;
|
||
}
|
||
}
|
||
|
||
// 如果没找到匹配,直接返回类别ID(可能是旧数据或其他情况)
|
||
return categoryId;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 格式化物流属性为显示字符串
|
||
/// </summary>
|
||
private string FormatLogisticsAttributes(LogisticsAttributeInfo info)
|
||
{
|
||
if (info == null) return "无属性";
|
||
|
||
// 简单格式化:可通行、优先级、限制等
|
||
return $"可通行: {(info.IsTraversable ? "是" : "否")}, " +
|
||
$"优先级: {info.Priority}, " +
|
||
$"高度限制: {info.HeightLimit}m, " +
|
||
$"速度限制: {info.SpeedLimit}m/s";
|
||
}
|
||
|
||
private void RefreshAllCommands()
|
||
{
|
||
OnPropertyChanged(nameof(CanSetLogisticsAttribute));
|
||
OnPropertyChanged(nameof(CanClearLogisticsAttribute));
|
||
OnPropertyChanged(nameof(HasSelectedModels));
|
||
OnPropertyChanged(nameof(HasSelectedModelsWithLogisticsAttributes));
|
||
|
||
// 🔧 强制WPF重新查询所有Command的CanExecute状态
|
||
System.Windows.Input.CommandManager.InvalidateRequerySuggested();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 聚焦到物流模型
|
||
/// </summary>
|
||
public void FocusOnLogisticsModel(LogisticsModel logisticsModel)
|
||
{
|
||
if (logisticsModel?.NavisworksItem == null) return;
|
||
|
||
try
|
||
{
|
||
// 聚焦到模型(斜上方60度视角)
|
||
ViewpointHelper.FocusOnModelItem(logisticsModel.NavisworksItem, ViewpointHelper.ViewpointStrategy.ModelFocus);
|
||
|
||
UpdateMainStatus($"已聚焦到物流模型: {logisticsModel.Name}");
|
||
LogManager.Info($"聚焦到物流模型: {logisticsModel.Name}, 类别: {logisticsModel.Category}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"聚焦到物流模型失败: {ex.Message}", ex);
|
||
UpdateMainStatus($"聚焦失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 向后兼容性接口
|
||
|
||
/// <summary>
|
||
/// 向后兼容:提供UIStateManager访问接口
|
||
/// </summary>
|
||
public UIStateManager UIStateManager => _uiStateManager;
|
||
|
||
/// <summary>
|
||
/// 验证ViewModel状态是否正常
|
||
/// </summary>
|
||
public bool IsValidState()
|
||
{
|
||
return _uiStateManager != null &&
|
||
AvailableCategories != null &&
|
||
LogisticsModels != null &&
|
||
PriorityLevels != null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取ViewModel状态信息
|
||
/// </summary>
|
||
public string GetStateInfo()
|
||
{
|
||
return $"UIStateManager: {(_uiStateManager != null ? "已初始化" : "未初始化")}, " +
|
||
$"可用类别数量: {AvailableCategories?.Count ?? 0}, " +
|
||
$"物流模型数量: {LogisticsModels?.Count ?? 0}";
|
||
}
|
||
|
||
/// <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
|
||
{
|
||
// 取消选择事件订阅
|
||
UnsubscribeFromSelectionEvents();
|
||
|
||
// 取消文档模型集合事件订阅
|
||
UnsubscribeFromDocumentModelsEvents();
|
||
|
||
// 取消配置变更事件订阅
|
||
UnsubscribeFromConfigChanges();
|
||
|
||
LogManager.Info("[ModelSettingsViewModel] 资源清理完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ModelSettingsViewModel] 资源清理失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
_disposed = true;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
}
|
||
}
|