开源节点框架STNodeEditor使用

节点,一般都为树形Tree结构,如TreeNode,XmlNode。

树形结构有其关键属性Parent【父节点】,Children【子节点】

LinkedListNode为链表线性结构,有其关键属性Next【下一个】,Previous【上一个】,

可以用其进行工作流workFlow设计

右键 项目 STNodeDemo,管理NuGet程序包

输入关键字STNodeEditor

安装完成后

我们可以查看工具箱

开源的 .NET 轻量级且功能强大的节点编辑器STNodeEditor

STNodeEditor 是一个轻量且功能强大的节点编辑器 使用方式非常简洁 提供了丰富的属性以及事件可以非常方便的完成节点之间数据的交互及通知 大量的虚函数可供开发者重写具有很高的自由性。

当有很多应用程序(模块) 它们之间需要相互调用传递数据来完成一整套流程的工作 开发单一功能的应用程序(模块)相对比较容易 而实现一整套很多功能相互调用的应用程序相对比较繁琐 此套框架开发者只需要定义好传递的数据类型 然后分别实现单一节点功能 至于执行流程交给框架和用户布线即可。

项目地址

https://github.com/DebugST/STNodeEditor

STNodeEditor是基于WinForm的一套框架 使用GDI+开发 不包含任何额外依赖 整个调用库仅100+kb

项目主页:https://debugst.github.io/STNodeEditor/

教程文档:https://debugst.github.io/STNodeEditor/doc_cn.html

NuGet:https://www.nuget.org/packages/ST.Library.UI/

GitHub:https://github.com/DebugST/STNodeEditor

由上图可见 STNodeEditor 包含3部分 TreeView PropertyGrid NodeEditor 这三部分组成了一套完整的可使用框架

TreeView

开发这可以把执行功能编码到一个节点中 而TreeView则负责展示以及检索节点 在TreeView中的节点可直接拖拽添加到NodeEditor中
PropertyGrid
类似与WinForm开发使用的属性窗口 作为一个节点 它也是可以有属性的 而作者在编辑器进行设计的过程中也把一个节点视作一个Form让开发者几乎没有什么学习成本直接上手一个节点的开发
NodeEditor
NodeEditor是用户组合自己执行流程的地方 使得功能模块执行流程可视化

因ST.Library.UI.NodeEditor.STNode 是一个抽象类,因此我们需要新建一个子类SnakeNode

cs 复制代码
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using ST.Library.UI.NodeEditor;

namespace STNodeDemo
{
    public class SnakeNode : ST.Library.UI.NodeEditor.STNode
    {
        public SnakeNode(string title) 
        {
            this.Title = title;
            this.TitleColor = Color.FromArgb(200, Color.Goldenrod);
        }

        /// <summary>
        /// 输出节点集合
        /// </summary>
        public List<STNodeOption> OutputOptionCollection = new List<STNodeOption>();
        STNodeOption outputTest;
        protected override void OnCreate()
        {
            base.OnCreate();
            this.Title = "SnakeNode";
            //输入项集合
            int index = this.InputOptions.Add(new STNodeOption("Input1", typeof(string), false));
            STNodeOption nodeIn2 = this.InputOptions.Add("Input2", typeof(int), false);
            STNodeOption nodeIn3 = this.InputOptions.Add("Input3", typeof(float), false);
            //输出项集合
            STNodeOption nodeOut = this.OutputOptions.Add("Output1", typeof(string), false);
            outputTest = this.OutputOptions.Add("OutputTime", typeof(DateTime), false);
            OutputOptionCollection.Add(nodeOut);
            OutputOptionCollection.Add(outputTest);

            //STNodeOption[] ss = this.GetOutputOptions();
        }

        //当所有者发生改变(即:在NodeEditor中被添加或移除)
        //应当像容器提交自己拥有数据类型的连接点 所期望显示的颜色
        //颜色主要用于区分不同的数据类型
        protected override void OnOwnerChanged()
        {
            base.OnOwnerChanged();
            if (this.Owner == null) return;
            this.Owner.SetTypeColor(typeof(string), Color.Yellow);
            //当前容器中已有的颜色会被替换
            this.Owner.SetTypeColor(typeof(int), Color.DodgerBlue, true);
            this.Owner.SetTypeColor(typeof(float), Color.Pink, true);
            this.Owner.SetTypeColor(typeof(DateTime), Color.Green, true);
            //下面的代码将忽略容器中已有的颜色
            //this.SetOptionDotColor(op, Color.Red); //无需在OnOwnerChanged()中设置

            Task.Factory.StartNew(() => 
            {
                for (int i = 0; i < 50; i++)
                {
                    System.Threading.Thread.Sleep(1000);
                    //STNodeOption.TransferData(object)会自动设置STNodeOption.Data
                    //然后自动向所有连接的选项进行数据传递
                    outputTest.TransferData(DateTime.Now);
                }
            });
        }
    }
}

