using System; using System.IO; using System.Windows.Input; using Logger; using Prism.Ioc; using TestingModule.ViewModels; using UIShare; using UIShare.GlobalVariable; using UIShare.PubEvent; using UIShare.UIViewModel; using UIShare.ViewModelBase; namespace MainModule.ViewModels { public class AutomatedTestingViewModel : NavigateViewModelBase, IRegionMemberLifetime, IDisposable { #region 私有字段 private string _testStatus; private readonly IScopedProvider _scope; #endregion #region 属性 public bool KeepAlive => true; // 保持存活 public string TestStatus { get => _testStatus; set => SetProperty(ref _testStatus, value); } // 该 AutomatedTestingView 实例独占的 ScopedContext,5 个子面板共享 public ScopedContext _scopedContext { get; } public StepRunning _stepRunning { get; } public GlobalInfo _globalInfo { get; } public SystemConfig _systemConfig { get; set; } // 5 个子 ViewModel 全部从同一个 scope 解析,自动注入同一个 ScopedContext public CommandTreeViewModel CommandTreeVM { get; } public StepsManagerViewModel StepsManagerVM { get; } public SingleStepEditViewModel SingleStepEditVM { get; } public LogAreaViewModel LogAreaVM { get; } public ParametersManagerViewModel ParametersManagerVM { get; } #endregion public ICommand RefreshCommand { get; set; } public ICommand BackToProtocolCommand { get; set; } public AutomatedTestingViewModel(IContainerExtension container) : base(container) { // 每个 AutomatedTestingViewModel 实例创建独立的容器作用域 _scope = container.CreateScope(); _globalInfo=container.Resolve(); // 在该作用域内解析 ScopedContext —— 当前作用域唯一 _scopedContext = _scope.Resolve(); _stepRunning = _scope.Resolve(); _systemConfig = _scope.Resolve(); // 关键:从同一个 _scope 解析 5 个子 VM,DI 会把同一个 ScopedContext 注入它们 CommandTreeVM = _scope.Resolve(); StepsManagerVM = _scope.Resolve(); SingleStepEditVM = _scope.Resolve(); LogAreaVM = _scope.Resolve(); ParametersManagerVM = _scope.Resolve(); RefreshCommand = new DelegateCommand(OnRefresh); BackToProtocolCommand = new DelegateCommand(OnBackToProtocol); } public void Dispose() { if (string.IsNullOrEmpty(TestStatus)) { _scope?.Dispose(); return; } try { _globalInfo.ContextDic?.Remove(TestStatus); _globalInfo.StepRunningDic?.Remove(TestStatus); _globalInfo.ConfigDic?.Remove(TestStatus); } catch (Exception ex) { Logger.LoggerHelper.ErrorWithNotify($"卸载机台 [{TestStatus}] 全局引用失败: {ex.Message}"); } finally { _scope?.Dispose(); } } #region 命令处理与事件 private void OnRefresh() { // 双击:把自己的名字扔出去 _globalInfo.CurrentScope = TestStatus; _eventAggregator.GetEvent().Publish(TestStatus); } private void OnBackToProtocol() { // 返回:扔个空字符串出去 _eventAggregator.GetEvent().Publish(""); } #endregion #region 重写 public override void OnNavigatedTo(NavigationContext navigationContext) { base.OnNavigatedTo(navigationContext); if (navigationContext.Parameters.ContainsKey("Name")) { TestStatus = navigationContext.Parameters.GetValue("Name"); if (ConfigService.IsExit(TestStatus)) { string filePath = System.IO.Path.Combine(_systemConfig.SystemPath, $"{TestStatus}.json"); if (System.IO.File.Exists(filePath)) { string json = System.IO.File.ReadAllText(filePath); // 🔥 关键:把 json 数据直接灌入当前实例,对象引用没有任何改变 Newtonsoft.Json.JsonConvert.PopulateObject(json, _systemConfig); } } _globalInfo.ContextDic.Add(TestStatus, _scopedContext); _globalInfo.StepRunningDic.Add(TestStatus, _stepRunning); _globalInfo.ScopeDic.Add(TestStatus, _scope); _globalInfo.ConfigDic.Add(TestStatus, _systemConfig); if(_systemConfig.DefaultProgramFilePath != null&&File.Exists(_systemConfig.DefaultProgramFilePath)) { var filePath = _systemConfig.DefaultProgramFilePath; // 读取 JSON 文件 string json = File.ReadAllText(filePath); // 反序列化为 ProgramModel var program = Newtonsoft.Json.JsonConvert.DeserializeObject(json); if (program == null) { LoggerHelper.WarnWithNotify($"文件格式不正确或为空: {filePath}"); return; } // 💡 2. 严格赋值给快照锁定下的当前工位上下文,实现数据完全隔离 _scopedContext.Program.Parameters = program.Parameters; _scopedContext.Program.StepCollection = program.StepCollection; _scopedContext.Program.ErrorStepCollection = program.ErrorStepCollection; _scopedContext.CurrentFilePath = filePath; } } } #endregion } }