| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 | 
							- 
 
- using System;
 
- using System.Collections.Generic;
 
- using System.Collections.Specialized;
 
- using System.Diagnostics;
 
- using System.Linq;
 
- using System.Windows;
 
- using Aitex.Core.RT.DataCenter;
 
- using Aitex.Core.RT.Event;
 
- using Aitex.Core.Util;
 
- using MECF.Framework.Common.DataCenter;
 
- using MECF.Framework.UI.Client.ClientBase.EventArgs;
 
- namespace MECF.Framework.UI.Client.ClientBase.Tree
 
- {
 
-     public class TreeNode : BindableBase
 
-     {
 
-         #region Variables
 
-         /// <summary>
 
-         /// 当末端的节点选择属性发生变化时触发此事件。
 
-         /// </summary>
 
-         public event EventHandler<TreeNodeSelectionChangedEventArgs> TerminalNodeSelectionChanged;
 
-         /// <summary>
 
-         /// 当子节点中有元素发生变化时触发事件。
 
-         /// </summary>
 
-         public event EventHandler<NotifyCollectionChangedEventArgs> ChildNodesChanged;
 
-         private bool? _isSelected = false;
 
-         private double _averageValue;
 
-         private double _minValue;
 
-         private double _maxValue;
 
-         private bool _isExpanded;
 
-         private bool _isMatch = true;
 
-         private string _filterKeyword;
 
-         private Visibility _visibility = Visibility.Visible;
 
-         private int _totalTerminalCount;
 
-         private int _selectedTerminalCount;
 
-         /// <summary>
 
-         /// 挂起更新操作时的变量锁,防止多线程问题。
 
-         /// </summary>
 
-         private readonly object _suspendUpdateLocker;
 
-         /// <summary>
 
-         /// 时否挂起更新操作。
 
-         /// </summary>
 
-         private bool _isUpdateSuspended;
 
-         #endregion
 
-         #region Constructors
 
-         public TreeNode(string name)
 
-         {
 
-             _suspendUpdateLocker = new object();
 
-             _isUpdateSuspended = false;
 
-             Name = name;
 
-             DisplayName = name;
 
-             
 
-             
 
-             //RawData = new List<ParameterNodePoint>();
 
-             MaxTerminalSelectionAllowed = -1;
 
-             ChildNodes = new TreeNodeCollection(this);
 
-             ChildNodes.TerminalNodeSelectionChanged += OnTerminalNodeSelectionChanged;
 
-             ChildNodes.CollectionChanged += ChildNodesOnCollectionChanged;
 
-         }
 
-         #endregion
 
-         #region Properties
 
-         /// <summary>
 
-         /// 当前树中被选中的终端节点的数量。
 
-         /// </summary>
 
-         public int SelectedTerminalCount
 
-         {
 
-             get => _selectedTerminalCount;
 
-             private set => Set(ref _selectedTerminalCount, value);
 
-         }
 
-         /// <summary>
 
-         /// 当前树中终端节点的总数量。
 
-         /// </summary>
 
-         public int TotalTerminalCount
 
-         {
 
-             get => _totalTerminalCount;
 
-             private set => Set(ref _totalTerminalCount, value);
 
-         }
 
-         /// <summary>
 
-         /// 返回允许的最大终端节点选中数量。
 
-         /// <para>-1表示可选中所有终端节点。</para>
 
-         /// </summary>
 
-         public int MaxTerminalSelectionAllowed { get; set; }
 
-         public bool? IsSelected
 
-         {
 
-             get => _isSelected;
 
-             set
 
-             {
 
-                 if (value == _isSelected)
 
-                 {
 
-                     OnPropertyChanged();
 
-                     return;
 
-                 }
 
-                 var snapshot = _isSelected;
 
-                 Set(ref _isSelected, value);
 
-                 // 仅当该节点未最末端节点时触发事件。
 
-                 var cancelEventArgs = new TreeNodeSelectionChangedEventArgs(this, snapshot, value);
 
-                 RaiseTerminalNodeSelectionChangedEvent(cancelEventArgs);
 
-                 if (cancelEventArgs.Cancel)
 
-                 {
 
-                     _isSelected = snapshot;
 
-                     OnPropertyChanged();
 
-                 }
 
-                 // 处理子节点的选择状态
 
-                 UpdateChildrenSelection();
 
-                 // 处理我的状态。
 
-                 UpdateSelectionState(this);
 
-                 // 处理父节点的选择状态
 
-                 UpdateParentSelection();
 
-             }
 
-         }
 
-         public string Name { get; }
 
-         public string DisplayName { get; set; }
 
-         /// <summary>
 
-         /// 返回包含完整路劲的节点名称。
 
-         /// </summary>
 
-         public string FullName => ToString();
 
-         public double AverageValue
 
-         {
 
-             get => _averageValue;
 
-             private set => Set(ref _averageValue, value);
 
-         }
 
-         public double MinValue
 
-         {
 
-             get => _minValue;
 
-             private set => Set(ref _minValue, value);
 
-         }
 
-         public double MaxValue
 
-         {
 
-             get => _maxValue;
 
-             private set => Set(ref _maxValue, value);
 
-         }
 
-         /// <summary>
 
-         /// 是否展开节点。
 
-         /// </summary>
 
-         public bool IsExpanded
 
-         {
 
-             get => _isExpanded;
 
-             set => Set(ref _isExpanded, value);
 
-         }
 
-         /// <summary>
 
-         /// 是否包含Filter指定的关键字。
 
-         /// </summary>
 
-         public bool IsMatch
 
-         {
 
-             get => _isMatch;
 
-             set => Set(ref _isMatch, value);
 
-         }
 
-         /// <summary>
 
-         /// 设置或返回节点是否可见。
 
-         /// </summary>
 
-         public Visibility Visibility
 
-         {
 
-             get => _visibility;
 
-             set
 
-             {
 
-                 Set(ref _visibility, value);
 
-                 if (IsTerminal)
 
-                     UpdateParentVisibility(ParentNode);
 
-             }
 
-         }
 
-         public TreeNode ParentNode { get; set; }
 
-         //public List<ParameterNodePoint> RawData { get; }
 
-         public Visibility IsVisibilityParentNode { get; set; }
 
-         public TreeNodeCollection ChildNodes { get; set; }
 
-         /// <summary>
 
-         /// 筛选器的关键字。
 
-         /// </summary>
 
-         public string FilterKeyWord
 
-         {
 
-             get => _filterKeyword;
 
-             set => Set(ref _filterKeyword, value);
 
-         }
 
-         /// <summary>
 
-         /// 返回我是否为最末端节点。
 
-         /// </summary>
 
-         public bool IsTerminal => (ChildNodes == null || ChildNodes.Count == 0);
 
-         /// <summary>
 
-         /// 返回我是否属于一级节点。
 
-         /// </summary>
 
-         public bool IsTopLevel => ParentNode == null;
 
-         /// <summary>
 
-         /// 返回我是否属于二级节点。
 
-         /// </summary>
 
-         public bool IsSecondLevel => ParentNode != null && ParentNode.ParentNode == null;
 
-         /// <summary>
 
-         /// 返回是否有终端节点被选中。
 
-         /// </summary>
 
-         public bool HasTerminalSelected => Flatten(true).FirstOrDefault(x => x.IsSelected == true) != null;
 
-         #endregion
 
-         #region Methods
 
-         /// <summary>
 
-         /// 如果是末端节点,触发事件。
 
-         /// </summary>
 
-         private void RaiseTerminalNodeSelectionChangedEvent(TreeNodeSelectionChangedEventArgs e)
 
-         {
 
-             // 仅当该节点未最末端节点时触发事件。
 
-             if (IsTerminal)
 
-                 TerminalNodeSelectionChanged?.Invoke(this, e);
 
-         }
 
-         /// <summary>
 
-         /// 处理子节点和父节点的选择状态。
 
-         /// </summary>
 
-         private void UpdateChildrenSelection()
 
-         {
 
-             if (_isSelected.HasValue == false)
 
-                 return;
 
-             // 刷新子节点的选择状态
 
-             if (IsTerminal)
 
-                 return;
 
-             //! 遍历子节点时,本节点的_isSelected属性可能被改写。
 
-             // 例如:某个终端节点的Selected改变时,会反向设置父节点的Selected属性;则接下来所有子节点赋值Selected属性时会使用新值。
 
-             var fixedIsSelected = _isSelected;
 
-             Flatten(true).ToList()
 
-                 .ForEach(x =>
 
-                 {
 
-                     if (x.IsMatch)
 
-                         x.SetSelectionProperty(fixedIsSelected);
 
-                 });
 
-         }
 
-         /// <summary>
 
-         /// 刷新父节点的选择状态。
 
-         /// </summary>
 
-         public void UpdateParentSelection()
 
-         {
 
-             if (ParentNode == null)
 
-                 return;
 
-             // 递归更新IsSelected属性
 
-             UpdateSelectionState(ParentNode);
 
-             ParentNode.UpdateParentSelection();
 
-         }
 
-         /// <summary>
 
-         /// 刷新指定节点的IsSelected属性。
 
-         /// </summary>
 
-         /// <param name="node"></param>
 
-         private static void UpdateSelectionState(TreeNode node)
 
-         {
 
-             if (node.IsTerminal)
 
-                 return;
 
-             // 刷新父节点的选择状态
 
-             var selectionGroup = node.Flatten(true).GroupBy(x => x.IsSelected);
 
-             var enumerable = selectionGroup.ToList();
 
-             if (!enumerable.Any())
 
-                 return;
 
-             if (enumerable.Count() > 1)
 
-                 node.SetSelectionProperty(null);
 
-             else if (enumerable.Count() == 1)
 
-             {
 
-                 var ss = enumerable[0].Key;
 
-                 if (ss.HasValue)
 
-                     node.SetSelectionProperty(ss.Value);
 
-             }
 
-             else
 
-             {
 
-                 // 非法情况
 
-                 Debugger.Break();
 
-             }
 
-         }
 
-         /// <summary>
 
-         /// 刷新父级节点的显示属性。
 
-         /// </summary>
 
-         private static void UpdateParentVisibility(TreeNode node)
 
-         {
 
-             while (true)
 
-             {
 
-                 // 刷新父节点的选择状态
 
-                 var selectionGroup = node.Flatten(true).GroupBy(x => x.Visibility);
 
-                 var enumerable = selectionGroup.ToList();
 
-                 if (!enumerable.Any())
 
-                     return;
 
-                 if (enumerable.Count() > 1)
 
-                     // 如果终端节点存在多种情况,则一定显示本节点。
 
-                     node.Visibility = Visibility.Visible;
 
-                 else if (enumerable.Count == 1)
 
-                 {
 
-                     // 如果存在一种情况,则判断是全部为显示还是全部为折叠。
 
-                     var ss = enumerable[0].Key;
 
-                     if (ss == Visibility.Visible)
 
-                         node.Visibility = Visibility.Visible;
 
-                     else
 
-                         node.Visibility = Visibility.Collapsed;
 
-                 }
 
-                 else
 
-                 {
 
-                     // 非法情况
 
-                     Debugger.Break();
 
-                 }
 
-                 // 如果本节点已经是顶层节点,则退出
 
-                 if (node.ParentNode == null)
 
-                     break;
 
-                 node = node.ParentNode;
 
-             }
 
-         }
 
-         /// <summary>
 
-         /// 当终端节点被选则或取消选择时触发此事件。
 
-         /// </summary>
 
-         /// <param name="sender"></param>
 
-         /// <param name="e"></param>
 
-         private void OnTerminalNodeSelectionChanged(object sender, TreeNodeSelectionChangedEventArgs e)
 
-         {
 
-             //! 在树的根部检查终端节点选中数是否超过最大限制
 
-             if (IsTopLevel)
 
-             {
 
-                 if (MaxTerminalSelectionAllowed > -1)
 
-                     if (GetSelectedTerminalCount() > MaxTerminalSelectionAllowed)
 
-                     {
 
-                         e.Cancel = true;
 
-                         return;
 
-                     }
 
-                 UpdateTerminalNodeCountInfo();
 
-             }
 
-             TerminalNodeSelectionChanged?.Invoke(sender, e);
 
-         }
 
-         /// <summary>
 
-         /// 当ChildNodes元素发生变化时触发此事件。
 
-         /// </summary>
 
-         /// <param name="sender"></param>
 
-         /// <param name="e"></param>
 
-         private void ChildNodesOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
 
-         {
 
-             /*
 
-              * 如果当前节点是顶层节点,则统计终端节点总数,否则继续向父级节点传递事件。
 
-              */
 
-             if (IsTopLevel)
 
-                 UpdateTerminalNodeCountInfo();
 
-             ChildNodesChanged?.Invoke(this, e);
 
-         }
 
-         /// <summary>
 
-         /// 更新终端节点数量信息。
 
-         /// </summary>
 
-         private void UpdateTerminalNodeCountInfo()
 
-         {
 
-             lock (_suspendUpdateLocker)
 
-             {
 
-                 if (_isUpdateSuspended)
 
-                     return;
 
-                 SelectedTerminalCount = GetSelectedTerminalCount();
 
-                 TotalTerminalCount = ChildNodes.Sum(x => x.Flatten(true).ToList().Count);
 
-             }
 
-         }
 
-         /// <summary>
 
-         /// 该方法用于Tree内部设置<see cref="IsSelected"/>属性的状态。
 
-         /// </summary>
 
-         /// <param name="isSelected"></param>
 
-         private void SetSelectionProperty(bool? isSelected)
 
-         {
 
-             var snapshot = _isSelected;
 
-             _isSelected = isSelected;
 
-             var args = new TreeNodeSelectionChangedEventArgs(this, snapshot, _isSelected);
 
-             RaiseTerminalNodeSelectionChangedEvent(args);
 
-             if (args.Cancel)
 
-                 _isSelected = snapshot;
 
-             OnPropertyChanged(nameof(IsSelected));
 
-             if (IsTerminal)
 
-                 UpdateParentSelection();
 
-         }
 
-         /// <summary>
 
-         /// 查找指定完整路径名称的末端节点。
 
-         /// </summary>
 
-         /// <param name="fullName"></param>
 
-         /// <returns></returns>
 
-         public TreeNode FindTerminalByFullName(string fullName)
 
-         {
 
-             return Flatten(true).FirstOrDefault(x => x.FullName == fullName);
 
-         }
 
-         /// <summary>
 
-         /// 获取指定层级的节点。
 
-         /// </summary>
 
-         /// <param name="level">0-based层级,0表示Tree的根节点。</param>
 
-         /// <returns></returns>
 
-         public TreeNode GetNodeByLevel(int level)
 
-         {
 
-             var list = new List<TreeNode>();
 
-             list.Add(this);
 
-             while (true)
 
-             {
 
-                 if (ParentNode != null)
 
-                     list.Add(this.ParentNode);
 
-                 else
 
-                     break;
 
-             }
 
-             if (level >= list.Count)
 
-                 return null;
 
-             return list[level];
 
-         }
 
-         /// <summary>
 
-         /// 获取被选中的终端节点的数量。
 
-         /// </summary>
 
-         /// <returns></returns>
 
-         public int GetSelectedTerminalCount()
 
-         {
 
-             return Flatten(true).Where(x => x.IsSelected == true).ToList().Count;
 
-         }
 
-         /// <summary>
 
-         /// 将数中的节点转换为节点数组。
 
-         /// </summary>
 
-         /// <param name="terminalOnly">是否仅枚举最末端节点。</param>
 
-         /// <para>更新子节点的Selected属性时,需要同时更新中间节点的属性;更新父节点时,只评估终端节点的Selected属性。</para>
 
-         /// <returns></returns>
 
-         public List<TreeNode> Flatten(bool terminalOnly)
 
-         {
 
-             if (ChildNodes == null || ChildNodes.Count <= 0)
 
-                 return new List<TreeNode>(new[] { this });
 
-             var lst = ChildNodes.SelectMany(x => x.Flatten(terminalOnly)).ToList();
 
-             if (!terminalOnly)
 
-                 lst.Add(this);
 
-             return lst;
 
-         }
 
-         /// <summary>
 
-         /// 清除统计数据。
 
-         /// </summary>
 
-         public void ClearStatistic()
 
-         {
 
-             MinValue = double.NaN;
 
-             MaxValue = double.NaN;
 
-             AverageValue = double.NaN;
 
-         }
 
-         /// <summary>
 
-         /// 设置统计数据。
 
-         /// </summary>
 
-         /// <param name="min"></param>
 
-         /// <param name="max"></param>
 
-         /// <param name="average"></param>
 
-         public void SetStatistic(double min, double max, double average)
 
-         {
 
-             MinValue = min;
 
-             MaxValue = max;
 
-             AverageValue = average;
 
-         }
 
-         /// <summary>
 
-         /// 取消所有节点选择状态。
 
-         /// </summary>
 
-         public void UnselectAll()
 
-         {
 
-             SuspendUpdate();
 
-             Flatten(true).ForEach(x => x.IsSelected = false);
 
-             ResumeUpdate();
 
-         }
 
-         /// <summary>
 
-         /// 选择所有节点。
 
-         /// </summary>
 
-         public void SelectAll()
 
-         {
 
-             SuspendUpdate();
 
-             Flatten(true).ForEach(x => x.IsSelected = true);
 
-             ResumeUpdate();
 
-         }
 
-         /// <summary>
 
-         /// 折叠所有节点。
 
-         /// </summary>
 
-         public void CollapseAll()
 
-         {
 
-             Flatten(false).ForEach(x => x.IsExpanded = false);
 
-         }
 
-         /// <summary>
 
-         /// 展开所有节点。
 
-         /// </summary>
 
-         public void ExpandAll()
 
-         {
 
-             Flatten(false).ForEach(x => x.IsExpanded = true);
 
-         }
 
-         /// <summary>
 
-         /// 显示所有节点。
 
-         /// </summary>
 
-         public void ShowAll()
 
-         {
 
-             Flatten(true).ForEach(x => x.Visibility = Visibility.Visible);
 
-         }
 
-         /// <summary>
 
-         /// 显示选中的项目。
 
-         /// </summary>
 
-         public void ShowSelectedOnly()
 
-         {
 
-             Flatten(true).Where(x => x.IsSelected == false).ToList()
 
-                 .ForEach(x => x.Visibility = Visibility.Collapsed);
 
-         }
 
-         /// <summary>
 
-         /// 应用筛选器。
 
-         /// </summary>
 
-         /// <param name="keyWord"></param>
 
-         public void ApplyFilter(string keyWord)
 
-         {
 
-             //! 自顶向下遍历,仅需关心子节点状态。
 
-             // 如果父节点匹配,则我强制匹配。
 
-             IsMatch = (ParentNode != null && ParentNode.IsMatch) || Name.ToLower().Contains(keyWord.ToLower());
 
-             FilterKeyWord = keyWord;
 
-             // 如果是终端节点,则仅检查是否匹配关键字,然后退出
 
-             if (IsTerminal)
 
-                 return;
 
-             // 否则检查我的名称是否匹配关键字,并检查我的子节点中是否有匹配关键字的节点,如果有,则我也要显示。
 
-             foreach (var node in ChildNodes)
 
-             {
 
-                 node.ApplyFilter(keyWord);
 
-             }
 
-             var isMatchedNodes = Flatten(false).Where(x => x.IsMatch).ToList();
 
-             if (isMatchedNodes.Any())
 
-                 IsMatch = true;
 
-         }
 
-         /// <summary>
 
-         /// 清除筛选器。
 
-         /// </summary>
 
-         public void ClearFilter()
 
-         {
 
-             IsMatch = true;
 
-             FilterKeyWord = "";
 
-             if (IsTerminal)
 
-                 return;
 
-             foreach (var node in ChildNodes)
 
-             {
 
-                 node.ClearFilter();
 
-             }
 
-         }
 
-         /// <summary>
 
-         /// 挂起TreeView视图更新。
 
-         /// <para>通常在后端对节点Selected属性操作时,挂起视图更新,以提高性能。</para>
 
-         /// </summary>
 
-         public void SuspendUpdate()
 
-         {
 
-             lock (_suspendUpdateLocker)
 
-             {
 
-                 _isUpdateSuspended = true;
 
-             }
 
-         }
 
-         /// <summary>
 
-         /// 恢复TreeView视图更新。
 
-         /// <para>通常在后端对节点Selected属性操作时,挂起视图更新,以提高性能。</para>
 
-         /// <para>恢复视图更新后,主动更新节点数量统计值。</para>
 
-         /// </summary>
 
-         public void ResumeUpdate()
 
-         {
 
-             lock (_suspendUpdateLocker)
 
-             {
 
-                 _isUpdateSuspended = false;
 
-             }
 
-             UpdateTerminalNodeCountInfo();
 
-         }
 
-         public override string ToString()
 
-         {
 
-             // 完整路径中删除根节点的名称,以确保FullName匹配系统中的定义。
 
-             return (ParentNode?.ParentNode== null) ? Name : $"{ParentNode}.{Name}";
 
-         }
 
-         #endregion
 
-     }
 
- }
 
 
  |