新建测试接收节点类ViperNode

cs 复制代码
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using ST.Library.UI.NodeEditor;

namespace STNodeDemo
{
    public class ViperNode : ST.Library.UI.NodeEditor.STNode
    {
        public ViperNode(string title) 
        {
            this.Title = title;
            this.TitleColor = Color.FromArgb(200, Color.Goldenrod);
        }

        /// <summary>
        /// 输入节点集合
        /// </summary>
        public List<STNodeOption> InputOptionCollection = new List<STNodeOption>();
        STNodeOption inputTest;
        protected override void OnCreate()
        {
            base.OnCreate();
            this.Title = "ViperNode";
            //输入项集合
            inputTest = this.InputOptions.Add("ViperTime", typeof(DateTime), false);
            inputTest.DataTransfer += new STNodeOptionEventHandler(InputTest_DataTransfer);

            InputOptionCollection.Add(inputTest);
        }

        private void InputTest_DataTransfer(object sender, STNodeOptionEventArgs e)
        {
            //当连接的建立与断开都会触发此事件 所以需要判断连接状态
            if (e.Status != ConnectionStatus.Connected || e.TargetOption.Data == null)
            {
                //当 STNode.AutoSize=true 并不建议使用STNode.SetOptionText
                //因为当文本发生改变时候会重新计算布局 正确的做法是自定义一个如Lable控件
                //作为时间的显示 当然这里为了演示方式采用此方案
                this.SetOptionText(inputTest, "--");
            }
            else
            {
                this.SetOptionText(inputTest, Convert.ToDateTime(e.TargetOption.Data).ToString("yyyy-MM-dd HH:mm:ss.fff"));
            }
        }
    }
}

新建测试窗体FormSTNode,设计如下

FormSTNode.Designer.cs设计器代码如下:

cs 复制代码
namespace STNodeDemo
{
    partial class FormSTNode
    {
        /// <summary>
        /// 必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows 窗体设计器生成的代码

        /// <summary>
        /// 设计器支持所需的方法 - 不要修改
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            this.stNodeEditor1 = new ST.Library.UI.NodeEditor.STNodeEditor();
            this.btnConnectLine = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // stNodeEditor1
            // 
            this.stNodeEditor1.AllowDrop = true;
            this.stNodeEditor1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(34)))), ((int)(((byte)(34)))), ((int)(((byte)(34)))));
            this.stNodeEditor1.Curvature = 0.3F;
            this.stNodeEditor1.Location = new System.Drawing.Point(12, 67);
            this.stNodeEditor1.LocationBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
            this.stNodeEditor1.MarkBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(180)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
            this.stNodeEditor1.MarkForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(180)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
            this.stNodeEditor1.MinimumSize = new System.Drawing.Size(100, 100);
            this.stNodeEditor1.Name = "stNodeEditor1";
            this.stNodeEditor1.Size = new System.Drawing.Size(1160, 475);
            this.stNodeEditor1.TabIndex = 0;
            this.stNodeEditor1.Text = "stNodeEditor1";
            // 
            // btnConnectLine
            // 
            this.btnConnectLine.Font = new System.Drawing.Font("宋体", 16F);
            this.btnConnectLine.Location = new System.Drawing.Point(52, 12);
            this.btnConnectLine.Name = "btnConnectLine";
            this.btnConnectLine.Size = new System.Drawing.Size(169, 33);
            this.btnConnectLine.TabIndex = 1;
            this.btnConnectLine.Text = "连线-显示时间";
            this.btnConnectLine.UseVisualStyleBackColor = true;
            this.btnConnectLine.Click += new System.EventHandler(this.btnConnectLine_Click);
            // 
            // FormSTNode
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(1200, 554);
            this.Controls.Add(this.btnConnectLine);
            this.Controls.Add(this.stNodeEditor1);
            this.Name = "FormSTNode";
            this.Text = "STNodeEditor开源框架,输入输出";
            this.Load += new System.EventHandler(this.FormSTNode_Load);
            this.ResumeLayout(false);

        }

        #endregion

        private ST.Library.UI.NodeEditor.STNodeEditor stNodeEditor1;
        private System.Windows.Forms.Button btnConnectLine;
    }
}

