using System;
|
using System.Collections.Generic;
|
using System.Collections.ObjectModel;
|
using System.Collections.Specialized;
|
using System.ComponentModel;
|
using System.Data;
|
using System.Drawing;
|
using System.Linq;
|
using System.Net.Http;
|
using System.Text;
|
using System.Threading.Tasks;
|
using System.Windows.Forms;
|
using DPumpHydr.WinFrmUI.RLT.Controls;
|
using DPumpHydr.WinFrmUI.RLT.Util;
|
using static DPumpHydr.WinFrmUI.RLT.Helper.CrownHelper;
|
|
namespace DPumpHydr.WinFrmUI.Base
|
{
|
public partial class StepTreeView : CrownScrollView
|
{
|
private StepTreeNode _selStepNode = null;
|
private StepTreeNodePara SelStepNodePara {
|
get
|
{
|
if (_selStepNode == null)
|
return null;
|
return _selStepNode.NodePara;
|
}
|
}
|
|
/// <summary>
|
/// 步骤节点选择改变前后事件
|
/// </summary>
|
public event Func<StepTreeNodePara, StepTreeNodePara, bool> BeforeSelectedNodeChangedEvent;
|
public event Func<StepTreeNodePara, StepTreeNodePara, bool> AfterSelectedNodeChangedEvent;
|
|
|
|
/// <summary>
|
///
|
/// </summary>
|
public StepTreeView()
|
{
|
BuildNodeImageList();
|
|
|
|
_nodes = new ObservableList<StepTreeNode>();
|
|
|
|
}
|
|
#region 构建和获取树
|
/// <summary>
|
/// 根据步骤ID聚焦节点
|
/// </summary>
|
/// <param name="StepNodeID"></param>
|
public void SetFocusedNode(long StepNodeID)
|
{
|
this._selStepNode = GetNodeByID(StepNodeID);
|
RefreshAllNodesImage();
|
Invalidate();
|
}
|
/// <summary>
|
/// 内部使用, 不对外
|
/// </summary>
|
/// <param name="node"></param>
|
private void SetSelectNode4Click(StepTreeNode node)
|
{
|
if (node == null || !node.NodePara.AllowSelect)
|
return;
|
|
|
if (BeforeSelectedNodeChangedEvent != null)
|
{
|
if (BeforeSelectedNodeChangedEvent.Invoke(this.SelStepNodePara, node.NodePara) == false)
|
{
|
return;
|
}
|
}
|
|
if (AfterSelectedNodeChangedEvent != null)
|
{
|
if (AfterSelectedNodeChangedEvent.Invoke(this.SelStepNodePara, node.NodePara) == false)
|
{
|
return;
|
}
|
}
|
//
|
_selStepNode = node;
|
|
RefreshAllNodesImage();
|
|
Invalidate();
|
}
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="StepNodeID"></param>
|
/// <returns></returns>
|
private StepTreeNode GetNodeByID(long StepNodeID)
|
{
|
foreach (var node1 in this._nodes)
|
{
|
var step1 = node1.NodePara;
|
if (step1.ID == StepNodeID)
|
{
|
return node1;
|
}
|
|
if (node1.Nodes != null && node1.Nodes.Count > 0)
|
{
|
foreach (var node2 in node1.Nodes)
|
{
|
var step2 = node2.NodePara;
|
if (step2.ID == StepNodeID)
|
{
|
return node2;
|
}
|
|
if (node2.Nodes != null && node2.Nodes.Count > 0)
|
{
|
foreach (var node3 in node2.Nodes)
|
{
|
var step3 = node3.NodePara;
|
if (step3.ID == StepNodeID)
|
{
|
return node3;
|
}
|
}
|
}
|
|
}
|
}
|
}
|
|
return null;
|
}
|
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="node_paras_list"></param>
|
private void BuildTree(List<StepTreeNodePara> node_paras_list )
|
{
|
this._nodes.Clear();
|
var node_paras1 = node_paras_list.Where(x => x.ParentID == 0).OrderByDescending(x => x.ID);
|
foreach (var node_para1 in node_paras1)
|
{
|
StepTreeNode node1 = new(this,node_para1) ;
|
|
var step_paras2 = node_paras_list.Where(x => x.ParentID == node_para1.ID).OrderBy(x => x.ID);
|
for (int x2 = 0; x2 < step_paras2.Count(); x2++)
|
{
|
var step_para2 = step_paras2.ElementAt(x2);
|
StepTreeNode node2 = new(this, step_para2);
|
node1.Nodes.Add(node2);
|
|
var step_paras3 = node_paras_list.Where(x => x.ParentID == step_para2.ID).OrderBy(x => x.ID);
|
if (step_paras3 != null && step_paras3.Count() > 0)
|
{
|
for (int x3 = 0; x3 < step_paras3.Count(); x3++)
|
{
|
var step_para3 = step_paras3.ElementAt(x3);
|
StepTreeNode node3 = new(this, step_para3);
|
node2.Nodes.Add(node3);
|
}
|
node2.Expanded = true;
|
}
|
}
|
|
node1.Expanded = true;
|
|
this._nodes.Add(node1);
|
}
|
|
UpdateNodes();
|
}
|
|
|
/// <summary>
|
/// 设置步骤数据源
|
/// </summary>
|
/// <param name="value"></param>
|
public void SetStepSource(List<StepTreeNodePara> steps)
|
{
|
BuildTree(steps);
|
RefreshAllNodesImage();
|
}
|
|
/// <summary>
|
/// 设置步骤数据源
|
/// </summary>
|
/// <param name="list"></param>
|
/// <param name="selStepNode"></param>
|
public void SetStepSource(List<StepTreeNodePara> steps, long ID)
|
{
|
BuildTree(steps);
|
SetFocusedNode(ID);
|
}
|
|
|
#endregion
|
|
|
|
#region 图标
|
public bool IsShowIcons { get; set; } = true;
|
|
private System.Windows.Forms.ImageList _imageList = null;
|
private void BuildNodeImageList()
|
{
|
_imageList = new System.Windows.Forms.ImageList();
|
_imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
|
_imageList.TransparentColor = System.Drawing.Color.Transparent;
|
_imageList.Images.Add(DPumpHydr.WinFrmUI.Volute.Properties.Resources.errorstep16);//0
|
_imageList.Images.Add(DPumpHydr.WinFrmUI.Volute.Properties.Resources.finishstep16);//1
|
_imageList.Images.Add(DPumpHydr.WinFrmUI.Volute.Properties.Resources.xiangxia32);//2
|
_imageList.Images.Add(DPumpHydr.WinFrmUI.Volute.Properties.Resources.ingstep32); //3
|
_imageList.Images.Add(DPumpHydr.WinFrmUI.Volute.Properties.Resources.WaitingGray32);//4
|
}
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="step_para"></param>
|
/// <returns></returns>
|
private void RefreshAllNodesImage()
|
{
|
foreach (var node1 in this._nodes)
|
{
|
node1.Icon = BuildNodeImage(node1.NodePara);
|
|
if (node1.Nodes != null)
|
{
|
foreach (var node2 in node1.Nodes)
|
{
|
node2.Icon = BuildNodeImage(node2.NodePara);
|
|
//最多遍历3层
|
if (node2.Nodes != null)
|
{
|
foreach (var node3 in node2.Nodes)
|
{
|
node3.Icon = BuildNodeImage(node3.NodePara);
|
}
|
}
|
}
|
}
|
|
}
|
|
|
}
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="node"></param>
|
/// <returns></returns>
|
private System.Drawing.Image BuildNodeImage(StepTreeNodePara node)
|
{
|
if (node.SpecImage != null)
|
return node.SpecImage;
|
if (this._selStepNode != null && node.ID == this._selStepNode.NodePara.ID)
|
{
|
return this._imageList.Images[3];
|
}
|
if (node.ParentID == 0)
|
return this._imageList.Images[2];
|
if (node.DataState == StepTreeNodePara.eDataState.错误)
|
return this._imageList.Images[0];
|
|
if (node.ProgressState == StepTreeNodePara.eProgressState.已完成)
|
return this._imageList.Images[1];
|
if (node.ProgressState == StepTreeNodePara.eProgressState.未完成)
|
return this._imageList.Images[4];
|
|
if (node.ProgressState == StepTreeNodePara.eProgressState.正在进行)
|
return this._imageList.Images[3];
|
return null;
|
}
|
|
//注册事件(备份)
|
private void RegistEvents()
|
{
|
//this.treeList1.GetSelectImage += (sender, e) =>
|
//{
|
// var ID = Convert.ToInt64(e.Node.GetValue("ID"));
|
// var stepNode = GetStepSource().Find(x => x.ID == ID);
|
// if (stepNode == null)
|
// return;
|
// switch (stepNode.DataState)
|
// {
|
// case TreeStepNode.eDataState.错误: e.NodeImageIndex = 0; break;
|
// case TreeStepNode.eDataState.正确: e.NodeImageIndex = 1; break;
|
// case TreeStepNode.eDataState.未知: e.NodeImageIndex = -1; break;
|
// default: break;
|
// }
|
//};
|
|
//this.treeList1.GetStateImage += (sender, e) =>
|
//{
|
// var ID = Convert.ToInt64(e.Node.GetValue("ID"));
|
// var stepNode = GetStepSource().Find(x => x.ID == ID);
|
// if (stepNode == null)
|
// return;
|
// switch (stepNode.ProgressState)
|
// {
|
// case TreeStepNode.eProgressState.未完成: e.NodeImageIndex = 0; break;
|
// case TreeStepNode.eProgressState.正在进行: e.NodeImageIndex = 1; break;
|
// case TreeStepNode.eProgressState.已完成: e.NodeImageIndex = 2; break;
|
// default: break;
|
// }
|
//};
|
|
//this.treeList1.BeforeFocusNode += (sender, e) =>
|
//{
|
// var nextStepID = Convert.ToInt64(e.Node.GetValue("ID"));
|
// var nextStepNode = GetStepSource().Find(x => x.ID == nextStepID);
|
// if (!nextStepNode.AllowSelect)
|
// {
|
// e.CanFocus = false;
|
// return;
|
// }
|
// if (_selStepNode != null)
|
// {
|
// if (_selStepNode.ID == nextStepNode.ID)
|
// return;
|
// }
|
// if (this.BeforeStepNodeSelectedChangedEvent != null)
|
// {
|
// TreeStepNode beforeStepNode = null;
|
// if (e.OldNode != null)
|
// {
|
// var beforeStepID = Convert.ToInt64(e.OldNode.GetValue("ID"));
|
// beforeStepNode = GetStepSource().Find(x => x.ID == beforeStepID);
|
// }
|
// if (!this.BeforeStepNodeSelectedChangedEvent(beforeStepNode, nextStepNode))
|
// {
|
// e.CanFocus = false;
|
// return;
|
// }
|
// }
|
//};
|
|
//this.treeList1.AfterFocusNode += (sender, e) =>
|
//{
|
// var ID = Convert.ToInt64(e.Node.GetValue("ID"));
|
// var nodeModel = GetStepSource().Find(x => x.ID == ID);
|
// this.SelectedStepNode = nodeModel;
|
//};
|
|
//this.treeList1.AfterExpand += (sender, e) =>
|
//{
|
// if (_selStepNode != null)
|
// {
|
// var node = this.treeList1.FindNodeByKeyID(_selStepNode.ID);
|
// if (e.Node.Nodes.Contains(node))
|
// this.treeList1.FocusedNode = node;
|
// }
|
//};
|
}
|
|
#endregion
|
|
|
|
#region Field Region
|
|
private bool _disposed;
|
|
private readonly int _topNodeSpaceWidth = 5;//顶部节点左边的间隔空间
|
private readonly int _iconSize = 16;
|
|
private int _itemHeight = 30;
|
private int _indent = 15;//缩进 父节点给定的水平空间量 Determines the amount of horizontal space given by parent node.
|
|
|
|
|
#endregion
|
|
|
#region 鼠标操作
|
protected override void OnMouseMove(MouseEventArgs e)
|
{
|
CheckNodeHover();
|
|
base.OnMouseMove(e);
|
}
|
|
protected override void OnMouseWheel(MouseEventArgs e)
|
{
|
CheckNodeHover();
|
|
base.OnMouseWheel(e);
|
}
|
|
protected override void OnMouseDown(MouseEventArgs e)
|
{
|
if (e.Button is MouseButtons.Left or MouseButtons.Right)
|
{
|
foreach (StepTreeNode node in _nodes)
|
{
|
CheckNodeClick(node, OffsetMousePosition, e.Button);
|
}
|
}
|
|
base.OnMouseDown(e);
|
}
|
|
protected override void OnMouseClick(MouseEventArgs e)
|
{
|
base.OnMouseClick(e);
|
}
|
|
protected override void OnMouseDoubleClick(MouseEventArgs e)
|
{
|
if (ModifierKeys == Keys.Control)
|
{
|
return;
|
}
|
|
if (e.Button == MouseButtons.Left)
|
{
|
foreach (StepTreeNode node in _nodes)
|
{
|
CheckNodeDoubleClick(node, OffsetMousePosition);
|
}
|
}
|
|
base.OnMouseDoubleClick(e);
|
}
|
|
|
protected override void OnMouseLeave(EventArgs e)
|
{
|
base.OnMouseLeave(e);
|
|
foreach (StepTreeNode node in _nodes)
|
{
|
NodeMouseLeave(node);
|
}
|
}
|
|
|
|
#endregion
|
|
|
|
#region 构建Node
|
|
private ObservableList<StepTreeNode> _nodes;
|
|
private void UpdateNodes()
|
{
|
if (IsDragging)
|
{
|
return;
|
}
|
|
ContentSize = new(0, 0);
|
|
if (_nodes.Count == 0)
|
{
|
return;
|
}
|
|
int yOffset = 0;
|
bool isOdd = false;
|
int index = 0;
|
StepTreeNode prevNode = null;
|
|
for (int i = 0; i <= _nodes.Count - 1; i++)
|
{
|
StepTreeNode node = _nodes[i];
|
UpdateNode(node, ref prevNode, 0, ref yOffset, ref isOdd, ref index);
|
}
|
|
ContentSize = new(ContentSize.Width, yOffset);
|
|
Invalidate();
|
}
|
|
private void UpdateNode(StepTreeNode node, ref StepTreeNode prevNode, int indent, ref int yOffset,
|
ref bool isOdd, ref int index)
|
{
|
UpdateNodeBounds(node, yOffset, indent);
|
|
yOffset += _itemHeight;
|
|
node.Odd = isOdd;
|
isOdd = !isOdd;
|
|
node.VisibleIndex = index;
|
index++;
|
|
node.PrevVisibleNode = prevNode;
|
|
if (prevNode != null)
|
{
|
prevNode.NextVisibleNode = node;
|
}
|
|
prevNode = node;
|
|
if (node.Expanded)
|
{
|
foreach (StepTreeNode childNode in node.Nodes)
|
{
|
UpdateNode(childNode, ref prevNode, indent + _indent, ref yOffset, ref isOdd, ref index);
|
}
|
}
|
}
|
|
private void UpdateNodeBounds(StepTreeNode node, int yOffset, int indent)
|
{
|
int expandTop = yOffset + (_itemHeight / 2) - (_topNodeSpaceWidth / 2);
|
node.ExpandArea = new(indent + 3, expandTop, _topNodeSpaceWidth, _topNodeSpaceWidth);
|
|
int iconTop = yOffset + (_itemHeight / 2) - (_iconSize / 2);
|
|
if (IsShowIcons)
|
{
|
node.IconArea = new(node.ExpandArea.Right + 2, iconTop, _iconSize, _iconSize);
|
}
|
else
|
{
|
node.IconArea = new(node.ExpandArea.Right, iconTop, 0, 0);
|
}
|
|
using (Graphics g = CreateGraphics())
|
{
|
int textSize = (int)g.MeasureString(node.Text, Font).Width;
|
node.TextArea = new(node.IconArea.Right + 2, yOffset, textSize + 1, _itemHeight);
|
}
|
|
node.FullArea = new(indent, yOffset, node.TextArea.Right - indent, _itemHeight);
|
|
if (ContentSize.Width < node.TextArea.Right + 2)
|
{
|
ContentSize = new(node.TextArea.Right + 2, ContentSize.Height);
|
}
|
}
|
|
/// <summary>
|
/// 节点是否悬停
|
/// </summary>
|
private void CheckNodeHover()
|
{
|
if (!ClientRectangle.Contains(PointToClient(MousePosition)))
|
{
|
return;
|
}
|
|
foreach (StepTreeNode node in _nodes)
|
{
|
CheckNodeHover(node, OffsetMousePosition);
|
}
|
}
|
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="node"></param>
|
/// <param name="location"></param>
|
private void CheckNodeHover(StepTreeNode node, Point location)
|
{
|
bool isHover = node.FullArea.Contains(location);
|
if (node.IsHover != isHover)
|
{
|
node.IsHover = isHover;
|
Invalidate();
|
}
|
|
foreach (StepTreeNode childNode in node.Nodes)
|
{
|
CheckNodeHover(childNode, location);
|
}
|
}
|
|
/// <summary>
|
/// 鼠标离开
|
/// </summary>
|
/// <param name="node"></param>
|
private void NodeMouseLeave(StepTreeNode node)
|
{
|
node.IsHover = false;
|
|
foreach (StepTreeNode childNode in node.Nodes)
|
{
|
NodeMouseLeave(childNode);
|
}
|
|
Invalidate();
|
}
|
|
|
/// <summary>
|
/// 检查鼠标点击在哪个
|
/// </summary>
|
/// <param name="node"></param>
|
/// <param name="location"></param>
|
/// <param name="button"></param>
|
private void CheckNodeClick(StepTreeNode node, Point location, MouseButtons button)
|
{
|
Rectangle rect = GetNodeFullRowArea(node);
|
|
if (rect.Contains(location))
|
{
|
if (node.ExpandArea.Contains(location))
|
{
|
if (button == MouseButtons.Left)
|
{
|
node.Expanded = !node.Expanded;
|
}
|
}
|
else
|
{
|
if (button == MouseButtons.Left)
|
{
|
SetSelectNode4Click(node);
|
return;
|
}
|
else if (button == MouseButtons.Right)
|
{//以后做菜单
|
//if (!SelectedNodes.Contains(node))
|
//{
|
// SelectNode(node);
|
//}
|
|
return;
|
}
|
}
|
}
|
|
//检查下一级
|
if (node.Expanded)
|
{
|
foreach (StepTreeNode childNode in node.Nodes)
|
{
|
CheckNodeClick(childNode, location, button);
|
}
|
}
|
}
|
|
/// <summary>
|
/// 双击 展开
|
/// </summary>
|
/// <param name="node"></param>
|
/// <param name="location"></param>
|
private void CheckNodeDoubleClick(StepTreeNode node, Point location)
|
{
|
Rectangle rect = GetNodeFullRowArea(node);
|
if (rect.Contains(location))
|
{
|
if (!node.ExpandArea.Contains(location))
|
{
|
node.Expanded = !node.Expanded;
|
}
|
|
return;
|
}
|
|
if (node.Expanded)
|
{
|
foreach (StepTreeNode childNode in node.Nodes)
|
{
|
CheckNodeDoubleClick(childNode, location);
|
}
|
}
|
}
|
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="node"></param>
|
/// <returns></returns>
|
public Rectangle GetNodeFullRowArea(StepTreeNode node)
|
{
|
if (node.ParentNode != null && !node.ParentNode.Expanded)
|
{
|
return new Rectangle(-1, -1, -1, -1);
|
}
|
|
int width = Math.Max(ContentSize.Width, Viewport.Width);
|
Rectangle rect = new(0, node.FullArea.Top, width, _itemHeight);
|
return rect;
|
}
|
|
/// <summary>
|
/// 确保所选节点可见
|
/// </summary>
|
public void EnsureVisible()
|
{
|
if (_selStepNode == null)
|
{
|
return;
|
}
|
|
// foreach (StepTreeNode node in SelectedNodes)
|
{
|
_selStepNode.EnsureVisible();
|
}
|
|
int itemTop = -1;
|
|
if (_selStepNode != null)
|
{
|
itemTop = _selStepNode.FullArea.Top;
|
}
|
|
|
int itemBottom = itemTop + _itemHeight;
|
|
if (itemTop < Viewport.Top)
|
{
|
VScrollTo(itemTop);
|
}
|
|
if (itemBottom > Viewport.Bottom)
|
{
|
VScrollTo(itemBottom - Viewport.Height);
|
}
|
}
|
|
#endregion
|
|
|
#region Paint Region
|
|
protected override void OnPaintBackground(PaintEventArgs e)
|
{
|
base.OnPaintBackground(e);
|
//LoadIcons();
|
}
|
|
protected override void PaintContent(Graphics g)
|
{
|
// Fill body
|
using (SolidBrush b = new(ThemeProvider.Theme.Colors.GreyBackground))
|
{
|
g.FillRectangle(b, ClientRectangle);
|
}
|
|
foreach (StepTreeNode node in _nodes)
|
{
|
DrawNode(node, g);
|
}
|
}
|
private System.Drawing.Color _oddRowBackgroundColor = System.Drawing.Color.White;
|
private System.Drawing.Color _evenRowBackgroundColor = System.Drawing.Color.FromArgb(238 ,233, 233);
|
private void DrawNode(StepTreeNode node, Graphics g)
|
{
|
Rectangle rect = GetNodeFullRowArea(node);
|
|
// 1.绘制背景 Draw background
|
Color bgColor = node.Odd ? _oddRowBackgroundColor : _evenRowBackgroundColor;
|
if (_selStepNode != null && _selStepNode == node)
|
{//被选中的背景
|
bgColor = ThemeProvider.Theme.Colors.BlueSelection;// Focused ? ThemeProvider.Theme.Colors.BlueSelection : System.Drawing.Color.DodgerBlue;
|
}
|
using (SolidBrush b = new(bgColor))
|
{
|
g.FillRectangle(b, rect);
|
}
|
|
|
// 2.绘制图标 Draw icon
|
if (IsShowIcons && node.Icon != null)
|
{
|
g.DrawImageUnscaled(node.Icon, node.IconArea.Location);
|
}
|
|
// 3. 绘制文字 Draw text
|
if ( node == this._selStepNode)
|
{
|
System.Drawing.Color text_color = System.Drawing.Color.White;
|
if (node.IsHover)
|
{
|
text_color = ThemeProvider.Theme.Colors.BlueSelection;
|
}
|
using (SolidBrush b = new(text_color))
|
{
|
StringFormat stringFormat = new()
|
{
|
Alignment = StringAlignment.Near,
|
LineAlignment = StringAlignment.Center
|
};
|
|
g.DrawString(node.Text, Font, b, node.TextArea, stringFormat);
|
}
|
}
|
else
|
{
|
using (SolidBrush b = new(System.Drawing.Color.Black))
|
{
|
StringFormat stringFormat = new()
|
{
|
Alignment = StringAlignment.Near,
|
LineAlignment = StringAlignment.Center
|
};
|
|
g.DrawString(node.Text, Font, b, node.TextArea, stringFormat);
|
}
|
}
|
|
|
// 4. 绘制下一级 Draw child nodes
|
if (node.Expanded)
|
{
|
foreach (StepTreeNode childNode in node.Nodes)
|
{
|
DrawNode(childNode, g);
|
}
|
}
|
}
|
|
#endregion
|
|
|
#region Dispose Region
|
|
protected override void Dispose(bool disposing)
|
{
|
if (!_disposed)
|
{
|
if (BeforeSelectedNodeChangedEvent != null)
|
{
|
BeforeSelectedNodeChangedEvent = null;
|
}
|
if (AfterSelectedNodeChangedEvent != null)
|
{
|
AfterSelectedNodeChangedEvent = null;
|
}
|
|
if (_nodes != null)
|
{
|
_nodes.Dispose();
|
}
|
|
_disposed = true;
|
}
|
|
base.Dispose(disposing);
|
}
|
|
#endregion
|
|
}
|
|
|
}
|