Sic.Framework-Nanjing-Baishi/MECF.Framework.UI.Client/RecipeEditorLib/RecipeModel/RecipeStepCollection.cs

420 lines
12 KiB
C#
Raw Normal View History

2023-04-13 11:51:03 +08:00
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel.Params;
using Sicentury.Core;
2023-04-13 11:51:03 +08:00
namespace MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel
2023-04-13 11:51:03 +08:00
{
/// <summary>
/// 配方步骤集合。
/// </summary>
/// <remarks>
/// 该集合继承自<see cref="ObservableCollection{T}"/>。
/// </remarks>
public sealed class RecipeStepCollection : ObservableCollection<RecipeStep>, IDisposable
2023-04-13 11:51:03 +08:00
{
#region Variables
public event EventHandler<bool> OnSavedStateChanged;
public event EventHandler HighlightStateChanged;
public event EventHandler GasFlowCalculated;
private bool _isLoadingRecipe;
private bool? _isAllStepsSelected;
2023-04-13 11:51:03 +08:00
private bool _isSaved;
#endregion
#region Constructors
/// <summary>
/// 创建配方步骤集合的实例。
/// </summary>
public RecipeStepCollection()
2023-04-13 11:51:03 +08:00
{
_isSaved = true;
}
/// <summary>
/// 创建配方步骤集合的实例。
/// </summary>
/// <param name="parent">
/// 父级配方对象,请参考<see cref="RecipeData"/>。
/// </param>
public RecipeStepCollection(RecipeData parent) : this()
2023-04-13 11:51:03 +08:00
{
Parent = parent;
}
/// <summary>
/// 创建配方步骤集合的实例。
/// </summary>
/// <param name="steps">配方步骤数组。</param>
/// <param name="parent">
/// 父级配方对象,请参考<see cref="RecipeData"/>。
/// </param>
public RecipeStepCollection(IList<RecipeStep> steps, RecipeData parent)
: base(steps)
{
_isSaved = true;
Parent = parent;
foreach (var step in steps)
step.Parent = parent;
2023-04-13 11:51:03 +08:00
}
#endregion
2023-04-13 11:51:03 +08:00
#region Properties
/// <summary>
/// 返回父级配方对象的实例。
/// </summary>
public RecipeData Parent { get; }
2023-04-13 11:51:03 +08:00
/// <summary>
/// 返回当前配方是否已经被保存。
/// </summary>
public bool IsSaved
{
get => _isSaved;
private set
2023-04-13 11:51:03 +08:00
{
_isSaved = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsSaved)));
OnSavedStateChanged?.Invoke(this, _isSaved);
}
}
public bool? IsAllStepsSelected
{
get
{
var groupSelection = this.GroupBy(x => x.IsSelected).ToList();
if (groupSelection.Count == 1)
_isAllStepsSelected = groupSelection[0].Key;
else
_isAllStepsSelected = null;
return _isAllStepsSelected;
}
set
{
if (!_isAllStepsSelected.HasValue || _isAllStepsSelected == true)
DeselectAll();
else
SelectAll();
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsAllStepsSelected)));
2023-04-13 11:51:03 +08:00
}
}
/// <summary>
/// 返回被选中的配方步骤。
/// </summary>
public IReadOnlyList<RecipeStep> SelectedSteps =>
this.ToList().Where(x => x.StepNoParam != null && x.StepNoParam.IsChecked).ToList();
#endregion
#region Methods
/// <summary>
/// 当配方中的某步骤的IsSaved状态发生变化时触发此事件。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ItemOnSavedStateChanged(object sender, bool e)
{
if (e == false)
IsSaved = false;
else
IsSaved = (this.ToList().FirstOrDefault(x => x.IsSaved == false) == null);
}
/// <summary>
/// 当配方步骤的选中状态发生变化时,触发此事件。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void StepOnSelectionStateChanged(object sender, bool e)
{
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsAllStepsSelected)));
}
2023-04-13 11:51:03 +08:00
/// <summary>
/// 更新Step序列号。
/// </summary>
/// <param name="markAsSaved"></param>
public void UpdateStepIndex(bool markAsSaved = false)
2023-04-13 11:51:03 +08:00
{
var step = this.FirstOrDefault();
var stepNo = step?.StepNoParam;
if (stepNo == null)
2023-04-13 11:51:03 +08:00
return;
var index = RecipeFormatBuilder.STEP_INDEX_BASE;
2023-04-13 11:51:03 +08:00
while (true)
{
stepNo.Value = index;
if (markAsSaved)
stepNo.Save();
2023-04-13 11:51:03 +08:00
//! 最后一个步骤的Next应该为Null此时跳出循环。
if (stepNo.Next is StepParam nextParam)
stepNo = nextParam;
2023-04-13 11:51:03 +08:00
else
break;
index++;
}
}
/// <summary>
/// 开始加载Recipe。
/// </summary>
public IDisposable BeginLoading()
{
_isLoadingRecipe = true;
IsSaved = true;
return this;
}
/// <summary>
/// 结束加载Recipe
/// </summary>
public void EndLoading()
{
this.ToList().ForEach(step =>
{
step.Validate();
});
_isLoadingRecipe = false;
IsSaved = true;
}
/// <summary>
/// 选择当前集合中的所有配方步骤。
/// </summary>
public void SelectAll()
{
foreach (var step in this)
{
step.IsSelected = true;
}
}
/// <summary>
/// 取消选择当前集合中的所有配方步骤。
/// </summary>
public void DeselectAll()
{
foreach (var step in this)
{
step.IsSelected = false;
}
}
/// <summary>
/// 反向选择当前集合中的所有配方步骤。
/// </summary>
public void ReverseSelection()
{
foreach (var step in this)
{
step.IsSelected = !step.IsSelected;
}
}
2023-04-13 11:51:03 +08:00
/// <summary>
/// 保存当前配方。
/// </summary>
public void Save()
{
this.ToList().ForEach(x => x.Save());
}
/// <summary>
/// 取消所有参数的Highlight状态。
/// </summary>
public void ResetHighlight()
{
this.ToList().ForEach(x => x.ResetHighlight());
}
2023-04-13 11:51:03 +08:00
#endregion
#region Overrided Methods
/// <summary>
///
/// </summary>
/// <param name="previous"></param>
/// <param name="next"></param>
private static void LinkSteps(RecipeStep previous, RecipeStep next)
{
if (previous != null && next != null)
{
previous.SetNextStep(next);
next.SetPreviousStep(previous);
}
else if (previous == null && next != null)
{
next.SetPreviousStep(null);
}
else
{
previous?.SetNextStep(null);
}
}
protected override void SetItem(int index, RecipeStep item)
{
throw new NotSupportedException();
}
protected override void InsertItem(int index, RecipeStep step)
2023-04-13 11:51:03 +08:00
{
if (step != null)
{
// 如果Step没有分配Uid先分配一个。
if (string.IsNullOrEmpty(step.StepUid))
{
// 尝试20次如果均重号则报错。
var retry = 0;
for (retry = 0; retry < 20; retry++)
{
var uid = GlobalDefs.GetShortUid();
if (this.FirstOrDefault(x => x.StepUid == uid) == null)
{
step.StepUid = uid;
break;
}
}
if (retry == 20)
throw new Exception("unable to add recipe step since the exclusive UID can not be assigned.");
}
}
base.InsertItem(index, step);
2023-04-13 11:51:03 +08:00
if (step != null)
2023-04-13 11:51:03 +08:00
{
step.Parent = Parent;
step.SavedStateChanged += ItemOnSavedStateChanged;
step.SelectionStateChanged += StepOnSelectionStateChanged;
step.HighlightStateChanged += HighlightStateChanged;
step.GasFlowCalculated += GasFlowCalculated;
2023-04-13 11:51:03 +08:00
if (index > 0 && index < Count - 1)
{
LinkSteps(this[index - 1], this[index]);
LinkSteps(this[index], this[index + 1]);
}
else if (index == 0)
{
if (Count > 1)
{
LinkSteps(null, this[index]);
LinkSteps(this[index], this[index + 1]);
}
else
{
LinkSteps(null, this[index]);
LinkSteps(this[index], null);
}
}
else if (index == Count - 1)
{
LinkSteps(this[index - 1], this[index]);
LinkSteps(this[index], null);
}
}
// 如果不是加载Recipe状态则为新增步骤需要校验并更新保存状态
if (!_isLoadingRecipe)
{
UpdateStepIndex();
this[index].Validate();
// 强制标记为为保存状态
IsSaved = false;
}
2023-04-13 11:51:03 +08:00
}
protected override void RemoveItem(int index)
{
if (Count > 0)
{
if (index >= 0 && index < Count)
{
this[index].SavedStateChanged -= ItemOnSavedStateChanged;
this[index].SelectionStateChanged -= StepOnSelectionStateChanged;
this[index].HighlightStateChanged -= HighlightStateChanged;
this[index].GasFlowCalculated -= GasFlowCalculated;
2023-04-13 11:51:03 +08:00
}
}
IsSaved = false;
// 将前后两个Param链接起来
var preStep = this[index].Previous;
var nextStep = this[index].Next;
LinkSteps(preStep, nextStep);
preStep?.Validate();
nextStep?.Validate();
base.RemoveItem(index);
UpdateStepIndex();
// 强制标记为为保存状态
IsSaved = false;
2023-04-13 11:51:03 +08:00
}
protected override void ClearItems()
{
if (Count > 0)
{
this.ToList().ForEach(x =>
{
x.SavedStateChanged -= ItemOnSavedStateChanged;
x.SelectionStateChanged -= StepOnSelectionStateChanged;
x.HighlightStateChanged -= HighlightStateChanged;
x.GasFlowCalculated -= GasFlowCalculated;
});
2023-04-13 11:51:03 +08:00
IsSaved = false;
}
else
{
IsSaved = true;
}
base.ClearItems();
// 强制标记为为保存状态
IsSaved = false;
2023-04-13 11:51:03 +08:00
}
protected override void MoveItem(int oldIndex, int newIndex)
{
throw new NotSupportedException();
}
#endregion
public void Dispose()
{
}
2023-04-13 11:51:03 +08:00
}
}