窗体FormSTNode.cs测试代码如下:

cs 复制代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using ST.Library.UI.NodeEditor;

namespace STNodeDemo
{
    public partial class FormSTNode : Form
    {
        public FormSTNode()
        {
            InitializeComponent();
            //参考地址 https://blog.csdn.net/crystal_lz/article/details/117131080
            /*single-connection
单连接模式 在单连接模式下一个连接点同时 只能被一个 同数据类型点的连接
multi-connection
多连接模式 在多连接模式下一个连接点同时 可以被多个 同数据类型点连接
            数据交互:
            STNodeOption可以通过绑定DataTransfer事件获取到传入该选项的所有数据
STNodeOption可以通过TransferData(object obj)向该选项上所有连接的选项进行数据投递
            */
        }

        private void FormSTNode_Load(object sender, EventArgs e)
        {
            SnakeNode stNode = new SnakeNode("STNode的标题");
            stNode.Location = new Point(10, 10);
            stNodeEditor1.Nodes.Add(stNode);

            ViperNode destNode = new ViperNode("显示时间");
            destNode.Location = new Point(400, 10);
            stNodeEditor1.Nodes.Add(destNode);
        }

        /// <summary>
        /// 获取指定的输出节点选项
        /// </summary>
        /// <param name="node"></param>
        /// <param name="strText"></param>
        /// <returns></returns>
        private STNodeOption GetOutNodeOption(SnakeNode node, string strText) 
        {
            STNodeOption outNodeOption = node.OutputOptionCollection.FirstOrDefault(option => option.Text == strText);
            return outNodeOption;
        }

        /// <summary>
        /// 获取指定的输入节点选项
        /// </summary>
        /// <param name="node"></param>
        /// <param name="strText"></param>
        /// <returns></returns>
        private STNodeOption GetInNodeOption(ViperNode node, string strText)
        {
            STNodeOption inNodeOption = node.InputOptionCollection.FirstOrDefault(option => option.Text == strText);
            return inNodeOption;
        }

        private void btnConnectLine_Click(object sender, EventArgs e)
        {
            STNodeOption outNodeOption = GetOutNodeOption((SnakeNode)stNodeEditor1.Nodes[0], "OutputTime");
            STNodeOption inNodeOption = GetInNodeOption((ViperNode)stNodeEditor1.Nodes[1], "ViperTime");
            ConnectionStatus connectionStatus = outNodeOption.ConnectOption(inNodeOption);
            MessageBox.Show($"输出节点 连接 输入节点 的状态结果【{connectionStatus}】", "提示");
        }
    }
}

测试运行如图:

相关推荐
_xaboy2 小时前
开源项目低代码表单设计器FcDesigner扩展自定义的容器组件.例如col
vue.js·低代码·开源·动态表单·formcreate·低代码表单·可视化表单设计器
_xaboy2 小时前
开源项目低代码表单设计器FcDesigner扩展自定义组件
vue.js·低代码·开源·动态表单·formcreate·可视化表单设计器
SafePloy安策13 小时前
ES信息防泄漏:策略与实践
大数据·elasticsearch·开源
学编程的小程13 小时前
【安全通信】告别信息泄露:搭建你的开源视频聊天系统briefing
安全·开源·音视频
问道飞鱼13 小时前
【微服务知识】开源RPC框架Dubbo入门介绍
微服务·rpc·开源·dubbo
customer0818 小时前
【开源免费】基于SpringBoot+Vue.JS课程答疑系统(JAVA毕业设计)
java·jvm·vue.js·spring boot·spring cloud·kafka·开源
多客软件佳佳19 小时前
校园交友系统的设计与实现(开源版+三端交付+搭建+售后)
小程序·前端框架·uni-app·开源·php·交友
OpenTiny社区1 天前
重磅更新!Fluent Editor 开源富文本支持 LaTeX 可编辑公式啦~
前端·ui·开源·opentiny
小牛itbull1 天前
ReactPress 安装指南:从 MySQL 安装到项目启动
前端·javascript·数据库·mysql·react.js·开源·reactpress
蜜桃小阿雯1 天前
JAVA开源项目 微服务在线教育系统 计算机毕业设计
java·开发语言·spring boot·微服务·java-ee·开源·maven