NavisworksTransport/src/UI/WPF/ViewModels/ModelSettingsViewModel.cs

1688 lines
64 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

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

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
}
}