Sic08/SicUI/Models/RecipeEditors/RecipeEditorViewModel.cs

1966 lines
66 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 Aitex.Core.RT.Log;
using Caliburn.Micro;
using Caliburn.Micro.Core;
using MECF.Framework.Common.CommonData;
using MECF.Framework.Common.DataCenter;
using MECF.Framework.UI.Client.CenterViews.Editors;
using MECF.Framework.UI.Client.CenterViews.Editors.Recipe;
using MECF.Framework.UI.Client.CenterViews.Editors.Sequence;
using MECF.Framework.UI.Client.ClientBase;
using OpenSEMI.ClientBase;
using OpenSEMI.ClientBase.Command;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using MECF.Framework.UI.Client.CenterViews.Configs.Roles;
using MECF.Framework.UI.Client.CenterViews.Core;
using MECF.Framework.UI.Client.RecipeEditorLib.DGExtension;
using MECF.Framework.UI.Client.RecipeEditorLib.DGExtension.CustomColumn;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel.Params;
using Sicentury.Core;
using Action = System.Action;
using DataGridCellEditEndingEventArgs = ExtendedGrid.Microsoft.Windows.Controls.DataGridCellEditEndingEventArgs;
using DataGridRowEventArgs = ExtendedGrid.Microsoft.Windows.Controls.DataGridRowEventArgs;
using Sicentury.Core.Collections;
namespace SicUI.Models.RecipeEditors
{
public class ChamberTypeItem : NotifiableItem
{
public string ChamberType { get; set; }
public ObservableCollection<ProcessTypeFileItem> FileListByChamberType { get; set; }
public ChamberTypeItem()
{
FileListByChamberType = new ObservableCollection<ProcessTypeFileItem>();
}
}
public class RecipeEditorViewModel : UiViewModelBase, IHandle<UserMode> //BaseModel
{
#region Variables
private readonly IRecipeGasFlowCalculator _gasFlowCalculator;
private RecipeData _currentRecipe;
private bool _isRunPurgeSwitch;
private bool _isTmaNotInstall;
private bool _isHClNotInstall;
private bool _isHClFlowNotInstall;
private bool _isLoading;
private Window _winValidationInfo;
private EditMode _editMode;
private int _errorsCount;
private readonly RecipeFormatBuilder _columnBuilder;
private readonly RecipeProvider _recipeProvider;
private ObservableCollection<ProcessTypeFileItem> _processTypeFileList;
private readonly ObservableRangeCollection<RecipeStepValidationInfo> _validationResultList;
private Point? _lastPosOfValidationWin = null;
#endregion
#region Constructors
public RecipeEditorViewModel()
{
var eventAggregator = IoC.Get<IEventAggregator>();
eventAggregator?.Subscribe(this);
_gasFlowCalculator = IoC.Get<IRecipeGasFlowCalculator>();
_columnBuilder = new RecipeFormatBuilder();
_recipeProvider = new RecipeProvider();
_validationResultList = new ObservableRangeCollection<RecipeStepValidationInfo>();
}
#endregion
#region Commands
private ICommand _RenameFolderCommand;
public ICommand RenameFolderCommand
{
get
{
if (_RenameFolderCommand == null)
_RenameFolderCommand = new BaseCommand(() => RenameFolder());
return _RenameFolderCommand;
}
}
private ICommand _DeleteFolderCommand;
public ICommand DeleteFolderCommand
{
get
{
if (_DeleteFolderCommand == null)
_DeleteFolderCommand = new BaseCommand(() => DeleteFolder());
return _DeleteFolderCommand;
}
}
private ICommand _NewFolderCommand;
public ICommand NewFolderCommand
{
get
{
if (_NewFolderCommand == null)
_NewFolderCommand = new BaseCommand(() => NewFolder());
return _NewFolderCommand;
}
}
private ICommand _NewFolderRootCommand;
public ICommand NewFolderRootCommand
{
get
{
if (_NewFolderRootCommand == null)
_NewFolderRootCommand = new BaseCommand(() => NewFolderRoot());
return _NewFolderRootCommand;
}
}
private ICommand _NewRecipeCommand;
public ICommand NewRecipeCommand
{
get
{
if (_NewRecipeCommand == null)
_NewRecipeCommand = new BaseCommand(() => NewRecipe());
return _NewRecipeCommand;
}
}
private ICommand _NewRecipeRootCommand;
public ICommand NewRecipeRootCommand
{
get
{
if (_NewRecipeRootCommand == null)
_NewRecipeRootCommand = new BaseCommand(() => NewRecipeRoot());
return _NewRecipeRootCommand;
}
}
private ICommand _RenameRecipeCommand;
public ICommand RenameRecipeCommand
{
get
{
if (_RenameRecipeCommand == null)
_RenameRecipeCommand = new BaseCommand(() => RenameRecipe());
return _RenameRecipeCommand;
}
}
private ICommand _DeleteRecipeCommand;
public ICommand DeleteRecipeCommand
{
get
{
if (_DeleteRecipeCommand == null)
_DeleteRecipeCommand = new BaseCommand(() => DeleteRecipe());
return _DeleteRecipeCommand;
}
}
private ICommand _SaveAsRecipeCommand;
public ICommand SaveAsRecipeCommand
{
get
{
if (_SaveAsRecipeCommand == null)
_SaveAsRecipeCommand = new BaseCommand(() => SaveAsRecipe());
return _SaveAsRecipeCommand;
}
}
#endregion
#region Properties
public ObservableCollection<ProcessTypeFileItem> ProcessTypeFileList
{
get => _processTypeFileList;
set
{
_processTypeFileList = value;
NotifyOfPropertyChange(nameof(ProcessTypeFileList));
}
}
public RecipeData CurrentRecipe
{
get => _currentRecipe;
set
{
_currentRecipe = value;
NotifyOfPropertyChange(nameof(CurrentRecipe));
}
}
public bool IsPermission => Permission == 3; //&& RtStatus != "AutoRunning";
public FileNode CurrentFileNode { get; set; }
private bool IsChanged => _editMode == EditMode.Edit || CurrentRecipe.IsChanged;
public List<EditorDataGridTemplateColumnBase> Columns { get; set; }
public Dictionary<string, List<EditorDataGridTemplateColumnBase>> DicColunms { get; set; }
public bool EnableNew { get; set; }
public bool EnableReName { get; set; }
public bool EnableCopy { get; set; }
public bool EnableDelete { get; set; }
public bool EnableSave { get; set; }
public bool EnableStep { get; set; }
public bool EnableReload { get; set; }
public bool EnableSaveToAll { get; set; }
public bool EnableSaveTo { get; set; }
public int ErrorsCount
{
set
{
// 如果错误数没发生变化不要修改Badge的值以免触发Badge动画。
if (value == _errorsCount)
return;
if(value == 0)
((RecipeEditorView)View).txtErrorCount.Badge = null;
else
((RecipeEditorView)View).txtErrorCount.Badge = value;
_errorsCount = value;
}
}
/// <summary>
/// 是否正在加载配方。
/// </summary>
public bool IsLoading
{
get => _isLoading;
private set
{
_isLoading = value;
NotifyOfPropertyChange(nameof(IsLoading));
}
}
public List<RecipeStep> SelectedRecipeSteps { get; set; }
public ICollectionView ParamValidationInfoCollection =>
CollectionViewSource.GetDefaultView(_validationResultList);
public ObservableCollection<string> ChamberType { get; set; }
public int ChamberTypeIndexSelection { get; set; }
public int ProcessTypeIndexSelection { get; set; }
public string CurrentChamberType => ChamberType[ChamberTypeIndexSelection];
public string CurrentProcessType => ProcessTypeFileList[ProcessTypeIndexSelection].ProcessType;
public Visibility MultiChamberVisibility { get; set; }
public ObservableCollection<string> Chambers { get; set; }
public string SelectedChamber { get; set; }
public object View { get; set; }
#endregion
protected override void OnInitialize()
{
base.OnInitialize();
var chamberType = QueryDataClient.Instance.Service.GetConfig("System.Recipe.SupportedChamberType");
if (chamberType == null)
{
ChamberType = new ObservableCollection<string>() { "Default" };
}
else
{
ChamberType = new ObservableCollection<string>(((string)(chamberType)).Split(','));
}
ChamberTypeIndexSelection = 0;
var processType = "Process,Routine,Clean";
ProcessTypeFileList = new ObservableCollection<ProcessTypeFileItem>();
var recipeProcessType = ((string)processType).Split(',');
for (var i = 0; i < recipeProcessType.Length; i++)
{
var type = new ProcessTypeFileItem();
type.ProcessType = recipeProcessType[i];
var prefix = $"{ChamberType[ChamberTypeIndexSelection]}\\{recipeProcessType[i]}";
var recipes = _recipeProvider.GetXmlRecipeList(prefix);
type.FileListByProcessType =
RecipeSequenceTreeBuilder.BuildFileNode(prefix, "", false, recipes)[0].Files;
type.FilterFileListByProcessType = type.FileListByProcessType;
ProcessTypeFileList.Add(type);
}
DicColunms = new Dictionary<string, List<EditorDataGridTemplateColumnBase>>();
UpdateRecipeFormat();
}
protected override void OnActivate()
{
//初始化RoleManager
var roleManager = new RoleManager();
roleManager.Initialize();
//得到当前登录的RoleItem
var roleItem = roleManager.GetRoleByName(BaseApp.Instance.UserContext.RoleName);
var menuPermission = new MenuPermission();
menuPermission.ParsePermission(roleItem.Role.MenuPermission);
foreach (var col in Columns)
{
if (col.ControlName == "StepNo")
continue;
RecipeFormatBuilder.SetPermission(col, menuPermission);
}
//this.Columns = this._columnBuilder.Build($"{CurrentChamberType}\\{CurrentProcessType}", SelectedChamber, true, ClientApp.Instance.UserContext.RoleName);
base.OnActivate();
}
protected override void OnDeactivate(bool close)
{
base.OnDeactivate(close);
if (IsChanged)
{
if (DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM,
$"Recipe {CurrentRecipe.Name} content is changed, do you want to save it?") == DialogButton.Yes)
{
SaveRecipe();
}
}
_winValidationInfo?.Close();
// 锁定编辑器
((RecipeEditorView)View).editorLocker.Lock();
}
protected override void OnViewLoaded(object view)
{
View = view;
base.OnViewLoaded(view);
RecipeFormatBuilder.ApplyTemplate((UserControl)view, Columns);
var u = (RecipeEditorView)view;
u.dgCustom.Columns.Clear();
Columns.Apply((c) =>
{
c.Header = c;
u.dgCustom.Columns.Add(c);
});
u.dgCustom.ItemsSource = CurrentRecipe.Steps;
u.dgCustom.LostFocus += DgCustom_LostFocus;
u.dgCustom.CellEditEnding += DgCustomOnCellEditEnding;
u.dgCustom.LoadingRow += DgCustomOnLoadingRow;
u.dgCustom.FrozenColumnCount = 4;
}
private void DgCustomOnLoadingRow(object sender, DataGridRowEventArgs e)
{
if (sender is XDataGrid dg && e.Row.DataContext is RecipeStep step)
{
for (var i = 0; i < step.Count; i++)
{
step[i].RowOwner = e.Row;
step[i].ColumnOwner = dg.Columns[i];
}
}
}
private void DgCustomOnCellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
// ValidateEntireRecipe();
if (SelectedRecipeSteps != null && SelectedRecipeSteps.Count == 1)
{
SelectedRecipeSteps[0].CalculateGasFlow();
CheckIfAllCellsValid(out _);
}
}
private void DgCustom_LostFocus(object sender, RoutedEventArgs e)
{
// ValidateEntireRecipe();
if (SelectedRecipeSteps != null && SelectedRecipeSteps.Count == 1)
{
SelectedRecipeSteps[0].CalculateGasFlow();
CheckIfAllCellsValid(out _);
}
}
/// <summary>
/// 校验整个Recipe。
/// </summary>
private void ValidateEntireRecipe()
{
_isRunPurgeSwitch = false;
_isTmaNotInstall = false;
_isHClNotInstall = false;
_isHClFlowNotInstall = false;
CurrentRecipe.RecipeTotalTime = 0;
for (var selectIndex = 0; selectIndex < CurrentRecipe.Steps.Count; selectIndex++)
{
var previousIndex = selectIndex == 0 ? 0 : selectIndex - 1;
var currentIndex = selectIndex;
var nextIndex = selectIndex == CurrentRecipe.Steps.Count - 1 ? selectIndex : selectIndex + 1;
var previousStep = CurrentRecipe.Steps[previousIndex];
var currentStep = CurrentRecipe.Steps[currentIndex];
var nextStep = CurrentRecipe.Steps[nextIndex];
//判断每步与上一步值是否相同,不同则以颜色区分
RenderStepsWithSameValue(previousStep, currentStep);
//如果是Routine则在下面代码不执行在这里continue
if (CurrentProcessType == ProcessTypeFileList[1].ProcessType)
{
//如果是Routine,则总时间需*循环次数
var loopTimes = ((DoubleParam)currentStep[8]).Value <= 0
? 1
: (int)((DoubleParam)currentStep[8]).Value;
CurrentRecipe.RecipeTotalTime += (int)currentStep.StepTime * loopTimes;
continue;
}
else
{
//如果是Recipe则总时间直接加
CurrentRecipe.RecipeTotalTime += (int)currentStep.StepTime;
}
NotInstallDevice(currentStep);
currentStep.CalculateGasFlow(); // 自动计算比例值
currentStep.Validate(); // 校验单元格
}
//如果是Routine则在下面代码不执行在这里返回
if (CurrentProcessType == ProcessTypeFileList[1].ProcessType)
{
return;
}
if (_isRunPurgeSwitch)
MessageBox.Show("Flow mode 'Run' and 'Purge' can't switch directly", "Warning", MessageBoxButton.OK,
MessageBoxImage.Warning);
//if (_isTmaNotInstall)
// MessageBox.Show("TMA is not install, only Purge mode is valid", "Warning", MessageBoxButton.OK,
// MessageBoxImage.Warning);
if (_isHClNotInstall)
MessageBox.Show("GR Purge HCl is not install, only Disable is valid", "Warning", MessageBoxButton.OK,
MessageBoxImage.Warning);
if (_isHClFlowNotInstall)
MessageBox.Show("GR Purge HCl Flow is not install, lock the value as zero", "Warning",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
private static void RenderStepsWithSameValue(IReadOnlyList<Param> previousStep, IReadOnlyList<Param> currentStep)
{
for (var i = 0; i < currentStep.Count; i++)
{
if (currentStep[i] is DoubleParam dblParamCurr && previousStep[i] is DoubleParam dblParamPrev)
{
dblParamCurr.IsEqualsToPrevious =
GlobalDefs.IsDoubleValueEquals(dblParamCurr.Value, dblParamPrev.Value);
}
else if (currentStep[i] is ComboxParam cbxParamCurr && previousStep[i] is ComboxParam cbxParamPrev)
{
if (cbxParamCurr.Value != cbxParamPrev.Value)
{
cbxParamCurr.IsEqualsToPrevious = false;
}
else
{
cbxParamCurr.IsEqualsToPrevious = true;
}
}
}
}
private void NotInstallDevice(RecipeStep currentStep)
{
if (!(currentStep.FindParamByControlName("TMA.SetValve") is ComboxParam currentTmaFlowMode))
throw new InvalidOperationException(
$"the param TMA.SetValve seems not to be TMA Flow Mode, please check the recipe format file.");
if (string.Equals(currentTmaFlowMode.Value,
FlowModeParam.FlowModeEnum.Purge.ToString(), StringComparison.OrdinalIgnoreCase))
return;
currentTmaFlowMode.Value = FlowModeParam.FlowModeEnum.Purge.ToString();
_isTmaNotInstall = true;
}
public void TabSelectionChanged()
{
UpdateRecipeFormat();
OnViewLoaded(View);
}
public void UpdateRecipeFormat()
{
var chamber = QueryDataClient.Instance.Service.GetConfig("System.Recipe.ChamberModules");
if (chamber == null)
{
chamber = "PM1";
}
Chambers = new ObservableCollection<string>(((string)chamber).Split(','));
if (Chambers.Count > 1)
{
for (var i = 0; i < Chambers.Count; i++)
{
var isPmInstall =
(bool)QueryDataClient.Instance.Service.GetConfig(
$"System.SetUp.Is{Chambers[i].ToString()}Installed");
{
if (!isPmInstall)
{
Chambers.RemoveAt(i);
}
}
}
}
if (Chambers.Count == 0)
{
Chambers = new ObservableCollection<string>(new string[] { "PM1" });
}
SelectedChamber = Chambers[0];
if (DicColunms.Keys.Contains(CurrentProcessType))
{
Columns = DicColunms[CurrentProcessType];
}
else
{
Columns = _columnBuilder.Build($"{CurrentChamberType}\\{CurrentProcessType}", SelectedChamber, true,
BaseApp.Instance.UserContext.RoleName);
DicColunms[CurrentProcessType] = Columns;
}
CurrentRecipe = new RecipeData(_gasFlowCalculator);
CurrentRecipe.RecipeChamberType = _columnBuilder.RecipeChamberType;
CurrentRecipe.RecipeVersion = _columnBuilder.RecipeVersion;
_editMode = EditMode.None;
MultiChamberVisibility = Chambers.Count > 1 ? Visibility.Visible : Visibility.Collapsed;
}
/// <summary>
/// Chamber变更时重新加载Recipe。
/// </summary>
public async void ChamberSelectionChanged()
{
if (IsChanged)
{
var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No,
DialogType.CONFIRM,
$"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
}
}
IsLoading = true;
await CurrentRecipe.ChangeChamber(Columns
, _columnBuilder.Configs, SelectedChamber, GetLoadingDispatcher());
IsLoading = false;
}
/// <summary>
/// 左侧Recipe列表选择改变时重新加载Recipe。
/// </summary>
/// <param name="node"></param>
public async void TreeSelectChanged(FileNode node)
{
if (IsChanged)
{
var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No,
DialogType.CONFIRM,
$"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
}
}
CurrentFileNode = node;
if (node != null && node.IsFile)
{
await LoadData(node.PrefixPath, node.FullPath);
}
else
{
ClearData();
_editMode = EditMode.None;
}
UpdateView();
}
#region folder
public void NewFolder()
{
if (IsChanged)
{
var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel,
DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
if (selection == DialogButton.Cancel)
return;
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
}
}
var dialog = new InputFileNameDialogViewModel("Input New Folder Name");
dialog.FileName = "new folder";
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var name = dialog.FileName.Trim();
if (string.IsNullOrEmpty(name))
{
DialogBox.ShowWarning("Folder name should not be empty");
return;
}
var prefix = ChamberType[ChamberTypeIndexSelection] + "\\" +
ProcessTypeFileList[ProcessTypeIndexSelection].ProcessType;
var processType = string.Empty;
var newFolder = string.Empty;
if (CurrentFileNode != null)
{
prefix = CurrentFileNode.PrefixPath;
var folder = CurrentFileNode.FullPath;
if (CurrentFileNode.IsFile)
{
folder = folder.Substring(0, folder.LastIndexOf("\\") + 1);
if (!string.IsNullOrEmpty(folder))
newFolder = folder;
}
else
{
newFolder = folder + "\\";
}
}
newFolder = newFolder + name;
if (IsExist(newFolder, false))
{
DialogBox.ShowWarning($"Can not create folder {newFolder}, Folder with the same name already exist.");
return;
}
if (newFolder.Length > 200)
{
DialogBox.ShowWarning($"Can not create folder {newFolder}, Folder name too long, should be less 200.");
return;
}
_recipeProvider.CreateRecipeFolder(prefix, newFolder);
ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, newFolder, true);
}
public void NewFolderRoot()
{
if (IsChanged)
{
var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel,
DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
if (selection == DialogButton.Cancel)
return;
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
}
}
var dialog = new InputFileNameDialogViewModel("Input New Folder Name");
dialog.FileName = "new folder";
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var name = dialog.FileName.Trim();
if (string.IsNullOrEmpty(name))
{
DialogBox.ShowWarning("Folder name should not be empty");
return;
}
if (IsExist(name, false))
{
DialogBox.ShowWarning($"Can not create folder {name}, Folder with the same name already exist.");
return;
}
if (name.Length > 200)
{
DialogBox.ShowWarning($"Can not create folder {name}, Folder name too long, should be less 200.");
return;
}
var prefix = ChamberType[ChamberTypeIndexSelection] + "\\" +
ProcessTypeFileList[ProcessTypeIndexSelection].ProcessType;
_recipeProvider.CreateRecipeFolder(prefix, name);
ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, name, true);
}
public void DeleteFolder()
{
if (CurrentFileNode == null || CurrentFileNode.IsFile)
return;
if (CurrentFileNode.Files.Count > 0)
{
DialogBox.ShowWarning(
$"Can not delete non-empty folder, Remove the files or folders under \r\n{CurrentFileNode.FullPath}.");
return;
}
var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM,
$"Are you sure you want to delete \r\n {CurrentFileNode.FullPath}?");
if (selection == DialogButton.No)
return;
var nextFocus = CurrentFileNode.Parent.FullPath;
var isFolder = true;
if (CurrentFileNode.Parent.Files.Count > 1)
{
for (var i = 0; i < CurrentFileNode.Parent.Files.Count; i++)
{
if (CurrentFileNode.Parent.Files[i] == CurrentFileNode)
{
if (i == 0)
{
nextFocus = CurrentFileNode.Parent.Files[i + 1].FullPath;
isFolder = !CurrentFileNode.Parent.Files[i + 1].IsFile;
}
else
{
nextFocus = CurrentFileNode.Parent.Files[i - 1].FullPath;
isFolder = !CurrentFileNode.Parent.Files[i - 1].IsFile;
}
}
}
}
_recipeProvider.DeleteRecipeFolder(CurrentFileNode.PrefixPath, CurrentFileNode.FullPath);
ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, nextFocus, isFolder);
}
public void RenameFolder()
{
if (CurrentFileNode == null || CurrentFileNode.IsFile)
return;
var dialog = new InputFileNameDialogViewModel("Input New Folder Name");
dialog.FileName = CurrentFileNode.Name;
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var name = dialog.FileName.Trim();
if (string.IsNullOrEmpty(name))
return;
var newFolder = CurrentFileNode.FullPath.Substring(0, CurrentFileNode.FullPath.LastIndexOf("\\") + 1);
if (!string.IsNullOrEmpty(newFolder))
newFolder = newFolder + name;
else
newFolder = name;
if (newFolder == CurrentFileNode.FullPath)
return;
if (IsExist(newFolder, false))
{
DialogBox.ShowWarning($"Can not rename to {newFolder}, Folder with the same name already exist.");
return;
}
if (newFolder.Length > 200)
{
DialogBox.ShowWarning($"Can not create folder {newFolder}, Folder name too long, should be less 200.");
return;
}
_recipeProvider.RenameFolder(CurrentFileNode.PrefixPath, CurrentFileNode.FullPath, newFolder);
ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, newFolder, true);
}
#endregion
#region recipe
public void NewRecipe()
{
if (IsChanged)
{
var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel,
DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
if (selection == DialogButton.Cancel)
return;
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
}
}
var dialog = new InputFileNameDialogViewModel("Input New Name");
dialog.FileName = "";
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var recipeName = dialog.FileName.Trim();
if (string.IsNullOrEmpty(dialog.FileName))
{
DialogBox.ShowWarning("Recipe file name should not be empty");
return;
}
var prefix = CurrentChamberType + "\\" + CurrentProcessType;
var processType = string.Empty;
if (CurrentFileNode != null)
{
var folder = CurrentFileNode.FullPath;
if (CurrentFileNode.IsFile)
{
folder = folder.Substring(0, folder.LastIndexOf("\\") + 1);
//if (!string.IsNullOrEmpty(folder))
// folder = folder;
}
else
{
folder = folder + "\\";
}
recipeName = folder + recipeName;
}
if (IsExist(recipeName, true))
{
DialogBox.ShowWarning($"Can not create {recipeName}, Recipe with the same name already exist.");
return;
}
if (recipeName.Length > 200)
{
DialogBox.ShowWarning($"Can not create folder {recipeName}, Folder name too long, should be less 200.");
return;
}
var recipe = new RecipeData(_gasFlowCalculator);
recipe.Name = recipeName;
recipe.PrefixPath = prefix;
recipe.Creator = BaseApp.Instance.UserContext.LoginName;
recipe.CreateTime = DateTime.Now;
recipe.Revisor = BaseApp.Instance.UserContext.LoginName;
recipe.ReviseTime = DateTime.Now;
recipe.Description = string.Empty;
if (!Save(recipe, true))
return;
var types = prefix.Split('\\');
ReloadRecipeFileList(types[0], types[1], recipeName, false);
}
public void NewRecipeRoot()
{
if (IsChanged)
{
var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel,
DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
if (selection == DialogButton.Cancel)
return;
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
}
}
var dialog = new InputFileNameDialogViewModel("Input New Recipe Name");
dialog.FileName = "new recipe";
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var recipeName = dialog.FileName.Trim();
if (string.IsNullOrEmpty(dialog.FileName))
{
DialogBox.ShowWarning("Recipe file name should not be empty");
return;
}
if (IsExist(recipeName, true))
{
DialogBox.ShowWarning($"Can not create {recipeName}, Recipe with the same name already exist.");
return;
}
if (recipeName.Length > 200)
{
DialogBox.ShowWarning($"Can not create folder {recipeName}, Folder name too long, should be less 200.");
return;
}
var recipe = new RecipeData(_gasFlowCalculator);
recipe.Name = recipeName;
recipe.PrefixPath = CurrentChamberType + "\\" + CurrentProcessType;
recipe.Creator = BaseApp.Instance.UserContext.LoginName;
recipe.CreateTime = DateTime.Now;
recipe.Revisor = BaseApp.Instance.UserContext.LoginName;
recipe.ReviseTime = DateTime.Now;
recipe.Description = string.Empty;
if (!Save(recipe, true))
return;
ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, recipeName, false);
}
private void ReloadRecipeFileList(string chamberType, string processType, string selectedFile,
bool selectionIsFolder)
{
var item = ProcessTypeFileList.FirstOrDefault(x => x.ProcessType == processType);
if (item == null)
{
LOG.Write("error reload recipe file list, type = " + processType);
}
var prefix = $"{ChamberType[ChamberTypeIndexSelection]}\\{item.ProcessType}";
var recipes = _recipeProvider.GetXmlRecipeList(prefix);
item.FileListByProcessType =
RecipeSequenceTreeBuilder.BuildFileNode(prefix, selectedFile, selectionIsFolder, recipes)[0].Files;
item.FilterFileListByProcessType = item.FileListByProcessType;
item.InvokePropertyChanged();
}
private bool IsExist(string fullPath, bool isFile)
{
for (var i = 0; i < ProcessTypeFileList.Count; i++)
{
if (ProcessTypeFileList[i].ProcessType == CurrentProcessType)
{
if (ProcessTypeFileList[i].FileListByProcessType.Count == 0)
return false;
return FindFile(fullPath, ProcessTypeFileList[i].FileListByProcessType[0].Parent, isFile);
}
}
return true;
}
private bool FindFile(string path, FileNode root, bool isFile)
{
if (root.FullPath == path && !isFile)
{
return true;
}
foreach (var node in root.Files)
{
if (isFile && node.IsFile && node.FullPath == path)
return true;
if (!node.IsFile && FindFile(path, node, isFile))
return true;
}
return false;
}
public void SaveAsRecipe()
{
if (CurrentFileNode == null || !CurrentFileNode.IsFile)
return;
if (IsChanged)
{
var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel,
DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
if (selection == DialogButton.Cancel)
return;
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
}
}
var dialog = new InputFileNameDialogViewModel("Input New Recipe Name");
dialog.FileName = CurrentFileNode.Name;
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var recipeName = dialog.FileName.Trim();
if (string.IsNullOrEmpty(dialog.FileName))
{
DialogBox.ShowWarning("Recipe file name should not be empty");
return;
}
var prefix = CurrentChamberType + "\\" + CurrentProcessType;
var processType = string.Empty;
var folder = CurrentFileNode.FullPath;
if (CurrentFileNode.IsFile)
{
folder = folder.Substring(0, folder.LastIndexOf("\\") + 1);
}
if (!string.IsNullOrEmpty(folder))
recipeName = folder + "\\" + recipeName;
if (CurrentFileNode.FullPath == recipeName)
return;
if (IsExist(recipeName, true))
{
DialogBox.ShowWarning($"Can not copy to {recipeName}, Recipe with the same name already exist.");
return;
}
if (recipeName.Length > 200)
{
DialogBox.ShowWarning($"Can not create folder {recipeName}, Folder name too long, should be less 200.");
return;
}
CurrentRecipe.Creator = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.CreateTime = DateTime.Now;
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
CurrentRecipe.Description = CurrentRecipe.Description + ". Renamed from " + CurrentFileNode.Name;
_recipeProvider.SaveAsRecipe(prefix, recipeName, CurrentRecipe.GetXmlString());
ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, recipeName, false);
}
public void RenameRecipe()
{
if (CurrentFileNode == null || !CurrentFileNode.IsFile)
return;
if (IsChanged)
{
var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel,
DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
if (selection == DialogButton.Cancel)
return;
if (selection == DialogButton.Yes)
{
CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
CurrentRecipe.ReviseTime = DateTime.Now;
Save(CurrentRecipe, false);
}
}
var dialog = new InputFileNameDialogViewModel("Input New Recipe Name");
dialog.FileName = CurrentFileNode.Name;
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var recipeName = dialog.FileName.Trim();
if (string.IsNullOrEmpty(dialog.FileName))
{
DialogBox.ShowWarning("Recipe file name should not be empty");
return;
}
var prefix = CurrentChamberType + "\\" + CurrentProcessType;
var processType = string.Empty;
var newName = CurrentFileNode.FullPath.Substring(0, CurrentFileNode.FullPath.LastIndexOf("\\") + 1);
if (!string.IsNullOrEmpty(newName))
newName = newName + recipeName;
else
newName = recipeName;
if (newName == CurrentFileNode.FullPath)
return;
if (IsExist(newName, true))
{
DialogBox.ShowWarning($"Can not rename to {newName}, Recipe with the same name already exist.");
return;
}
if (newName.Length > 200)
{
DialogBox.ShowWarning($"Can not create folder {newName}, Folder name too long, should be less 200.");
return;
}
_recipeProvider.RenameRecipe(prefix, CurrentFileNode.FullPath, newName);
ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, newName, false);
}
public void DeleteRecipe()
{
if (CurrentFileNode == null || !CurrentFileNode.IsFile)
return;
var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM,
$"Are you sure you want to delete \r\n {CurrentFileNode.FullPath}?");
if (selection == DialogButton.No)
return;
var nextFocus = CurrentFileNode.Parent.FullPath;
var isFolder = true;
if (CurrentFileNode.Parent.Files.Count > 1)
{
for (var i = 0; i < CurrentFileNode.Parent.Files.Count; i++)
{
if (CurrentFileNode.Parent.Files[i] == CurrentFileNode)
{
if (i == 0)
{
nextFocus = CurrentFileNode.Parent.Files[i + 1].FullPath;
isFolder = !CurrentFileNode.Parent.Files[i + 1].IsFile;
}
else
{
nextFocus = CurrentFileNode.Parent.Files[i - 1].FullPath;
isFolder = !CurrentFileNode.Parent.Files[i - 1].IsFile;
}
}
}
}
_recipeProvider.DeleteRecipe(CurrentFileNode.PrefixPath, CurrentFileNode.FullPath);
ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, nextFocus, isFolder);
}
public void RefreshRecipe()
{
ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, "", false);
}
public async void ReloadRecipe()
{
if (_editMode == EditMode.Normal || _editMode == EditMode.Edit)
{
if (CurrentRecipe == null)
return;
if (CurrentRecipe.IsChanged)
{
var ret = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No,
DialogType.CONFIRM,
$"Recipe {CurrentRecipe.Name} has changed, discard and reload?");
if (ret == DialogButton.No)
return;
}
await LoadData(CurrentRecipe.PrefixPath, CurrentRecipe.Name);
UpdateView();
ValidateEntireRecipe();
// ValidateEntireRecipe()会重新计算比例相关的值导致IsChanged == true, 刷新一下。
foreach (var step in CurrentRecipe.Steps)
step.Save();
}
}
public void SaveToAll()
{
ValidateEntireRecipe();
if (!CurrentRecipe.IsCompatibleWithCurrentFormat)
{
DialogBox.ShowWarning($"Saving failed, {CurrentRecipe.Name} is not a valid recipe file");
return;
}
//var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No,
// DialogType.CONFIRM,
// $"Do you want to save to all chambers? \r\n This will replace all the other chambers recipe content");
//if (selection == DialogButton.No)
// return;
CurrentRecipe.SaveTo(Chambers.ToArray());
Save(CurrentRecipe, false);
}
public void SaveTo()
{
ValidateEntireRecipe();
if (!CurrentRecipe.IsCompatibleWithCurrentFormat)
{
DialogBox.ShowWarning($"Saving failed, {CurrentRecipe.Name} is not a valid recipe file.");
return;
}
var dialog =
new SaveToDialogViewModel("Select the chamber to copy to", SelectedChamber, Chambers.ToList());
var wm = new WindowManager();
var dialogReturn = wm.ShowDialog(dialog);
if (!dialogReturn.HasValue || !dialogReturn.Value)
return;
var chambers = new List<string>();
foreach (var dialogChamber in dialog.Chambers)
{
if (dialogChamber.IsEnabled && dialogChamber.IsChecked)
chambers.Add(dialogChamber.Name);
}
if (chambers.Count == 0)
return;
CurrentRecipe.SaveTo(chambers.ToArray());
Save(CurrentRecipe, false);
}
#endregion
#region Steps
/// <summary>
/// 展开DataGrid列组。
/// </summary>
/// <param name="col"></param>
public void ExpandColumnsGroup(ExpanderColumn col)
{
foreach (var subCol in col.ChildColumns)
{
subCol.UpdateVisibility();
}
}
/// <summary>
/// 折叠DataGrid列组。
/// </summary>
/// <param name="col"></param>
public void CollapseColumnsGroup(ExpanderColumn col)
{
foreach (var subCol in col.ChildColumns)
{
subCol.Visibility = Visibility.Collapsed;
}
}
/// <summary>
/// 检查是否所有的参数均有效。
/// </summary>
/// <returns></returns>
private bool CheckIfAllCellsValid(out string errorMessage)
{
errorMessage = string.Empty;
if (CurrentProcessType == ProcessTypeFileList[1].ProcessType)
{
return true;
}
/*var lstError = new List<string>();*/
_validationResultList.Clear();
for (var a = 0; a < CurrentRecipe.Steps.Count; a++)
{
var invalidParam =
CurrentRecipe.Steps[a]
.Where(x => x is IParam p && p.IsValidated == false).ToList();
_validationResultList.AddRange(invalidParam.Cast<IParam>()
.Select(x => new RecipeStepValidationInfo(
_currentRecipe.Steps[a],
x,
x.ValidationError)));
}
if (_validationResultList.Count <= 0)
{
ErrorsCount = 0;
return true;
}
ErrorsCount = _validationResultList.Count;
var errCnt = 0;
var strInfo = new StringBuilder();
foreach (var t in _validationResultList)
{
strInfo.AppendLine(t.ToString());
errCnt++;
if (errCnt > 10)
break;
}
if (errCnt < _validationResultList.Count)
strInfo.AppendLine("\r\n<please check for more errors...>");
// MessageBox.Show(strInfo.ToString(), "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
errorMessage = strInfo.ToString();
return false;
}
public void SaveRecipe()
{
/*if (Save(CurrentRecipe, false))
MessageBox.Show($"The recipe has been saved successfully!", "Succeeded", MessageBoxButton.OK,
MessageBoxImage.Information);*/
Save(CurrentRecipe, false);
}
public void PopSetting(string controlName, Param paramData)
{
var stepNum = Convert.ToInt32(((StepParam)paramData.Parent[1]).Value);
var dialog = new PublicPopSettingDialogViewModel();
dialog.DisplayName = paramData.DisplayName;
var Parameters = new RecipeStep(null);
Parameters = CurrentRecipe.PopSettingSteps[controlName][stepNum - 1];
var ControlParameters = new RecipeStep(null);
var BrandParameters = new ObservableCollection<BandParam>();
foreach (var item in Parameters)
{
if (item.Name.Contains("Band"))
{
var name = item.Name.Replace("Wavelength", "").Replace("Bandwidth", "");
var displayName = item.DisplayName.Replace("Wavelength", "").Replace("Bandwidth", "");
if (BrandParameters.Where(x => x.Name == name).Count() == 0)
{
BrandParameters.Add(new BandParam()
{
Name = name,
DisplayName = displayName
});
}
if (item.Name.Contains("Wavelength"))
{
BrandParameters.First(x => x.Name == name).WavelengthDoubleParam = item;
}
else if (item.Name.Contains("Bandwidth"))
{
BrandParameters.First(x => x.Name == name).BandwidthDoubleParam = item;
}
}
else
ControlParameters.Add(item);
}
dialog.Parameters = Parameters;
dialog.ControlParameters = ControlParameters;
dialog.BandParameters = BrandParameters;
var wm = new WindowManager();
var bret = wm.ShowDialog(dialog);
if (bret == true)
{
CurrentRecipe.PopSettingSteps[controlName][stepNum - 1] = dialog.Parameters;
}
}
private bool Save(RecipeData recipe, bool createNew)
{
if (string.IsNullOrEmpty(recipe.Name))
{
MessageBox.Show("Recipe name can't be empty", "Error",
MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
ValidateEntireRecipe();
if (CheckIfAllCellsValid(out var errors) == false)
{
var mbr = MessageBox.Show($"{errors}\r\n Are you sure to continue to save?", "Warning",
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (mbr == MessageBoxResult.No)
return false;
}
var result = false;
recipe.Revisor = BaseApp.Instance.UserContext.LoginName;
recipe.ReviseTime = DateTime.Now;
result = _recipeProvider.WriteRecipeFile(recipe.PrefixPath, recipe.Name, recipe.GetXmlString());
if (result)
{
recipe.DataSaved();
_editMode = EditMode.Normal;
UpdateView();
}
else
{
MessageBox.Show("Save failed!");
}
return result;
}
public void AddStep()
{
Columns.Clear();
//add时重新从配方中加载数据
Columns = _columnBuilder.Build($"{CurrentChamberType}\\{CurrentProcessType}", SelectedChamber, true, BaseApp.Instance.UserContext.RoleName);
CurrentRecipe.Steps.Add(CurrentRecipe.CreateStep(Columns));
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
ValidateEntireRecipe();
CheckIfAllCellsValid(out _);
UpdateView();
}
public void InsertStepToLeft()
{
if (SelectedRecipeSteps.Count != 1)
return;
var stepNo = SelectedRecipeSteps[0]?.StepNo;
if (RecipeStep.ValidateStepNo(stepNo, out var vStepNo) == false)
return;
var index = vStepNo - RecipeStep.START_INDEX;
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
CurrentRecipe.Steps.Insert(index, CurrentRecipe.CreateStep(Columns));
}
public void InsertStepToRight()
{
if (SelectedRecipeSteps.Count != 1)
return;
var stepNo = SelectedRecipeSteps[0]?.StepNo;
if (RecipeStep.ValidateStepNo(stepNo, out var vStepNo) == false)
return;
var index = vStepNo - RecipeStep.START_INDEX;
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
if (index < _currentRecipe.Steps.Count - 1)
CurrentRecipe.Steps.Insert(index + 1, CurrentRecipe.CreateStep(Columns));
else
CurrentRecipe.Steps.Add(CurrentRecipe.CreateStep(Columns));
}
private readonly RecipeStepCollection _copySteps =
new RecipeStepCollection();
private readonly Dictionary<string, ObservableCollection<RecipeStep>> _popCopySteps =
new Dictionary<string, ObservableCollection<RecipeStep>>();
/// <summary>
/// 拷贝所有选中的Steps。
/// </summary>
/// <param name="steps"></param>
public void CopyStep()
{
_copySteps.Clear();
_popCopySteps.Clear();
if (SelectedRecipeSteps == null || SelectedRecipeSteps.Count <= 0)
return;
foreach (var step in SelectedRecipeSteps)
_copySteps.Add(_currentRecipe.CloneStep(Columns, step));
CurrentRecipe.ValidLoopData();
}
/// <summary>
/// 在左侧粘贴Step。
/// </summary>
public async void PasteStepToLeft()
{
await Paste(true);
}
/// <summary>
/// 在右侧粘贴Step。
/// </summary>
public async void PasteStepToRight()
{
await Paste(false);
}
/// <summary>
/// 粘贴Step。
/// <para>注意async Task 标记的方法不能直接应用于Caliburn.Message.Attach 方法。</para>
/// </summary>
/// <param name="isToLeft">是否粘贴到当前选中的Step的左侧。</param>
/// <returns></returns>
private async Task Paste(bool isToLeft)
{
if (SelectedRecipeSteps == null || SelectedRecipeSteps.Count != 1)
return;
if (_copySteps.Count <= 0)
return;
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
var stepNo = SelectedRecipeSteps[0].StepNo;
if (RecipeStep.ValidateStepNo(stepNo, out var vStepNo) == false)
return;
foreach (var t in _copySteps)
{
var cloned = CurrentRecipe.CloneStep(Columns, t);
var pos = isToLeft ? vStepNo - RecipeStep.START_INDEX : vStepNo;
await Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
(Action)(() => { CurrentRecipe.Steps.Insert(pos, cloned); }));
}
ValidateEntireRecipe();
CurrentRecipe.ValidLoopData();
CheckIfAllCellsValid(out _);
UpdateView();
}
/// <summary>
/// 在左侧复制Step。
/// </summary>
public async void DuplicateStepToLeft()
{
await Duplicate(true);
}
/// <summary>
/// 在右侧复制Step。
/// </summary>
public async void DuplicateStepToRight()
{
await Duplicate(false);
}
/// <summary>
/// 复制选中的Step。
/// </summary>
/// <param name="isToLeft">是否复制到选中的Step的左侧。</param>
private async Task Duplicate(bool isToLeft)
{
if (SelectedRecipeSteps == null || SelectedRecipeSteps.Count != 1)
return;
CopyStep();
await Paste(isToLeft);
}
/// <summary>
/// 删除步骤。
/// </summary>
public void DeleteStep()
{
if (_editMode != EditMode.New && _editMode != EditMode.ReName)
_editMode = EditMode.Edit;
for (var i = SelectedRecipeSteps.Count - 1; i >= 0; i--)
{
_currentRecipe.Steps.Remove(SelectedRecipeSteps[i]);
}
CheckIfAllCellsValid(out _);
}
private void CreateValidationDetailWindow()
{
_winValidationInfo = new RecipeEditorValidationDetailWindow(_validationResultList, _lastPosOfValidationWin);
_winValidationInfo.DataContext = this;
_winValidationInfo.Closed += (sender, args) =>
{
_lastPosOfValidationWin = new Point(_winValidationInfo.Left, _winValidationInfo.Top);
ResetHighlight();
};
}
public void ShowValidationDetailWindow()
{
ValidateEntireRecipe();
CheckIfAllCellsValid(out _);
if (Application.Current.Windows.OfType<RecipeEditorValidationDetailWindow>().Any())
_winValidationInfo.Activate();
else
{
CreateValidationDetailWindow();
_winValidationInfo.Show();
}
}
public void ShowHideParamValues()
{
CurrentRecipe.Steps.IsHideValue = !CurrentRecipe.Steps.IsHideValue;
}
private TreeViewItem GetParentObjectEx<TreeViewItem>(DependencyObject obj) where TreeViewItem : FrameworkElement
{
var parent = VisualTreeHelper.GetParent(obj);
while (parent != null)
{
if (parent is TreeViewItem)
{
return (TreeViewItem)parent;
}
parent = VisualTreeHelper.GetParent(parent);
}
return null;
}
public void TreeRightMouseDown(MouseButtonEventArgs e)
{
var item = GetParentObjectEx<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
if (item != null)
{
item.Focus();
}
}
/// <summary>
/// 在DataGrid中聚焦并高亮指定的参数。
/// </summary>
/// <param name="param"></param>
public void FocusToParam(IParam param)
{
if (param == null)
return;
ResetHighlight();
var row = param.RowOwner;
var col = param.ColumnOwner;
if (row != null && col != null)
{
((RecipeEditorView)View).dgCustom.ScrollIntoView(param.Parent, col);
param.Highlight();
}
}
public void ResetHighlight()
{
_currentRecipe.Steps.ResetHighlight();
}
#endregion
private void ClearData()
{
_editMode = EditMode.None;
CurrentRecipe.Clear();
CurrentRecipe.Name = string.Empty;
CurrentRecipe.Description = string.Empty;
}
/// <summary>
/// 根据配置获取Cascade方式呈现Recipe的Dispatcher或返回null。
/// </summary>
/// <returns></returns>
private static Dispatcher GetLoadingDispatcher()
{
// 判断Step呈现方式
var isCascadeLoading = (bool)QueryDataClient.Instance.Service.GetConfig("System.Recipe.CascadeLoading");
var dispatcher = isCascadeLoading ? Dispatcher.CurrentDispatcher : null;
return dispatcher;
}
/// <summary>
/// 从配置文件中获取是否加载Recipe后自动隐藏参数值的配置。
/// </summary>
/// <returns></returns>
private static bool GetIsHideValueAfterLoading()
{
try
{
return false;
//return (bool)QueryDataClient.Instance.Service.GetConfig("System.Recipe.HideParamValueAfterLoading");
}
catch
{
// 如果没有找到参数,默认隐藏参数值。
return false;
}
}
private async Task LoadData(string prefixPath, string recipeName)
{
var dg = ((RecipeEditorView)View).dgCustom;
IsLoading = true;
CurrentRecipe.Clear();
CurrentRecipe.Steps.Save(); // 重置为已保存状态
ErrorsCount = 0;
var recipeContent = _recipeProvider.ReadRecipeFile(prefixPath, recipeName);
if (string.IsNullOrEmpty(recipeContent))
{
MessageBox.Show($"{prefixPath}\\{recipeName} is empty, please confirm the file is valid.",
"Error",
MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
CurrentRecipe.RecipeChamberType = _columnBuilder.RecipeChamberType;
CurrentRecipe.RecipeVersion = _columnBuilder.RecipeVersion;
CurrentRecipe.Steps.IsHideValue = GetIsHideValueAfterLoading();
await CurrentRecipe.InitData(prefixPath, recipeName, recipeContent, Columns,
_columnBuilder.Configs, SelectedChamber, GetLoadingDispatcher());
_editMode = EditMode.Normal;
IsLoading = false;
// 如果当前Recipe非本机Recipe比例相关的参数可能由于算法不同导致结果不同
// 重新计算一次Recipe以更新比例计算值并校验其合法性。
ValidateEntireRecipe();
CheckIfAllCellsValid(out var _);
}
private void UpdateView()
{
var isFileSelected = CurrentFileNode != null && CurrentFileNode.IsFile;
EnableNew = isFileSelected;
EnableReName = isFileSelected;
EnableCopy = isFileSelected;
EnableDelete = isFileSelected;
EnableSave = isFileSelected;
EnableStep = isFileSelected;
EnableSaveTo = isFileSelected;
EnableSaveToAll = isFileSelected;
if (_editMode == EditMode.None)
{
EnableNew = true;
EnableReName = false;
EnableCopy = false;
EnableDelete = false;
EnableStep = false;
EnableSave = false;
EnableReload = true;
}
NotifyOfPropertyChange(nameof(EnableNew));
NotifyOfPropertyChange(nameof(EnableReName));
NotifyOfPropertyChange(nameof(EnableCopy));
NotifyOfPropertyChange(nameof(EnableDelete));
NotifyOfPropertyChange(nameof(EnableSave));
NotifyOfPropertyChange(nameof(EnableStep));
NotifyOfPropertyChange(nameof(EnableSaveTo));
NotifyOfPropertyChange(nameof(EnableSaveToAll));
NotifyOfPropertyChange(nameof(EnableReload));
NotifyOfPropertyChange(nameof(CurrentRecipe));
}
private string _currentCriteria = string.Empty;
public string CurrentCriteria
{
get => _currentCriteria;
set
{
if (value == _currentCriteria)
return;
_currentCriteria = value;
NotifyOfPropertyChange(nameof(CurrentCriteria));
ApplyFilter();
}
}
private void ApplyFilter()
{
ProcessTypeFileList[ProcessTypeIndexSelection].FilterFileListByProcessType =
new ObservableCollection<FileNode>(ProcessTypeFileList[ProcessTypeIndexSelection].FileListByProcessType
.Where(d => d.Name.IndexOf(CurrentCriteria, StringComparison.OrdinalIgnoreCase) >= 0));
}
public void ClearFilter()
{
CurrentCriteria = "";
}
/// <summary>
/// 当主窗口状态Login状态发生变化时处理当前窗口状态。
/// </summary>
/// <param name="message"></param>
public void Handle(UserMode message)
{
switch (message)
{
case UserMode.None:
break;
case UserMode.Normal:
break;
case UserMode.Lock:
case UserMode.Logoff:
case UserMode.Exit:
case UserMode.Shutdown:
case UserMode.Breakdown:
_winValidationInfo?.Close();
break;
default:
// ignore
break;
}
}
}
}