publish 2.0

This commit is contained in:
DebugST 2021-04-29 21:32:54 +08:00
parent ccb60f4bc3
commit d28553d46d
144 changed files with 15250 additions and 7427 deletions

View File

@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace ST.Library.UI.NodeEditor
{
internal class FrmNodePreviewPanel : Form
{
public Color BorderColor { get; set; }
public bool AutoBorderColor { get; set; }
private bool m_bRight;
private Point m_ptHandle;
private int m_nHandleSize;
private Rectangle m_rect_handle;
private Rectangle m_rect_panel;
private Rectangle m_rect_exclude;
private Region m_region;
private Type m_type;
private STNode m_node;
private STNodeEditor m_editor;
private STNodePropertyGrid m_property;
private Pen m_pen = new Pen(Color.Black);
private SolidBrush m_brush = new SolidBrush(Color.Black);
private static FrmNodePreviewPanel m_last_frm;
[DllImport("user32.dll")]
private static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
public FrmNodePreviewPanel(Type stNodeType, Point ptHandle, int nHandleSize, bool bRight, STNodeEditor editor, STNodePropertyGrid propertyGrid) {
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
if (m_last_frm != null) m_last_frm.Close();
m_last_frm = this;
m_editor = editor;
m_property = propertyGrid;
m_editor.Size = new Size(200, 200);
m_property.Size = new Size(200, 200);
m_editor.Location = new Point(1 + (bRight ? nHandleSize : 0), 1);
m_property.Location = new Point(m_editor.Right, 1);
m_property.InfoFirstOnDraw = true;
this.Controls.Add(m_editor);
this.Controls.Add(m_property);
this.ShowInTaskbar = false;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Size = new Size(402 + nHandleSize, 202);
m_type = stNodeType;
m_ptHandle = ptHandle;
m_nHandleSize = nHandleSize;
m_bRight = bRight;
this.AutoBorderColor = true;
this.BorderColor = Color.DodgerBlue;
}
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
m_node = (STNode)Activator.CreateInstance(m_type);
m_node.Left = 20; m_node.Top = 20;
m_editor.Nodes.Add(m_node);
m_property.SetNode(m_node);
m_rect_panel = new Rectangle(0, 0, 402, 202);
m_rect_handle = new Rectangle(m_ptHandle.X, m_ptHandle.Y, m_nHandleSize, m_nHandleSize);
m_rect_exclude = new Rectangle(0, m_nHandleSize, m_nHandleSize, this.Height - m_nHandleSize);
if (m_bRight) {
this.Left = m_ptHandle.X;
m_rect_panel.X = m_ptHandle.X + m_nHandleSize;
} else {
this.Left = m_ptHandle.X - this.Width + m_nHandleSize;
m_rect_exclude.X = this.Width - m_nHandleSize;
m_rect_panel.X = this.Left;
}
if (m_ptHandle.Y + this.Height > Screen.GetWorkingArea(this).Bottom) {
this.Top = m_ptHandle.Y - this.Height + m_nHandleSize;
m_rect_exclude.Y -= m_nHandleSize;
} else this.Top = m_ptHandle.Y;
m_rect_panel.Y = this.Top;
m_region = new Region(new Rectangle(Point.Empty, this.Size));
m_region.Exclude(m_rect_exclude);
using (Graphics g = this.CreateGraphics()) {
IntPtr h = m_region.GetHrgn(g);
FrmNodePreviewPanel.SetWindowRgn(this.Handle, h, false);
m_region.ReleaseHrgn(h);
}
this.MouseLeave += Event_MouseLeave;
m_editor.MouseLeave += Event_MouseLeave;
m_property.MouseLeave += Event_MouseLeave;
this.BeginInvoke(new MethodInvoker(() => {
m_property.Focus();
}));
}
protected override void OnClosing(CancelEventArgs e) {
base.OnClosing(e);
this.Controls.Clear();
m_editor.Nodes.Clear();
m_editor.MouseLeave -= Event_MouseLeave;
m_property.MouseLeave -= Event_MouseLeave;
m_last_frm = null;
}
void Event_MouseLeave(object sender, EventArgs e) {
Point pt = Control.MousePosition;
if (m_rect_panel.Contains(pt) || m_rect_handle.Contains(pt)) return;
this.Close();
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Graphics g = e.Graphics;
m_pen.Color = this.AutoBorderColor ? m_node.TitleColor : this.BorderColor;
m_brush.Color = m_pen.Color;
g.DrawRectangle(m_pen, 0, 0, this.Width - 1, this.Height - 1);
g.FillRectangle(m_brush, m_rect_exclude.X - 1, m_rect_exclude.Y - 1, m_rect_exclude.Width + 2, m_rect_exclude.Height + 2);
Rectangle rect = this.RectangleToClient(m_rect_handle);
rect.Y = (m_nHandleSize - 14) / 2;
rect.X += rect.Y + 1;
rect.Width = rect.Height = 14;
m_pen.Width = 2;
g.DrawLine(m_pen, rect.X + 4, rect.Y + 3, rect.X + 10, rect.Y + 3);
g.DrawLine(m_pen, rect.X + 4, rect.Y + 6, rect.X + 10, rect.Y + 6);
g.DrawLine(m_pen, rect.X + 4, rect.Y + 11, rect.X + 10, rect.Y + 11);
g.DrawLine(m_pen, rect.X + 7, rect.Y + 7, rect.X + 7, rect.Y + 10);
m_pen.Width = 1;
g.DrawRectangle(m_pen, rect.X, rect.Y, rect.Width - 1, rect.Height - 1);
}
}
}

View File

@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using ST.Library.UI.NodeEditor;
namespace ST.Library.UI
{
internal class FrmSTNodePropertyInput : Form
{
private STNodePropertyDescriptor m_descriptor;
private Rectangle m_rect;
private Pen m_pen;
private SolidBrush m_brush;
private TextBox m_tbx;
public FrmSTNodePropertyInput(STNodePropertyDescriptor descriptor) {
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
m_rect = descriptor.RectangleR;
m_descriptor = descriptor;
this.ShowInTaskbar = false;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.BackColor = descriptor.Control.AutoColor ? descriptor.Node.TitleColor : descriptor.Control.ItemSelectedColor;
m_pen = new Pen(descriptor.Control.ForeColor, 1);
m_brush = new SolidBrush(this.BackColor);
}
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
Point pt = m_descriptor.Control.PointToScreen(m_rect.Location);
pt.Y += m_descriptor.Control.ScrollOffset;
this.Location = pt;
this.Size = new System.Drawing.Size(m_rect.Width + m_rect.Height, m_rect.Height);
m_tbx = new TextBox();
m_tbx.Font = m_descriptor.Control.Font;
m_tbx.ForeColor = m_descriptor.Control.ForeColor;
m_tbx.BackColor = Color.FromArgb(255, m_descriptor.Control.ItemValueBackColor);
m_tbx.BorderStyle = BorderStyle.None;
m_tbx.Size = new Size(this.Width - 4 - m_rect.Height, this.Height - 2);
m_tbx.Text = m_descriptor.GetStringFromValue();
this.Controls.Add(m_tbx);
m_tbx.Location = new Point(2, (this.Height - m_tbx.Height) / 2);
m_tbx.SelectAll();
m_tbx.LostFocus += (s, ea) => this.Close();
m_tbx.KeyDown += new KeyEventHandler(tbx_KeyDown);
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Graphics g = e.Graphics;
m_brush.Color = m_tbx.BackColor;
g.FillRectangle(m_brush, 1, 1, this.Width - 2 - m_rect.Height, this.Height - 2);
m_brush.Color = m_descriptor.Control.ForeColor;
//Enter
g.FillPolygon(m_brush, new Point[]{
new Point(this.Width - 21, this.Height - 2),
new Point(this.Width - 14, this.Height - 2),
new Point(this.Width - 14, this.Height - 8)
});
g.DrawLine(m_pen, this.Width - 14, this.Height - 3, this.Width - 4, this.Height - 3);
g.DrawLine(m_pen, this.Width - 4, this.Height - 3, this.Width - 4, 14);
g.DrawLine(m_pen, this.Width - 8, 13, this.Width - 4, 13);
//----
g.DrawLine(m_pen, this.Width - 19, 11, this.Width - 4, 11);
//E
g.DrawLine(m_pen, this.Width - 19, 3, this.Width - 16, 3);
g.DrawLine(m_pen, this.Width - 19, 6, this.Width - 16, 6);
g.DrawLine(m_pen, this.Width - 19, 9, this.Width - 16, 9);
g.DrawLine(m_pen, this.Width - 19, 3, this.Width - 19, 9);
//S
g.DrawLine(m_pen, this.Width - 13, 3, this.Width - 10, 3);
g.DrawLine(m_pen, this.Width - 13, 6, this.Width - 10, 6);
g.DrawLine(m_pen, this.Width - 13, 9, this.Width - 10, 9);
g.DrawLine(m_pen, this.Width - 13, 3, this.Width - 13, 6);
g.DrawLine(m_pen, this.Width - 10, 6, this.Width - 10, 9);
//C
g.DrawLine(m_pen, this.Width - 7, 3, this.Width - 4, 3);
g.DrawLine(m_pen, this.Width - 7, 9, this.Width - 4, 9);
g.DrawLine(m_pen, this.Width - 7, 3, this.Width - 7, 9);
}
void tbx_KeyDown(object sender, KeyEventArgs e) {
if (e.KeyCode == Keys.Escape) this.Close();
if (e.KeyCode != Keys.Enter) return;
try {
m_descriptor.SetValue(((TextBox)sender).Text, null);
m_descriptor.Control.Invalidate();//add rect;
} catch (Exception ex) {
m_descriptor.OnSetValueError(ex);
}
this.Close();
}
private void InitializeComponent() {
this.SuspendLayout();
//
// FrmSTNodePropertyInput
//
this.ClientSize = new System.Drawing.Size(292, 273);
this.Name = "FrmSTNodePropertyInput";
this.ResumeLayout(false);
}
}
}

View File

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
namespace ST.Library.UI.NodeEditor
{
internal class FrmSTNodePropertySelect : Form
{
private STNodePropertyDescriptor m_descriptor;
private int m_nItemHeight = 25;
private static Type m_t_bool = typeof(bool);
private Pen m_pen;
private SolidBrush m_brush;
private StringFormat m_sf;
private Color m_clr_item_1 = Color.FromArgb(10, 0, 0, 0);// Color.FromArgb(255, 40, 40, 40);
private Color m_clr_item_2 = Color.FromArgb(10, 255, 255, 255);// Color.FromArgb(255, 50, 50, 50);
private object m_item_hover;
public FrmSTNodePropertySelect(STNodePropertyDescriptor descriptor) {
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
m_descriptor = descriptor;
this.Size = descriptor.RectangleR.Size;
this.ShowInTaskbar = false;
this.BackColor = descriptor.Control.BackColor;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
m_pen = new Pen(descriptor.Control.AutoColor ? descriptor.Node.TitleColor : descriptor.Control.ItemSelectedColor, 1);
m_brush = new SolidBrush(this.BackColor);
m_sf = new StringFormat();
m_sf.LineAlignment = StringAlignment.Center;
m_sf.FormatFlags = StringFormatFlags.NoWrap;
}
private List<object> m_lst_item = new List<object>();
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
Point pt = m_descriptor.Control.PointToScreen(m_descriptor.RectangleR.Location);
pt.Y += m_descriptor.Control.ScrollOffset;
this.Location = pt;
if (m_descriptor.PropertyInfo.PropertyType.IsEnum) {
foreach (var v in Enum.GetValues(m_descriptor.PropertyInfo.PropertyType)) m_lst_item.Add(v);
} else if (m_descriptor.PropertyInfo.PropertyType == m_t_bool) {
m_lst_item.Add(true);
m_lst_item.Add(false);
} else {
this.Close();
return;
}
this.Height = m_lst_item.Count * m_nItemHeight;
Rectangle rect = Screen.GetWorkingArea(this);
if (this.Bottom > rect.Bottom) this.Top -= (this.Bottom - rect.Bottom);
this.MouseLeave += (s, ea) => this.Close();
this.LostFocus += (s, ea) => this.Close();
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Graphics g = e.Graphics;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
Rectangle rect_back = new Rectangle(0, 0, this.Width, m_nItemHeight);
Rectangle rect_font = new Rectangle(10, 0, this.Width - 13, m_nItemHeight);
int nIndex = 0;
string strVal = m_descriptor.GetStringFromValue();
foreach (var v in m_lst_item) {
m_brush.Color = nIndex++ % 2 == 0 ? m_clr_item_1 : m_clr_item_2;
g.FillRectangle(m_brush, rect_back);
if (v == m_item_hover) {
m_brush.Color = m_descriptor.Control.ItemHoverColor;
g.FillRectangle(m_brush, rect_back);
}
if (v.ToString() == strVal) {
m_brush.Color = m_descriptor.Control.ItemSelectedColor;
g.FillRectangle(m_brush, 4, rect_back.Top + 10, 5, 5);
}
m_brush.Color = m_descriptor.Control.ForeColor;
g.DrawString(v.ToString(), m_descriptor.Control.Font, m_brush, rect_font, m_sf);
rect_back.Y += m_nItemHeight;
rect_font.Y += m_nItemHeight;
}
g.DrawRectangle(m_pen, 0, 0, this.Width - 1, this.Height - 1);
}
protected override void OnMouseMove(MouseEventArgs e) {
base.OnMouseMove(e);
int nIndex = e.Y / m_nItemHeight;
if (nIndex < 0 || nIndex >= m_lst_item.Count) return;
var item = m_lst_item[e.Y / m_nItemHeight];
if (m_item_hover == item) return;
m_item_hover = item;
this.Invalidate();
}
protected override void OnMouseClick(MouseEventArgs e) {
base.OnMouseClick(e);
this.Close();
int nIndex = e.Y / m_nItemHeight;
if (nIndex < 0) return;
if (nIndex > m_lst_item.Count) return;
try {
m_descriptor.SetValue(m_lst_item[nIndex], null);
} catch (Exception ex) {
m_descriptor.OnSetValueError(ex);
}
}
}
}

View File

@ -30,12 +30,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
* time: 2021-01-06
* create: 2021-12-08
* modify: 2021-03-02
* Author: Crystal_lz
* blog: st233.com
* Github: DebugST.github.io
* blog: http://st233.com
* Gitee: https://gitee.com/DebugST
* Github: https://github.com/DebugST
*/
namespace ST.Library.UI
namespace ST.Library.UI.NodeEditor
{
public abstract class STNode
{
@ -52,6 +54,7 @@ namespace ST.Library.UI
foreach (STNodeOption op in this._OutputOptions.ToArray()) op.DisConnectionAll();
}
_Owner = value;
if (!this._AutoSize) this.SetOptionsLocation();
this.BuildSize(true, true, false);
this.OnOwnerChanged();
}
@ -68,6 +71,7 @@ namespace ST.Library.UI
_IsSelected = value;
this.Invalidate();
this.OnSelectedChanged();
if (this._Owner != null) this._Owner.OnSelectedChanged(EventArgs.Empty);
}
}
@ -92,7 +96,7 @@ namespace ST.Library.UI
get { return _TitleColor; }
protected set {
_TitleColor = value;
this.Invalidate(this.TitleRectangle);
this.Invalidate(new Rectangle(0, 0, this._Width, this._TitleHeight));
}
}
@ -140,7 +144,8 @@ namespace ST.Library.UI
get { return _Title; }
protected set {
_Title = value;
this.Invalidate(this.TitleRectangle);
if (this._AutoSize) this.BuildSize(true, true, true);
//this.Invalidate(this.TitleRectangle);
}
}
@ -156,10 +161,7 @@ namespace ST.Library.UI
_MarkLines = null;
else
_MarkLines = (from s in value.Split('\n') select s.Trim()).ToArray();
this.BuildSize(false, true, true);
//if (this._Owner != null) this._Owner.Invalidate();
//this.Invalidate();
//this.Invalidate(this._MarkRectangle);
this.Invalidate(new Rectangle(-5, -5, this._MarkRectangle.Width + 10, this._MarkRectangle.Height + 10));
}
}
@ -178,15 +180,15 @@ namespace ST.Library.UI
public int Left {
get { return _Left; }
set {
if (this._LockLocation) return;
if (this._LockLocation || value == _Left) return;
_Left = value;
this.SetOptionsLocation();
this.BuildSize(false, true, false);
//this._MarkRectangle = this.OnBuildMarkRectangle();
this.OnMove(EventArgs.Empty);
if (this._Owner != null) {
this._Owner.BuildLinePath();
this._Owner.BuildBounds();
}
this.OnMove(new EventArgs());
}
}
@ -197,32 +199,87 @@ namespace ST.Library.UI
public int Top {
get { return _Top; }
set {
if (this._LockLocation) return;
if (this._LockLocation || value == _Top) return;
_Top = value;
this.SetOptionsLocation();
this.BuildSize(false, true, false);
//this._MarkRectangle = this.OnBuildMarkRectangle();
this.OnMove(EventArgs.Empty);
if (this._Owner != null) {
this._Owner.BuildLinePath();
this._Owner.BuildBounds();
}
this.OnMove(new EventArgs());
}
}
private int _Width;
private int _Width = 100;
/// <summary>
/// 获取或设置 Node 宽度
/// 获取或设置 Node 宽度 当AutoSize被设置时 无法设置此值
/// </summary>
public int Width {
get { return _Width; }
protected set {
if (value < 50) return;
if (this._AutoSize || value == _Width) return;
_Width = value;
this.SetOptionsLocation();
this.BuildSize(false, true, false);
this.OnResize(EventArgs.Empty);
if (this._Owner != null) {
this._Owner.BuildLinePath();
this._Owner.BuildBounds();
}
this.Invalidate();
}
}
private int _Height;
private int _Height = 40;
/// <summary>
/// 获取或设置 Node 高度
/// 获取或设置 Node 高度 当AutoSize被设置时 无法设置此值
/// </summary>
public int Height {
get { return _Height; }
protected set {
if (value < 40) return;
if (this._AutoSize || value == _Height) return;
_Height = value;
this.SetOptionsLocation();
this.BuildSize(false, true, false);
this.OnResize(EventArgs.Empty);
if (this._Owner != null) {
this._Owner.BuildLinePath();
this._Owner.BuildBounds();
}
this.Invalidate();
}
}
private int _ItemHeight = 20;
/// <summary>
/// 获取或设置 Node 每个选项的高度
/// </summary>
public int ItemHeight {
get { return _ItemHeight; }
protected set {
if (value < 16) value = 16;
if (value > 200) value = 200;
if (value == _ItemHeight) return;
_ItemHeight = value;
if (this._AutoSize) {
this.BuildSize(true, false, true);
} else {
this.SetOptionsLocation();
if (this._Owner != null) this._Owner.Invalidate();
}
}
}
private bool _AutoSize = true;
/// <summary>
/// 获取或设置 Node 是否自动计算宽高
/// </summary>
public bool AutoSize {
get { return _AutoSize; }
protected set { _AutoSize = value; }
}
/// <summary>
/// 获取 Node 右边边坐标
@ -308,11 +365,23 @@ namespace ST.Library.UI
/// <summary>
/// 获取 Node 坐标位置
/// </summary>
public Point Location { get { return new Point(this._Left, this._Top); } }
public Point Location {
get { return new Point(this._Left, this._Top); }
set {
this.Left = value.X;
this.Top = value.Y;
}
}
/// <summary>
/// 获取 Node 大小
/// </summary>
public Size Size { get { return new Size(this._Width, this._Height); } }
public Size Size {
get { return new Size(this._Width, this._Height); }
set {
this.Width = value.Width;
this.Height = value.Height;
}
}
private Font _Font;
/// <summary>
@ -377,12 +446,19 @@ namespace ST.Library.UI
get { return _Guid; }
}
private bool _LetGetOptions = false;
/// <summary>
/// 获取或设置是否允许外部访问STNodeOption
/// </summary>
public bool LetGetOptions {
get { return _LetGetOptions; }
protected set { _LetGetOptions = value; }
}
private static Point m_static_pt_init = new Point(10, 10);
public STNode(/*string strTitle, int x, int y*/) {
//this._Title = strTitle;
public STNode() {
this._Title = "Untitled";
this._Height = this._TitleHeight;
this._MarkRectangle.Height = this._Height;
this._Left = this._MarkRectangle.X = m_static_pt_init.X;
this._Top = m_static_pt_init.Y;
@ -406,7 +482,7 @@ namespace ST.Library.UI
this.OnCreate();
}
private int m_nItemHeight = 20;
//private int m_nItemHeight = 30;
protected StringFormat m_sf;
/// <summary>
/// 当前Node中 活动的控件
@ -416,27 +492,25 @@ namespace ST.Library.UI
/// 当前Node中 悬停的控件
/// </summary>
protected STNodeControl m_ctrl_hover;
/// <summary>
/// 当前Node中 鼠标点下的控件
/// </summary>
protected STNodeControl m_ctrl_down;
protected internal void BuildSize(bool bBuildNode, bool bBuildMark, bool bRedraw) {
if (this._Owner == null) return;
Pen p = new Pen(this._BackColor);
SolidBrush sb = new SolidBrush(this._BackColor);
using (Graphics g = this._Owner.CreateGraphics()) {
DrawingTools dt = new DrawingTools() {
Graphics = g,
Pen = p,
SolidBrush = sb
};
if (bBuildNode) {
Size sz = this.OnBuildNodeSize(dt);
this._Width = sz.Width;
this._Height = sz.Height;
this.SetOptionLocation();
this.OnResize(new EventArgs());
if (this._AutoSize && bBuildNode) {
Size sz = this.GetDefaultNodeSize(g);
if (this._Width != sz.Width || this._Height != sz.Height) {
this._Width = sz.Width;
this._Height = sz.Height;
this.SetOptionsLocation();
this.OnResize(EventArgs.Empty);
}
}
if (bBuildMark) {
if (string.IsNullOrEmpty(this._Mark)) return;
this._MarkRectangle = this.OnBuildMarkRectangle(dt);
if (bBuildMark && !string.IsNullOrEmpty(this._Mark)) {
this._MarkRectangle = this.OnBuildMarkRectangle(g);
}
}
if (bRedraw) this._Owner.Invalidate();
@ -447,17 +521,37 @@ namespace ST.Library.UI
dic.Add("Guid", this._Guid.ToByteArray());
dic.Add("Left", BitConverter.GetBytes(this._Left));
dic.Add("Top", BitConverter.GetBytes(this._Top));
dic.Add("Mark", string.IsNullOrEmpty(this._Mark) ? new byte[] { 0 } : Encoding.UTF8.GetBytes(this._Mark));
dic.Add("Width", BitConverter.GetBytes(this._Width));
dic.Add("Height", BitConverter.GetBytes(this._Height));
dic.Add("AutoSize", new byte[] { (byte)(this._AutoSize ? 1 : 0) });
if (this._Mark != null) dic.Add("Mark", Encoding.UTF8.GetBytes(this._Mark));
dic.Add("LockOption", new byte[] { (byte)(this._LockLocation ? 1 : 0) });
dic.Add("LockLocation", new byte[] { (byte)(this._LockLocation ? 1 : 0) });
Type t = this.GetType();
foreach (var p in t.GetProperties()) {
var attrs = p.GetCustomAttributes(true);
foreach (var a in attrs) {
if (!(a is STNodePropertyAttribute)) continue;
var attr = a as STNodePropertyAttribute;
object obj = Activator.CreateInstance(attr.DescriptorType);
if (!(obj is STNodePropertyDescriptor))
throw new InvalidOperationException("[STNodePropertyAttribute.Type]参数值必须为[STNodePropertyDescriptor]或者其子类的类型");
var desc = (STNodePropertyDescriptor)Activator.CreateInstance(attr.DescriptorType);
desc.Node = this;
desc.PropertyInfo = p;
byte[] byData = desc.GetBytesFromValue();
if (byData == null) continue;
dic.Add(p.Name, byData);
}
}
this.OnSaveNode(dic);
return dic;
}
internal virtual byte[] GetSaveData() {
internal byte[] GetSaveData() {
List<byte> lst = new List<byte>();
Type t = this.GetType();
byte[] byData = Encoding.UTF8.GetBytes(t.Module.Name);
byte[] byData = Encoding.UTF8.GetBytes(t.Module.Name + "|" + t.FullName);
lst.Add((byte)byData.Length);
lst.AddRange(byData);
byData = Encoding.UTF8.GetBytes(t.GUID.ToString());
@ -477,36 +571,6 @@ namespace ST.Library.UI
return lst.ToArray();
}
//internal virtual byte[] GetSaveData() {
// List<byte> lst = new List<byte>();
// Type t = this.GetType();
// //lst.AddRange(BitConverter.GetBytes(this._Left));
// //lst.AddRange(BitConverter.GetBytes(this._Top));
// byte[] byData = Encoding.UTF8.GetBytes(t.Module.Name);
// lst.Add((byte)byData.Length);
// lst.AddRange(byData);
// byData = Encoding.UTF8.GetBytes(t.FullName);
// lst.Add((byte)byData.Length);
// lst.AddRange(byData);
// //if (!string.IsNullOrEmpty(this._Mark)) {
// // byData = Encoding.UTF8.GetBytes(this._Mark);
// // lst.AddRange(BitConverter.GetBytes(byData.Length));
// // lst.AddRange(byData);
// //} else lst.AddRange(new byte[] { 0, 0, 0, 0 });
// var dic = this.OnSaveNode();
// if (dic != null) {
// foreach (var v in dic) {
// byData = Encoding.UTF8.GetBytes(v.Key);
// lst.AddRange(BitConverter.GetBytes(byData.Length));
// lst.AddRange(byData);
// lst.AddRange(BitConverter.GetBytes(v.Value.Length));
// lst.AddRange(v.Value);
// }
// }
// return lst.ToArray();
//}
#region protected
/// <summary>
/// 当Node被构造时候发生
@ -540,21 +604,30 @@ namespace ST.Library.UI
g.FillRectangle(brush, this.TitleRectangle);
}
if (this._LockOption) {
dt.Pen.Color = this.ForeColor;
//dt.Pen.Color = this.ForeColor;
brush.Color = this._ForeColor;
int n = this._Top + this._TitleHeight / 2 - 5;
g.DrawRectangle(dt.Pen, this._Left + 3, n + 0, 6, 3);
g.DrawRectangle(dt.Pen, this._Left + 2, n + 3, 8, 6);
g.DrawLine(dt.Pen, this._Left + 6, n + 5, this._Left + 6, n + 7);
g.FillRectangle(dt.SolidBrush, this._Left + 4, n + 0, 2, 4);
g.FillRectangle(dt.SolidBrush, this._Left + 6, n + 0, 2, 2);
g.FillRectangle(dt.SolidBrush, this._Left + 8, n + 0, 2, 4);
g.FillRectangle(dt.SolidBrush, this._Left + 3, n + 4, 8, 6);
//g.DrawLine(dt.Pen, this._Left + 6, n + 5, this._Left + 6, n + 7);
//g.DrawRectangle(dt.Pen, this._Left + 3, n + 0, 6, 3);
//g.DrawRectangle(dt.Pen, this._Left + 2, n + 3, 8, 6);
//g.DrawLine(dt.Pen, this._Left + 6, n + 5, this._Left + 6, n + 7);
}
if (this._LockLocation) {
dt.Pen.Color = this.ForeColor;
//dt.Pen.Color = this.ForeColor;
brush.Color = this._ForeColor;
int n = this._Top + this._TitleHeight / 2 - 5;
g.FillRectangle(brush, this.Right - 9, n, 5, 7);
g.DrawLine(dt.Pen, this.Right - 10, n, this.Right - 4, n);
g.DrawLine(dt.Pen, this.Right - 11, n + 6, this.Right - 3, n + 6);
g.DrawLine(dt.Pen, this.Right - 7, n + 7, this.Right - 7, n + 9);
g.FillRectangle(brush, this.Right - 9, n, 4, 4);
g.FillRectangle(brush, this.Right - 11, n + 4, 8, 2);
g.FillRectangle(brush, this.Right - 8, n + 6, 2, 4);
//g.DrawLine(dt.Pen, this.Right - 10, n + 6, this.Right - 4, n + 6);
//g.DrawLine(dt.Pen, this.Right - 10, n, this.Right - 4, n);
//g.DrawLine(dt.Pen, this.Right - 11, n + 6, this.Right - 3, n + 6);
//g.DrawLine(dt.Pen, this.Right - 7, n + 7, this.Right - 7, n + 9);
}
if (!string.IsNullOrEmpty(this._Title) && this._ForeColor.A != 0) {
brush.Color = this._ForeColor;
@ -569,27 +642,33 @@ namespace ST.Library.UI
protected virtual void OnDrawBody(DrawingTools dt) {
SolidBrush brush = dt.SolidBrush;
foreach (STNodeOption op in this._InputOptions) {
if (op == STNodeOption.Empty) continue;
this.OnDrawOptionDot(dt, op);
this.OnDrawOptionText(dt, op);
}
foreach (STNodeOption op in this._OutputOptions) {
if (op == STNodeOption.Empty) continue;
this.OnDrawOptionDot(dt, op);
this.OnDrawOptionText(dt, op);
}
if (this._Controls.Count != 0) { //绘制子控件
//将坐标原点与节点对齐
//dt.Graphics.ResetTransform();
dt.Graphics.TranslateTransform(this._Left, this._Top + this._TitleHeight);
Point pt = Point.Empty; //当前需要偏移的量
Point pt_last = Point.Empty; //最后一个控件相对于节点的坐标
foreach (STNodeControl v in this._Controls) {
pt.X = v.Left - pt.X;
pt.Y = v.Top - pt.Y;
if (!v.Visable) continue;
pt.X = v.Left - pt_last.X;
pt.Y = v.Top - pt_last.Y;
pt_last = v.Location;
dt.Graphics.TranslateTransform(pt.X, pt.Y); //将原点坐标移动至控件位置
dt.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
v.OnPaint(dt);
}
//dt.Graphics.TranslateTransform(-pt_last.X, -pt_last.Y); 还原坐标
dt.Graphics.TranslateTransform(-this._Left - pt_last.X, -this._Top - this._TitleHeight - pt_last.Y);
//dt.Graphics.
}
}
/// <summary>
@ -608,7 +687,7 @@ namespace ST.Library.UI
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; //确定文本绘制所需大小
var sz = g.MeasureString(this.Mark, this.Font, this._MarkRectangle.Width);
brush.Color = this._ForeColor;
if (sz.Height > m_nItemHeight || sz.Width > this._MarkRectangle.Width) { //如果超过绘图区 则绘制部分
if (sz.Height > this._ItemHeight || sz.Width > this._MarkRectangle.Width) { //如果超过绘图区 则绘制部分
Rectangle rect = new Rectangle(this._MarkRectangle.Left + 2, this._MarkRectangle.Top + 2, this._MarkRectangle.Width - 20, 16);
m_sf.Alignment = StringAlignment.Near;
g.DrawString(this._MarkLines[0], this._Font, brush, rect, m_sf);
@ -673,8 +752,9 @@ namespace ST.Library.UI
/// </summary>
/// <param name="op">需要计算的Option</param>
/// <param name="pt">自动计算出的位置</param>
/// <param name="nIndex">当前Option的索引</param>
/// <returns>新的位置</returns>
protected virtual Point OnSetOptionDotLocation(STNodeOption op, Point pt) {
protected virtual Point OnSetOptionDotLocation(STNodeOption op, Point pt, int nIndex) {
return pt;
}
/// <summary>
@ -682,45 +762,48 @@ namespace ST.Library.UI
/// </summary>
/// <param name="op">需要计算的Option</param>
/// <param name="rect">自动计算出的区域</param>
/// <param name="nIndex">当前Option的索引</param>
/// <returns>新的区域</returns>
protected virtual Rectangle OnSetOptionTextRectangle(STNodeOption op, Rectangle rect) {
protected virtual Rectangle OnSetOptionTextRectangle(STNodeOption op, Rectangle rect, int nIndex) {
return rect;
}
/// <summary>
/// 计算当前Node所需要的矩形区域
/// 若需要自己重绘Node 则应当重写此函数 以确定绘图区域大小
/// 获取当前STNode所需要的默认大小
/// 返回的大小并不会限制绘制区域 任然可以在此区域之外绘制
/// 但是并不会被STNodeEditor所接受 并触发对应事件
/// </summary>
protected virtual Size OnBuildNodeSize(DrawingTools dt) {
/// <param name="g">绘图面板</param>
/// <returns>计算出来的大小</returns>
protected virtual Size GetDefaultNodeSize(Graphics g) {
int nInputHeight = 0, nOutputHeight = 0;
foreach (STNodeOption op in this._InputOptions) nInputHeight += m_nItemHeight;
foreach (STNodeOption op in this._OutputOptions) nOutputHeight += m_nItemHeight;
foreach (STNodeOption op in this._InputOptions) nInputHeight += this._ItemHeight;
foreach (STNodeOption op in this._OutputOptions) nOutputHeight += this._ItemHeight;
int nHeight = this._TitleHeight + (nInputHeight > nOutputHeight ? nInputHeight : nOutputHeight);
SizeF szf_input = SizeF.Empty, szf_output = SizeF.Empty;
foreach (STNodeOption v in this._InputOptions) {
if (string.IsNullOrEmpty(v.Text)) continue;
SizeF szf = dt.Graphics.MeasureString(v.Text, this._Font);
SizeF szf = g.MeasureString(v.Text, this._Font);
if (szf.Width > szf_input.Width) szf_input = szf;
}
foreach (STNodeOption v in this._OutputOptions) {
if (string.IsNullOrEmpty(v.Text)) continue;
SizeF szf = dt.Graphics.MeasureString(v.Text, this._Font);
SizeF szf = g.MeasureString(v.Text, this._Font);
if (szf.Width > szf_output.Width) szf_output = szf;
}
int nWidth = (int)(szf_input.Width + szf_output.Width + 25);
if (!string.IsNullOrEmpty(this.Title)) szf_input = dt.Graphics.MeasureString(this.Title, this.Font);
if (!string.IsNullOrEmpty(this.Title)) szf_input = g.MeasureString(this.Title, this.Font);
if (szf_input.Width + 30 > nWidth) nWidth = (int)szf_input.Width + 30;
return new Size(nWidth, nHeight);
}
/// <summary>
/// 计算当前Mark所需要的矩形区域
/// 若需要自己重绘Mark 则应当重写此函数 以确定绘图区域大小
/// 返回的大小并不会限制绘制区域 任然可以在此区域之外绘制
/// 但是并不会被STNodeEditor所接受 并触发对应事件
/// </summary>
protected virtual Rectangle OnBuildMarkRectangle(DrawingTools dt) {
/// <param name="g">绘图面板</param>
/// <returns>计算后的区域</returns>
protected virtual Rectangle OnBuildMarkRectangle(Graphics g) {
//if (string.IsNullOrEmpty(this._Mark)) return Rectangle.Empty;
return new Rectangle(this._Left, this._Top - 30, this._Width, 20);
}
@ -736,21 +819,77 @@ namespace ST.Library.UI
/// </summary>
/// <param name="dic">保存时候的数据</param>
protected internal virtual void OnLoadNode(Dictionary<string, byte[]> dic) {
if (dic.ContainsKey("AutoSize")) this._AutoSize = dic["AutoSize"][0] == 1;
if (dic.ContainsKey("LockOption")) this._LockOption = dic["LockOption"][0] == 1;
if (dic.ContainsKey("LockLocation")) this._LockLocation = dic["LockLocation"][0] == 1;
if (dic.ContainsKey("Guid")) this._Guid = new Guid(dic["Guid"]);
if (dic.ContainsKey("Left")) this._Left = BitConverter.ToInt32(dic["Left"], 0);
if (dic.ContainsKey("Top")) this._Top = BitConverter.ToInt32(dic["Top"], 0);
if (dic.ContainsKey("Mark")) {
string strText = Encoding.UTF8.GetString(dic["Mark"]);
if (strText != "\0") this.Mark = strText;
if (dic.ContainsKey("Width") && !this._AutoSize) this._Width = BitConverter.ToInt32(dic["Width"], 0);
if (dic.ContainsKey("Height") && !this._AutoSize) this._Height = BitConverter.ToInt32(dic["Height"], 0);
if (dic.ContainsKey("Mark")) this.Mark = Encoding.UTF8.GetString(dic["Mark"]);
Type t = this.GetType();
foreach (var p in t.GetProperties()) {
var attrs = p.GetCustomAttributes(true);
foreach (var a in attrs) {
if (!(a is STNodePropertyAttribute)) continue;
var attr = a as STNodePropertyAttribute;
object obj = Activator.CreateInstance(attr.DescriptorType);
if (!(obj is STNodePropertyDescriptor))
throw new InvalidOperationException("[STNodePropertyAttribute.Type]参数值必须为[STNodePropertyDescriptor]或者其子类的类型");
var desc = (STNodePropertyDescriptor)Activator.CreateInstance(attr.DescriptorType);
desc.Node = this;
desc.PropertyInfo = p;
try {
if (dic.ContainsKey(p.Name)) desc.SetValue(dic[p.Name]);
} catch (Exception ex) {
string strErr = "属性[" + this.Title + "." + p.Name + "]的值无法被还原 可通过重写[STNodePropertyAttribute.GetBytesFromValue(),STNodePropertyAttribute.GetValueFromBytes(byte[])]确保保存和加载时候的二进制数据正确";
Exception e = ex;
while (e != null) {
strErr += "\r\n----\r\n[" + e.GetType().Name + "] -> " + e.Message;
e = e.InnerException;
}
throw new InvalidOperationException(strErr, ex);
}
}
}
if (dic.ContainsKey("LockOption")) this._LockOption = dic["LockOption"][0] == 1;
if (dic.ContainsKey("LockLocation")) this._LockLocation = dic["LockLocation"][0] == 1;
}
/// <summary>
/// 当编辑器加载完成所有的节点时候发生
/// </summary>
protected internal virtual void OnEditorLoadCompleted() {
protected internal virtual void OnEditorLoadCompleted() { }
/// <summary>
/// 设置Option的文本信息
/// </summary>
/// <param name="op">目标Option</param>
/// <param name="strText">文本</param>
/// <returns>是否成功</returns>
protected bool SetOptionText(STNodeOption op, string strText) {
if (op.Owner != this) return false;
op.Text = strText;
return true;
}
/// <summary>
/// 设置Option文本信息颜色
/// </summary>
/// <param name="op">目标Option</param>
/// <param name="clr">颜色</param>
/// <returns>是否成功</returns>
protected bool SetOptionTextColor(STNodeOption op, Color clr) {
if (op.Owner != this) return false;
op.TextColor = clr;
return true;
}
/// <summary>
/// 设置Option连线点颜色
/// </summary>
/// <param name="op">目标Option</param>
/// <param name="clr">颜色</param>
/// <returns>是否成功</returns>
protected bool SetOptionDotColor(STNodeOption op, Color clr) {
if (op.Owner != this) return false;
op.DotColor = clr;
return false;
}
//[event]===========================[event]==============================[event]============================[event]
@ -767,93 +906,98 @@ namespace ST.Library.UI
for (int i = this._Controls.Count - 1; i >= 0; i--) {
var c = this._Controls[i];
if (c.DisplayRectangle.Contains(pt)) {
if (!c.Enabled) return;
if (!c.Visable) continue;
c.OnMouseDown(new MouseEventArgs(e.Button, e.Clicks, e.X - c.Left, pt.Y - c.Top, e.Delta));
m_ctrl_down = c;
if (m_ctrl_active != c) {
c.OnGotFocus(new EventArgs());
if (m_ctrl_active != null) m_ctrl_active.OnLostFocus(new EventArgs());
c.OnGotFocus(EventArgs.Empty);
if (m_ctrl_active != null) m_ctrl_active.OnLostFocus(EventArgs.Empty);
m_ctrl_active = c;
}
return;
}
}
if (m_ctrl_active != null) m_ctrl_active.OnLostFocus(new EventArgs());
if (m_ctrl_active != null) m_ctrl_active.OnLostFocus(EventArgs.Empty);
m_ctrl_active = null;
}
protected internal virtual void OnMouseMove(MouseEventArgs e) {
Point pt = e.Location;
pt.Y -= this._TitleHeight;
if (m_ctrl_down != null) {
if (m_ctrl_down.Enabled && m_ctrl_down.Visable)
m_ctrl_down.OnMouseMove(new MouseEventArgs(e.Button, e.Clicks, e.X - m_ctrl_down.Left, pt.Y - m_ctrl_down.Top, e.Delta));
return;
}
for (int i = this._Controls.Count - 1; i >= 0; i--) {
var c = this._Controls[i];
if (c.DisplayRectangle.Contains(pt)) {
if (m_ctrl_hover != this._Controls[i]) {
c.OnMouseEnter(new EventArgs());
if (m_ctrl_hover != null) m_ctrl_hover.OnMouseLeave(new EventArgs());
c.OnMouseEnter(EventArgs.Empty);
if (m_ctrl_hover != null) m_ctrl_hover.OnMouseLeave(EventArgs.Empty);
m_ctrl_hover = c;
}
m_ctrl_hover.OnMouseMove(new MouseEventArgs(e.Button, e.Clicks, e.X - c.Left, pt.Y - c.Top, e.Delta));
return;
}
}
if (m_ctrl_hover != null) m_ctrl_hover.OnMouseLeave(new EventArgs());
if (m_ctrl_hover != null) m_ctrl_hover.OnMouseLeave(EventArgs.Empty);
m_ctrl_hover = null;
}
protected internal virtual void OnMouseUp(MouseEventArgs e) {
Point pt = e.Location;
pt.Y -= this._TitleHeight;
if (m_ctrl_active != null) {
m_ctrl_active.OnMouseUp(new MouseEventArgs(e.Button, e.Clicks,
e.X - m_ctrl_active.Left, pt.Y - m_ctrl_active.Top, e.Delta));
if (m_ctrl_down != null && m_ctrl_down.Enabled && m_ctrl_down.Visable) {
m_ctrl_down.OnMouseUp(new MouseEventArgs(e.Button, e.Clicks, e.X - m_ctrl_down.Left, pt.Y - m_ctrl_down.Top, e.Delta));
}
//for (int i = this._Controls.Count - 1; i >= 0; i--) {
// var c = this._Controls[i];
// if (c.DisplayRectangle.Contains(pt)) {
// c.OnMouseUp(new MouseEventArgs(e.Button, e.Clicks, e.X - c.Left, pt.Y - c.Top, e.Delta));
// return;
// }
//if (m_ctrl_active != null) {
// m_ctrl_active.OnMouseUp(new MouseEventArgs(e.Button, e.Clicks,
// e.X - m_ctrl_active.Left, pt.Y - m_ctrl_active.Top, e.Delta));
//}
m_ctrl_down = null;
}
protected internal virtual void OnMouseLeave(EventArgs e) {
if (m_ctrl_hover != null) m_ctrl_hover.OnMouseLeave(e);
if (m_ctrl_hover != null && m_ctrl_hover.Enabled && m_ctrl_hover.Visable) m_ctrl_hover.OnMouseLeave(e);
m_ctrl_hover = null;
}
protected internal virtual void OnMouseClick(MouseEventArgs e) {
Point pt = e.Location;
pt.Y -= this._TitleHeight;
if (m_ctrl_active != null)
if (m_ctrl_active != null && m_ctrl_active.Enabled && m_ctrl_active.Visable)
m_ctrl_active.OnMouseClick(new MouseEventArgs(e.Button, e.Clicks, e.X - m_ctrl_active.Left, pt.Y - m_ctrl_active.Top, e.Delta));
}
protected internal virtual void OnMouseWheel(MouseEventArgs e) {
Point pt = e.Location;
pt.Y -= this._TitleHeight;
if (m_ctrl_hover != null) {
if (m_ctrl_hover != null && m_ctrl_active.Enabled && m_ctrl_hover.Visable) {
m_ctrl_hover.OnMouseWheel(new MouseEventArgs(e.Button, e.Clicks, e.X - m_ctrl_hover.Left, pt.Y - m_ctrl_hover.Top, e.Delta));
return;
}
}
protected internal virtual void OnMouseHWheel(MouseEventArgs e) {
if (m_ctrl_hover != null) {
if (m_ctrl_hover != null && m_ctrl_active.Enabled && m_ctrl_hover.Visable) {
m_ctrl_hover.OnMouseHWheel(e);
return;
}
}
protected internal virtual void OnKeyDown(KeyEventArgs e) {
if (m_ctrl_active != null) m_ctrl_active.OnKeyDown(e);
if (m_ctrl_active != null && m_ctrl_active.Enabled && m_ctrl_active.Visable) m_ctrl_active.OnKeyDown(e);
}
protected internal virtual void OnKeyUp(KeyEventArgs e) {
if (m_ctrl_active != null) m_ctrl_active.OnKeyUp(e);
if (m_ctrl_active != null && m_ctrl_active.Enabled && m_ctrl_active.Visable) m_ctrl_active.OnKeyUp(e);
}
protected internal virtual void OnKeyPress(KeyPressEventArgs e) {
if (m_ctrl_active != null) m_ctrl_active.OnKeyPress(e);
if (m_ctrl_active != null && m_ctrl_active.Enabled && m_ctrl_active.Visable) m_ctrl_active.OnKeyPress(e);
}
protected internal virtual void OnMove(EventArgs e) { this.SetOptionLocation(); }
protected internal virtual void OnResize(EventArgs e) { this.SetOptionLocation(); }
protected virtual void OnMove(EventArgs e) { /*this.SetOptionLocation();*/ }
protected virtual void OnResize(EventArgs e) { /*this.SetOptionLocation();*/ }
/// <summary>
@ -870,24 +1014,33 @@ namespace ST.Library.UI
protected virtual void OnActiveChanged() { }
#endregion protected
private void SetOptionLocation() {
Rectangle rect = new Rectangle(this.Left + 10, this._Top + this._TitleHeight, this._Width - 20, m_nItemHeight);
/// <summary>
/// 计算每个Option的位置
/// </summary>
protected virtual void SetOptionsLocation() {
int nIndex = 0;
Rectangle rect = new Rectangle(this.Left + 10, this._Top + this._TitleHeight, this._Width - 20, this._ItemHeight);
foreach (STNodeOption op in this._InputOptions) {
Point pt = this.OnSetOptionDotLocation(op, new Point(this.Left - 5, rect.Y + 5));
op.TextRectangle = this.OnSetOptionTextRectangle(op, rect);
op.DotLeft = pt.X;
op.DotTop = pt.Y;
rect.Y += m_nItemHeight;
if (op != STNodeOption.Empty) {
Point pt = this.OnSetOptionDotLocation(op, new Point(this.Left - op.DotSize / 2, rect.Y + (rect.Height - op.DotSize) / 2), nIndex);
op.TextRectangle = this.OnSetOptionTextRectangle(op, rect, nIndex);
op.DotLeft = pt.X;
op.DotTop = pt.Y;
}
rect.Y += this._ItemHeight;
nIndex++;
}
rect.Y = this._Top + this._TitleHeight;
m_sf.Alignment = StringAlignment.Far;
foreach (STNodeOption op in this._OutputOptions) {
Point pt = this.OnSetOptionDotLocation(op, new Point(this._Left + this._Width - 5, rect.Y + 5));
op.TextRectangle = this.OnSetOptionTextRectangle(op, rect);
op.DotLeft = pt.X;
op.DotTop = pt.Y;
rect.Y += m_nItemHeight;
if (op != STNodeOption.Empty) {
Point pt = this.OnSetOptionDotLocation(op, new Point(this._Left + this._Width - op.DotSize / 2, rect.Y + (rect.Height - op.DotSize) / 2), nIndex);
op.TextRectangle = this.OnSetOptionTextRectangle(op, rect, nIndex);
op.DotLeft = pt.X;
op.DotTop = pt.Y;
}
rect.Y += this._ItemHeight;
nIndex++;
}
}
@ -907,7 +1060,9 @@ namespace ST.Library.UI
rect.X += this._Left;
rect.Y += this._Top;
if (this._Owner != null) {
this._Owner.Invalidate(this._Owner.CanvasToControl(rect));
rect = this._Owner.CanvasToControl(rect);
rect.Width += 1; rect.Height += 1;//坐标系统转换可能导致进度丢失 多加上一个像素
this._Owner.Invalidate(rect);
}
}
/// <summary>
@ -915,6 +1070,7 @@ namespace ST.Library.UI
/// </summary>
/// <returns>Option集合</returns>
public STNodeOption[] GetInputOptions() {
if (!this._LetGetOptions) return null;
STNodeOption[] ops = new STNodeOption[this._InputOptions.Count];
for (int i = 0; i < this._InputOptions.Count; i++) ops[i] = this._InputOptions[i];
return ops;
@ -924,6 +1080,7 @@ namespace ST.Library.UI
/// </summary>
/// <returns>Option集合</returns>
public STNodeOption[] GetOutputOptions() {
if (!this._LetGetOptions) return null;
STNodeOption[] ops = new STNodeOption[this._OutputOptions.Count];
for (int i = 0; i < this._OutputOptions.Count; i++) ops[i] = this._OutputOptions[i];
return ops;
@ -936,8 +1093,15 @@ namespace ST.Library.UI
public void SetSelected(bool bSelected, bool bRedraw) {
if (this._IsSelected == bSelected) return;
this._IsSelected = bSelected;
if (this._Owner != null) {
if (bSelected)
this._Owner.AddSelectedNode(this);
else
this._Owner.RemoveSelectedNode(this);
}
if (bRedraw) this.Invalidate();
this.OnSelectedChanged();
if (this._Owner != null) this._Owner.OnSelectedChanged(EventArgs.Empty);
}
public IAsyncResult BeginInvoke(Delegate method) { return this.BeginInvoke(method, null); }
public IAsyncResult BeginInvoke(Delegate method, params object[] args) {

View File

@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Text.RegularExpressions;
namespace ST.Library.UI.NodeEditor
{
/// <summary>
/// STNode节点特性
/// 用于描述STNode开发者信息 以及部分行为
/// </summary>
public class STNodeAttribute : Attribute
{
private string _Path;
/// <summary>
/// 获取STNode节点期望在树形控件的路径
/// </summary>
public string Path {
get { return _Path; }
}
private string _Author;
/// <summary>
/// 获取STNode节点的作者名称
/// </summary>
public string Author {
get { return _Author; }
}
private string _Mail;
/// <summary>
/// 获取STNode节点的作者邮箱
/// </summary>
public string Mail {
get { return _Mail; }
}
private string _Link;
/// <summary>
/// 获取STNode节点的作者链接
/// </summary>
public string Link {
get { return _Link; }
}
private string _Description;
/// <summary>
/// 获取STNode节点的描述信息
/// </summary>
public string Description {
get { return _Description; }
}
private static char[] m_ch_splitter = new char[] { '/', '\\' };
private static Regex m_reg = new Regex(@"^https?://", RegexOptions.IgnoreCase);
/// <summary>
/// 构造一个STNode特性
/// </summary>
/// <param name="strPath">期望路径</param>
public STNodeAttribute(string strPath) : this(strPath, null, null, null, null) { }
/// <summary>
/// 构造一个STNode特性
/// </summary>
/// <param name="strPath">期望路径</param>
/// <param name="strDescription">描述信息</param>
public STNodeAttribute(string strPath, string strDescription) : this(strPath, null, null, null, strDescription) { }
/// <summary>
/// 构造一个STNode特性
/// </summary>
/// <param name="strPath">期望路径</param>
/// <param name="strAuthor">STNode作者名称</param>
/// <param name="strMail">STNode作者邮箱</param>
/// <param name="strLink">STNode作者链接</param>
/// <param name="strDescription">STNode节点描述信息</param>
public STNodeAttribute(string strPath, string strAuthor, string strMail, string strLink, string strDescription) {
if (!string.IsNullOrEmpty(strPath))
strPath = strPath.Trim().Trim(m_ch_splitter).Trim();
this._Path = strPath;
this._Author = strAuthor;
this._Mail = strMail;
this._Description = strDescription;
if (string.IsNullOrEmpty(strLink) || strLink.Trim() == string.Empty) return;
strLink = strLink.Trim();
if (m_reg.IsMatch(strLink))
this._Link = strLink;
else
this._Link = "http://" + strLink;
}
private static Dictionary<Type, MethodInfo> m_dic = new Dictionary<Type, MethodInfo>();
/// <summary>
/// 获取类型的帮助函数
/// </summary>
/// <param name="stNodeType">节点类型</param>
/// <returns>函数信息</returns>
public static MethodInfo GetHelpMethod(Type stNodeType) {
if (m_dic.ContainsKey(stNodeType)) return m_dic[stNodeType];
var mi = stNodeType.GetMethod("ShowHelpInfo");
if (mi == null) return null;
if (!mi.IsStatic) return null;
var ps = mi.GetParameters();
if (ps.Length != 1) return null;
if (ps[0].ParameterType != typeof(string)) return null;
m_dic.Add(stNodeType, mi);
return mi;
}
/// <summary>
/// 执行对应节点类型的帮助函数
/// </summary>
/// <param name="stNodeType">节点类型</param>
public static void ShowHelp(Type stNodeType) {
var mi = STNodeAttribute.GetHelpMethod(stNodeType);
if (mi == null) return;
mi.Invoke(null, new object[] { stNodeType.Module.FullyQualifiedName });
}
}
}

View File

@ -5,7 +5,7 @@ using System.Text;
using System.Collections;
using System.Drawing;
namespace ST.Library.UI
namespace ST.Library.UI.NodeEditor
{
public class STNodeCollection : IList, ICollection, IEnumerable
{
@ -20,6 +20,19 @@ namespace ST.Library.UI
m_nodes = new STNode[4];
}
public void MoveToEnd(STNode node) {
if (this._Count < 1) return;
if (m_nodes[this._Count - 1] == node) return;
bool bFound = false;
for (int i = 0; i < _Count - 1; i++) {
if (m_nodes[i] == node) {
bFound = true;
}
if (bFound) m_nodes[i] = m_nodes[i + 1];
}
m_nodes[this._Count - 1] = node;
}
public int Add(STNode node) {
if (node == null) throw new ArgumentNullException("添加对象不能为空");
this.EnsureSpace(1);
@ -59,9 +72,11 @@ namespace ST.Library.UI
foreach (STNodeOption op in m_nodes[i].InputOptions) op.DisConnectionAll();
foreach (STNodeOption op in m_nodes[i].OutputOptions) op.DisConnectionAll();
m_owner.OnNodeRemoved(new STNodeEditorEventArgs(m_nodes[i]));
m_owner.InternalRemoveSelectedNode(m_nodes[i]);
}
this._Count = 0;
m_nodes = new STNode[4];
m_owner.SetActiveNode(null);
m_owner.BuildBounds();
m_owner.ScaleCanvas(1, 0, 0); //当不存在节点时候 坐标系回归
m_owner.MoveCanvas(10, 10, true, CanvasMoveArgs.All);
@ -109,6 +124,8 @@ namespace ST.Library.UI
if (nIndex < 0 || nIndex >= this._Count)
throw new IndexOutOfRangeException("索引越界");
m_nodes[nIndex].Owner = null;
m_owner.InternalRemoveSelectedNode(m_nodes[nIndex]);
if (m_owner.ActiveNode == m_nodes[nIndex]) m_owner.SetActiveNode(null);
m_owner.OnNodeRemoved(new STNodeEditorEventArgs(m_nodes[nIndex]));
this._Count--;
for (int i = nIndex, Len = this._Count; i < Len; i++)

View File

@ -33,7 +33,7 @@ SOFTWARE.
* blog: st233.com
* Github: DebugST.github.io
*/
namespace ST.Library.UI
namespace ST.Library.UI.NodeEditor
{
public class STNodeControl
{
@ -50,7 +50,7 @@ namespace ST.Library.UI
get { return _Left; }
set {
_Left = value;
this.OnMove(new EventArgs());
this.OnMove(EventArgs.Empty);
this.Invalidate();
}
}
@ -61,7 +61,7 @@ namespace ST.Library.UI
get { return _Top; }
set {
_Top = value;
this.OnMove(new EventArgs());
this.OnMove(EventArgs.Empty);
this.Invalidate();
}
}
@ -72,7 +72,7 @@ namespace ST.Library.UI
get { return _Width; }
set {
_Width = value;
this.OnResize(new EventArgs());
this.OnResize(EventArgs.Empty);
this.Invalidate();
}
}
@ -83,19 +83,37 @@ namespace ST.Library.UI
get { return _Height; }
set {
_Height = value;
this.OnResize(new EventArgs());
this.OnResize(EventArgs.Empty);
this.Invalidate();
}
}
public int Right { get { return this._Left + this._Width; } }
public int Bottom { get { return this._Top + this._Height; } }
public Point Location {
get { return new Point(this._Left, this._Top); }
set {
this.Left = value.X;
this.Top = value.Y;
}
}
public Size Size {
get { return new Size(this._Width, this._Height); }
set {
this.Width = value.Width;
this.Height = value.Height;
}
}
public Rectangle DisplayRectangle {
get { return new Rectangle(this._Left, this._Top, this._Width, this._Height); }
set {
this.Left = value.X;
this.Top = value.Y;
this.Width = value.Width;
this.Height = value.Height;
}
}
public Rectangle ClientRectangle {
get { return new Rectangle(0, 0, this._Width, this._Height); }
@ -143,6 +161,28 @@ namespace ST.Library.UI
}
}
private bool _Enabled = true;
public bool Enabled {
get { return _Enabled; }
set {
if (value == _Enabled) return;
_Enabled = value;
this.Invalidate();
}
}
private bool _Visable = true;
public bool Visable {
get { return _Visable; }
set {
if (value == _Visable) return;
_Visable = value;
this.Invalidate();
}
}
protected StringFormat m_sf;
public STNodeControl() {
@ -158,13 +198,14 @@ namespace ST.Library.UI
Graphics g = dt.Graphics;
SolidBrush brush = dt.SolidBrush;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
brush.Color = this._BackColor;
g.FillRectangle(brush, 0, 0, this.Width, this.Height);
if (!string.IsNullOrEmpty(this._Text)) {
brush.Color = this._ForeColor;
g.DrawString(this._Text, this._Font, brush, this.ClientRectangle, m_sf);
}
if (this.Paint != null) this.Paint(this, new STNodeControlPaintEventArgs(dt));
}
public void Invalidate() {
@ -199,6 +240,7 @@ namespace ST.Library.UI
public event EventHandler Move;
public event EventHandler Resize;
public event STNodeControlPaintEventHandler Paint;
protected internal virtual void OnGotFocus(EventArgs e) {
if (this.GotFocus != null) this.GotFocus(this, e);
@ -260,4 +302,18 @@ namespace ST.Library.UI
return this._Owner.Invoke(method, args);
}
}
public delegate void STNodeControlPaintEventHandler(object sender, STNodeControlPaintEventArgs e);
public class STNodeControlPaintEventArgs : EventArgs
{
/// <summary>
/// 绘制工具
/// </summary>
public DrawingTools DrawingTools { get; private set; }
public STNodeControlPaintEventArgs(DrawingTools dt) {
this.DrawingTools = dt;
}
}
}

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Collections;
namespace ST.Library.UI
namespace ST.Library.UI.NodeEditor
{
public class STNodeControlCollection: IList, ICollection, IEnumerable
{

View File

@ -35,12 +35,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
* time: 2021-01-06
* create: 2020-12-08
* modify: 2021-04-12
* Author: Crystal_lz
* blog: st233.com
* Github: DebugST.github.io
* blog: http://st233.com
* Gitee: https://gitee.com/DebugST
* Github: https://github.com/DebugST
*/
namespace ST.Library.UI
namespace ST.Library.UI.NodeEditor
{
public class STNodeEditor : Control
{
@ -136,14 +138,14 @@ namespace ST.Library.UI
}
}
private bool _Magnet = true;
private bool _ShowMagnet = true;
/// <summary>
/// 获取或设置移动画布中 Node 时候 是否启用磁铁效果
/// </summary>
[Description("获取或设置移动画布中 Node 时候 是否启用磁铁效果"), DefaultValue(true)]
public bool Magnet {
get { return _Magnet; }
set { _Magnet = value; }
public bool ShowMagnet {
get { return _ShowMagnet; }
set { _ShowMagnet = value; }
}
private bool _ShowBorder = true;
@ -198,18 +200,18 @@ namespace ST.Library.UI
private STNode _ActiveNode;
/// <summary>
/// 获取或设置当前画布中被选中的活动 Node
/// 获取当前画布中被选中的活动 Node
/// </summary>
[Browsable(false)]
public STNode ActiveNode {
get { return _ActiveNode; }
//set {
// if (value == _ActiveSelectedNode) return;
// if (_ActiveSelectedNode != null) _ActiveSelectedNode.OnLostFocus(new EventArgs());
// if (_ActiveSelectedNode != null) _ActiveSelectedNode.OnLostFocus(EventArgs.Empty);
// _ActiveSelectedNode = value;
// _ActiveSelectedNode.IsActive = true;
// this.Invalidate();
// this.OnSelectedChanged(new EventArgs());
// this.OnSelectedChanged(EventArgs.Empty);
//}
}
@ -265,15 +267,15 @@ namespace ST.Library.UI
}
}
private Color _BorderSelectColor = Color.Orange;
private Color _BorderSelectedColor = Color.Orange;
/// <summary>
/// 获取或设置画布中选中 Node 边框颜色
/// </summary>
[Description("获取或设置画布中选中 Node 边框颜色"), DefaultValue(typeof(Color), "Orange")]
public Color BorderSelectColor {
get { return _BorderSelectColor; }
public Color BorderSelectedColor {
get { return _BorderSelectedColor; }
set {
_BorderSelectColor = value;
_BorderSelectedColor = value;
if (m_img_border_selected != null) m_img_border_selected.Dispose();
m_img_border_selected = this.CreateBorderImage(value);
this.Invalidate();
@ -312,7 +314,7 @@ namespace ST.Library.UI
/// <summary>
/// 获取或设置画布绘制 Node 标记详情采用的背景色
/// </summary>
[Description("获取或设置画布绘制 Node 标记详情采用的背景色"), DefaultValue(typeof(Color), "Black")]
[Description("获取或设置画布绘制 Node 标记详情采用的背景色")]
public Color MarkBackColor {
get { return _MarkBackColor; }
set {
@ -321,14 +323,14 @@ namespace ST.Library.UI
}
}
private Color _MagnetLineColor = Color.Magenta;
private Color _MagnetColor = Color.Lime;
/// <summary>
/// 获取或设置画布中移动 Node 时候 磁铁标记线颜色
/// 获取或设置画布中移动 Node 时候 磁铁标记颜色
/// </summary>
[Description("获取或设置画布中移动 Node 时候 磁铁标记线颜色"), DefaultValue(typeof(Color), "Magenta")]
public Color MagnetLineColor {
get { return _MagnetLineColor; }
set { _MagnetLineColor = value; }
[Description("获取或设置画布中移动 Node 时候 磁铁标记颜色"), DefaultValue(typeof(Color), "Lime")]
public Color MagnetColor {
get { return _MagnetColor; }
set { _MagnetColor = value; }
}
private Color _SelectedRectangleColor = Color.DodgerBlue;
@ -430,6 +432,14 @@ namespace ST.Library.UI
/// 用于保存连线过程中鼠标点下的起点Option 当MouseUP时候 确定是否连接此节点
/// </summary>
protected STNodeOption m_option_down;
/// <summary>
/// 当前鼠标点下的 STNode
/// </summary>
protected STNode m_node_down;
/// <summary>
/// 当前鼠标是否位于控件中
/// </summary>
protected bool m_mouse_in_control;
#endregion
@ -441,7 +451,12 @@ namespace ST.Library.UI
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this._Nodes = new STNodeCollection(this);
this.BackColor = Color.FromArgb(255, 34, 34, 34);
this.MinimumSize = new Size(100, 100);
this.Size = new Size(200, 200);
this.AllowDrop = true;
m_real_canvas_x = this._CanvasOffsetX = 10;
m_real_canvas_y = this._CanvasOffsetY = 10;
}
#region private fields --------------------------------------------------------------------------------------
@ -475,7 +490,7 @@ namespace ST.Library.UI
//保存已选中的节点
private HashSet<STNode> m_hs_node_selected = new HashSet<STNode>();
private bool m_is_process_mouse_event; //是否向下(Node or NodeControls)传递鼠标相关事件 如断开连接相关操作不应向下传递
private bool m_is_process_mouse_event = true; //是否向下(Node or NodeControls)传递鼠标相关事件 如断开连接相关操作不应向下传递
private bool m_is_buildpath; //用于重绘过程中 判断该次是否要重新建立缓存连线的路径
private Pen m_p_line = new Pen(Color.Cyan, 2f); //用于绘制已经连接的线条
private Pen m_p_line_hover = new Pen(Color.Cyan, 4f); //用于绘制鼠标悬停时候的线条
@ -501,6 +516,11 @@ namespace ST.Library.UI
#region event ----------------------------------------------------------------------------------------------------
/// <summary>
/// 活动的节点发生变化时候发生
/// </summary>
[Description("活动的节点发生变化时候发生")]
public event EventHandler ActiveChanged;
/// <summary>
/// 选择的节点发生变化时候发生
/// </summary>
[Description("选择的节点发生变化时候发生")]
@ -551,9 +571,12 @@ namespace ST.Library.UI
[Description("正在断开节点选项时候发生")]
public event STNodeEditorOptionEventHandler OptionDisConnecting;
protected virtual void OnSelectedChanged(EventArgs e) {
protected virtual internal void OnSelectedChanged(EventArgs e) {
if (this.SelectedChanged != null) this.SelectedChanged(this, e);
}
protected virtual void OnActiveChanged(EventArgs e) {
if (this.ActiveChanged != null) this.ActiveChanged(this, e);
}
protected virtual void OnHoverChanged(EventArgs e) {
if (this.HoverChanged != null) this.HoverChanged(this, e);
}
@ -594,7 +617,7 @@ namespace ST.Library.UI
m_img_border = this.CreateBorderImage(this._BorderColor);
m_img_border_active = this.CreateBorderImage(this._BorderActiveColor);
m_img_border_hover = this.CreateBorderImage(this._BorderHoverColor);
m_img_border_selected = this.CreateBorderImage(this._BorderSelectColor);
m_img_border_selected = this.CreateBorderImage(this._BorderSelectedColor);
base.OnCreateControl();
new Thread(this.MoveCanvasThread) { IsBackground = true }.Start();
new Thread(this.ShowAlertThread) { IsBackground = true }.Start();
@ -606,25 +629,27 @@ namespace ST.Library.UI
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
Point pt = new Point(((int)m.LParam) >> 16, (ushort)m.LParam);
pt = base.PointToClient(pt);
if (m.Msg == WM_MOUSEHWHEEL) { //获取水平滚动消息
MouseButtons mb = MouseButtons.None;
int n = (ushort)m.WParam;
if ((n & 0x0001) == 0x0001) mb |= MouseButtons.Left;
if ((n & 0x0010) == 0x0010) mb |= MouseButtons.Middle;
if ((n & 0x0002) == 0x0002) mb |= MouseButtons.Right;
if ((n & 0x0020) == 0x0020) mb |= MouseButtons.XButton1;
if ((n & 0x0040) == 0x0040) mb |= MouseButtons.XButton2;
this.OnMouseHWheel(new MouseEventArgs(mb, 0, pt.X, pt.Y, ((int)m.WParam) >> 16));
}
try {
Point pt = new Point(((int)m.LParam) >> 16, (ushort)m.LParam);
pt = this.PointToClient(pt);
if (m.Msg == WM_MOUSEHWHEEL) { //获取水平滚动消息
MouseButtons mb = MouseButtons.None;
int n = (ushort)m.WParam;
if ((n & 0x0001) == 0x0001) mb |= MouseButtons.Left;
if ((n & 0x0010) == 0x0010) mb |= MouseButtons.Middle;
if ((n & 0x0002) == 0x0002) mb |= MouseButtons.Right;
if ((n & 0x0020) == 0x0020) mb |= MouseButtons.XButton1;
if ((n & 0x0040) == 0x0040) mb |= MouseButtons.XButton2;
this.OnMouseHWheel(new MouseEventArgs(mb, 0, pt.X, pt.Y, ((int)m.WParam) >> 16));
}
} catch { /*add code*/ }
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Graphics g = e.Graphics;
g.Clear(this.BackColor);
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
m_drawing_tools.Graphics = g;
SolidBrush brush = m_drawing_tools.SolidBrush;
@ -649,7 +674,7 @@ namespace ST.Library.UI
switch (m_ca) {
case CanvasAction.MoveNode: //移动过程中 绘制对齐参考线
this.OnDrawMagnetLine(m_drawing_tools, m_mi);
if (this._ShowMagnet && this._ActiveNode != null) this.OnDrawMagnet(m_drawing_tools, m_mi);
break;
case CanvasAction.SelectRectangle: //绘制矩形选取
this.OnDrawSelectedRectangle(m_drawing_tools, this.CanvasToControl(m_rect_select));
@ -697,24 +722,18 @@ namespace ST.Library.UI
bool bCtrlDown = (Control.ModifierKeys & Keys.Control) == Keys.Control;
if (bCtrlDown) {
if (nfi.Node.IsSelected) {
nfi.Node.SetSelected(false, false);
m_hs_node_selected.Remove(nfi.Node);
if (nfi.Node == this._ActiveNode) {
this.SetActiveNode(null);
}
} else {
nfi.Node.SetSelected(true, false);
m_hs_node_selected.Add(nfi.Node);
nfi.Node.SetSelected(true, true);
}
this.Invalidate();
this.OnSelectedChanged(new EventArgs());
return;
} else if (!nfi.Node.IsSelected) {
foreach (var n in m_hs_node_selected) n.SetSelected(false, false);
m_hs_node_selected.Clear();
foreach (var n in m_hs_node_selected.ToArray()) n.SetSelected(false, false);
}
nfi.Node.SetSelected(true, false);
m_hs_node_selected.Add(nfi.Node); //添加到已选择节点
nfi.Node.SetSelected(true, false); //添加到已选择节点
this.SetActiveNode(nfi.Node);
if (this.PointInRectangle(nfi.Node.TitleRectangle, m_pt_down_in_canvas.X, m_pt_down_in_canvas.Y)) {
if (e.Button == MouseButtons.Right) {
if (nfi.Node.ContextMenuStrip != null) {
@ -722,19 +741,23 @@ namespace ST.Library.UI
}
} else {
m_dic_pt_selected.Clear();
foreach (STNode n in m_hs_node_selected)//记录已选择节点位置 如果需要移动已选中节点时候 将会有用
m_dic_pt_selected.Add(n, n.Location);
m_ca = CanvasAction.MoveNode; //如果点下的是节点的标题 则可以移动该节点
if (this._Magnet) this.BuildMagnetLocation(); //建立磁铁需要的坐标 如果需要移动已选中节点时候 将会有用
lock (m_hs_node_selected) {
foreach (STNode n in m_hs_node_selected) //记录已选择节点位置 如果需要移动已选中节点时候 将会有用
m_dic_pt_selected.Add(n, n.Location);
}
m_ca = CanvasAction.MoveNode; //如果点下的是节点的标题 则可以移动该节点
if (this._ShowMagnet && this._ActiveNode != null) this.BuildMagnetLocation(); //建立磁铁需要的坐标 如果需要移动已选中节点时候 将会有用
}
}
//nfi.Node.OnMouseDown(new MouseEventArgs(e.Button, e.Clicks, (int)m_pt_down_in_canvas.X - nfi.Node.Left, (int)m_pt_down_in_canvas.Y - nfi.Node.Top, e.Delta));
} else
m_node_down = nfi.Node;
} else {
foreach (var n in m_hs_node_selected) n.SetSelected(false, false);//没有点下任何东西 清空已经选择节点
m_ca = CanvasAction.SelectRectangle; //进入矩形区域选择模式
this.SetActiveNode(null);
foreach (var n in m_hs_node_selected.ToArray()) n.SetSelected(false, false);//没有点下任何东西 清空已经选择节点
m_ca = CanvasAction.SelectRectangle; //进入矩形区域选择模式
m_rect_select.Width = m_rect_select.Height = 0;
m_node_down = null;
}
this.SetActiveNode(nfi.Node);
//this.SetActiveNode(nfi.Node);
}
protected override void OnMouseMove(MouseEventArgs e) {
@ -743,6 +766,13 @@ namespace ST.Library.UI
m_pt_in_canvas.X = ((e.X - this._CanvasOffsetX) / this._CanvasScale);
m_pt_in_canvas.Y = ((e.Y - this._CanvasOffsetY) / this._CanvasScale);
if (m_node_down != null) {
m_node_down.OnMouseMove(new MouseEventArgs(e.Button, e.Clicks,
(int)m_pt_in_canvas.X - m_node_down.Left,
(int)m_pt_in_canvas.Y - m_node_down.Top, e.Delta));
return;
}
if (e.Button == MouseButtons.Middle) { //鼠标中键移动画布
this._CanvasOffsetX = m_real_canvas_x = m_pt_canvas_old.X + (e.X - m_pt_down_in_control.X);
this._CanvasOffsetY = m_real_canvas_y = m_pt_canvas_old.Y + (e.Y - m_pt_down_in_control.Y);
@ -770,13 +800,13 @@ namespace ST.Library.UI
NodeFindInfo nfi = this.FindNodeFromPoint(m_pt_in_canvas);
bool bRedraw = false;
if (this._HoverNode != nfi.Node) { //鼠标悬停到Node上
if (nfi.Node != null) nfi.Node.OnMouseEnter(new EventArgs());
if (nfi.Node != null) nfi.Node.OnMouseEnter(EventArgs.Empty);
if (this._HoverNode != null)
this._HoverNode.OnMouseLeave(new MouseEventArgs(e.Button, e.Clicks,
(int)m_pt_in_canvas.X - this._HoverNode.Left,
(int)m_pt_in_canvas.Y - this._HoverNode.Top, e.Delta));
this._HoverNode = nfi.Node;
this.OnHoverChanged(new EventArgs());
this.OnHoverChanged(EventArgs.Empty);
bRedraw = true;
}
if (this._HoverNode != null) {
@ -816,27 +846,27 @@ namespace ST.Library.UI
m_option_down.ConnectOption(nfi.NodeOption);
}
break;
case CanvasAction.SelectRectangle: //若正在进行选取 则判断是否有选中目标
m_hs_node_selected.Clear();
foreach (STNode n in this._Nodes) {
if (n.IsSelected) m_hs_node_selected.Add(n);
}
if (m_hs_node_selected.Count != 0) this.OnSelectedChanged(new EventArgs());
break;
}
if (m_is_process_mouse_event && this._ActiveNode != null) {
var mea = new MouseEventArgs(e.Button, e.Clicks,
(int)m_pt_in_canvas.X - this._ActiveNode.Left,
(int)m_pt_in_canvas.Y - this._ActiveNode.Top, e.Delta);
this._ActiveNode.OnMouseUp(mea);
m_node_down = null;
}
m_is_process_mouse_event = true; //当前为断开连接操作不进行事件传递 下次将接受事件
m_ca = CanvasAction.None;
this.Invalidate();
}
protected override void OnMouseEnter(EventArgs e) {
base.OnMouseEnter(e);
m_mouse_in_control = true;
}
protected override void OnMouseLeave(EventArgs e) {
base.OnMouseLeave(e);
m_mouse_in_control = false;
if (this._HoverNode != null) this._HoverNode.OnMouseLeave(e);
this._HoverNode = null;
this.Invalidate();
@ -848,6 +878,7 @@ namespace ST.Library.UI
float f = this._CanvasScale + (e.Delta < 0 ? -0.1f : 0.1f);
this.ScaleCanvas(f, this.Width / 2, this.Height / 2);
} else {
if (!m_mouse_in_control) return;
var nfi = this.FindNodeFromPoint(m_pt_in_canvas);
if (this._HoverNode != null) {
this._HoverNode.OnMouseWheel(new MouseEventArgs(e.Button, e.Clicks,
@ -868,6 +899,7 @@ namespace ST.Library.UI
protected virtual void OnMouseHWheel(MouseEventArgs e) {
if ((Control.ModifierKeys & Keys.Control) == Keys.Control) return;
if (!m_mouse_in_control) return;
if (this._HoverNode != null) {
this._HoverNode.OnMouseWheel(new MouseEventArgs(e.Button, e.Clicks,
(int)m_pt_in_canvas.X - this._HoverNode.Left,
@ -902,6 +934,7 @@ namespace ST.Library.UI
protected override void OnKeyUp(KeyEventArgs e) {
base.OnKeyUp(e);
if (this._ActiveNode != null) this._ActiveNode.OnKeyUp(e);
m_node_down = null;
}
protected override void OnKeyPress(KeyPressEventArgs e) {
@ -911,6 +944,33 @@ namespace ST.Library.UI
#endregion
protected override void OnDragEnter(DragEventArgs drgevent) {
base.OnDragEnter(drgevent);
if (this.DesignMode) return;
if (drgevent.Data.GetDataPresent("STNodeType"))
drgevent.Effect = DragDropEffects.Copy;
else
drgevent.Effect = DragDropEffects.None;
}
protected override void OnDragDrop(DragEventArgs drgevent) {
base.OnDragDrop(drgevent);
if (this.DesignMode) return;
if (drgevent.Data.GetDataPresent("STNodeType")) {
object data = drgevent.Data.GetData("STNodeType");
if (!(data is Type)) return;
var t = (Type)data;
if (!t.IsSubclassOf(typeof(STNode))) return;
STNode node = (STNode)Activator.CreateInstance((t));
Point pt = new Point(drgevent.X, drgevent.Y);
pt = this.PointToClient(pt);
pt = this.ControlToCanvas(pt);
node.Left = pt.X; node.Top = pt.Y;
this.Nodes.Add(node);
}
}
#region protected ----------------------------------------------------------------------------------------------------
/// <summary>
/// 当绘制背景网格线时候发生
@ -943,18 +1003,8 @@ namespace ST.Library.UI
/// <param name="rect">可视画布区域大小</param>
protected virtual void OnDrawNode(DrawingTools dt, Rectangle rect) {
m_lst_node_out.Clear(); //清空超出视觉区域的 Node 的坐标
//var rect_canvas_display = this.ControlToCanvas(rect);
Image img_border = null;
foreach (STNode n in this._Nodes) {
//n.CheckSize(dt);
img_border = m_img_border;
if (this._ShowBorder) { //如果绘制边框 判断状态
if (this._ActiveNode == n) img_border = m_img_border_active;
else if (/*m_hs_node_selected.Contains(n)*/n.IsSelected) img_border = m_img_border_selected;
else if (this._HoverNode == n) img_border = m_img_border_hover;
this.RenderBorder(dt.Graphics, n.Rectangle, img_border);
if (!string.IsNullOrEmpty(n.Mark)) this.RenderBorder(dt.Graphics, n.MarkRectangle, img_border);
}
if (this._ShowBorder) this.OnDrawNodeBorder(dt, n);
n.OnDrawNode(dt); //调用 Node 进行自身绘制主体部分
if (!string.IsNullOrEmpty(n.Mark)) n.OnDrawMark(dt); //调用 Node 进行自身绘制 Mark 区域
if (!rect.IntersectsWith(n.Rectangle)) {
@ -963,6 +1013,20 @@ namespace ST.Library.UI
}
}
/// <summary>
/// 当绘制 Node 边框时候发生
/// </summary>
/// <param name="dt">绘制工具</param>
/// <param name="node">目标node</param>
protected virtual void OnDrawNodeBorder(DrawingTools dt, STNode node) {
Image img_border = null;
if (this._ActiveNode == node) img_border = m_img_border_active;
else if (node.IsSelected) img_border = m_img_border_selected;
else if (this._HoverNode == node) img_border = m_img_border_hover;
else img_border = m_img_border;
this.RenderBorder(dt.Graphics, node.Rectangle, img_border);
if (!string.IsNullOrEmpty(node.Mark)) this.RenderBorder(dt.Graphics, node.MarkRectangle, img_border);
}
/// <summary>
/// 当绘制已连接路径时候发生
/// </summary>
/// <param name="dt">绘制工具</param>
@ -973,6 +1037,7 @@ namespace ST.Library.UI
var t = typeof(object);
foreach (STNode n in this._Nodes) {
foreach (STNodeOption op in n.OutputOptions) {
if (op == STNodeOption.Empty) continue;
if (op.DotColor != Color.Transparent) //确定线条颜色
m_p_line.Color = op.DotColor;
else {
@ -1040,12 +1105,39 @@ namespace ST.Library.UI
/// </summary>
/// <param name="dt">绘制工具</param>
/// <param name="mi">匹配的磁铁信息</param>
protected virtual void OnDrawMagnetLine(DrawingTools dt, MagnetInfo mi) {
protected virtual void OnDrawMagnet(DrawingTools dt, MagnetInfo mi) {
if (this._ActiveNode == null) return;
Graphics g = dt.Graphics;
Pen pen = m_drawing_tools.Pen;
pen.Color = this._MagnetLineColor;
SolidBrush brush = dt.SolidBrush;
pen.Color = this._MagnetColor;
brush.Color = Color.FromArgb(this._MagnetColor.A / 3, this._MagnetColor);
g.SmoothingMode = SmoothingMode.None;
int nL = this._ActiveNode.Left, nMX = this._ActiveNode.Left + this._ActiveNode.Width / 2, nR = this._ActiveNode.Right;
int nT = this._ActiveNode.Top, nMY = this._ActiveNode.Top + this._ActiveNode.Height / 2, nB = this._ActiveNode.Bottom;
if (mi.XMatched) g.DrawLine(pen, this.CanvasToControl(mi.X, true), 0, this.CanvasToControl(mi.X, true), this.Height);
if (mi.YMatched) g.DrawLine(pen, 0, this.CanvasToControl(mi.Y, false), this.Width, this.CanvasToControl(mi.Y, false));
g.TranslateTransform(this._CanvasOffsetX, this._CanvasOffsetY); //移动坐标系
g.ScaleTransform(this._CanvasScale, this._CanvasScale); //缩放绘图表面
if (mi.XMatched) {
//g.DrawLine(pen, this.CanvasToControl(mi.X, true), 0, this.CanvasToControl(mi.X, true), this.Height);
foreach (STNode n in this._Nodes) {
if (n.Left == mi.X || n.Right == mi.X || n.Left + n.Width / 2 == mi.X) {
//g.DrawRectangle(pen, n.Left, n.Top, n.Width - 1, n.Height - 1);
g.FillRectangle(brush, n.Rectangle);
}
}
}
if (mi.YMatched) {
//g.DrawLine(pen, 0, this.CanvasToControl(mi.Y, false), this.Width, this.CanvasToControl(mi.Y, false));
foreach (STNode n in this._Nodes) {
if (n.Top == mi.Y || n.Bottom == mi.Y || n.Top + n.Height / 2 == mi.Y) {
//g.DrawRectangle(pen, n.Left, n.Top, n.Width - 1, n.Height - 1);
g.FillRectangle(brush, n.Rectangle);
}
}
}
g.ResetTransform();
}
/// <summary>
/// 绘制选择的矩形区域
@ -1055,8 +1147,9 @@ namespace ST.Library.UI
protected virtual void OnDrawSelectedRectangle(DrawingTools dt, RectangleF rectf) {
Graphics g = dt.Graphics;
SolidBrush brush = dt.SolidBrush;
g.DrawRectangle(Pens.DodgerBlue, rectf.Left, rectf.Y, rectf.Width, rectf.Height);
brush.Color = Color.FromArgb(50, Color.DodgerBlue);
dt.Pen.Color = this._SelectedRectangleColor;
g.DrawRectangle(dt.Pen, rectf.Left, rectf.Y, rectf.Width, rectf.Height);
brush.Color = Color.FromArgb(this._SelectedRectangleColor.A / 3, this._SelectedRectangleColor);
g.FillRectangle(brush, this.CanvasToControl(m_rect_select));
}
/// <summary>
@ -1176,6 +1269,16 @@ namespace ST.Library.UI
this.OnDrawAlert(m_drawing_tools, m_rect_alert, m_str_alert, clr_fore, clr_back, m_al);
}
internal void InternalAddSelectedNode(STNode node) {
node.IsSelected = true;
lock (m_hs_node_selected) m_hs_node_selected.Add(node);
}
internal void InternalRemoveSelectedNode(STNode node) {
node.IsSelected = false;
lock (m_hs_node_selected) m_hs_node_selected.Remove(node);
}
#endregion internal
#region private -----------------------------------------------------------------------------------------------------
@ -1286,17 +1389,19 @@ namespace ST.Library.UI
private void MoveNode(Point pt) {
int nX = (int)((pt.X - m_pt_down_in_control.X) / this._CanvasScale);
int nY = (int)((pt.Y - m_pt_down_in_control.Y) / this._CanvasScale);
foreach (STNode v in m_hs_node_selected) {
v.Left = m_dic_pt_selected[v].X + nX;
v.Top = m_dic_pt_selected[v].Y + nY;
}
if (this._Magnet) {
MagnetInfo mi = this.CheckMagnet(this._ActiveNode);
if (mi.XMatched) {
foreach (STNode v in m_hs_node_selected) v.Left -= mi.OffsetX;
lock (m_hs_node_selected) {
foreach (STNode v in m_hs_node_selected) {
v.Left = m_dic_pt_selected[v].X + nX;
v.Top = m_dic_pt_selected[v].Y + nY;
}
if (mi.YMatched) {
foreach (STNode v in m_hs_node_selected) v.Top -= mi.OffsetY;
if (this._ShowMagnet) {
MagnetInfo mi = this.CheckMagnet(this._ActiveNode);
if (mi.XMatched) {
foreach (STNode v in m_hs_node_selected) v.Left -= mi.OffsetX;
}
if (mi.YMatched) {
foreach (STNode v in m_hs_node_selected) v.Top -= mi.OffsetY;
}
}
}
this.Invalidate();
@ -1450,9 +1555,11 @@ namespace ST.Library.UI
return m_find;
}
foreach (STNodeOption v in this._Nodes[i].InputOptions) {
if (v == STNodeOption.Empty) continue;
if (this.PointInRectangle(v.DotRectangle, pt.X, pt.Y)) m_find.NodeOption = v;
}
foreach (STNodeOption v in this._Nodes[i].OutputOptions) {
if (v == STNodeOption.Empty) continue;
if (this.PointInRectangle(v.DotRectangle, pt.X, pt.Y)) m_find.NodeOption = v;
}
if (this.PointInRectangle(this._Nodes[i].Rectangle, pt.X, pt.Y)) {
@ -1486,6 +1593,8 @@ namespace ST.Library.UI
public PointF CanvasToControl(PointF pt) {
pt.X = (pt.X * this._CanvasScale) + this._CanvasOffsetX;
pt.Y = (pt.Y * this._CanvasScale) + this._CanvasOffsetY;
//pt.X += this._CanvasOffsetX;
//pt.Y += this._CanvasOffsetY;
return pt;
}
/// <summary>
@ -1496,6 +1605,8 @@ namespace ST.Library.UI
public Point CanvasToControl(Point pt) {
pt.X = (int)(pt.X * this._CanvasScale + this._CanvasOffsetX);
pt.Y = (int)(pt.Y * this._CanvasScale + this._CanvasOffsetY);
//pt.X += (int)this._CanvasOffsetX;
//pt.Y += (int)this._CanvasOffsetY;
return pt;
}
/// <summary>
@ -1508,6 +1619,8 @@ namespace ST.Library.UI
rect.Y = (int)((rect.Y * this._CanvasScale) + this._CanvasOffsetY);
rect.Width = (int)(rect.Width * this._CanvasScale);
rect.Height = (int)(rect.Height * this._CanvasScale);
//rect.X += (int)this._CanvasOffsetX;
//rect.Y += (int)this._CanvasOffsetY;
return rect;
}
/// <summary>
@ -1520,6 +1633,8 @@ namespace ST.Library.UI
rect.Y = (rect.Y * this._CanvasScale) + this._CanvasOffsetY;
rect.Width = (rect.Width * this._CanvasScale);
rect.Height = (rect.Height * this._CanvasScale);
//rect.X += this._CanvasOffsetX;
//rect.Y += this._CanvasOffsetY;
return rect;
}
/// <summary>
@ -1605,15 +1720,15 @@ namespace ST.Library.UI
m_real_canvas_x = this._CanvasOffsetX = x;
m_real_canvas_y = this._CanvasOffsetY = y;
}
this.OnCanvasMoved(new EventArgs());
this.OnCanvasMoved(EventArgs.Empty);
}
/// <summary>
/// 缩放画布
/// 当不存在 Node 时候 无法缩放
/// </summary>
/// <param name="f">缩放比例</param>
/// <param name="x">以指定控件坐标 X 为中心进行缩放</param>
/// <param name="y">以指定控件坐标 Y 为中心进行缩放</param>
/// <param name="x">缩放中心X位于控件上的坐标</param>
/// <param name="y">缩放中心Y位于控件上的坐标</param>
public void ScaleCanvas(float f, float x, float y) {
if (this._Nodes.Count == 0) {
this._CanvasScale = 1F;
@ -1626,7 +1741,7 @@ namespace ST.Library.UI
this._CanvasScale = f;
this._CanvasOffsetX = m_real_canvas_x -= this.CanvasToControl(x_c, true) - x;
this._CanvasOffsetY = m_real_canvas_y -= this.CanvasToControl(y_c, false) - y;
this.OnCanvasScaled(new EventArgs());
this.OnCanvasScaled(EventArgs.Empty);
this.Invalidate();
}
/// <summary>
@ -1715,8 +1830,8 @@ namespace ST.Library.UI
byte[] byNode = node.GetSaveData();
gs.Write(BitConverter.GetBytes(byNode.Length), 0, 4);
gs.Write(byNode, 0, byNode.Length);
foreach (STNodeOption op in node.InputOptions) dic.Add(op, dic.Count);
foreach (STNodeOption op in node.OutputOptions) dic.Add(op, dic.Count);
foreach (STNodeOption op in node.InputOptions) if (!dic.ContainsKey(op)) dic.Add(op, dic.Count);
foreach (STNodeOption op in node.OutputOptions) if (!dic.ContainsKey(op)) dic.Add(op, dic.Count);
} catch (Exception ex) {
throw new Exception("获取节点数据出错-" + node.Title, ex);
}
@ -1816,6 +1931,7 @@ namespace ST.Library.UI
gs.Read(byLen, 0, 4);
int nCount = BitConverter.ToInt32(byLen, 0);
Dictionary<long, STNodeOption> dic = new Dictionary<long, STNodeOption>();
HashSet<STNodeOption> hs = new HashSet<STNodeOption>();
byte[] byData = null;
for (int i = 0; i < nCount; i++) {
gs.Read(byLen, 0, byLen.Length);
@ -1829,8 +1945,8 @@ namespace ST.Library.UI
try { this._Nodes.Add(node); } catch (Exception ex) {
throw new Exception("加载节点出错-" + node.Title, ex);
}
foreach (STNodeOption op in node.InputOptions) dic.Add(dic.Count, op);
foreach (STNodeOption op in node.OutputOptions) dic.Add(dic.Count, op);
foreach (STNodeOption op in node.InputOptions) if (hs.Add(op)) dic.Add(dic.Count, op);
foreach (STNodeOption op in node.OutputOptions) if (hs.Add(op)) dic.Add(dic.Count, op);
}
gs.Read(byLen, 0, 4);
nCount = BitConverter.ToInt32(byLen, 0);
@ -1871,7 +1987,7 @@ namespace ST.Library.UI
nIndex += nLen;
dic.Add(strKey, byValue);
}
if (!m_dic_type.ContainsKey(strGUID)) throw new TypeLoadException("无法找到类型 {" + strGUID + "} 所在程序集 确保程序集{" + strModel + "}已被编辑器正确加载 可通过调用LoadAssembly()加载程序集");
if (!m_dic_type.ContainsKey(strGUID)) throw new TypeLoadException("无法找到类型 {" + strModel.Split('|')[1] + "} 所在程序集 确保程序集 {" + strModel.Split('|')[0] + "} 已被编辑器正确加载 可通过调用LoadAssembly()加载程序集");
Type t = m_dic_type[strGUID]; ;
STNode node = (STNode)Activator.CreateInstance(t);
node.OnLoadNode(dic);
@ -1884,7 +2000,7 @@ namespace ST.Library.UI
/// <param name="foreColor">信息前景色</param>
/// <param name="backColor">信息背景色</param>
public void ShowAlert(string strText, Color foreColor, Color backColor) {
this.ShowAlert(strText, foreColor, backColor, 1000, AlertLocation.LeftBottom, true);
this.ShowAlert(strText, foreColor, backColor, 1000, AlertLocation.RightBottom, true);
}
/// <summary>
/// 在画布中显示提示信息
@ -1921,32 +2037,71 @@ namespace ST.Library.UI
/// <param name="node">需要被设置为活动的节点</param>
/// <returns>设置前的活动节点</returns>
public STNode SetActiveNode(STNode node) {
if (node != null && !this._Nodes.Contains(node)) return this._ActiveNode;
STNode ret = this._ActiveNode;
if (this._ActiveNode != node) { //重置活动选择节点
if (node != null) {
node.IsSelected = node.IsActive = true;
node.OnGotFocus(new EventArgs());
this._Nodes.MoveToEnd(node);
node.IsActive = true;
node.SetSelected(true, false);
node.OnGotFocus(EventArgs.Empty);
}
if (this._ActiveNode != null) {
this._ActiveNode.IsActive = this._ActiveNode.IsSelected = false;
this._ActiveNode.OnLostFocus(new EventArgs());
this._ActiveNode.IsActive /*= this._ActiveNode.IsSelected*/ = false;
this._ActiveNode.OnLostFocus(EventArgs.Empty);
}
this._ActiveNode = node;
this.Invalidate();
this.OnSelectedChanged(new EventArgs());
this.OnActiveChanged(EventArgs.Empty);
//this.OnSelectedChanged(EventArgs.Empty);
}
return ret;
}
/// <summary>
/// 向画布中添加一个被选中的节点
/// </summary>
/// <param name="node">需要被选中的节点</param>
/// <returns>是否添加成功</returns>
public bool AddSelectedNode(STNode node) {
if (!this._Nodes.Contains(node)) return false;
bool b = !node.IsSelected;
node.IsSelected = true;
lock (m_hs_node_selected) return m_hs_node_selected.Add(node) || b;
}
/// <summary>
/// 向画布中移除一个被选中的节点
/// </summary>
/// <param name="node">需要被移除的节点</param>
/// <returns>是移除否成功</returns>
public bool RemoveSelectedNode(STNode node) {
if (!this._Nodes.Contains(node)) return false;
bool b = node.IsSelected;
node.IsSelected = false;
lock (m_hs_node_selected) return m_hs_node_selected.Remove(node) || b;
}
/// <summary>
/// 向编辑器中添加默认数据类型颜色
/// </summary>
/// <param name="t">数据类型</param>
/// <param name="clr">对应颜色</param>
public void SetTypeColor(Type t, Color clr) {
if (this._TypeColor.ContainsKey(t))
this._TypeColor[t] = clr;
else
/// <returns>被设置后的颜色</returns>
public Color SetTypeColor(Type t, Color clr) {
return this.SetTypeColor(t, clr, false);
}
/// <summary>
/// 向编辑器中添加默认数据类型颜色
/// </summary>
/// <param name="t">数据类型</param>
/// <param name="clr">对应颜色</param>
/// <param name="bReplace">若已经存在是否替换颜色</param>
/// <returns>被设置后的颜色</returns>
public Color SetTypeColor(Type t, Color clr, bool bReplace) {
if (this._TypeColor.ContainsKey(t)) {
if (bReplace) this._TypeColor[t] = clr;
} else {
this._TypeColor.Add(t, clr);
}
return this._TypeColor[t];
}
#endregion public

View File

@ -5,7 +5,7 @@ using System.Text;
using System.ComponentModel;
using System.Drawing;
namespace ST.Library.UI
namespace ST.Library.UI.NodeEditor
{
public enum ConnectionStatus
{
@ -45,6 +45,11 @@ namespace ST.Library.UI
[Description("已存在的连接")]
Exists,
/// <summary>
/// 空白选项
/// </summary>
[Description("空白选项")]
EmptyOption,
/// <summary>
/// 已经连接
/// </summary>
[Description("已经连接")]

View File

@ -0,0 +1,329 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace ST.Library.UI.NodeEditor
{
public class STNodeEditorPannel : Control
{
private bool _LeftLayout = true;
/// <summary>
/// 获取或设置是否是左边布局
/// </summary>
[Description("获取或设置是否是左边布局"), DefaultValue(true)]
public bool LeftLayout {
get { return _LeftLayout; }
set {
if (value == _LeftLayout) return;
_LeftLayout = value;
this.SetLocation();
this.Invalidate();
}
}
private Color _SplitLineColor = Color.Black;
/// <summary>
/// 获取或这是分割线颜色
/// </summary>
[Description("获取或这是分割线颜色"), DefaultValue(typeof(Color), "Black")]
public Color SplitLineColor {
get { return _SplitLineColor; }
set { _SplitLineColor = value; }
}
private Color _HandleLineColor = Color.Gray;
/// <summary>
/// 获取或设置分割线手柄颜色
/// </summary>
[Description("获取或设置分割线手柄颜色"), DefaultValue(typeof(Color), "Gray")]
public Color HandleLineColor {
get { return _HandleLineColor; }
set { _HandleLineColor = value; }
}
private bool _ShowScale = true;
/// <summary>
/// 获取或设置编辑器缩放时候显示比例
/// </summary>
[Description("获取或设置编辑器缩放时候显示比例"), DefaultValue(true)]
public bool ShowScale {
get { return _ShowScale; }
set { _ShowScale = value; }
}
private bool _ShowConnectionStatus = true;
/// <summary>
/// 获取或设置节点连线时候是否显示状态
/// </summary>
[Description("获取或设置节点连线时候是否显示状态"), DefaultValue(true)]
public bool ShowConnectionStatus {
get { return _ShowConnectionStatus; }
set { _ShowConnectionStatus = value; }
}
private int _X;
/// <summary>
/// 获取或设置分割线水平宽度
/// </summary>
[Description("获取或设置分割线水平宽度"), DefaultValue(201)]
public int X {
get { return _X; }
set {
if (value < 122) value = 122;
else if (value > this.Width - 122) value = this.Width - 122;
if (_X == value) return;
_X = value;
this.SetLocation();
}
}
private int _Y;
/// <summary>
/// 获取或设置分割线垂直高度
/// </summary>
[Description("获取或设置分割线垂直高度")]
public int Y {
get { return _Y; }
set {
if (value < 122) value = 122;
else if (value > this.Height - 122) value = this.Height - 122;
if (_Y == value) return;
_Y = value;
this.SetLocation();
}
}
/// <summary>
/// 获取面板中的STNodeEditor
/// </summary>
[Description("获取面板中的STNodeEditor"), Browsable(false)]
public STNodeEditor Editor {
get { return m_editor; }
}
/// <summary>
/// 获取面板中的STNodeTreeView
/// </summary>
[Description("获取面板中的STNodeTreeView"), Browsable(false)]
public STNodeTreeView TreeView {
get { return m_tree; }
}
/// <summary>
/// 获取面板中的STNodePropertyGrid
/// </summary>
[Description("获取面板中的STNodePropertyGrid"), Browsable(false)]
public STNodePropertyGrid PropertyGrid {
get { return m_grid; }
}
private Point m_pt_down;
private bool m_is_mx;
private bool m_is_my;
private Pen m_pen;
private bool m_nInited;
private Dictionary<ConnectionStatus, string> m_dic_status_key = new Dictionary<ConnectionStatus, string>();
private STNodeEditor m_editor;
private STNodeTreeView m_tree;
private STNodePropertyGrid m_grid;
public override Size MinimumSize {
get {
return base.MinimumSize;
}
set {
value = new Size(250, 250);
base.MinimumSize = value;
}
}
[DllImport("user32.dll")]
private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool bRedraw);
public STNodeEditorPannel() {
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
m_editor = new STNodeEditor();
m_tree = new STNodeTreeView();
m_grid = new STNodePropertyGrid();
m_grid.Text = "NodeProperty";
this.Controls.Add(m_editor);
this.Controls.Add(m_tree);
this.Controls.Add(m_grid);
this.Size = new Size(500, 500);
this.MinimumSize = new Size(250, 250);
this.BackColor = Color.FromArgb(255, 34, 34, 34);
m_pen = new Pen(this.BackColor, 3);
Type t = typeof(ConnectionStatus);
var vv = Enum.GetValues(t);
var vvv = vv.GetValue(0);
foreach (var f in t.GetFields()) {
if (!f.FieldType.IsEnum) continue;
foreach (var a in f.GetCustomAttributes(true)) {
if (!(a is DescriptionAttribute)) continue;
m_dic_status_key.Add((ConnectionStatus)f.GetValue(f), ((DescriptionAttribute)a).Description);
}
}
m_editor.ActiveChanged += (s, e) => m_grid.SetNode(m_editor.ActiveNode);
m_editor.CanvasScaled += (s, e) => {
if (this._ShowScale)
m_editor.ShowAlert(m_editor.CanvasScale.ToString("F2"), Color.White, Color.FromArgb(127, 255, 255, 0));
};
m_editor.OptionConnected += (s, e) => {
if (this._ShowConnectionStatus)
m_editor.ShowAlert(m_dic_status_key[e.Status], Color.White, e.Status == ConnectionStatus.Connected ? Color.FromArgb(125, Color.Lime) : Color.FromArgb(125, Color.Red));
};
}
protected override void OnResize(EventArgs e) {
base.OnResize(e);
if (!m_nInited) {
this._Y = this.Height / 2;
if (this._LeftLayout)
this._X = 201;
else
this._X = this.Width - 202;
m_nInited = true;
this.SetLocation();
return;
}
this.SetLocation();
}
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
if (width < 250) width = 250;
if (height < 250) height = 250;
base.SetBoundsCore(x, y, width, height, specified);
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Graphics g = e.Graphics;
m_pen.Width = 3;
m_pen.Color = this._SplitLineColor;
g.DrawLine(m_pen, this._X, 0, this._X, this.Height);
int nX = 0;
if (this._LeftLayout) {
g.DrawLine(m_pen, 0, this._Y, this._X - 1, this._Y);
nX = this._X / 2;
} else {
g.DrawLine(m_pen, this._X + 2, this._Y, this.Width, this._Y);
nX = this._X + (this.Width - this._X) / 2;
}
m_pen.Width = 1;
this._HandleLineColor = Color.Gray;
m_pen.Color = this._HandleLineColor;
g.DrawLine(m_pen, this._X, this._Y - 10, this._X, this._Y + 10);
g.DrawLine(m_pen, nX - 10, this._Y, nX + 10, this._Y);
}
private void SetLocation() {
if (this._LeftLayout) {
//m_tree.Location = Point.Empty;
//m_tree.Size = new Size(m_sx - 1, m_sy - 1);
STNodeEditorPannel.MoveWindow(m_tree.Handle, 0, 0, this._X - 1, this._Y - 1, false);
//m_grid.Location = new Point(0, m_sy + 2);
//m_grid.Size = new Size(m_sx - 1, this.Height - m_sy - 2);
STNodeEditorPannel.MoveWindow(m_grid.Handle, 0, this._Y + 2, this._X - 1, this.Height - this._Y - 2, false);
//m_editor.Location = new Point(m_sx + 2, 0);
//m_editor.Size = new Size(this.Width - m_sx - 2, this.Height);
STNodeEditorPannel.MoveWindow(m_editor.Handle, this._X + 2, 0, this.Width - this._X - 2, this.Height, false);
} else {
STNodeEditorPannel.MoveWindow(m_editor.Handle, 0, 0, this._X - 1, this.Height, false);
STNodeEditorPannel.MoveWindow(m_tree.Handle, this._X + 2, 0, this.Width - this._X - 2, this._Y - 1, false);
STNodeEditorPannel.MoveWindow(m_grid.Handle, this._X + 2, this._Y + 2, this.Width - this._X - 2, this.Height - this._Y - 2, false);
}
}
protected override void OnMouseDown(MouseEventArgs e) {
base.OnMouseDown(e);
m_pt_down = e.Location;
m_is_mx = m_is_my = false;
if (this.Cursor == Cursors.VSplit) {
m_is_mx = true;
} else if (this.Cursor == Cursors.HSplit) {
m_is_my = true;
}
}
protected override void OnMouseMove(MouseEventArgs e) {
base.OnMouseMove(e);
if (e.Button == MouseButtons.Left) {
int nw = 122;// (int)(this.Width * 0.1f);
int nh = 122;// (int)(this.Height * 0.1f);
if (m_is_mx) {
this._X = e.X;// -m_pt_down.X;
if (this._X < nw) this._X = nw;
else if (_X + nw > this.Width) this._X = this.Width - nw;
} else if (m_is_my) {
this._Y = e.Y;
if (this._Y < nh) this._Y = nh;
else if (this._Y + nh > this.Height) this._Y = this.Height - nh;
}
//m_rx = this.Width - m_sx;// (float)m_sx / this.Width;
//m_fh = (float)m_sy / this.Height;
this.SetLocation();
this.Invalidate();
return;
}
if (Math.Abs(e.X - this._X) < 2)
this.Cursor = Cursors.VSplit;
else if (Math.Abs(e.Y - this._Y) < 2)
this.Cursor = Cursors.HSplit;
else this.Cursor = Cursors.Arrow;
}
protected override void OnMouseLeave(EventArgs e) {
base.OnMouseLeave(e);
m_is_mx = m_is_my = false;
this.Cursor = Cursors.Arrow;
}
/// <summary>
/// 向树控件中添加一个STNode
/// </summary>
/// <param name="stNodeType">STNode类型</param>
/// <returns>是否添加成功</returns>
public bool AddSTNode(Type stNodeType) {
return m_tree.AddNode(stNodeType);
}
/// <summary>
/// 从程序集中加载STNode
/// </summary>
/// <param name="strFileName">程序集路径</param>
/// <returns>添加成功个数</returns>
public int LoadAssembly(string strFileName) {
m_editor.LoadAssembly(strFileName);
return m_tree.LoadAssembly(strFileName);
}
/// <summary>
/// 设置编辑器显示连接状态的文本
/// </summary>
/// <param name="status">连接状态</param>
/// <param name="strText">对应显示文本</param>
/// <returns>旧文本</returns>
public string SetConnectionStatusText(ConnectionStatus status, string strText) {
string strOld = null;
if (m_dic_status_key.ContainsKey(status)) {
strOld = m_dic_status_key[status];
m_dic_status_key[status] = strText;
return strOld;
}
m_dic_status_key.Add(status, strText);
return strText;
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
/*
MIT License
@ -26,12 +27,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
* time: 2021-01-06
* create: 2021-12-08
* modify: 2021-03-02
* Author: Crystal_lz
* blog: st233.com
* Github: DebugST.github.io
* blog: http://st233.com
* Gitee: https://gitee.com/DebugST
* Github: https://github.com/DebugST
*/
namespace ST.Library.UI
namespace ST.Library.UI.NodeEditor
{
public class STNodeHub : STNode
{
@ -47,9 +50,18 @@ namespace ST.Library.UI
m_strOut = strTextOut;
this.Addhub();
this.Title = "HUB";
this.AutoSize = false;
this.TitleColor = System.Drawing.Color.FromArgb(200, System.Drawing.Color.DarkOrange);
}
protected override void OnOwnerChanged() {
base.OnOwnerChanged();
if (this.Owner == null) return;
using (Graphics g = this.Owner.CreateGraphics()) {
this.Width = base.GetDefaultNodeSize(g).Width;
}
}
private void Addhub() {
var input = new STNodeHubOption(m_strIn, typeof(object), m_bSingle);
var output = new STNodeHubOption(m_strOut, typeof(object), false);
@ -60,6 +72,7 @@ namespace ST.Library.UI
input.DisConnected += new STNodeOptionEventHandler(input_DisConnected);
output.Connected += new STNodeOptionEventHandler(output_Connected);
output.DisConnected += new STNodeOptionEventHandler(output_DisConnected);
this.Height = this.TitleHeight + this.InputOptions.Count * 20;
}
void output_DisConnected(object sender, STNodeOptionEventArgs e) {
@ -70,6 +83,7 @@ namespace ST.Library.UI
this.InputOptions.RemoveAt(nIndex);
this.OutputOptions.RemoveAt(nIndex);
if (this.Owner != null) this.Owner.BuildLinePath();
this.Height -= 20;
}
void output_Connected(object sender, STNodeOptionEventArgs e) {
@ -94,6 +108,7 @@ namespace ST.Library.UI
this.InputOptions.RemoveAt(nIndex);
this.OutputOptions.RemoveAt(nIndex);
if (this.Owner != null) this.Owner.BuildLinePath();
this.Height -= 20;
}
void input_DataTransfer(object sender, STNodeOptionEventArgs e) {
@ -123,20 +138,20 @@ namespace ST.Library.UI
}
}
protected override void OnSaveNode(Dictionary<string,byte[]> dic) {
protected override void OnSaveNode(Dictionary<string, byte[]> dic) {
dic.Add("count", BitConverter.GetBytes(this.InputOptionsCount));
dic.Add("single", new byte[] { (byte)(m_bSingle ? 1 : 0) });
dic.Add("strin", Encoding.UTF8.GetBytes(m_strIn));
dic.Add("strout", Encoding.UTF8.GetBytes(m_strOut));
//dic.Add("single", new byte[] { (byte)(m_bSingle ? 1 : 0) });
//dic.Add("strin", Encoding.UTF8.GetBytes(m_strIn));
//dic.Add("strout", Encoding.UTF8.GetBytes(m_strOut));
}
protected internal override void OnLoadNode(Dictionary<string, byte[]> dic) {
base.OnLoadNode(dic);
int nCount = BitConverter.ToInt32(dic["count"], 0);
while (this.InputOptionsCount < nCount && this.InputOptionsCount != nCount) this.Addhub();
m_bSingle = dic["single"][0] == 1;
m_strIn = Encoding.UTF8.GetString(dic["strin"]);
m_strOut = Encoding.UTF8.GetString(dic["strout"]);
//m_bSingle = dic["single"][0] == 1;
//m_strIn = Encoding.UTF8.GetString(dic["strin"]);
//m_strOut = Encoding.UTF8.GetString(dic["strout"]);
}
public class STNodeHubOption : STNodeOption
@ -153,10 +168,12 @@ namespace ST.Library.UI
}
public override ConnectionStatus CanConnect(STNodeOption op) {
if (op == STNodeOption.Empty) return ConnectionStatus.EmptyOption;
if (this.DataType != typeof(object)) return base.CanConnect(op);
if (this.IsInput == op.IsInput) return ConnectionStatus.SameInputOrOutput;
if (op.Owner == this.Owner) return ConnectionStatus.SameOwner;
if (op.Owner == null || this.Owner == null) return ConnectionStatus.NoOwner;
if (op.Owner == this.Owner) return ConnectionStatus.SameOwner;
if (this.Owner.LockOption || op.Owner.LockOption) return ConnectionStatus.Locked;
if (this.IsSingle && m_hs_connected.Count == 1) return ConnectionStatus.SingleOption;
if (op.IsInput && STNodeEditor.CanFindNodePath(op.Owner, this.Owner)) return ConnectionStatus.Loop;
if (m_hs_connected.Contains(op)) return ConnectionStatus.Exists;

View File

@ -5,12 +5,14 @@ using System.Text;
using System.Drawing;
namespace ST.Library.UI
namespace ST.Library.UI.NodeEditor
{
public class STNodeOption
{
#region Properties
public static readonly STNodeOption Empty = new STNodeOption();
private STNode _Owner;
/// <summary>
/// 获取当前 Option 所属的 Node
@ -47,7 +49,7 @@ namespace ST.Library.UI
/// </summary>
public Color TextColor {
get { return _TextColor; }
protected set {
internal set {
if (value == _TextColor) return;
_TextColor = value;
this.Invalidate();
@ -60,7 +62,7 @@ namespace ST.Library.UI
/// </summary>
public Color DotColor {
get { return _DotColor; }
protected set {
internal set {
if (value == _DotColor) return;
_DotColor = value;
this.Invalidate();
@ -70,13 +72,15 @@ namespace ST.Library.UI
private string _Text;
/// <summary>
/// 获取或设置当前 Option 显示文本
/// 当AutoSize被设置时 无法修改此属性
/// </summary>
public string Text {
get { return _Text; }
protected set {
internal set {
if (value == _Text) return;
_Text = value;
this.Invalidate();
if (this._Owner == null) return;
this._Owner.BuildSize(true, true, true);
}
}
@ -123,6 +127,7 @@ namespace ST.Library.UI
get { return _Data; }
set {
if (value != null) {
if (this._DataType == null) return;
var t = value.GetType();
if (t != this._DataType && !t.IsSubclassOf(this._DataType)) {
throw new ArgumentException("无效数据类型 数据类型必须为指定的数据类型或其子类");
@ -171,6 +176,8 @@ namespace ST.Library.UI
#region Constructor
private STNodeOption() { }
/// <summary>
/// 构造一个 Option
/// </summary>
@ -317,14 +324,15 @@ namespace ST.Library.UI
/// <param name="op">需要连接的 Option</param>
/// <returns>检测结果</returns>
public virtual ConnectionStatus CanConnect(STNodeOption op) {
if (this == STNodeOption.Empty || op == STNodeOption.Empty) return ConnectionStatus.EmptyOption;
if (this._IsInput == op.IsInput) return ConnectionStatus.SameInputOrOutput;
if (op.Owner == this._Owner) return ConnectionStatus.SameOwner;
if (op.Owner == null || this._Owner == null) return ConnectionStatus.NoOwner;
if (op.Owner == this._Owner) return ConnectionStatus.SameOwner;
if (this._Owner.LockOption || op._Owner.LockOption) return ConnectionStatus.Locked;
if (this._IsSingle && m_hs_connected.Count == 1) return ConnectionStatus.SingleOption;
if (op.IsInput && STNodeEditor.CanFindNodePath(op.Owner, this._Owner)) return ConnectionStatus.Loop;
if (m_hs_connected.Contains(op)) return ConnectionStatus.Exists;
if (this._IsInput && op.DataType != this._DataType && !op.DataType.IsSubclassOf(this._DataType)) return ConnectionStatus.ErrorType;
if (this._Owner.LockOption) return ConnectionStatus.Locked;
if (this._IsInput && op._DataType != this._DataType && !op._DataType.IsSubclassOf(this._DataType)) return ConnectionStatus.ErrorType;
return ConnectionStatus.Connected;
}
/// <summary>
@ -355,6 +363,7 @@ namespace ST.Library.UI
/// 断开当前 Option 的所有连接
/// </summary>
public void DisConnectionAll() {
if (this._DataType == null) return;
var arr = m_hs_connected.ToArray();
foreach (var v in arr) {
this.DisConnectOption(v);
@ -365,6 +374,7 @@ namespace ST.Library.UI
/// </summary>
/// <returns>如果为null 则表示不存在所有者 否则返回集合</returns>
public List<STNodeOption> GetConnectedOption() {
if (this._DataType == null) return null;
if (!this._IsInput)
return m_hs_connected.ToList();
List<STNodeOption> lst = new List<STNodeOption>();
@ -379,6 +389,7 @@ namespace ST.Library.UI
/// 向当前 Option 所连接的所有 Option 投递数据
/// </summary>
public void TransferData() {
if (this._DataType == null) return;
foreach (var v in m_hs_connected) {
v.OnDataTransfer(new STNodeOptionEventArgs(true, this, ConnectionStatus.Connected));
}
@ -388,17 +399,31 @@ namespace ST.Library.UI
/// </summary>
/// <param name="data">需要投递的数据</param>
public void TransferData(object data) {
if (this._DataType == null) return;
this.Data = data; //不是this._Data
foreach (var v in m_hs_connected) {
v.OnDataTransfer(new STNodeOptionEventArgs(true, this, ConnectionStatus.Connected));
}
}
/// <summary>
/// 向当前 Option 所连接的所有 Option 投递数据
/// </summary>
/// <param name="data">需要投递的数据</param>
/// <param name="bDisposeOld">是否释放旧数据</param>
public void TransferData(object data, bool bDisposeOld) {
if (bDisposeOld && this._Data != null) {
if (this._Data is IDisposable) ((IDisposable)this._Data).Dispose();
this._Data = null;
}
this.TransferData(data);
}
#endregion public
#region internal
private bool AddConnection(STNodeOption op, bool bSponsor) {
if (this._DataType == null) return false;
bool b = m_hs_connected.Add(op);
this.OnConnected(new STNodeOptionEventArgs(bSponsor, op, ConnectionStatus.Connected));
if (this._IsInput) this.OnDataTransfer(new STNodeOptionEventArgs(bSponsor, op, ConnectionStatus.Connected));
@ -406,6 +431,7 @@ namespace ST.Library.UI
}
private bool RemoveConnection(STNodeOption op, bool bSponsor) {
if (this._DataType == null) return false;
bool b = false;
if (m_hs_connected.Contains(op)) {
b = m_hs_connected.Remove(op);

View File

@ -5,7 +5,7 @@ using System.Text;
using System.Collections;
namespace ST.Library.UI
namespace ST.Library.UI.NodeEditor
{
public class STNodeOptionCollection : IList, ICollection, IEnumerable
{
@ -30,16 +30,23 @@ namespace ST.Library.UI
m_options = new STNodeOption[4];
}
public STNodeOption Add(string strText, Type dataType, bool bSingle) {
//not do this code -> out of bounds
//return m_options[this.Add(new STNodeOption(strText, dataType, bSingle))];
int nIndex = this.Add(new STNodeOption(strText, dataType, bSingle));
return m_options[nIndex];
}
public int Add(STNodeOption option) {
if (option == null) throw new ArgumentNullException("添加对象不能为空");
this.EnsureSpace(1);
int nIndex = this.IndexOf(option);
int nIndex = option == STNodeOption.Empty ? -1 : this.IndexOf(option);
if (-1 == nIndex) {
nIndex = this._Count;
option.Owner = m_owner;
option.IsInput = m_isInput;
m_options[this._Count++] = option;
this.Redraw();
this.Invalidate();
}
return nIndex;
}
@ -55,14 +62,14 @@ namespace ST.Library.UI
m_options[this._Count++] = op;
}
}
this.Redraw();
this.Invalidate();
}
public void Clear() {
for (int i = 0; i < this._Count; i++) m_options[i].Owner = null;
this._Count = 0;
m_options = new STNodeOption[4];
this.Redraw();
this.Invalidate();
}
public bool Contains(STNodeOption option) {
@ -84,7 +91,7 @@ namespace ST.Library.UI
option.Owner = m_owner;
m_options[index] = option;
this._Count++;
this.Redraw();
this.Invalidate();
}
public bool IsFixedSize {
@ -107,7 +114,7 @@ namespace ST.Library.UI
m_options[index].Owner = null;
for (int i = index, Len = this._Count; i < Len; i++)
m_options[i] = m_options[i + 1];
this.Redraw();
this.Invalidate();
}
public STNodeOption this[int index] {
@ -149,7 +156,7 @@ namespace ST.Library.UI
}
}
protected void Redraw() {
protected void Invalidate() {
if (m_owner != null && m_owner.Owner != null) {
m_owner.BuildSize(true, true, true);
//m_owner.Invalidate();//.Owner.Invalidate();

View File

@ -0,0 +1,334 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
namespace ST.Library.UI.NodeEditor
{
/// <summary>
/// STNode节点属性特性
/// 用于描述STNode节点属性信息 以及在属性编辑器上的行为
/// </summary>
public class STNodePropertyAttribute : Attribute
{
private string _Name;
/// <summary>
/// 获取属性需要在属性编辑器上显示的名称
/// </summary>
public string Name {
get { return _Name; }
}
private string _Description;
/// <summary>
/// 获取属性需要在属性编辑器上显示的描述
/// </summary>
public string Description {
get { return _Description; }
}
private Type _ConverterType = typeof(STNodePropertyDescriptor);
/// <summary>
/// 获取属性描述器类型
/// </summary>
public Type DescriptorType {
get { return _ConverterType; }
set { _ConverterType = value; }
}
/// <summary>
/// 构造一个STNode属性特性
/// </summary>
/// <param name="strKey">需要显示的名称</param>
/// <param name="strDesc">需要显示的描述信息</param>
public STNodePropertyAttribute(string strKey, string strDesc) {
this._Name = strKey;
this._Description = strDesc;
}
//private Type m_descriptor_type_base = typeof(STNodePropertyDescriptor);
}
/// <summary>
/// STNode属性描述器
/// 用于确定在属性编辑器上如何与属性的值进行交互 以及确定属性值在属性编辑器上将如何绘制并交互
/// </summary>
public class STNodePropertyDescriptor
{
/// <summary>
/// 获取目标节点
/// </summary>
public STNode Node { get; internal set; }
/// <summary>
/// 获取所属的节点属性编辑器控件
/// </summary>
public STNodePropertyGrid Control { get; internal set; }
/// <summary>
/// 获取选项所在区域
/// </summary>
public Rectangle Rectangle { get; internal set; }
/// <summary>
/// 获取选项名称所在区域
/// </summary>
public Rectangle RectangleL { get; internal set; }
/// <summary>
/// 获取选项值所在区域
/// </summary>
public Rectangle RectangleR { get; internal set; }
/// <summary>
/// 获取选项需要显示的名称
/// </summary>
public string Name { get; internal set; }
/// <summary>
/// 获取属性对应的描述信息
/// </summary>
public string Description { get; internal set; }
/// <summary>
/// 获取属性信息
/// </summary>
public PropertyInfo PropertyInfo { get; internal set; }
private static Type m_t_int = typeof(int);
private static Type m_t_float = typeof(float);
private static Type m_t_double = typeof(double);
private static Type m_t_string = typeof(string);
private static Type m_t_bool = typeof(bool);
private StringFormat m_sf;
/// <summary>
/// 构造一个描述器
/// </summary>
public STNodePropertyDescriptor() {
m_sf = new StringFormat();
m_sf.LineAlignment = StringAlignment.Center;
m_sf.FormatFlags = StringFormatFlags.NoWrap;
}
/// <summary>
/// 当确定STNode属性在属性编辑器上的位置时候发生
/// </summary>
protected internal virtual void OnSetItemLocation() { }
/// <summary>
/// 将字符串形式的属性值转换为属性目标类型的值
/// 默认只支持 int float double string bool 以及上述类型的Array
/// 若目标类型不在上述中 请重写此函数自行转换
/// </summary>
/// <param name="strText">字符串形式的属性值</param>
/// <returns>属性真实目标类型的值</returns>
protected internal virtual object GetValueFromString(string strText) {
Type t = this.PropertyInfo.PropertyType;
if (t == m_t_int) return int.Parse(strText);
if (t == m_t_float) return float.Parse(strText);
if (t == m_t_double) return double.Parse(strText);
if (t == m_t_string) return strText;
if (t == m_t_bool) return bool.Parse(strText);
if (t.IsEnum) {
return Enum.Parse(t, strText);
} else if (t.IsArray) {
var t_1 = t.GetElementType();
if (t_1 == m_t_string) return strText.Split(',');
string[] strs = strText.Trim(new char[] { ' ', ',' }).Split(',');//add other place trim()
if (t_1 == m_t_int) {
int[] arr = new int[strs.Length];
for (int i = 0; i < strs.Length; i++) arr[i] = int.Parse(strs[i].Trim());
return arr;
}
if (t_1 == m_t_float) {
float[] arr = new float[strs.Length];
for (int i = 0; i < strs.Length; i++) arr[i] = float.Parse(strs[i].Trim());
return arr;
}
if (t_1 == m_t_int) {
double[] arr = new double[strs.Length];
for (int i = 0; i < strs.Length; i++) arr[i] = double.Parse(strs[i].Trim());
return arr;
}
if (t_1 == m_t_int) {
bool[] arr = new bool[strs.Length];
for (int i = 0; i < strs.Length; i++) arr[i] = bool.Parse(strs[i].Trim());
return arr;
}
}
throw new InvalidCastException("无法完成[string]到[" + t.FullName + "]的转换 请重载[STNodePropertyDescriptor.GetValueFromString(string)]");
}
/// <summary>
/// 将属性目标类型的值转换为字符串形式的值
/// 默认对类型值进行 ToString() 操作
/// 如需特殊处理 请重写此函数自行转换
/// </summary>
/// <returns>属性值的字符串形式</returns>
protected internal virtual string GetStringFromValue() {
var v = this.PropertyInfo.GetValue(this.Node, null);
var t = this.PropertyInfo.PropertyType;
if (v == null) return null;
if (t.IsArray) {
List<string> lst = new List<string>();
foreach (var item in (Array)v) lst.Add(item.ToString());
return string.Join(",", lst.ToArray());
}
return v.ToString();
}
/// <summary>
/// 将二进制形式的属性值转换为属性目标类型的值 用于从文件存储中的数据还原属性值
/// 默认将其转换为字符串然后调用 GetValueFromString(string)
/// 此函数与 GetBytesFromValue() 相对应 若需要重写函数应当两个函数一起重写
/// </summary>
/// <param name="byData">二进制数据</param>
/// <returns>属性真实目标类型的值</returns>
protected internal virtual object GetValueFromBytes(byte[] byData) {
if (byData == null) return null;
string strText = Encoding.UTF8.GetString(byData);
return this.GetValueFromString(strText);
}
/// <summary>
/// 将属性目标类型的值转换为二进制形式的值 用于文件存储时候调用
/// 默认调用 GetStringFromValue() 然后将字符串转换为二进制数据
/// 如需特殊处理 请重写此函数自行转换 并且重写 GetValueFromBytes()
/// </summary>
/// <returns>属性值的二进制形式</returns>
protected internal virtual byte[] GetBytesFromValue() {
string strText = this.GetStringFromValue();
if (strText == null) return null;
return Encoding.UTF8.GetBytes(strText);
}
/// <summary>
/// 此函数对应 System.Reflection.PropertyInfo.GetValue()
/// </summary>
/// <param name="index">索引属性的可选索引值 对于非索引属性 此值应为null</param>
/// <returns>属性值</returns>
protected internal virtual object GetValue(object[] index) {
return this.PropertyInfo.GetValue(this.Node, index);
}
/// <summary>
/// 此函数对应 System.Reflection.PropertyInfo.SetValue()
/// </summary>
/// <param name="value">需要设置的属性值</param>
protected internal virtual void SetValue(object value) {
this.PropertyInfo.SetValue(this.Node, value, null);
}
/// <summary>
/// 此函数对应 System.Reflection.PropertyInfo.SetValue()
/// 在调用之前会默认进行 GetValueFromString(strValue) 处理
/// </summary>
/// <param name="strValue">需要设置的属性字符串形式的值</param>
protected internal virtual void SetValue(string strValue) {
this.PropertyInfo.SetValue(this.Node, this.GetValueFromString(strValue), null);
}
/// <summary>
/// 此函数对应 System.Reflection.PropertyInfo.SetValue()
/// 在调用之前会默认进行 GetValueFromBytes(byte[]) 处理
/// </summary>
/// <param name="byData">需要设置的属性二进制数据</param>
protected internal virtual void SetValue(byte[] byData) {
this.PropertyInfo.SetValue(this.Node, this.GetValueFromBytes(byData), null);
}
/// <summary>
/// 此函数对应 System.Reflection.PropertyInfo.SetValue()
/// </summary>
/// <param name="value">需要设置的属性值</param>
/// <param name="index">索引属性的可选索引值 对于非索引属性 此值应为null</param>
protected internal virtual void SetValue(object value, object[] index) {
this.PropertyInfo.SetValue(this.Node, value, index);
}
/// <summary>
/// 此函数对应 System.Reflection.PropertyInfo.SetValue()
/// 在调用之前会默认进行 GetValueFromString(strValue) 处理
/// </summary>
/// <param name="strValue">需要设置的属性字符串形式的值</param>
/// <param name="index">索引属性的可选索引值 对于非索引属性 此值应为null</param>
protected internal virtual void SetValue(string strValue, object[] index) {
this.PropertyInfo.SetValue(this.Node, this.GetValueFromString(strValue), index);
}
/// <summary>
/// 此函数对应 System.Reflection.PropertyInfo.SetValue()
/// 在调用之前会默认进行 GetValueFromBytes(byte[]) 处理
/// </summary>
/// <param name="byData">需要设置的属性二进制数据</param>
/// <param name="index">索引属性的可选索引值 对于非索引属性 此值应为null</param>
protected internal virtual void SetValue(byte[] byData, object[] index) {
this.PropertyInfo.SetValue(this.Node, this.GetValueFromBytes(byData), index);
}
/// <summary>
/// 当设置属性值发生错误时候发生
/// </summary>
/// <param name="ex">异常信息</param>
protected internal virtual void OnSetValueError(Exception ex) {
this.Control.SetErrorMessage(ex.Message);
}
/// <summary>
/// 当绘制属性在属性编辑器上的值所在区域时候发生
/// </summary>
/// <param name="dt">绘制工具</param>
protected internal virtual void OnDrawValueRectangle(DrawingTools dt) {
Graphics g = dt.Graphics;
SolidBrush brush = dt.SolidBrush;
STNodePropertyGrid ctrl = this.Control;
//STNodePropertyItem item = this._PropertyItem;
brush.Color = ctrl.ItemValueBackColor;
g.FillRectangle(brush, this.RectangleR);
Rectangle rect = this.RectangleR;
rect.Width--; rect.Height--;
brush.Color = this.Control.ForeColor;
g.DrawString(this.GetStringFromValue(), ctrl.Font, brush, this.RectangleR, m_sf);
if (this.PropertyInfo.PropertyType.IsEnum || this.PropertyInfo.PropertyType == m_t_bool) {
g.FillPolygon(Brushes.Gray, new Point[]{
new Point(rect.Right - 13, rect.Top + rect.Height / 2 - 2),
new Point(rect.Right - 4, rect.Top + rect.Height / 2 - 2),
new Point(rect.Right - 9, rect.Top + rect.Height / 2 + 3)
});
}
}
/// <summary>
/// 当鼠标进入属性值所在区域时候发生
/// </summary>
/// <param name="e">事件参数</param>
protected internal virtual void OnMouseEnter(EventArgs e) { }
/// <summary>
/// 当鼠标在属性值所在区域点击时候发生
/// </summary>
/// <param name="e">事件参数</param>
protected internal virtual void OnMouseDown(MouseEventArgs e) {
}
/// <summary>
/// 当鼠标在属性值所在区域移动时候发生
/// </summary>
/// <param name="e">事件参数</param>
protected internal virtual void OnMouseMove(MouseEventArgs e) { }
/// <summary>
/// 当鼠标在属性值所在区域抬起时候发生
/// </summary>
/// <param name="e">事件参数</param>
protected internal virtual void OnMouseUp(MouseEventArgs e) { }
/// <summary>
/// 当鼠标在属性值所在区域离开时候发生
/// </summary>
/// <param name="e">事件参数</param>
protected internal virtual void OnMouseLeave(EventArgs e) { }
/// <summary>
/// 当鼠标在属性值所在区域点击时候发生
/// </summary>
/// <param name="e">事件参数</param>
protected internal virtual void OnMouseClick(MouseEventArgs e) {
Type t = this.PropertyInfo.PropertyType;
if (t == m_t_bool || t.IsEnum) {
new FrmSTNodePropertySelect(this).Show(this.Control);
return;
}
Rectangle rect = this.Control.RectangleToScreen(this.RectangleR);
new FrmSTNodePropertyInput(this).Show(this.Control);
}
/// <summary>
/// 重绘选项区域
/// </summary>
public void Invalidate() {
Rectangle rect = this.Rectangle;
rect.X -= this.Control.ScrollOffset;
this.Control.Invalidate(rect);
}
}
}

View File

@ -0,0 +1,860 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using System.ComponentModel;
/*
MIT License
Copyright (c) 2021 DebugST@crystal_lz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
* create: 2021-01-28
* modify: 2021-03-02
* Author: Crystal_lz
* blog: http://st233.com
* Gitee: https://gitee.com/DebugST
* Github: https://github.com/DebugST
*/
namespace ST.Library.UI.NodeEditor
{
/// <summary>
/// STNode节点属性编辑器
/// </summary>
public class STNodePropertyGrid : Control
{
#region properties ==========
private STNode _STNode;
/// <summary>
/// 当前显示的STNode
/// </summary>
[Description("当前显示的STNode"), Browsable(false)]
public STNode STNode {
get { return _STNode; }
}
private Color _ItemHoverColor = Color.FromArgb(50, 125, 125, 125);
/// <summary>
/// 获取或设置属性选项被鼠标悬停时候背景色
/// </summary>
[Description("获取或设置属性选项被鼠标悬停时候背景色")]
public Color ItemHoverColor {
get { return _ItemHoverColor; }
set { _ItemHoverColor = value; }
}
private Color _ItemSelectedColor = Color.DodgerBlue;
/// <summary>
/// 获取或设置属性选项被选中时候背景色 当AutoColor被设置时此属性不能被设置
/// </summary>
[Description("获取或设置属性选项被选中时候背景色 当AutoColor被设置时此属性不能被设置"), DefaultValue(typeof(Color), "DodgerBlue")]
public Color ItemSelectedColor {
get { return _ItemSelectedColor; }
set {
if (this._AutoColor) return;
if (value == _ItemSelectedColor) return;
_ItemSelectedColor = value;
this.Invalidate();
}
}
private Color _ItemValueBackColor = Color.FromArgb(255, 80, 80, 80);
/// <summary>
/// 获取或设置属性选项值背景色
/// </summary>
[Description("获取或设置属性选项值背景色")]
public Color ItemValueBackColor {
get { return _ItemValueBackColor; }
set {
_ItemValueBackColor = value;
this.Invalidate();
}
}
private Color _TitleColor = Color.FromArgb(127, 0, 0, 0);
/// <summary>
/// 获取或设置默认标题背景色
/// </summary>
[Description("获取或设置默认标题背景色")]
public Color TitleColor {
get { return _TitleColor; }
set {
_TitleColor = value;
if (!this._ShowTitle) return;
this.Invalidate(m_rect_title);
}
}
private Color _ErrorColor = Color.FromArgb(200, Color.Brown);
/// <summary>
/// 获取或设置属性设置错误时候提示信息背景色
/// </summary>
[Description("获取或设置属性设置错误时候提示信息背景色")]
public Color ErrorColor {
get { return _ErrorColor; }
set { _ErrorColor = value; }
}
private Color _DescriptionColor = Color.FromArgb(200, Color.DarkGoldenrod);
/// <summary>
/// 获取或设置属性描述信息背景色
/// </summary>
[Description("获取或设置属性描述信息背景色")]
public Color DescriptionColor {
get { return _DescriptionColor; }
set { _DescriptionColor = value; }
}
private bool _ShowTitle = true;
/// <summary>
/// 获取或设置是否显示节点标题
/// </summary>
[Description("获取或设置是否显示节点标题")]
public bool ShowTitle {
get { return _ShowTitle; }
set {
_ShowTitle = value;
this.SetItemRectangle();
this.Invalidate();
}
}
private bool _AutoColor = true;
/// <summary>
/// 获取或设置是否根据STNode自动设置控件高亮颜色
/// </summary>
[Description("获取或设置是否根据STNode自动设置控件高亮颜色"), DefaultValue(true)]
public bool AutoColor {
get { return _AutoColor; }
set { _AutoColor = value; }
}
private bool _InfoFirstOnDraw;
/// <summary>
/// 获取或当节点被设置时候 是否优先绘制信息面板
/// </summary>
[Description("获取或设置当节点被设置时候 是否优先绘制信息面板"), DefaultValue(false)]
public bool InfoFirstOnDraw {
get { return _InfoFirstOnDraw; }
set { _InfoFirstOnDraw = value; }
}
private bool _ReadOnlyModel;
/// <summary>
/// 获取或设置当前属性编辑器是否处于只读模式
/// </summary>
[Description("获取或设置当前属性编辑器是否处于只读模式"), DefaultValue(false)]
public bool ReadOnlyModel {
get { return _ReadOnlyModel; }
set {
if (value == _ReadOnlyModel) return;
_ReadOnlyModel = value;
this.Invalidate(m_rect_title);
}
}
/// <summary>
/// 获取当前滚动条高度
/// </summary>
[Description("获取当前滚动条高度")]
public int ScrollOffset { get { return m_nOffsetY; } }
#endregion
#region protected fields ==========
/// <summary>
/// 作者链接地址区域
/// </summary>
protected Rectangle m_rect_link;
/// <summary>
/// 查看帮助按钮区域
/// </summary>
protected Rectangle m_rect_help;
/// <summary>
/// 编辑器标题区域
/// </summary>
protected Rectangle m_rect_title;
/// <summary>
/// 面板切换按钮区域
/// </summary>
protected Rectangle m_rect_switch;
/// <summary>
/// 控件在绘制过程中使用的垂直滚动偏移
/// </summary>
protected int m_nOffsetY;
/// <summary>
/// 保存的信息面板垂直滚动偏移
/// </summary>
protected int m_nInfoOffsetY;
/// <summary>
/// 保存的属性面板垂直滚动偏移
/// </summary>
protected int m_nPropertyOffsetY;
/// <summary>
/// 控件在绘制过程中使用的绘图区域总高度
/// </summary>
protected int m_nVHeight;
/// <summary>
/// 保存的信息面板需要的总高度
/// </summary>
protected int m_nInfoVHeight;
/// <summary>
/// 保存的属性面板需要的总高度
/// </summary>
protected int m_nPropertyVHeight;
/// <summary>
/// 信息面板中Key显示需要的水平宽度
/// </summary>
protected int m_nInfoLeft;
#endregion
private Type m_type;
private string[] m_KeysString = new string[] { "作者", "邮箱", "链接", "查看帮助" };
private int m_nTitleHeight = 20;
private int m_item_height = 30;
private Color m_clr_item_1 = Color.FromArgb(10, 0, 0, 0);
private Color m_clr_item_2 = Color.FromArgb(10, 255, 255, 255);
//所有属性列表保存在此List中
private List<STNodePropertyDescriptor> m_lst_item = new List<STNodePropertyDescriptor>();
private STNodePropertyDescriptor m_item_hover; //当前被鼠标悬停的选项
private STNodePropertyDescriptor m_item_hover_value; //当前值区域被鼠标悬停的选项
private STNodePropertyDescriptor m_item_down_value; //当前值区域被鼠标点击的选项
private STNodePropertyDescriptor m_item_selected; //当前选中的选项
private STNodeAttribute m_node_attribute; //节点参数信息
private bool m_b_hover_switch; //是否鼠标悬停在面板切换按钮上
private bool m_b_current_draw_info; //当前绘制的时候是信息面板
private Point m_pt_move; //鼠标在控件上的实时坐标
private Point m_pt_down; //上次鼠标在控件上点下的坐标
private string m_str_err; //当被设置时 绘制错误信息
private string m_str_desc; //当被设置时 绘制描述信息
private Pen m_pen;
private SolidBrush m_brush;
private StringFormat m_sf;
private DrawingTools m_dt;
/// <summary>
/// 构造一个节点属性编辑器
/// </summary>
public STNodePropertyGrid() {
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
m_pen = new Pen(Color.Black, 1);
m_brush = new SolidBrush(Color.Black);
m_sf = new StringFormat();
m_sf.LineAlignment = StringAlignment.Center;
m_sf.FormatFlags = StringFormatFlags.NoWrap;
m_dt.Pen = m_pen;
m_dt.SolidBrush = m_brush;
this.ForeColor = Color.White;
this.BackColor = Color.FromArgb(255, 35, 35, 35);
this.MinimumSize = new Size(120, 50);
this.Size = new Size(200, 150);
}
#region private method ==========
private List<STNodePropertyDescriptor> GetProperties(STNode node) {
List<STNodePropertyDescriptor> lst = new List<STNodePropertyDescriptor>();
if (node == null) return lst;
Type t = node.GetType();
foreach (var p in t.GetProperties()) {
var attrs = p.GetCustomAttributes(true);
foreach (var a in attrs) {
if (!(a is STNodePropertyAttribute)) continue;
var attr = a as STNodePropertyAttribute;
object obj = Activator.CreateInstance(attr.DescriptorType);
if (!(obj is STNodePropertyDescriptor))
throw new ArgumentException("[STNodePropertyAttribute.DescriptorType]参数值必须为[STNodePropertyDescriptor]或者其子类的类型");
var desc = (STNodePropertyDescriptor)Activator.CreateInstance(attr.DescriptorType);
desc.Node = node;
desc.Name = attr.Name;
desc.Description = attr.Description;
desc.PropertyInfo = p;
desc.Control = this;
lst.Add(desc);
}
}
return lst;
}
private STNodeAttribute GetNodeAttribute(STNode node) {
if (node == null) return null;
Type t = node.GetType();
foreach (var v in t.GetCustomAttributes(true)) {
if (!(v is STNodeAttribute)) continue;
return (STNodeAttribute)v;
}
return null;
}
private void SetItemRectangle() {
int nw_p = 0, nw_h = 0;
using (Graphics g = this.CreateGraphics()) {
foreach (var v in m_lst_item) {
SizeF szf = g.MeasureString(v.Name, this.Font);
if (szf.Width > nw_p) nw_p = (int)Math.Ceiling(szf.Width);
}
for (int i = 0; i < m_KeysString.Length - 1; i++) {
SizeF szf = g.MeasureString(m_KeysString[i], this.Font);
if (szf.Width > nw_h) nw_h = (int)Math.Ceiling(szf.Width);
}
nw_p += 5; nw_h += 5;
nw_p = Math.Min(nw_p, this.Width >> 1);
m_nInfoLeft = Math.Min(nw_h, this.Width >> 1);
int nTitleHeight = this._ShowTitle ? m_nTitleHeight : 0;
for (int i = 0; i < m_lst_item.Count; i++) {
STNodePropertyDescriptor item = m_lst_item[i];
Rectangle rect = new Rectangle(0, i * m_item_height + nTitleHeight, this.Width, m_item_height);
item.Rectangle = rect;
rect.Width = nw_p;
item.RectangleL = rect;
rect.X = rect.Right;
rect.Width = this.Width - rect.Left - 1;
rect.Inflate(-4, -4);
item.RectangleR = rect;
item.OnSetItemLocation();
}
m_nPropertyVHeight = m_lst_item.Count * m_item_height;
if (this._ShowTitle) m_nPropertyVHeight += m_nTitleHeight;
}
}
#endregion
#region override ==========
/// <summary>
/// 当控件重绘时候发生
/// </summary>
/// <param name="e">事件参数</param>
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Graphics g = e.Graphics;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
m_dt.Graphics = g;
m_nOffsetY = m_b_current_draw_info ? m_nInfoOffsetY : m_nPropertyOffsetY;
g.TranslateTransform(0, m_nOffsetY);
if (m_b_current_draw_info) {
m_nVHeight = m_nInfoVHeight;
this.OnDrawInfo(m_dt);
} else {
m_nVHeight = m_nPropertyVHeight;
for (int i = 0; i < m_lst_item.Count; i++) {
this.OnDrawPropertyItem(m_dt, m_lst_item[i], i);
}
}
g.ResetTransform();
if (this._ShowTitle) this.OnDrawTitle(m_dt);
m_sf.FormatFlags = 0;
if (!string.IsNullOrEmpty(m_str_err)) this.OnDrawErrorInfo(m_dt);
if (!string.IsNullOrEmpty(m_str_desc)) this.OnDrawDescription(m_dt);
}
/// <summary>
/// 当鼠标在控件上移动时候发生
/// </summary>
/// <param name="e">事件参数</param>
protected override void OnMouseMove(MouseEventArgs e) {
base.OnMouseMove(e);
m_pt_move = e.Location;
bool bHover = this._ShowTitle && m_rect_switch.Contains(e.Location);
if (bHover != m_b_hover_switch) {
m_b_hover_switch = bHover;
this.Invalidate(m_rect_switch);
}
Point pt = new Point(e.X, e.Y - (int)m_nOffsetY);
MouseEventArgs mea = new MouseEventArgs(e.Button, e.Clicks, pt.X, pt.Y, e.Delta);
if (m_b_current_draw_info)
this.OnProcessHelpMouseMove(mea);
else
this.OnProcessPropertyMouseMove(mea);
}
/// <summary>
/// 当鼠标在控件上点下时候发生
/// </summary>
/// <param name="e">事件参数</param>
protected override void OnMouseDown(MouseEventArgs e) {
base.OnMouseDown(e);
m_pt_down = e.Location;
this.Focus();
bool bRedraw = false;
if (m_str_err != null) {
bRedraw = true;
m_str_err = null;
}
if (this._ShowTitle) {
if (m_rect_switch.Contains(e.Location)) {
if (m_node_attribute == null || m_lst_item.Count == 0) return;
m_b_current_draw_info = !m_b_current_draw_info;
this.Invalidate();
return;
} else if (m_rect_title.Contains(e.Location)) {
return;
}
}
if (this._ShowTitle && m_rect_switch.Contains(e.Location)) {
if (m_node_attribute == null || m_lst_item.Count == 0) return;
m_b_current_draw_info = !m_b_current_draw_info;
this.Invalidate();
return;
}
Point pt = new Point(e.X, e.Y - (int)m_nOffsetY);
MouseEventArgs mea = new MouseEventArgs(e.Button, e.Clicks, pt.X, pt.Y, e.Delta);
if (m_b_current_draw_info)
this.OnProcessInfoMouseDown(mea);
else
this.OnProcessPropertyMouseDown(mea);
if (bRedraw) this.Invalidate();
}
/// <summary>
/// 当鼠标在控件上抬起时候发生
/// </summary>
/// <param name="e">事件参数</param>
protected override void OnMouseUp(MouseEventArgs e) {
base.OnMouseUp(e);
m_str_desc = null;
if (m_item_down_value != null && !this._ReadOnlyModel) {
Point pt = new Point(e.X, e.Y - (int)m_nOffsetY);
MouseEventArgs mea = new MouseEventArgs(e.Button, e.Clicks, pt.X, pt.Y, e.Delta);
m_item_down_value.OnMouseUp(mea);
if (m_pt_down == e.Location && !this._ReadOnlyModel) {
m_item_down_value.OnMouseClick(mea);
}
}
m_item_down_value = null;
this.Invalidate();
}
/// <summary>
/// 当鼠标离开控件时候发生
/// </summary>
/// <param name="e">事件参数</param>
protected override void OnMouseLeave(EventArgs e) {
base.OnMouseLeave(e);
m_b_hover_switch = false;
if (m_item_hover_value != null && !this._ReadOnlyModel) m_item_hover_value.OnMouseLeave(e);
m_item_hover = null;
this.Invalidate();
}
/// <summary>
/// 当鼠标在控件上滚动滚轮时候发生
/// </summary>
/// <param name="e">事件参数</param>
protected override void OnMouseWheel(MouseEventArgs e) {
base.OnMouseWheel(e);
if (e.Delta > 0) {
if (m_nOffsetY == 0) return;
m_nOffsetY += m_item_height;
if (m_nOffsetY > 0) m_nOffsetY = 0;
} else {
if (this.Height - m_nOffsetY >= m_nVHeight) return;
m_nOffsetY -= m_item_height;
}
if (m_b_current_draw_info)
m_nInfoOffsetY = m_nOffsetY;
else
m_nPropertyOffsetY = m_nOffsetY;
this.Invalidate();
}
/// <summary>
/// 当设置控件矩形区域时候发生
/// </summary>
/// <param name="x">x坐标</param>
/// <param name="y">y坐标</param>
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
/// <param name="specified">指定需要设置的标识</param>
//protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
// if (width < 120) width = 120;
// if (height < 50) height = 50;
// base.SetBoundsCore(x, y, width, height, specified);
//}
/// <summary>
/// 当控件尺寸发生改变时候发生
/// </summary>
/// <param name="e">事件参数</param>
protected override void OnResize(EventArgs e) {
base.OnResize(e);
m_rect_title.Width = this.Width;
m_rect_title.Height = m_nTitleHeight;
if (this._ShowTitle)
m_rect_switch = new Rectangle(this.Width - m_nTitleHeight + 2, 2, m_nTitleHeight - 4, m_nTitleHeight - 4);
if (this._STNode != null) this.SetItemRectangle();
}
#endregion
#region virtual method ==========
/// <summary>
/// 当绘制属性选项时候发生
/// </summary>
/// <param name="dt">绘制工具</param>
/// <param name="item">目标属性选项描述器</param>
/// <param name="nIndex">选项所在索引</param>
protected virtual void OnDrawPropertyItem(DrawingTools dt, STNodePropertyDescriptor item, int nIndex) {
Graphics g = dt.Graphics;
m_brush.Color = (nIndex % 2 == 0) ? m_clr_item_1 : m_clr_item_2;
g.FillRectangle(m_brush, item.Rectangle);
if (item == m_item_hover || item == m_item_selected) {
m_brush.Color = this._ItemHoverColor;
g.FillRectangle(m_brush, item.Rectangle);
}
if (m_item_selected == item) {
g.FillRectangle(m_brush, item.Rectangle.X, item.Rectangle.Y, 5, item.Rectangle.Height);
if (this._AutoColor && this._STNode != null)
m_brush.Color = this._STNode.TitleColor;
else
m_brush.Color = this._ItemSelectedColor;
g.FillRectangle(m_brush, item.Rectangle.X, item.Rectangle.Y + 4, 5, item.Rectangle.Height - 8);
}
m_sf.Alignment = StringAlignment.Far;
m_brush.Color = this.ForeColor;
g.DrawString(item.Name, this.Font, m_brush, item.RectangleL, m_sf);
item.OnDrawValueRectangle(m_dt);
if (this._ReadOnlyModel) {
m_brush.Color = Color.FromArgb(125, 125, 125, 125);
g.FillRectangle(m_brush, item.RectangleR);
m_pen.Color = this.ForeColor;
//g.DrawLine(m_pen,
// item.RectangleR.Left - 2, item.RectangleR.Top + item.RectangleR.Height / 2,
// item.RectangleR.Right + 1, item.RectangleR.Top + item.RectangleR.Height / 2);
}
}
/// <summary>
/// 绘制属性窗口标题
/// </summary>
/// <param name="dt">绘制工具</param>
protected virtual void OnDrawTitle(DrawingTools dt) {
Graphics g = dt.Graphics;
if (this._AutoColor)
m_brush.Color = this._STNode == null ? this._TitleColor : this._STNode.TitleColor;
else
m_brush.Color = this._TitleColor;
g.FillRectangle(m_brush, m_rect_title);
m_brush.Color = this._STNode == null ? this.ForeColor : this._STNode.ForeColor;
m_sf.Alignment = StringAlignment.Center;
g.DrawString(this._STNode == null ? this.Text : this._STNode.Title, this.Font, m_brush, m_rect_title, m_sf);
if (this._ReadOnlyModel) {
m_brush.Color = this.ForeColor;
g.FillRectangle(dt.SolidBrush, 4, 5, 2, 4);
g.FillRectangle(dt.SolidBrush, 6, 5, 2, 2);
g.FillRectangle(dt.SolidBrush, 8, 5, 2, 4);
g.FillRectangle(dt.SolidBrush, 3, 9, 8, 6);
}
//是否绘制面板切换按钮
if (m_node_attribute == null || m_lst_item.Count == 0) return;
if (m_b_hover_switch) {
m_brush.Color = this.BackColor;
g.FillRectangle(m_brush, m_rect_switch);
}
m_pen.Color = this._STNode == null ? this.ForeColor : this._STNode.ForeColor;
m_brush.Color = m_pen.Color;
int nT1 = m_rect_switch.Top + m_rect_switch.Height / 2 - 2;
int nT2 = m_rect_switch.Top + m_rect_switch.Height / 2 + 1;
g.DrawRectangle(m_pen, m_rect_switch.Left, m_rect_switch.Top, m_rect_switch.Width - 1, m_rect_switch.Height - 1);
g.DrawLines(m_pen, new Point[]{
new Point(m_rect_switch.Left + 2, nT1), new Point(m_rect_switch.Right - 3, nT1),
new Point(m_rect_switch.Left + 3, nT1 - 1), new Point(m_rect_switch.Right - 3, nT1 - 1)
});
g.DrawLines(m_pen, new Point[]{
new Point(m_rect_switch.Left + 2, nT2), new Point(m_rect_switch.Right - 3, nT2),
new Point(m_rect_switch.Left + 2, nT2 + 1), new Point(m_rect_switch.Right - 4, nT2 + 1),
});
g.FillPolygon(m_brush, new Point[]{
new Point(m_rect_switch.Left + 2, nT1),
new Point(m_rect_switch.Left + 7, nT1),
new Point(m_rect_switch.Left + 7, m_rect_switch.Top ),
});
g.FillPolygon(m_brush, new Point[]{
new Point(m_rect_switch.Right - 2, nT2),
new Point(m_rect_switch.Right - 7, nT2),
new Point(m_rect_switch.Right - 7, m_rect_switch.Bottom - 2 ),
});
}
/// <summary>
/// 当需要绘制属性描述信息时发生
/// </summary>
/// <param name="dt">绘制工具</param>
protected virtual void OnDrawDescription(DrawingTools dt) {
if (string.IsNullOrEmpty(m_str_desc)) return;
Graphics g = dt.Graphics;
SizeF szf = g.MeasureString(m_str_desc, this.Font, this.Width - 4);
Rectangle rect_desc = new Rectangle(0, this.Height - (int)szf.Height - 4, this.Width, (int)szf.Height + 4);
m_brush.Color = this._DescriptionColor;
g.FillRectangle(m_brush, rect_desc);
m_pen.Color = this._DescriptionColor;
g.DrawRectangle(m_pen, 0, rect_desc.Top, rect_desc.Width - 1, rect_desc.Height - 1);
rect_desc.Inflate(-4, 0);
m_brush.Color = this.ForeColor;
m_sf.Alignment = StringAlignment.Near;
g.DrawString(m_str_desc, this.Font, m_brush, rect_desc, m_sf);
}
/// <summary>
/// 当需要绘制错误信息时发生
/// </summary>
/// <param name="dt">绘制工具</param>
protected virtual void OnDrawErrorInfo(DrawingTools dt) {
if (string.IsNullOrEmpty(m_str_err)) return;
Graphics g = dt.Graphics;
SizeF szf = g.MeasureString(m_str_err, this.Font, this.Width - 4);
Rectangle rect_desc = new Rectangle(0, 0, this.Width, (int)szf.Height + 4);
m_brush.Color = this._ErrorColor;
g.FillRectangle(m_brush, rect_desc);
m_pen.Color = this._ErrorColor;
g.DrawRectangle(m_pen, 0, rect_desc.Top, rect_desc.Width - 1, rect_desc.Height - 1);
rect_desc.Inflate(-4, 0);
m_brush.Color = this.ForeColor;
m_sf.Alignment = StringAlignment.Near;
g.DrawString(m_str_err, this.Font, m_brush, rect_desc, m_sf);
}
/// <summary>
/// 当绘制节点信息时候发生
/// </summary>
/// <param name="dt">绘制工具</param>
protected virtual void OnDrawInfo(DrawingTools dt) {
if (m_node_attribute == null) return;
var attr = m_node_attribute;
Graphics g = dt.Graphics;
Color clr_r = Color.FromArgb(this.ForeColor.A / 2, this.ForeColor);
m_sf.Alignment = StringAlignment.Near;
Rectangle rect = new Rectangle(0, this._ShowTitle ? m_nTitleHeight : 0, this.Width, m_item_height);
Rectangle rect_l = new Rectangle(2, rect.Top, m_nInfoLeft - 2, m_item_height);
Rectangle rect_r = new Rectangle(m_nInfoLeft, rect.Top, this.Width - m_nInfoLeft, m_item_height);
m_brush.Color = m_clr_item_2;
g.FillRectangle(m_brush, rect);
m_brush.Color = this.ForeColor;
m_sf.FormatFlags = StringFormatFlags.NoWrap;
m_sf.Alignment = StringAlignment.Near;
g.DrawString(m_KeysString[0], this.Font, m_brush, rect_l, m_sf); //author
m_brush.Color = clr_r;
g.DrawString(attr.Author, this.Font, m_brush, rect_r, m_sf);
rect.Y += m_item_height; rect_l.Y += m_item_height; rect_r.Y += m_item_height;
m_brush.Color = m_clr_item_1;
g.FillRectangle(m_brush, rect);
m_brush.Color = this.ForeColor;
g.DrawString(m_KeysString[1], this.Font, m_brush, rect_l, m_sf); //mail
m_brush.Color = clr_r;
g.DrawString(attr.Mail, this.Font, m_brush, rect_r, m_sf);
rect.Y += m_item_height; rect_l.Y += m_item_height; rect_r.Y += m_item_height;
m_brush.Color = m_clr_item_2;
g.FillRectangle(m_brush, rect);
m_brush.Color = this.ForeColor;
g.DrawString(m_KeysString[2], this.Font, m_brush, rect_l, m_sf); //link_key
m_brush.Color = clr_r;
g.DrawString(attr.Link, this.Font, Brushes.CornflowerBlue, rect_r, m_sf); //link
if (!string.IsNullOrEmpty(attr.Link)) m_rect_link = rect_r;
//fill left
m_brush.Color = Color.FromArgb(40, 125, 125, 125);
g.FillRectangle(m_brush, 0, this._ShowTitle ? m_nTitleHeight : 0, m_nInfoLeft - 1, m_item_height * 3);
rect.X = 5; rect.Y += m_item_height;
rect.Width = this.Width - 10;
if (!string.IsNullOrEmpty(m_node_attribute.Description)) {
float h = g.MeasureString(m_node_attribute.Description, this.Font, rect.Width).Height;
rect.Height = (int)Math.Ceiling(h / m_item_height) * m_item_height;
m_brush.Color = clr_r;
m_sf.FormatFlags = 0;
g.DrawString(m_node_attribute.Description, this.Font, m_brush, rect, m_sf);
}
m_nInfoVHeight = rect.Bottom;
bool bHasHelp = STNodeAttribute.GetHelpMethod(m_type) != null;
rect.X = 5; rect.Y += rect.Height;
rect.Height = m_item_height;
m_sf.Alignment = StringAlignment.Center;
m_brush.Color = Color.FromArgb(125, 125, 125, 125);
g.FillRectangle(m_brush, rect);
if (bHasHelp) m_brush.Color = Color.CornflowerBlue;
g.DrawString(m_KeysString[3], this.Font, m_brush, rect, m_sf);
if (bHasHelp) m_rect_help = rect;
else {
int w = (int)g.MeasureString(m_KeysString[3], this.Font).Width + 1;
int x = rect.X + (rect.Width - w) / 2, y = rect.Y + rect.Height / 2;
m_pen.Color = m_brush.Color;
g.DrawLine(m_pen, x, y, x + w, y);
}
m_nInfoVHeight = rect.Bottom;
}
/// <summary>
/// 当在属性面板鼠标点下时候发生
/// </summary>
/// <param name="e">鼠标事件参数</param>
protected virtual void OnProcessPropertyMouseDown(MouseEventArgs e) {
bool bRedraw = false;
if (m_item_selected != m_item_hover) {
m_item_selected = m_item_hover;
bRedraw = true;
}
m_item_down_value = null;
if (m_item_selected == null) {
if (bRedraw) this.Invalidate();
return;
}
if (m_item_selected.RectangleR.Contains(e.Location)) {
m_item_down_value = m_item_selected;
if (!this._ReadOnlyModel)
m_item_selected.OnMouseDown(e);
else {
return;
}
} else if (m_item_selected.RectangleL.Contains(e.Location)) {
m_str_desc = m_item_selected.Description;
bRedraw = true;
}
if (bRedraw) this.Invalidate();
}
/// <summary>
/// 当在信息面板鼠标点下时候发生
/// </summary>
/// <param name="e">鼠标事件参数</param>
protected virtual void OnProcessInfoMouseDown(MouseEventArgs e) {
try {
if (m_rect_link.Contains(e.Location)) {
System.Diagnostics.Process.Start(m_node_attribute.Link);
} else if (m_rect_help.Contains(e.Location)) {
STNodeAttribute.ShowHelp(m_type);
}
} catch (Exception ex) {
this.SetErrorMessage(ex.Message);
}
}
/// <summary>
/// 当在属性面板鼠标移动时候发生
/// </summary>
/// <param name="e">鼠标事件参数</param>
protected virtual void OnProcessPropertyMouseMove(MouseEventArgs e) {
if (m_item_down_value != null) {
m_item_down_value.OnMouseMove(e);
return;
}
STNodePropertyDescriptor item = null;
foreach (var v in m_lst_item) {
if (v.Rectangle.Contains(e.Location)) {
item = v;
break;
}
}
if (item != null) {
if (item.RectangleR.Contains(e.Location)) {
if (m_item_hover_value != item) {
if (m_item_hover_value != null) m_item_hover_value.OnMouseLeave(e);
m_item_hover_value = item;
m_item_hover_value.OnMouseEnter(e);
}
m_item_hover_value.OnMouseMove(e);
} else {
if (m_item_hover_value != null) m_item_hover_value.OnMouseLeave(e);
}
}
if (m_item_hover != item) {
m_item_hover = item;
this.Invalidate();
}
}
/// <summary>
/// 当在信息面板鼠标移动时候发生
/// </summary>
/// <param name="e">鼠标事件参数</param>
protected virtual void OnProcessHelpMouseMove(MouseEventArgs e) {
if (m_rect_link.Contains(e.Location) || m_rect_help.Contains(e.Location)) {
this.Cursor = Cursors.Hand;
} else this.Cursor = Cursors.Arrow;
}
#endregion
#region public ==========
/// <summary>
/// 设置需要显示的STNode节点
/// </summary>
/// <param name="node">目标节点</param>
public void SetNode(STNode node) {
if (node == this._STNode) return;
m_nInfoOffsetY = m_nPropertyOffsetY = 0;
m_nInfoVHeight = m_nPropertyVHeight = 0;
m_rect_link = m_rect_help = Rectangle.Empty;
m_str_desc = m_str_err = null;
this._STNode = node;
if (node != null) {
m_type = node.GetType();
m_lst_item = this.GetProperties(node);
m_node_attribute = this.GetNodeAttribute(node);
this.SetItemRectangle();
m_b_current_draw_info = m_lst_item.Count == 0 || this._InfoFirstOnDraw;
if (this._AutoColor) this._ItemSelectedColor = this._STNode.TitleColor;
} else {
m_type = null;
m_lst_item.Clear();
m_node_attribute = null;
}
this.Invalidate();
}
/// <summary>
/// 设置信息页面Key的显示文本
/// </summary>
/// <param name="strAuthor">作者</param>
/// <param name="strMail">邮箱</param>
/// <param name="strLink">连接</param>
/// <param name="strHelp">查看帮助</param>
public void SetInfoKey(string strAuthor, string strMail, string strLink, string strHelp) {
m_KeysString = new string[] { strAuthor, strMail, strLink, strHelp };
}
/// <summary>
/// 设置要显示的错误信息
/// </summary>
/// <param name="strText">错误信息</param>
public void SetErrorMessage(string strText) {
m_str_err = strText;
this.Invalidate();
}
#endregion
}
}

View File

@ -0,0 +1,907 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using System.ComponentModel;
using System.Collections;
/*
MIT License
Copyright (c) 2021 DebugST@crystal_lz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
* create: 2021-02-23
* modify: 2021-03-02
* Author: Crystal_lz
* blog: http://st233.com
* Gitee: https://gitee.com/DebugST
* Github: https://github.com/DebugST
*/
namespace ST.Library.UI.NodeEditor
{
public class STNodeTreeView : Control
{
private Color _ItemBackColor = Color.FromArgb(255, 45, 45, 45);
/// <summary>
/// 获取或设置每行属性选项背景色
/// </summary>
[Description("获取或设置每行属性选项背景色")]
public Color ItemBackColor {
get { return _ItemBackColor; }
set {
_ItemBackColor = value;
}
}
private Color _ItemHoverColor = Color.FromArgb(50, 125, 125, 125);
/// <summary>
/// 获取或设置属性选项被鼠标悬停时候背景色
/// </summary>
[Description("获取或设置属性选项被鼠标悬停时候背景色")]
public Color ItemHoverColor {
get { return _ItemHoverColor; }
set { _ItemHoverColor = value; }
}
private Color _TitleColor = Color.FromArgb(255, 60, 60, 60);
/// <summary>
/// 获取或设置顶部检索区域背景色
/// </summary>
[Description("获取或设置顶部检索区域背景颜色")]
public Color TitleColor {
get { return _TitleColor; }
set {
_TitleColor = value;
this.Invalidate(new Rectangle(0, 0, this.Width, m_nItemHeight));
}
}
/// <summary>
/// 获取或设置检索文本框的背景色
/// </summary>
[Description("获取或设置检索文本框的背景色")]
public Color TextBoxColor {
get { return m_tbx.BackColor; }
set {
m_tbx.BackColor = value;
this.Invalidate(new Rectangle(0, 0, this.Width, m_nItemHeight));
}
}
private Color _HightLightTextColor = Color.Lime;
/// <summary>
/// 获取或设置检索时候高亮文本颜色
/// </summary>
[Description("获取或设置检索时候高亮文本颜色"), DefaultValue(typeof(Color), "Lime")]
public Color HightLightTextColor {
get { return _HightLightTextColor; }
set { _HightLightTextColor = value; }
}
private Color _InfoButtonColor = Color.Gray;
/// <summary>
/// 获取或设置信息显示按钮颜色 若设置AutoColor无法设置此属性值
/// </summary>
[Description("获取或设置信息显示按钮颜色 若设置AutoColor无法设置此属性值"), DefaultValue(typeof(Color), "Gray")]
public Color InfoButtonColor {
get { return _InfoButtonColor; }
set { _InfoButtonColor = value; }
}
private Color _FolderCountColor = Color.FromArgb(40, 255, 255, 255);
/// <summary>
/// 获取或设置统计个数的文本颜色
/// </summary>
[Description("获取或设置统计个数的文本颜色")]
public Color FolderCountColor {
get { return _FolderCountColor; }
set { _FolderCountColor = value; }
}
private Color _SwitchColor = Color.LightGray;
private bool _ShowFolderCount = true;
/// <summary>
/// 获取或设置是否统计STNode的个数
/// </summary>
[Description("获取或设置是否统计STNode的个数"), DefaultValue(typeof(Color), "LightGray")]
public bool ShowFolderCount {
get { return _ShowFolderCount; }
set { _ShowFolderCount = value; }
}
private bool _ShowInfoButton = true;
/// <summary>
/// 获取或设置是否显示信息按钮
/// </summary>
[Description("获取或设置是否显示信息按钮"), DefaultValue(true)]
public bool ShowInfoButton {
get { return _ShowInfoButton; }
set { _ShowInfoButton = value; }
}
private bool _InfoPanelIsLeftLayout = true;
/// <summary>
/// 获取或设置预览窗口是否是向左布局
/// </summary>
[Description("获取或设置预览窗口是否是向左布局"), DefaultValue(true)]
public bool InfoPanelIsLeftLayout {
get { return _InfoPanelIsLeftLayout; }
set { _InfoPanelIsLeftLayout = value; }
}
private bool _AutoColor = true;
/// <summary>
/// 获取或设置控件中部分颜色来之对应的STNode的标题颜色
/// </summary>
[Description("获取或设置控件中部分颜色来之对应的STNode的标题颜色"), DefaultValue(true)]
public bool AutoColor {
get { return _AutoColor; }
set {
_AutoColor = value;
this.Invalidate();
}
}
private STNodeEditor _Editor;
/// <summary>
/// 获取节点预览时候使用的STNodeEditor
/// </summary>
[Description("获取节点预览时候使用的STNodeEditor"), Browsable(false)]
public STNodeEditor Editor {
get { return _Editor; }
}
private STNodePropertyGrid _PropertyGrid;
/// <summary>
/// 获取节点预览时候使用的STNodePropertyGrid
/// </summary>
[Description("获取节点预览时候使用的STNodePropertyGrid"), Browsable(false)]
public STNodePropertyGrid PropertyGrid {
get { return _PropertyGrid; }
}
private int m_nItemHeight = 29;
private static Type m_type_node_base = typeof(STNode);
private static char[] m_chr_splitter = new char[] { '/', '\\' };
private STNodeTreeCollection m_items_draw;
private STNodeTreeCollection m_items_source = new STNodeTreeCollection("ROOT");
private Dictionary<Type, string> m_dic_all_type = new Dictionary<Type, string>();
private Pen m_pen;
private SolidBrush m_brush;
private StringFormat m_sf;
private DrawingTools m_dt;
private Color m_clr_item_1 = Color.FromArgb(10, 0, 0, 0);// Color.FromArgb(255, 40, 40, 40);
private Color m_clr_item_2 = Color.FromArgb(10, 255, 255, 255);// Color.FromArgb(255, 50, 50, 50);
private int m_nOffsetY; //控件绘制时候需要偏移的垂直高度
private int m_nSourceOffsetY; //绘制源数据时候需要偏移的垂直高度
private int m_nSearchOffsetY; //绘制检索数据时候需要偏移的垂直高度
private int m_nVHeight; //控件中内容需要的总高度
private bool m_bHoverInfo; //当前鼠标是否悬停在信息显示按钮上
private STNodeTreeCollection m_item_hover; //当前鼠标悬停的树节点
private Point m_pt_control; //鼠标在控件上的坐标
private Point m_pt_offsety; //鼠标在控件上锤子偏移后的坐标
private Rectangle m_rect_clear; //清空检索按钮区域
private string m_str_search; //检索的文本
private TextBox m_tbx = new TextBox(); //检索文本框
/// <summary>
/// 构造一个STNode树控件
/// </summary>
public STNodeTreeView() {
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.MinimumSize = new System.Drawing.Size(100, 60);
this.Size = new System.Drawing.Size(200, 150);
m_items_draw = m_items_source;
m_pen = new Pen(Color.Black);
m_brush = new SolidBrush(Color.White);
m_sf = new StringFormat();
m_sf.LineAlignment = StringAlignment.Center;
m_dt.Pen = m_pen;
m_dt.SolidBrush = m_brush;
this.ForeColor = Color.FromArgb(255, 220, 220, 220);
this.BackColor = Color.FromArgb(255, 35, 35, 35);
m_tbx.Left = 6;
m_tbx.BackColor = Color.FromArgb(255, 30, 30, 30);
m_tbx.ForeColor = this.ForeColor;
m_tbx.BorderStyle = BorderStyle.None;
m_tbx.MaxLength = 20;
m_tbx.TextChanged += new EventHandler(m_tbx_TextChanged);
this.Controls.Add(m_tbx);
this.AllowDrop = true;
this._Editor = new STNodeEditor();
this._PropertyGrid = new STNodePropertyGrid();
}
#region private method ==========
private void m_tbx_TextChanged(object sender, EventArgs e) {
m_str_search = m_tbx.Text.Trim().ToLower();
m_nSearchOffsetY = 0;
if (m_str_search == string.Empty) {
m_items_draw = m_items_source;
this.Invalidate();
return;
}
m_items_draw = m_items_source.Copy();
this.Search(m_items_draw, new Stack<string>(), m_str_search);
this.Invalidate();
}
private bool Search(STNodeTreeCollection items, Stack<string> stack, string strText) {
bool bFound = false;
string[] strName = new string[items.Count];
int nCounter = 0;
foreach (STNodeTreeCollection v in items) {
if (v.NameLower.IndexOf(strText) != -1) {
v.IsOpen = bFound = true;
} else {
if (!this.Search(v, stack, strText)) {
stack.Push(v.Name);
nCounter++;
} else {
v.IsOpen = bFound = true;
}
}
}
for (int i = 0; i < nCounter; i++) items.Remove(stack.Pop(), false);
return bFound;
}
private bool AddSTNode(Type stNodeType, STNodeTreeCollection items, string strLibName, bool bShowException) {
if (m_dic_all_type.ContainsKey(stNodeType)) return false;
if (stNodeType == null) return false;
if (!stNodeType.IsSubclassOf(m_type_node_base)) {
if (bShowException)
throw new ArgumentException("不支持的类型[" + stNodeType.FullName + "] [stNodeType]参数值必须为[STNode]子类的类型");
else return false;
}
var attr = this.GetNodeAttribute(stNodeType);
if (attr == null) {
if (bShowException)
throw new InvalidOperationException("类型[" + stNodeType.FullName + "]未被[STNodeAttribute]所标记");
else return false;
}
string strPath = string.Empty;
items.STNodeCount++;
if (!string.IsNullOrEmpty(attr.Path)) {
var strKeys = attr.Path.Split(m_chr_splitter);
for (int i = 0; i < strKeys.Length; i++) {
items = items.Add(strKeys[i]);
items.STNodeCount++;
strPath += "/" + strKeys[i];
}
}
try {
STNode node = (STNode)Activator.CreateInstance(stNodeType);
STNodeTreeCollection stt = new STNodeTreeCollection(node.Title);
stt.Path = (strLibName + "/" + attr.Path).Trim('/');
stt.STNodeType = stNodeType;
items[stt.Name] = stt;
stt.STNodeTypeColor = node.TitleColor;
m_dic_all_type.Add(stNodeType, stt.Path);
this.Invalidate();
} catch (Exception ex) {
if (bShowException) throw ex;
return false;
}
return true;
}
private STNodeTreeCollection AddAssemblyPrivate(string strFile) {
strFile = System.IO.Path.GetFullPath(strFile);
var asm = Assembly.LoadFrom(strFile);
STNodeTreeCollection items = new STNodeTreeCollection(System.IO.Path.GetFileNameWithoutExtension(strFile));
foreach (var v in asm.GetTypes()) {
if (v.IsAbstract) continue;
if (v.IsSubclassOf(m_type_node_base)) this.AddSTNode(v, items, items.Name, false);
}
return items;
}
private STNodeAttribute GetNodeAttribute(Type stNodeType) {
if (stNodeType == null) return null;
foreach (var v in stNodeType.GetCustomAttributes(true)) {
if (!(v is STNodeAttribute)) continue;
return (STNodeAttribute)v;
}
return null;
}
private STNodeTreeCollection FindItemByPoint(STNodeTreeCollection items, Point pt) {
foreach (STNodeTreeCollection t in items) {
if (t.DisplayRectangle.Contains(pt)) return t;
if (t.IsOpen) {
var n = FindItemByPoint(t, pt);
if (n != null) return n;
}
}
return null;
}
#endregion
#region overide method ==========
protected override void OnCreateControl() {
base.OnCreateControl();
m_tbx.Top = (m_nItemHeight - m_tbx.Height) / 2;
}
protected override void OnResize(EventArgs e) {
base.OnResize(e);
m_tbx.Width = this.Width - 29;
m_rect_clear = new Rectangle(this.Width - 20, 9, 12, 12);
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
m_nOffsetY = string.IsNullOrEmpty(m_str_search) ? m_nSourceOffsetY : m_nSearchOffsetY;
Graphics g = e.Graphics;
m_dt.Graphics = g;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
g.TranslateTransform(0, m_nOffsetY);
int nCounter = 0;
foreach (STNodeTreeCollection v in m_items_draw)
nCounter = this.OnStartDrawItem(m_dt, v, nCounter, 0);
m_nVHeight = (nCounter + 1) * m_nItemHeight;
foreach (STNodeTreeCollection v in m_items_draw)
this.OnDrawSwitch(m_dt, v);
g.ResetTransform();
this.OnDrawSearch(m_dt);
}
protected override void OnMouseMove(MouseEventArgs e) {
base.OnMouseMove(e);
bool bRedraw = false;
m_pt_offsety = m_pt_control = e.Location;
m_pt_offsety.Y -= m_nOffsetY;
if (!string.IsNullOrEmpty(m_str_search) && m_rect_clear.Contains(e.Location))
this.Cursor = Cursors.Hand;
else
this.Cursor = Cursors.Arrow;
var node = this.FindItemByPoint(m_items_draw, m_pt_offsety);
if (m_item_hover != node) {
m_item_hover = node;
bRedraw = true;
}
if (node != null) {
bool bHoverInfo = node.InfoRectangle.Contains(m_pt_offsety);
if (bHoverInfo != m_bHoverInfo) {
m_bHoverInfo = bHoverInfo;
bRedraw = true;
}
}
if (bRedraw) this.Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e) {
base.OnMouseDown(e);
this.Focus();
if (!string.IsNullOrEmpty(m_str_search) && m_rect_clear.Contains(e.Location)) {
m_tbx.Text = string.Empty;
return;
}
m_pt_offsety = m_pt_control = e.Location;
m_pt_offsety.Y -= m_nOffsetY;
if (m_item_hover == null) return;
if (m_item_hover.SwitchRectangle.Contains(m_pt_offsety)) {
m_item_hover.IsOpen = !m_item_hover.IsOpen;
this.Invalidate();
} else if (m_item_hover.InfoRectangle.Contains(m_pt_offsety)) {
Rectangle rect = this.RectangleToScreen(m_item_hover.DisplayRectangle);
FrmNodePreviewPanel frm = new FrmNodePreviewPanel(m_item_hover.STNodeType,
new Point(rect.Right - m_nItemHeight, rect.Top + m_nOffsetY),
m_nItemHeight,
this._InfoPanelIsLeftLayout,
this._Editor, this._PropertyGrid);
frm.BackColor = this.BackColor;
frm.Show(this);
} else if (m_item_hover.STNodeType != null) {
DataObject d = new DataObject("STNodeType", m_item_hover.STNodeType);
this.DoDragDrop(d, DragDropEffects.Copy);
}
}
protected override void OnMouseDoubleClick(MouseEventArgs e) {
base.OnMouseDoubleClick(e);
m_pt_offsety = m_pt_control = e.Location;
m_pt_offsety.Y -= m_nOffsetY;
STNodeTreeCollection item = this.FindItemByPoint(m_items_draw, m_pt_offsety);
if (item == null || item.STNodeType != null) return;
item.IsOpen = !item.IsOpen;
this.Invalidate();
}
protected override void OnMouseLeave(EventArgs e) {
base.OnMouseLeave(e);
if (m_item_hover != null) {
m_item_hover = null;
this.Invalidate();
}
}
protected override void OnMouseWheel(MouseEventArgs e) {
base.OnMouseWheel(e);
if (e.Delta > 0) {
if (m_nOffsetY == 0) return;
m_nOffsetY += m_nItemHeight;
if (m_nOffsetY > 0) m_nOffsetY = 0;
} else {
if (this.Height - m_nOffsetY >= m_nVHeight) return;
m_nOffsetY -= m_nItemHeight;
}
if (string.IsNullOrEmpty(m_str_search))
m_nSourceOffsetY = m_nOffsetY;
else
m_nSearchOffsetY = m_nOffsetY;
this.Invalidate();
}
#endregion
#region protected method ==========
/// <summary>
/// 当绘制检索文本区域时候发生
/// </summary>
/// <param name="dt">绘制工具</param>
protected virtual void OnDrawSearch(DrawingTools dt) {
Graphics g = dt.Graphics;
m_brush.Color = this._TitleColor;
g.FillRectangle(m_brush, 0, 0, this.Width, m_nItemHeight);
m_brush.Color = m_tbx.BackColor;
g.FillRectangle(m_brush, 5, 5, this.Width - 10, m_nItemHeight - 10);
m_pen.Color = this.ForeColor;
if (string.IsNullOrEmpty(m_str_search)) {
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.DrawEllipse(m_pen, this.Width - 17, 8, 8, 8);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
g.DrawLine(m_pen, this.Width - 13, 17, this.Width - 13, m_nItemHeight - 9);
} else {
m_pen.Color = this.ForeColor;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.DrawEllipse(m_pen, this.Width - 20, 9, 10, 10);
g.DrawLine(m_pen, this.Width - 18, 11, this.Width - 12, 17);
g.DrawLine(m_pen, this.Width - 12, 11, this.Width - 18, 17);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
}
}
/// <summary>
/// 当开始绘制树节点的每一个节点时候发生
/// </summary>
/// <param name="dt">绘制工具</param>
/// <param name="Items">当前需要绘制的集合</param>
/// <param name="nCounter">已经绘制个数的计数器</param>
/// <param name="nLevel">当前位于第几级子集合</param>
/// <returns>已经绘制个数</returns>
protected virtual int OnStartDrawItem(DrawingTools dt, STNodeTreeCollection Items, int nCounter, int nLevel) {
Graphics g = dt.Graphics;
Items.DisplayRectangle = new Rectangle(0, m_nItemHeight * (nCounter + 1), this.Width, m_nItemHeight);
Items.SwitchRectangle = new Rectangle(5 + nLevel * 10, (nCounter + 1) * m_nItemHeight, 10, m_nItemHeight);
if (this._ShowInfoButton && Items.STNodeType != null)
Items.InfoRectangle = new Rectangle(this.Width - 18, Items.DisplayRectangle.Top + (m_nItemHeight - 14) / 2, 14, 14);
else Items.InfoRectangle = Rectangle.Empty;
this.OnDrawItem(dt, Items, nCounter++, nLevel);
if (!Items.IsOpen) return nCounter;
foreach (STNodeTreeCollection n in Items) {
if (n.STNodeType == null)
nCounter = this.OnStartDrawItem(dt, n, nCounter++, nLevel + 1);
}
foreach (STNodeTreeCollection n in Items) {
if (n.STNodeType != null)
nCounter = this.OnStartDrawItem(dt, n, nCounter++, nLevel + 1);
}
foreach (STNodeTreeCollection v in Items) this.OnDrawSwitch(dt, v);
return nCounter;
}
/// <summary>
/// 当绘制树节点每一个节点时候发生
/// </summary>
/// <param name="dt">绘制工具</param>
/// <param name="items">当前需要绘制的集合</param>
/// <param name="nCounter">已经绘制个数的计数器</param>
/// <param name="nLevel">当前位于第几级子集合</param>
protected virtual void OnDrawItem(DrawingTools dt, STNodeTreeCollection items, int nCounter, int nLevel) {
Graphics g = dt.Graphics;
m_brush.Color = nCounter % 2 == 0 ? m_clr_item_1 : m_clr_item_2;
g.FillRectangle(m_brush, items.DisplayRectangle);
if (items == m_item_hover) {
m_brush.Color = this._ItemHoverColor;
g.FillRectangle(m_brush, items.DisplayRectangle);
}
Rectangle rect = new Rectangle(45 + nLevel * 10, items.SwitchRectangle.Top, this.Width - 45 - nLevel * 10, m_nItemHeight);
m_pen.Color = Color.FromArgb(100, 125, 125, 125);
g.DrawLine(m_pen, 9, items.SwitchRectangle.Top + m_nItemHeight / 2, items.SwitchRectangle.Left + 19, items.SwitchRectangle.Top + m_nItemHeight / 2);
if (nCounter != 0) {
for (int i = 0; i <= nLevel; i++) {
g.DrawLine(m_pen, 9 + i * 10, items.SwitchRectangle.Top - m_nItemHeight / 2, 9 + i * 10, items.SwitchRectangle.Top + m_nItemHeight / 2 - 1);
}
}
this.OnDrawItemText(dt, items, rect);
this.OnDrawItemIcon(dt, items, rect);
}
/// <summary>
/// 当绘制树节点展开与关闭开关时候发生
/// </summary>
/// <param name="dt">绘制工具</param>
/// <param name="items">当前需要绘制的集合</param>
protected virtual void OnDrawSwitch(DrawingTools dt, STNodeTreeCollection items) {
Graphics g = dt.Graphics;
if (items.Count != 0) {
m_pen.Color = this._SwitchColor;
m_brush.Color = m_pen.Color;
int nT = items.SwitchRectangle.Y + m_nItemHeight / 2 - 4;
g.DrawRectangle(m_pen, items.SwitchRectangle.Left, nT, 8, 8);
g.DrawLine(m_pen, items.SwitchRectangle.Left + 1, nT + 4, items.SwitchRectangle.Right - 3, nT + 4);
if (items.IsOpen) return;
g.DrawLine(m_pen, items.SwitchRectangle.Left + 4, nT + 1, items.SwitchRectangle.Left + 4, nT + 7);
//if (items.IsOpen) {
// //g.FillPolygon(m_brush, new Point[]{
// // new Point(items.DotRectangle.Left + 0, items.DotRectangle.Top + m_nItemHeight / 2 - 2),
// // new Point(items.DotRectangle.Left + 9, items.DotRectangle.Top + m_nItemHeight / 2 - 2),
// // new Point(items.DotRectangle.Left + 4, items.DotRectangle.Top + m_nItemHeight / 2 + 3)
// //});
// g.DrawRectangle(m_pen, items.SwitchRectangle.Left, nT, 8, 8);
// g.DrawLine(m_pen, items.SwitchRectangle.Left + 1, nT + 4, items.SwitchRectangle.Right - 3, nT + 4);
//} else {
// //g.FillPolygon(m_brush, new Point[]{
// // new Point(items.DotRectangle.Left + 2, items.DotRectangle.Top + m_nItemHeight / 2 - 5),
// // new Point(items.DotRectangle.Left + 2, items.DotRectangle.Top + m_nItemHeight / 2 + 5),
// // new Point(items.DotRectangle.Left + 7, items.DotRectangle.Top + m_nItemHeight / 2)
// //});
// g.DrawRectangle(m_pen, items.SwitchRectangle.Left, nT, 8, 8);
// g.DrawLine(m_pen, items.SwitchRectangle.Left + 1, nT + 4, items.SwitchRectangle.Right - 3, nT + 4);
// g.DrawLine(m_pen, items.SwitchRectangle.Left + 4, nT + 1, items.SwitchRectangle.Left + 4, nT + 7);
//}
}
}
/// <summary>
/// 当绘制树节点的文本时候发生
/// </summary>
/// <param name="dt">绘制工具</param>
/// <param name="items">当前需要绘制的集合</param>
/// <param name="rect">文本域所在矩形区域</param>
protected virtual void OnDrawItemText(DrawingTools dt, STNodeTreeCollection items, Rectangle rect) {
Graphics g = dt.Graphics;
rect.Width -= 20;
m_sf.FormatFlags = StringFormatFlags.NoWrap;
if (!string.IsNullOrEmpty(m_str_search)) {
int nIndex = items.NameLower.IndexOf(m_str_search);
if (nIndex != -1) {
CharacterRange[] chrs = { new CharacterRange(nIndex, m_str_search.Length) };//global
m_sf.SetMeasurableCharacterRanges(chrs);
Region[] regions = g.MeasureCharacterRanges(items.Name, this.Font, rect, m_sf);
g.SetClip(regions[0], System.Drawing.Drawing2D.CombineMode.Intersect);
m_brush.Color = this._HightLightTextColor;
g.DrawString(items.Name, this.Font, m_brush, rect, m_sf);
g.ResetClip();
g.SetClip(regions[0], System.Drawing.Drawing2D.CombineMode.Exclude);
m_brush.Color = items.STNodeType == null ? Color.FromArgb(this.ForeColor.A * 1 / 2, this.ForeColor) : this.ForeColor;
g.DrawString(items.Name, this.Font, m_brush, rect, m_sf);
g.ResetClip();
return;
}
}
m_brush.Color = items.STNodeType == null ? Color.FromArgb(this.ForeColor.A * 2 / 3, this.ForeColor) : this.ForeColor;
g.DrawString(items.Name, this.Font, m_brush, rect, m_sf);
}
/// <summary>
/// 当绘制树节点图标时候发生
/// </summary>
/// <param name="dt">绘制工具</param>
/// <param name="items">当前需要绘制的集合</param>
/// <param name="rect">文本域所在矩形区域</param>
protected virtual void OnDrawItemIcon(DrawingTools dt, STNodeTreeCollection items, Rectangle rect) {
Graphics g = dt.Graphics;
if (items.STNodeType != null) {
m_pen.Color = this._AutoColor ? items.STNodeTypeColor : Color.DarkCyan;
m_brush.Color = Color.LightGray;
g.DrawRectangle(m_pen, rect.Left - 15, rect.Top + m_nItemHeight / 2 - 5, 11, 10);
g.FillRectangle(m_brush, rect.Left - 17, rect.Top + m_nItemHeight / 2 - 2, 5, 5);
g.FillRectangle(m_brush, rect.Left - 6, rect.Top + m_nItemHeight / 2 - 2, 5, 5);
if (m_item_hover == items && m_bHoverInfo) {
m_brush.Color = this.BackColor;
g.FillRectangle(m_brush, items.InfoRectangle);
}
m_pen.Color = this._AutoColor ? items.STNodeTypeColor : this._InfoButtonColor;
m_pen.Width = 2;
g.DrawLine(m_pen, items.InfoRectangle.X + 4, items.InfoRectangle.Y + 3, items.InfoRectangle.X + 10, items.InfoRectangle.Y + 3);
g.DrawLine(m_pen, items.InfoRectangle.X + 4, items.InfoRectangle.Y + 6, items.InfoRectangle.X + 10, items.InfoRectangle.Y + 6);
g.DrawLine(m_pen, items.InfoRectangle.X + 4, items.InfoRectangle.Y + 11, items.InfoRectangle.X + 10, items.InfoRectangle.Y + 11);
g.DrawLine(m_pen, items.InfoRectangle.X + 7, items.InfoRectangle.Y + 7, items.InfoRectangle.X + 7, items.InfoRectangle.Y + 10);
m_pen.Width = 1;
g.DrawRectangle(m_pen, items.InfoRectangle.X, items.InfoRectangle.Y, items.InfoRectangle.Width - 1, items.InfoRectangle.Height - 1);
} else {
if (items.IsLibraryRoot) {
Rectangle rect_box = new Rectangle(rect.Left - 15, rect.Top + m_nItemHeight / 2 - 5, 11, 10);
g.DrawRectangle(Pens.Gray, rect_box);
g.DrawLine(Pens.Cyan, rect_box.X - 2, rect_box.Top, rect_box.X + 2, rect_box.Top);
g.DrawLine(Pens.Cyan, rect_box.X, rect_box.Y - 2, rect_box.X, rect_box.Y + 2);
g.DrawLine(Pens.Cyan, rect_box.Right - 2, rect_box.Bottom, rect_box.Right + 2, rect_box.Bottom);
g.DrawLine(Pens.Cyan, rect_box.Right, rect_box.Bottom - 2, rect_box.Right, rect_box.Bottom + 2);
} else {
g.DrawRectangle(Pens.Goldenrod, new Rectangle(rect.Left - 16, rect.Top + m_nItemHeight / 2 - 6, 8, 3));
g.DrawRectangle(Pens.Goldenrod, new Rectangle(rect.Left - 16, rect.Top + m_nItemHeight / 2 - 3, 13, 9));
}
if (!this._ShowFolderCount) return;
m_sf.Alignment = StringAlignment.Far;
m_brush.Color = this._FolderCountColor;
rect.X -= 4;
g.DrawString("[" + items.STNodeCount.ToString() + "]", this.Font, m_brush, rect, m_sf);
m_sf.Alignment = StringAlignment.Near;
}
}
#endregion
#region public method ==========
/// <summary>
/// 在控件中检索STNode
/// </summary>
/// <param name="strText">需要检索的文本</param>
public void Search(string strText) {
if (strText == null) return;
if (strText.Trim() == string.Empty) return;
m_tbx.Text = strText.Trim();
}
/// <summary>
/// 向控件中添加一个STNode类型
/// </summary>
/// <param name="stNodeType">STNode类型</param>
/// <returns>是否添加成功</returns>
public bool AddNode(Type stNodeType) { return this.AddSTNode(stNodeType, m_items_source, null, true); }
/// <summary>
/// 从文件中向控件添加STNode类型
/// </summary>
/// <param name="strFile">指定文件路径</param>
/// <returns>添加成功个数</returns>
public int LoadAssembly(string strFile) {
strFile = System.IO.Path.GetFullPath(strFile);
var items = this.AddAssemblyPrivate(strFile);
if (items.STNodeCount == 0) return 0;
items.IsLibraryRoot = true;
m_items_source[items.Name] = items;
return items.STNodeCount;
}
/// <summary>
/// 清空控件中所有STNode类型
/// </summary>
public void Clear() {
m_items_source.Clear();
m_items_draw.Clear();
m_dic_all_type.Clear();
this.Invalidate();
}
/// <summary>
/// 向控件中移除一个STNode类型
/// </summary>
/// <param name="stNodeType">STNode类型</param>
/// <returns>是否移除成功</returns>
public bool RemoveNode(Type stNodeType) {
if (!m_dic_all_type.ContainsKey(stNodeType)) return false;
string strPath = m_dic_all_type[stNodeType];
STNodeTreeCollection items = m_items_source;
if (!string.IsNullOrEmpty(strPath)) {
string[] strKeys = strPath.Split(m_chr_splitter);
for (int i = 0; i < strKeys.Length; i++) {
items = items[strKeys[i]];
if (items == null) return false;
}
}
try {
STNode node = (STNode)Activator.CreateInstance(stNodeType);
if (items[node.Title] == null) return false;
items.Remove(node.Title, true);
m_dic_all_type.Remove(stNodeType);
} catch { return false; }
this.Invalidate();
return true;
}
#endregion
//=================================================================================================
/// <summary>
/// STNodeTreeView控件中每一项的集合
/// </summary>
protected class STNodeTreeCollection : IEnumerable
{
private string _Name;
/// <summary>
/// 获取当前树节点显示名称
/// </summary>
public string Name {
get {
return _Name;
}
}
/// <summary>
/// 获取当前树节点显示名称的小写字符串
/// </summary>
public string NameLower { get; private set; }
/// <summary>
/// 获取当前树节点对应的STNode类型
/// </summary>
public Type STNodeType { get; internal set; }
/// <summary>
/// 获取当前树节点的父级树节点
/// </summary>
public STNodeTreeCollection Parent { get; internal set; }
/// <summary>
/// 获取当前树节点下拥有的STNode类型个数
/// </summary>
public int STNodeCount { get; internal set; }
/// <summary>
/// 获取当前树节点对应STNode类型在树控件中对应路径
/// </summary>
public string Path { get; internal set; }
/// <summary>
/// 获取当前或设置树节点是否为打开状态
/// </summary>
public bool IsOpen { get; set; }
/// <summary>
/// 获取当前树节点是否为加载模块的根路劲节点
/// </summary>
public bool IsLibraryRoot { get; internal set; }
/// <summary>
/// 获取当前树节点在控件中的显示区域
/// </summary>
public Rectangle DisplayRectangle { get; internal set; }
/// <summary>
/// 获取当前树节点在控件中的开关按钮区域
/// </summary>
public Rectangle SwitchRectangle { get; internal set; }
/// <summary>
/// 获取当前树节点在控件中的信息按钮区域
/// </summary>
public Rectangle InfoRectangle { get; internal set; }
/// <summary>
/// 获取当前树节点对应STNode类型的标题颜色
/// </summary>
public Color STNodeTypeColor { get; internal set; }
/// <summary>
/// 获取当前树节点所包含子节点个数
/// </summary>
public int Count { get { return m_dic.Count; } }
/// <summary>
/// 获取或设置指定名称的集合
/// </summary>
/// <param name="strKey">指定名称</param>
/// <returns>集合</returns>
public STNodeTreeCollection this[string strKey] {
get {
if (string.IsNullOrEmpty(strKey)) return null;
if (m_dic.ContainsKey(strKey)) return m_dic[strKey];
return null;
}
set {
if (string.IsNullOrEmpty(strKey)) return;
if (value == null) return;
if (m_dic.ContainsKey(strKey)) {
m_dic[strKey] = value;
} else {
m_dic.Add(strKey, value);
}
value.Parent = this;
}
}
private SortedDictionary<string, STNodeTreeCollection> m_dic = new SortedDictionary<string, STNodeTreeCollection>();
/// <summary>
/// 构造一颗树节点集合
/// </summary>
/// <param name="strName">当前树节点在控件中的显示名称</param>
public STNodeTreeCollection(string strName) {
if (strName == null || strName.Trim() == string.Empty)
throw new ArgumentNullException("显示名称不能为空");
this._Name = strName.Trim();
this.NameLower = this._Name.ToLower();
}
/// <summary>
/// 向当前树节点中添加一个子节点
/// </summary>
/// <param name="strName">节点显示名称</param>
/// <returns>添加后的子节点集合</returns>
public STNodeTreeCollection Add(string strName) {
if (!m_dic.ContainsKey(strName))
m_dic.Add(strName, new STNodeTreeCollection(strName) { Parent = this });
return m_dic[strName];
}
/// <summary>
/// 向当前树节点中删除一个子集合
/// </summary>
/// <param name="strName">子集合名称</param>
/// <param name="isAutoDelFolder">是否递归向上自动清空无用节点</param>
/// <returns>是否删除成功</returns>
public bool Remove(string strName, bool isAutoDelFolder) {
if (!m_dic.ContainsKey(strName)) return false;
bool b = m_dic.Remove(strName);
var temp = this;
while (temp != null) {
temp.STNodeCount--;
temp = temp.Parent;
}
if (isAutoDelFolder && m_dic.Count == 0 && this.Parent != null)
return b && this.Parent.Remove(this.Name, isAutoDelFolder);
return b;
}
/// <summary>
/// 清空当前树节点中所有子节点
/// </summary>
public void Clear() { this.Clear(this); }
private void Clear(STNodeTreeCollection items) {
foreach (STNodeTreeCollection v in items) v.Clear(v);
m_dic.Clear();
}
/// <summary>
/// 获取当前树节点中所有的名称数组
/// </summary>
/// <returns></returns>
public string[] GetKeys() { return m_dic.Keys.ToArray(); }
/// <summary>
/// 拷贝当前树节点集合中所有数据
/// </summary>
/// <returns>拷贝的副本</returns>
public STNodeTreeCollection Copy() {
STNodeTreeCollection items = new STNodeTreeCollection("COPY");
this.Copy(this, items);
return items;
}
private void Copy(STNodeTreeCollection items_src, STNodeTreeCollection items_dst) {
foreach (STNodeTreeCollection v in items_src) {
this.Copy(v, items_dst.Add(v.Name));
}
items_dst.Path = items_src.Path;
items_dst.STNodeType = items_src.STNodeType;
items_dst.IsLibraryRoot = items_src.IsLibraryRoot;
items_dst.STNodeCount = items_src.STNodeCount;
items_dst.STNodeTypeColor = items_src.STNodeTypeColor;
}
/// <summary>
/// 返回 System.Collections.IEnumerator 的 Array
/// </summary>
/// <returns></returns>
public IEnumerator GetEnumerator() {
foreach (var v in m_dic.Values) yield return v;
}
IEnumerator IEnumerable.GetEnumerator() {
return this.GetEnumerator();
}
}
}
}

0
ST.Library.UI/Properties/AssemblyInfo.cs Executable file → Normal file
View File

42
ST.Library.UI/ST.Library.UI.csproj Executable file → Normal file
View File

@ -21,6 +21,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -35,24 +36,45 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="STNodeEditor\STNode.cs" />
<Compile Include="STNodeEditor\STNodeCollection.cs" />
<Compile Include="STNodeEditor\STNodeControl.cs" />
<Compile Include="STNodeEditor\STNodeControlCollection.cs" />
<Compile Include="STNodeEditor\STNodeEditor.cs">
<Compile Include="NodeEditor\FrmNodePreviewPanel.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="NodeEditor\FrmSTNodePropertySelect.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="NodeEditor\STNodeEditorPannel.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="NodeEditor\STNodeTreeView.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="NodeEditor\FrmSTNodePropertyInput.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="NodeEditor\STNode.cs" />
<Compile Include="NodeEditor\STNodeAttribute.cs" />
<Compile Include="NodeEditor\STNodeCollection.cs" />
<Compile Include="NodeEditor\STNodeControl.cs" />
<Compile Include="NodeEditor\STNodeControlCollection.cs" />
<Compile Include="NodeEditor\STNodeEditor.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="NodeEditor\STNodeEditorDataType.cs" />
<Compile Include="NodeEditor\STNodeHub.cs" />
<Compile Include="NodeEditor\STNodeOption.cs" />
<Compile Include="NodeEditor\STNodeOptionCollection.cs" />
<Compile Include="NodeEditor\STNodePropertyAttribute.cs" />
<Compile Include="NodeEditor\STNodePropertyGrid.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="STNodeEditor\STNodeEditorDataType.cs" />
<Compile Include="STNodeEditor\STNodeHub.cs" />
<Compile Include="STNodeEditor\STNodeOption.cs" />
<Compile Include="STNodeEditor\STNodeOptionCollection.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

Binary file not shown.

View File

@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI;
namespace ST.Library.UI
{
public class DemoNode : STNode
{
protected override void OnCreate() {
base.OnCreate();
this.InputOptions.Add(new STNodeOption("Input", typeof(string), false));
this.InputOptions.Add(new STNodeOption("SingleNode", typeof(System.Drawing.Image), true));
this.InputOptions.Add(new STNodeOption("SingleNode", typeof(object), true));
this.OutputOptions.Add(new STNodeOption("output", typeof(string), false));
this.OutputOptions.Add(new STNodeOption("Single", typeof(System.Drawing.Icon), true));
this.OutputOptions.Add(new STNodeOption("Single", typeof(object), true));
this.Title = "Demo_Node";
}
}
}

View File

@ -1,87 +0,0 @@
namespace ST.Library.UI.Demo_Image
{
partial class FrmImage
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.stNodeEditor1 = new ST.Library.UI.STNodeEditor();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(0, 0);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 1;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(0, 29);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(75, 23);
this.button2.TabIndex = 2;
this.button2.Text = "button2";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// stNodeEditor1
//
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(0, 0);
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.Name = "stNodeEditor1";
this.stNodeEditor1.Size = new System.Drawing.Size(200, 200);
this.stNodeEditor1.TabIndex = 0;
this.stNodeEditor1.Text = "stNodeEditor1";
//
// FrmImage
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Controls.Add(this.stNodeEditor1);
this.Name = "FrmImage";
this.Text = "FrmImage";
this.Load += new System.EventHandler(this.FrmImage_Load);
this.ResumeLayout(false);
}
#endregion
private STNodeEditor stNodeEditor1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
}
}

View File

@ -1,58 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace ST.Library.UI.Demo_Image
{
public partial class FrmImage : Form
{
public FrmImage() {
InitializeComponent();
button1.Text = "Save";
button2.Text = "Open";
}
private void FrmImage_Load(object sender, EventArgs e) {
stNodeEditor1.Dock = DockStyle.Fill;
stNodeEditor1.TypeColor.Add(typeof(Image), Color.BlueViolet);
STNode node = new STNodeImageInput();
stNodeEditor1.Nodes.Add(node);
node = new STNodeImageChannel();
stNodeEditor1.Nodes.Add(node);
node = new STNodeImageChannel();
stNodeEditor1.Nodes.Add(node);
node = new STNodeImageChannel();
stNodeEditor1.Nodes.Add(node);
node = new STNodeImageChannel();
stNodeEditor1.Nodes.Add(node);
stNodeEditor1.LoadAssembly(Application.ExecutablePath);
stNodeEditor1.LoadAssembly(Directory.GetFiles("./", "*.dll"));
}
private void button1_Click(object sender, EventArgs e) {
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "*.stn|*.stn";
if (sfd.ShowDialog() != DialogResult.OK) return;
stNodeEditor1.SaveCanvas(sfd.FileName);
}
private void button2_Click(object sender, EventArgs e) {
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "*.stn|*.stn";
if (ofd.ShowDialog() != DialogResult.OK) return;
stNodeEditor1.Nodes.Clear();
stNodeEditor1.LoadCanvas(ofd.FileName);
}
}
}

View File

@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace ST.Library.UI.Demo_Image
{
public class STNodeImage : STNode
{
protected STNodeOption m_input_image; //输入输出点
protected STNodeOption m_out_image;
protected override void OnCreate() {
base.OnCreate();
m_input_image = new STNodeOption("", typeof(Image), true);
this.InputOptions.Add(m_input_image);
m_out_image = new STNodeOption("", typeof(Image), false);
this.OutputOptions.Add(m_out_image);
m_input_image.DataTransfer += new STNodeOptionEventHandler(m_input_image_DataTransfer);
this.Title = "Image";
}
//监听输入点接入事件
void m_input_image_DataTransfer(object sender, STNodeOptionEventArgs e) {
if (e.Status != ConnectionStatus.Connected)
m_input_image.Data = null;
else
m_input_image.Data = e.TargetOption.Data;
m_out_image.TransferData(m_input_image.Data); //输出节点向下投递数据
this.OnDataTransfer(); //通知子类
this.Invalidate(); //重绘自己
}
protected override System.Drawing.Size OnBuildNodeSize(DrawingTools dt) {
//return base.OnBuildNodeSize();
return new System.Drawing.Size(160, 120); //设定节点大小
}
protected override void OnDrawBody(DrawingTools dt) { //重绘节点主体部分
base.OnDrawBody(dt);
Graphics g = dt.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
if (m_input_image.Data != null) {
g.DrawImage((Image)m_input_image.Data, this.Left + 15, this.Top + 30, this.Width - 40, this.Height - 40);
} else {
g.FillRectangle(Brushes.Gray, this.Left + 15, this.Top + 30, this.Width - 40, this.Height - 40);
}
}
protected virtual void OnDataTransfer() { }
}
}

View File

@ -1,57 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace ST.Library.UI.Demo_Image
{
public class STNodeImageChannel : STNodeImage
{
private STNodeOption m_out_r;
private STNodeOption m_out_g;
private STNodeOption m_out_b;
private Bitmap m_img_r;
private Bitmap m_img_g;
private Bitmap m_img_b;
protected override void OnCreate() {
base.OnCreate();
m_out_r = new STNodeOption("R", typeof(Image), false);
m_out_g = new STNodeOption("G", typeof(Image), false);
m_out_b = new STNodeOption("B", typeof(Image), false);
this.OutputOptions.Add(m_out_r);
this.OutputOptions.Add(m_out_g);
this.OutputOptions.Add(m_out_b);
this.Title = "Channel";
}
protected override void OnDataTransfer() {
base.OnDataTransfer();
if (m_img_r != null) {
m_img_r.Dispose();
m_img_g.Dispose();
m_img_b.Dispose();
m_img_r = m_img_g = m_img_b = null;
}
if (m_out_image.Data != null) { //分离通道 Demo 演示 Get/SetPixel() 效率极低 应当LockBitmap操作
Bitmap img = (Bitmap)base.m_input_image.Data;
m_img_r = new Bitmap(img.Width, img.Height);
m_img_g = new Bitmap(img.Width, img.Height);
m_img_b = new Bitmap(img.Width, img.Height);
for (int x = 0; x < img.Width; x++) {
for (int y = 0; y < img.Height; y++) {
Color clr = img.GetPixel(x, y);
m_img_r.SetPixel(x, y, Color.FromArgb(255, clr.R, clr.R, clr.R));
m_img_g.SetPixel(x, y, Color.FromArgb(255, clr.G, clr.G, clr.G));
m_img_b.SetPixel(x, y, Color.FromArgb(255, clr.B, clr.B, clr.B));
}
}
}
m_out_r.TransferData(m_img_r);
m_out_g.TransferData(m_img_b);
m_out_b.TransferData(m_img_b);
}
}
}

View File

@ -1,93 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
namespace ST.Library.UI.Demo_Image
{
public class STNodeImageInput : STNode
{
private STNodeOption m_option_out;
private string m_str_file;
private Size m_sz = new Size(100, 60);
protected override void OnCreate() {
base.OnCreate();
this.Title = "ImageInput";
m_option_out = new STNodeOption("", typeof(Image), false);
this.OutputOptions.Add(m_option_out);
STNodeButton btn = new STNodeButton();
btn.Left = (m_sz.Width - btn.Width) / 2;
btn.Top = (m_sz.Height - 20 - btn.Height) / 2;
btn.Text = "OpenImage";
btn.MouseClick += new MouseEventHandler(btn_MouseClick);
this.Controls.Add(btn);
}
void btn_MouseClick(object sender, MouseEventArgs e) {
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "*.jpg|*.jpg|*.png|*.png|*.bmp|*.bmp|*.*|*.*";
if (ofd.ShowDialog() != DialogResult.OK) return;
m_option_out.TransferData(Image.FromFile(ofd.FileName));
m_str_file = ofd.FileName;
}
protected override Size OnBuildNodeSize(DrawingTools dt) {
//return base.OnBuildNodeSize();
return m_sz;
}
protected override Point OnSetOptionDotLocation(STNodeOption op, Point pt) {
return new Point(pt.X, pt.Y + 10);
//return base.OnSetOptionLocation(op);
}
//protected override void OnDrawOptionDot(DrawingTools dt, STNodeOption op) {
// //if (op == m_option_out) op.DotTop = this.Top + 35;
// base.OnDrawOptionDot(dt, op);
//}
protected override void OnSaveNode(Dictionary<string, byte[]> dic) {
if (m_str_file == null) m_str_file = string.Empty;
dic.Add("file", Encoding.UTF8.GetBytes(m_str_file));
}
protected override void OnLoadNode(Dictionary<string, byte[]> dic) {
base.OnLoadNode(dic);
m_str_file = Encoding.UTF8.GetString(dic["file"]);
if (System.IO.File.Exists(m_str_file)) { //如果文件存在加载并投递数据
m_option_out.TransferData(Image.FromFile(m_str_file));
}
}
public class STNodeButton : STNodeControl //自定义一个Button控件
{
private bool m_isHover;
protected override void OnMouseEnter(EventArgs e) {
base.OnMouseEnter(e);
m_isHover = true;
this.Invalidate();
}
protected override void OnMouseLeave(EventArgs e) {
base.OnMouseLeave(e);
m_isHover = false;
this.Invalidate();
}
protected override void OnPaint(DrawingTools dt) {
//base.OnPaint(dt);
Graphics g = dt.Graphics;
SolidBrush brush = dt.SolidBrush;
brush.Color = m_isHover ? Color.DodgerBlue : this.BackColor;
g.FillRectangle(brush, 0, 0, this.Width, this.Height);
g.DrawString(this.Text, this.Font, Brushes.White, this.ClientRectangle, base.m_sf);
}
}
}
}

View File

@ -1,100 +0,0 @@
namespace ST.Library.UI
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.button3 = new System.Windows.Forms.Button();
this.stNodeEditor1 = new ST.Library.UI.STNodeEditor();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(0, 0);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 4;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(0, 29);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(75, 23);
this.button2.TabIndex = 5;
this.button2.Text = "button2";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// button3
//
this.button3.Location = new System.Drawing.Point(0, 58);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(75, 23);
this.button3.TabIndex = 6;
this.button3.Text = "button3";
this.button3.UseVisualStyleBackColor = true;
this.button3.Click += new System.EventHandler(this.button3_Click);
//
// stNodeEditor1
//
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(0, 0);
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.Name = "stNodeEditor1";
this.stNodeEditor1.Size = new System.Drawing.Size(200, 200);
this.stNodeEditor1.TabIndex = 3;
this.stNodeEditor1.Text = "stNodeEditor1";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.button3);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Controls.Add(this.stNodeEditor1);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
}
#endregion
private STNodeEditor stNodeEditor1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Button button3;
}
}

View File

@ -1,87 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ST.Library.UI;
using System.IO;
namespace ST.Library.UI
{
public partial class Form1 : Form
{
public Form1() {
InitializeComponent();
button1.Text = "Lock";
button2.Text = "Save";
button3.Text = "Open";
}
private void Form1_Load(object sender, EventArgs e) {
stNodeEditor1.Dock = DockStyle.Fill;
stNodeEditor1.TypeColor.Add(typeof(string), Color.Yellow);
stNodeEditor1.TypeColor.Add(typeof(Image), Color.Red);
stNodeEditor1.SelectedChanged += new EventHandler(stNodeEditor1_SelectedChanged);
stNodeEditor1.OptionConnected += new STNodeEditorOptionEventHandler(stNodeEditor1_OptionConnected);
stNodeEditor1.CanvasScaled += new EventHandler(stNodeEditor1_CanvasScaled);
for (int i = 0; i < 4; i++) {
STNode node = new DemoNode();
//if (i == 2)
// node.Mark = "这里是标记信息\r\n\t新的一行数据";
//else
// node.Mark = "this is mark Info\r\nthis is new line " + i;
stNodeEditor1.Nodes.Add(node);
}
stNodeEditor1.Nodes.Add(new STNodeHub());
stNodeEditor1.Nodes.Add(new STNodeHub());
stNodeEditor1.Nodes.Add(new NodeNumberAdd());
stNodeEditor1.LoadAssembly(Application.ExecutablePath);
stNodeEditor1.LoadAssembly(Directory.GetFiles("./", "*.dll"));
}
void stNodeEditor1_CanvasScaled(object sender, EventArgs e) {
stNodeEditor1.ShowAlert(stNodeEditor1.CanvasScale.ToString("F2"), Color.White, Color.Black);
}
void stNodeEditor1_OptionConnected(object sender, STNodeEditorOptionEventArgs e) {
Console.WriteLine(e.CurrentOption.Text + " - " + e.TargetOption.Text + " - " + e.Status);
}
void stNodeEditor1_SelectedChanged(object sender, EventArgs e) {
foreach (var v in stNodeEditor1.GetSelectedNode()) {
Console.WriteLine("Selected - " + v.Title);
}
}
private void button1_Click(object sender, EventArgs e) {
stNodeEditor1.Nodes[0].LockOption = !stNodeEditor1.Nodes[0].LockOption;
stNodeEditor1.Nodes[1].LockOption = !stNodeEditor1.Nodes[1].LockOption;
stNodeEditor1.Nodes[0].LockLocation = !stNodeEditor1.Nodes[0].LockLocation;
stNodeEditor1.Nodes[1].LockLocation = !stNodeEditor1.Nodes[1].LockLocation;
}
private void button2_Click(object sender, EventArgs e) {
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "*.stn|*.stn";
if (sfd.ShowDialog() != DialogResult.OK) return;
stNodeEditor1.SaveCanvas(sfd.FileName);
}
private void button3_Click(object sender, EventArgs e) {
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "*.stn|*.stn";
if (ofd.ShowDialog() != DialogResult.OK) return;
stNodeEditor1.Nodes.Clear();
stNodeEditor1.LoadCanvas(ofd.FileName);
}
}
}

View File

@ -1,96 +0,0 @@
namespace ST.Library.UI
{
partial class Form2
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.button3 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(0, 0);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(0, 29);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(75, 23);
this.button2.TabIndex = 1;
this.button2.Text = "button2";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// button3
//
this.button3.Location = new System.Drawing.Point(0, 58);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(75, 23);
this.button3.TabIndex = 2;
this.button3.Text = "button3";
this.button3.UseVisualStyleBackColor = true;
this.button3.Click += new System.EventHandler(this.button3_Click);
//
// button4
//
this.button4.Location = new System.Drawing.Point(0, 87);
this.button4.Name = "button4";
this.button4.Size = new System.Drawing.Size(75, 23);
this.button4.TabIndex = 3;
this.button4.Text = "button4";
this.button4.UseVisualStyleBackColor = true;
this.button4.Click += new System.EventHandler(this.button4_Click);
//
// Form2
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.button4);
this.Controls.Add(this.button3);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Name = "Form2";
this.Text = "Form2";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.Button button4;
}
}

View File

@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ST.Library.UI
{
public partial class Form2 : Form
{
public Form2() {
InitializeComponent();
}
Graphics m_g;
private void button1_Click(object sender, EventArgs e) {
m_g = this.CreateGraphics();
}
private void button2_Click(object sender, EventArgs e) {
m_g.DrawRectangle(Pens.Red, 10, 10, 30, 30);
}
private void button3_Click(object sender, EventArgs e) {
m_g.DrawRectangle(Pens.Yellow, 45, 45, 20, 20);
}
private void button4_Click(object sender, EventArgs e) {
this.CreateGraphics().FillRectangle(Brushes.Black, 20, 20, 20, 20);
}
}
}

View File

@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace ST.Library.UI
{
public class NodeNumberAdd : STNode
{
private STNodeOption m_in_num1;
private STNodeOption m_in_num2;
private STNodeOption m_out_num;
private int m_nNum1, m_nNum2;
protected override void OnCreate() {
base.OnCreate();
this.Title = "NumberAdd";
m_in_num1 = new STNodeOption("num1", typeof(int), true);//只能有一个连线
m_in_num2 = new STNodeOption("num2", typeof(int), true);//只能有一个连线
m_out_num = new STNodeOption("result", typeof(int), false);//可以多个连线
this.InputOptions.Add(m_in_num1);
this.InputOptions.Add(m_in_num2);
this.OutputOptions.Add(m_out_num);
m_in_num1.DataTransfer += new STNodeOptionEventHandler(m_in_num_DataTransfer);
m_in_num2.DataTransfer += new STNodeOptionEventHandler(m_in_num_DataTransfer);
}
//当有数据传入时
void m_in_num_DataTransfer(object sender, STNodeOptionEventArgs e) {
//判断连线是否是连接状态(建立连线 断开连线 都会触发该事件)
if (e.Status == ConnectionStatus.Connected && e.TargetOption.Data != null) {
if (sender == m_in_num1)
m_nNum1 = (int)e.TargetOption.Data;//TargetOption为触发此事件的Option
else
m_nNum2 = (int)e.TargetOption.Data;
} else {
if (sender == m_in_num1) m_nNum1 = 0; else m_nNum2 = 0;
}
//向输出选项上的所有连线传输数据 输出选项上的所有连线都会触发 DataTransfer 事件
m_out_num.TransferData(m_nNum1 + m_nNum2); //m_out_num.Data 将被自动设置
}
protected override void OnOwnerChanged() {
base.OnOwnerChanged();//通常刚被添加到节点编辑器时触发 如是以插件方式提供的节点 应当向编辑器提交数据类型颜色
if (this.Owner == null) return; //或者通过m_in_num1.DotColor = Color.Red;进行设置
this.Owner.SetTypeColor(typeof(int), Color.Red);
}
}
}

View File

@ -1,3 +0,0 @@
<?xml version="1.0"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>

View File

@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
using System.Windows.Forms;
namespace WinNodeEditorDemo
{
[STNode("/", "Crystal_lz", "2212233137@qq.com", "www.st233.com", "关于此节点的描述信息\r\n此类为\r\nSTNodeAttribute\r\nSTNodePropertyAttribute\r\n效果演示类")]
public class AttrTestNode : STNode
{
//因为属性编辑器默认并不支持Color类型数据 所以这里重写一个描述器并指定
[STNodeProperty("颜色", "颜色信息", DescriptorType = typeof(DescriptorForColor))]
public Color Color { get; set; }
[STNodeProperty("整型数组", "整型数组测试")]
public int[] IntArr { get; set; }
[STNodeProperty("布尔", "布尔类型测试")]
public bool Bool { get; set; }
[STNodeProperty("字符串", "字符串类型测试")]
public string String { get; set; }
[STNodeProperty("整型", "整型测试")]
public int Int { get; set; }
[STNodeProperty("浮点数", "浮点数类型测试")]
public float Float { get; set; }
[STNodeProperty("枚举值", "枚举类型测试 -> FormBorderStyle")]
public FormBorderStyle STYLE { get; set; }
public AttrTestNode() {
this.String = "string";
IntArr = new int[] { 10, 20 };
base.InputOptions.Add("string", typeof(string), false);
base.OutputOptions.Add("string", typeof(string), false);
this.Title = "AttrTestNode";
this.TitleColor = Color.FromArgb(200, Color.Goldenrod);
}
/// <summary>
/// 此方法为魔术方法(Magic function)
/// 若存在 static void ShowHelpInfo(string) 且此类被STNodeAttribute标记
/// 则此方法将作为属性编辑器上 查看帮助 功能
/// </summary>
/// <param name="strFileName">此类所在的模块所在的文件路径</param>
public static void ShowHelpInfo(string strFileName) {
MessageBox.Show("this is -> ShowHelpInfo(string);\r\n" + strFileName);
}
protected override void OnOwnerChanged() {
base.OnOwnerChanged();
if (this.Owner == null) return;
this.Owner.SetTypeColor(typeof(string), Color.Goldenrod);
}
}
/// <summary>
/// 因为属性编辑器默认并不支持Color类型数据 所以这里重写一个描述器
/// </summary>
public class DescriptorForColor : STNodePropertyDescriptor
{
private Rectangle m_rect;//此区域用作 属性窗口上绘制颜色预览
//当此属性在属性窗口中被确定位置时候发生
protected override void OnSetItemLocation() {
base.OnSetItemLocation();
Rectangle rect = base.RectangleR;
m_rect = new Rectangle(rect.Right - 25, rect.Top + 5, 19, 12);
}
//将属性值转换为字符串 属性窗口值绘制时将采用此字符串
protected override string GetStringFromValue() {
Color clr = (Color)this.GetValue(null);
return clr.A + "," + clr.R + "," + clr.G + "," + clr.B;
}
//将属性窗口中输入的字符串转化为Color属性 当属性窗口中用户确认输入时调用
protected override object GetValueFromString(string strText) {
string[] strClr = strText.Split(',');
return Color.FromArgb(
int.Parse(strClr[0]), //A
int.Parse(strClr[1]), //R
int.Parse(strClr[2]), //G
int.Parse(strClr[3])); //B
}
//绘制属性窗口值区域时候调用
protected override void OnDrawValueRectangle(DrawingTools dt) {
base.OnDrawValueRectangle(dt);//先采用默认的绘制 并再绘制颜色预览
dt.SolidBrush.Color = (Color)this.GetValue(null);
dt.Graphics.FillRectangle(dt.SolidBrush, m_rect);//填充颜色
dt.Graphics.DrawRectangle(Pens.Black, m_rect); //绘制边框
}
protected override void OnMouseClick(MouseEventArgs e) {
//如果用户点击在 颜色预览区域 则弹出系统颜色对话框
if (m_rect.Contains(e.Location)) {
ColorDialog cd = new ColorDialog();
if (cd.ShowDialog() != DialogResult.OK) return;
this.SetValue(cd.Color, null);
this.Invalidate();
return;
}
//否则其他区域将采用默认处理方式 弹出字符串输入框
base.OnMouseClick(e);
}
}
}

View File

@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo.Blender
{
/// <summary>
/// 此类仅仅是演示 并不包含颜色混合功能
/// </summary>
[STNode("/Blender/", "Crystal_lz", "2212233137@qq.com", "st233.com", "this is blender mixrgb node")]
public class BlenderMixColorNode : STNode
{
private ColorMixType _MixType;
[STNodeProperty("MixType","This is MixType")]
public ColorMixType MixType {
get { return _MixType; }
set {
_MixType = value;
m_ctrl_select.Enum = value; //当属性被赋值后 对应控件状态值也应当被修改
}
}
private bool _Clamp;
[STNodeProperty("Clamp","This is Clamp")]
public bool Clamp {
get { return _Clamp; }
set { _Clamp = value; m_ctrl_checkbox.Checked = value; }
}
private int _Fac = 50;
[STNodeProperty("Fac", "This is Fac")]
public int Fac {
get { return _Fac; }
set {
if (value < 0) value = 0;
if (value > 100) value = 100;
_Fac = value; m_ctrl_progess.Value = value;
}
}
private Color _Color1 = Color.LightGray;//默认的DescriptorType不支持颜色的显示 需要扩展
[STNodeProperty("Color1", "This is color1", DescriptorType = typeof(WinNodeEditorDemo.DescriptorForColor))]
public Color Color1 {
get { return _Color1; }
set { _Color1 = value; m_ctrl_btn_1.BackColor = value; }
}
private Color _Color2 = Color.LightGray;
[STNodeProperty("Color2", "This is color2", DescriptorType = typeof(WinNodeEditorDemo.DescriptorForColor))]
public Color Color2 {
get { return _Color2; }
set { _Color2 = value; m_ctrl_btn_2.BackColor = value; }
}
public enum ColorMixType {
Mix,
Value,
Color,
Hue,
Add,
Subtract
}
private STNodeSelectEnumBox m_ctrl_select; //自定义控件
private STNodeProgress m_ctrl_progess;
private STNodeCheckBox m_ctrl_checkbox;
private STNodeColorButton m_ctrl_btn_1;
private STNodeColorButton m_ctrl_btn_2;
protected override void OnCreate() {
base.OnCreate();
this.TitleColor = Color.FromArgb(200, Color.DarkKhaki);
this.Title = "MixRGB";
this.AutoSize = false;
this.Size = new Size(140, 142);
this.OutputOptions.Add("Color", typeof(Color), true);
this.InputOptions.Add(STNodeOption.Empty); //空白节点 仅站位 不参与绘制与事件触发
this.InputOptions.Add(STNodeOption.Empty);
this.InputOptions.Add(STNodeOption.Empty);
this.InputOptions.Add("", typeof(float), true);
this.InputOptions.Add("Color1", typeof(Color), true);
this.InputOptions.Add("Color2", typeof(Color), true);
m_ctrl_progess = new STNodeProgress(); //创建控件并添加到节点中
m_ctrl_progess.Text = "Fac";
m_ctrl_progess.DisplayRectangle = new Rectangle(10, 61, 120, 18);
m_ctrl_progess.ValueChanged += (s, e) => this._Fac = m_ctrl_progess.Value;
this.Controls.Add(m_ctrl_progess);
m_ctrl_checkbox = new STNodeCheckBox();
m_ctrl_checkbox.Text = "Clamp";
m_ctrl_checkbox.DisplayRectangle = new Rectangle(10, 40, 120, 20);
m_ctrl_checkbox.ValueChanged += (s, e) => this._Clamp = m_ctrl_checkbox.Checked;
this.Controls.Add(m_ctrl_checkbox);
m_ctrl_btn_1 = new STNodeColorButton();
m_ctrl_btn_1.Text = "";
m_ctrl_btn_1.BackColor = this._Color1;
m_ctrl_btn_1.DisplayRectangle = new Rectangle(80, 82, 50, 16);
m_ctrl_btn_1.ValueChanged += (s, e) => this._Color1 = m_ctrl_btn_1.BackColor;
this.Controls.Add(m_ctrl_btn_1);
m_ctrl_btn_2 = new STNodeColorButton();
m_ctrl_btn_2.Text = "";
m_ctrl_btn_2.BackColor = this._Color2;
m_ctrl_btn_2.DisplayRectangle = new Rectangle(80, 102, 50, 16);
m_ctrl_btn_2.ValueChanged += (s, e) => this._Color2 = m_ctrl_btn_2.BackColor;
this.Controls.Add(m_ctrl_btn_2);
m_ctrl_select = new STNodeSelectEnumBox();
m_ctrl_select.DisplayRectangle = new Rectangle(10, 21, 120, 18);
m_ctrl_select.Enum = this._MixType;
m_ctrl_select.ValueChanged += (s, e) => this._MixType = (ColorMixType)m_ctrl_select.Enum;
this.Controls.Add(m_ctrl_select);
}
protected override void OnOwnerChanged() { //当控件被添加时候 向编辑器提交自己的数据类型希望显示的颜色
base.OnOwnerChanged();
if (this.Owner == null) return;
this.Owner.SetTypeColor(typeof(float), Color.Gray);
this.Owner.SetTypeColor(typeof(Color), Color.Yellow);
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
namespace WinNodeEditorDemo.Blender
{
/// <summary>
/// 此类仅演示 作为MixRGB节点的下拉选择框弹出菜单
/// </summary>
public class FrmEnumSelect : Form
{
private Point m_pt;
private int m_nWidth;
private float m_scale;
private List<object> m_lst = new List<object>();
private StringFormat m_sf;
public Enum Enum { get; set; }
private bool m_bClosed;
public FrmEnumSelect(Enum e, Point pt, int nWidth, float scale) {
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
foreach (var v in Enum.GetValues(e.GetType())) m_lst.Add(v);
this.Enum = e;
m_pt = pt;
m_scale = scale;
m_nWidth = nWidth;
m_sf = new StringFormat();
m_sf.LineAlignment = StringAlignment.Center;
this.ShowInTaskbar = false;
this.BackColor = Color.FromArgb(255, 34, 34, 34);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
}
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
this.Location = m_pt;
this.Width = (int)(m_nWidth * m_scale);
this.Height = (int)(m_lst.Count * 20 * m_scale);
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Graphics g = e.Graphics;
g.ScaleTransform(m_scale, m_scale);
Rectangle rect = new Rectangle(0, 0, this.Width, 20);
foreach (var v in m_lst) {
g.DrawString(v.ToString(), this.Font, Brushes.White, rect, m_sf);
rect.Y += rect.Height;
}
}
protected override void OnMouseClick(MouseEventArgs e) {
base.OnMouseClick(e);
int nIndex = e.Y / (int)(20 * m_scale);
if (nIndex >= 0 && nIndex < m_lst.Count) this.Enum = (Enum)m_lst[nIndex];
this.DialogResult = System.Windows.Forms.DialogResult.OK;
m_bClosed = true;
}
protected override void OnMouseLeave(EventArgs e) {
base.OnMouseLeave(e);
if (m_bClosed) return;
//this.DialogResult = System.Windows.Forms.DialogResult.None;
this.Close();
}
}
}

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using ST.Library.UI.NodeEditor;
namespace WinNodeEditorDemo.Blender
{
/// <summary>
/// 此类仅演示 作为MixRGB节点的复选框控件
/// </summary>
public class STNodeCheckBox : STNodeControl
{
private bool _Checked;
public bool Checked {
get { return _Checked; }
set {
_Checked = value;
this.Invalidate();
}
}
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(EventArgs e) {
if (this.ValueChanged != null) this.ValueChanged(this, e);
}
protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e) {
base.OnMouseClick(e);
this.Checked = !this.Checked;
this.OnValueChanged(new EventArgs());
}
protected override void OnPaint(DrawingTools dt) {
//base.OnPaint(dt);
Graphics g = dt.Graphics;
g.FillRectangle(Brushes.Gray, 0, 5, 10, 10);
m_sf.Alignment = StringAlignment.Near;
g.DrawString(this.Text, this.Font, Brushes.LightGray, new Rectangle(15, 0, this.Width - 20, 20), m_sf);
if (this.Checked) {
g.FillRectangle(Brushes.Black, 2, 7, 6, 6);
}
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using ST.Library.UI.NodeEditor;
namespace WinNodeEditorDemo.Blender
{
/// <summary>
/// 此类仅演示 作为MixRGB节点的颜色选择按钮
/// </summary>
public class STNodeColorButton : STNodeControl
{
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(EventArgs e) {
if (this.ValueChanged != null) this.ValueChanged(this, e);
}
protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e) {
base.OnMouseClick(e);
ColorDialog cd = new ColorDialog();
if (cd.ShowDialog() != DialogResult.OK) return;
//this._Color = cd.Color;
this.BackColor = cd.Color;
this.OnValueChanged(new EventArgs());
}
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo.Blender
{
/// <summary>
/// 此类仅演示 作为MixRGB节点的进度条控件
/// </summary>
public class STNodeProgress : STNodeControl
{
private int _Value = 50;
public int Value {
get { return _Value; }
set {
_Value = value;
this.Invalidate();
}
}
private bool m_bMouseDown;
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(EventArgs e) {
if (this.ValueChanged != null) this.ValueChanged(this, e);
}
protected override void OnPaint(DrawingTools dt) {
base.OnPaint(dt);
Graphics g = dt.Graphics;
g.FillRectangle(Brushes.Gray, this.ClientRectangle);
g.FillRectangle(Brushes.CornflowerBlue, 0, 0, (int)((float)this._Value / 100 * this.Width), this.Height);
m_sf.Alignment = StringAlignment.Near;
g.DrawString(this.Text, this.Font, Brushes.White, this.ClientRectangle, m_sf);
m_sf.Alignment = StringAlignment.Far;
g.DrawString(((float)this._Value / 100).ToString("F2"), this.Font, Brushes.White, this.ClientRectangle, m_sf);
}
protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e) {
base.OnMouseDown(e);
m_bMouseDown = true;
}
protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs e) {
base.OnMouseUp(e);
m_bMouseDown = false;
}
protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e) {
base.OnMouseMove(e);
if (!m_bMouseDown) return;
int v = (int)((float)e.X / this.Width * 100);
if (v < 0) v = 0;
if (v > 100) v = 100;
this._Value = v;
this.OnValueChanged(new EventArgs());
this.Invalidate();
}
}
}

View File

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using ST.Library.UI.NodeEditor;
namespace WinNodeEditorDemo.Blender
{
/// <summary>
/// 此类仅演示 作为MixRGB节点的下拉框控件
/// </summary>
public class STNodeSelectEnumBox : STNodeControl
{
private Enum _Enum;
public Enum Enum {
get { return _Enum; }
set {
_Enum = value;
this.Invalidate();
}
}
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(EventArgs e) {
if (this.ValueChanged != null) this.ValueChanged(this, e);
}
protected override void OnPaint(DrawingTools dt) {
Graphics g = dt.Graphics;
dt.SolidBrush.Color = Color.FromArgb(80, 0, 0, 0);
g.FillRectangle(dt.SolidBrush, this.ClientRectangle);
m_sf.Alignment = StringAlignment.Near;
g.DrawString(this.Enum.ToString(), this.Font, Brushes.White, this.ClientRectangle, m_sf);
g.FillPolygon(Brushes.Gray, new Point[]{
new Point(this.Right - 25, 7),
new Point(this.Right - 15, 7),
new Point(this.Right - 20, 12)
});
}
protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e) {
base.OnMouseClick(e);
Point pt = new Point(this.Left + this.Owner.Left, this.Top + this.Owner.Top + this.Owner.TitleHeight);
pt = this.Owner.Owner.CanvasToControl(pt);
pt = this.Owner.Owner.PointToScreen(pt);
FrmEnumSelect frm = new FrmEnumSelect(this.Enum, pt, this.Width, this.Owner.Owner.CanvasScale);
var v = frm.ShowDialog();
if (v != System.Windows.Forms.DialogResult.OK) return;
this.Enum = frm.Enum;
this.OnValueChanged(new EventArgs());
}
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo
{
/// <summary>
/// 此节点仅演示UI自定义以及控件 并不包含功能
/// </summary>
[STNode("/", "DebugST", "2212233137@qq.com", "st233.com", "此节点仅演示UI自定义以及控件,并不包含功能.")]
public class CalcNode : STNode
{
private StringFormat m_f;
protected override void OnCreate() {
base.OnCreate();
m_sf = new StringFormat();
m_sf.LineAlignment = StringAlignment.Center;
this.Title = "Calculator";
this.AutoSize = false; //注意需要先设置AutoSize=false 才能够进行大小设置
this.Size = new Size(218, 308);
var ctrl = new STNodeControl();
ctrl.Text = ""; //此控件为显示屏幕
ctrl.Location = new Point(13, 31);
ctrl.Size = new Size(190, 50);
this.Controls.Add(ctrl);
ctrl.Paint += (s, e) => {
m_sf.Alignment = StringAlignment.Far;
STNodeControl c = s as STNodeControl;
Graphics g = e.DrawingTools.Graphics;
g.DrawString("0", ctrl.Font, Brushes.White, c.ClientRectangle, m_sf);
};
string[] strs = { //按钮文本
"MC", "MR", "MS", "M+",
"M-", "←", "CE", "C", "+", "√",
"7", "8", "9", "/", "%",
"4", "5", "6", "*", "1/x",
"1", "2", "3", "-", "=",
"0", " ", ".", "+" };
Point p = new Point(13, 86);
for (int i = 0; i < strs.Length; i++) {
if (strs[i] == " ") continue;
ctrl = new STNodeControl();
ctrl.Text = strs[i];
ctrl.Size = new Size(34, 27);
ctrl.Left = 13 + (i % 5) * 39;
ctrl.Top = 86 + (i / 5) * 32;
if (ctrl.Text == "=") ctrl.Height = 59;
if (ctrl.Text == "0") ctrl.Width = 73;
this.Controls.Add(ctrl);
if (i == 8) ctrl.Paint += (s, e) => {
m_sf.Alignment = StringAlignment.Center;
STNodeControl c = s as STNodeControl;
Graphics g = e.DrawingTools.Graphics;
g.DrawString("_", ctrl.Font, Brushes.White, c.ClientRectangle, m_sf);
};
ctrl.MouseClick += (s, e) => System.Windows.Forms.MessageBox.Show(((STNodeControl)s).Text);
}
this.OutputOptions.Add("Result", typeof(int), false);
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
namespace WinNodeEditorDemo
{
[STNode("/")]
public class EmptyOptionTestNode : STNode
{
protected override void OnCreate() {
base.OnCreate();
this.Title = "EmptyTest";
this.InputOptions.Add(STNodeOption.Empty);
this.InputOptions.Add("string", typeof(string), false);
}
}
}

173
WinNodeEditorDemo/Form1.Designer.cs generated Normal file
View File

@ -0,0 +1,173 @@
namespace WinNodeEditorDemo
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
this.btn_open = new System.Windows.Forms.Button();
this.btn_save = new System.Windows.Forms.Button();
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
this.removeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.lockLocationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.lockConnectionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.stNodePropertyGrid1 = new ST.Library.UI.NodeEditor.STNodePropertyGrid();
this.stNodeEditor1 = new ST.Library.UI.NodeEditor.STNodeEditor();
this.stNodeTreeView1 = new ST.Library.UI.NodeEditor.STNodeTreeView();
this.contextMenuStrip1.SuspendLayout();
this.SuspendLayout();
//
// btn_open
//
this.btn_open.Location = new System.Drawing.Point(12, 3);
this.btn_open.Name = "btn_open";
this.btn_open.Size = new System.Drawing.Size(75, 23);
this.btn_open.TabIndex = 3;
this.btn_open.Text = "&Open";
this.btn_open.UseVisualStyleBackColor = true;
this.btn_open.Click += new System.EventHandler(this.btn_open_Click);
//
// btn_save
//
this.btn_save.Location = new System.Drawing.Point(93, 3);
this.btn_save.Name = "btn_save";
this.btn_save.Size = new System.Drawing.Size(75, 23);
this.btn_save.TabIndex = 4;
this.btn_save.Text = "&Save";
this.btn_save.UseVisualStyleBackColor = true;
this.btn_save.Click += new System.EventHandler(this.btn_save_Click);
//
// contextMenuStrip1
//
this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.removeToolStripMenuItem,
this.lockLocationToolStripMenuItem,
this.lockConnectionToolStripMenuItem});
this.contextMenuStrip1.Name = "contextMenuStrip1";
this.contextMenuStrip1.Size = new System.Drawing.Size(164, 70);
//
// removeToolStripMenuItem
//
this.removeToolStripMenuItem.Name = "removeToolStripMenuItem";
this.removeToolStripMenuItem.Size = new System.Drawing.Size(163, 22);
this.removeToolStripMenuItem.Text = "&Remove";
this.removeToolStripMenuItem.Click += new System.EventHandler(this.removeToolStripMenuItem_Click);
//
// lockLocationToolStripMenuItem
//
this.lockLocationToolStripMenuItem.Name = "lockLocationToolStripMenuItem";
this.lockLocationToolStripMenuItem.Size = new System.Drawing.Size(163, 22);
this.lockLocationToolStripMenuItem.Text = "U/Lock &Location";
this.lockLocationToolStripMenuItem.Click += new System.EventHandler(this.lockLocationToolStripMenuItem_Click);
//
// lockConnectionToolStripMenuItem
//
this.lockConnectionToolStripMenuItem.Name = "lockConnectionToolStripMenuItem";
this.lockConnectionToolStripMenuItem.Size = new System.Drawing.Size(163, 22);
this.lockConnectionToolStripMenuItem.Text = "U/Lock &Connection";
this.lockConnectionToolStripMenuItem.Click += new System.EventHandler(this.lockConnectionToolStripMenuItem_Click);
//
// stNodePropertyGrid1
//
this.stNodePropertyGrid1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(35)))), ((int)(((byte)(35)))), ((int)(((byte)(35)))));
this.stNodePropertyGrid1.DescriptionColor = System.Drawing.Color.FromArgb(((int)(((byte)(200)))), ((int)(((byte)(184)))), ((int)(((byte)(134)))), ((int)(((byte)(11)))));
this.stNodePropertyGrid1.ErrorColor = System.Drawing.Color.FromArgb(((int)(((byte)(200)))), ((int)(((byte)(165)))), ((int)(((byte)(42)))), ((int)(((byte)(42)))));
this.stNodePropertyGrid1.ForeColor = System.Drawing.Color.White;
this.stNodePropertyGrid1.ItemHoverColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(125)))), ((int)(((byte)(125)))), ((int)(((byte)(125)))));
this.stNodePropertyGrid1.ItemValueBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(80)))), ((int)(((byte)(80)))), ((int)(((byte)(80)))));
this.stNodePropertyGrid1.Location = new System.Drawing.Point(12, 273);
this.stNodePropertyGrid1.MinimumSize = new System.Drawing.Size(120, 50);
this.stNodePropertyGrid1.Name = "stNodePropertyGrid1";
this.stNodePropertyGrid1.ShowTitle = true;
this.stNodePropertyGrid1.Size = new System.Drawing.Size(200, 228);
this.stNodePropertyGrid1.TabIndex = 2;
this.stNodePropertyGrid1.Text = "stNodePropertyGrid1";
this.stNodePropertyGrid1.TitleColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
//
// 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(218, 32);
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(432, 469);
this.stNodeEditor1.TabIndex = 1;
this.stNodeEditor1.Text = "stNodeEditor1";
//
// stNodeTreeView1
//
this.stNodeTreeView1.AllowDrop = true;
this.stNodeTreeView1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(35)))), ((int)(((byte)(35)))), ((int)(((byte)(35)))));
this.stNodeTreeView1.FolderCountColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255)))));
this.stNodeTreeView1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
this.stNodeTreeView1.ItemBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
this.stNodeTreeView1.ItemHoverColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(125)))), ((int)(((byte)(125)))), ((int)(((byte)(125)))));
this.stNodeTreeView1.Location = new System.Drawing.Point(12, 32);
this.stNodeTreeView1.MinimumSize = new System.Drawing.Size(100, 60);
this.stNodeTreeView1.Name = "stNodeTreeView1";
this.stNodeTreeView1.ShowFolderCount = true;
this.stNodeTreeView1.Size = new System.Drawing.Size(200, 235);
this.stNodeTreeView1.TabIndex = 0;
this.stNodeTreeView1.Text = "stNodeTreeView1";
this.stNodeTreeView1.TextBoxColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(30)))), ((int)(((byte)(30)))));
this.stNodeTreeView1.TitleColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(60)))), ((int)(((byte)(60)))));
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(662, 513);
this.Controls.Add(this.btn_save);
this.Controls.Add(this.btn_open);
this.Controls.Add(this.stNodePropertyGrid1);
this.Controls.Add(this.stNodeEditor1);
this.Controls.Add(this.stNodeTreeView1);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.contextMenuStrip1.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private ST.Library.UI.NodeEditor.STNodeTreeView stNodeTreeView1;
private ST.Library.UI.NodeEditor.STNodeEditor stNodeEditor1;
private ST.Library.UI.NodeEditor.STNodePropertyGrid stNodePropertyGrid1;
private System.Windows.Forms.Button btn_open;
private System.Windows.Forms.Button btn_save;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
private System.Windows.Forms.ToolStripMenuItem removeToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem lockLocationToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem lockConnectionToolStripMenuItem;
}
}

View File

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using ST.Library.UI.NodeEditor;
namespace WinNodeEditorDemo
{
public partial class Form1 : Form
{
public Form1() {
InitializeComponent();
this.StartPosition = FormStartPosition.CenterScreen;
}
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
stNodePropertyGrid1.Text = "Node_Property";
stNodeTreeView1.LoadAssembly(Application.ExecutablePath);
stNodeEditor1.LoadAssembly(Application.ExecutablePath);
stNodeEditor1.ActiveChanged += (s, ea) => stNodePropertyGrid1.SetNode(stNodeEditor1.ActiveNode);
//stNodeEditor1.SelectedChanged += (s, ea) => stNodePropertyGrid1.SetSTNode(stNodeEditor1.ActiveNode);
stNodeEditor1.OptionConnected += (s, ea) => stNodeEditor1.ShowAlert(ea.Status.ToString(), Color.White, ea.Status == ConnectionStatus.Connected ? Color.FromArgb(125, Color.Green) : Color.FromArgb(125, Color.Red));
stNodeEditor1.CanvasScaled += (s, ea) => stNodeEditor1.ShowAlert(stNodeEditor1.CanvasScale.ToString("F2"), Color.White, Color.FromArgb(125, Color.Yellow));
stNodeEditor1.NodeAdded += (s, ea) => ea.Node.ContextMenuStrip = contextMenuStrip1;
stNodePropertyGrid1.SetInfoKey("Author", "Mail", "Link", "Show Help");
stNodeTreeView1.PropertyGrid.SetInfoKey("Author", "Mail", "Link", "Show Help");
stNodeEditor1.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom;
contextMenuStrip1.ShowImageMargin = false;
contextMenuStrip1.Renderer = new ToolStripRendererEx();
}
private void Form1_Load(object sender, EventArgs e) {
//int nLines = 0;
//foreach (var v in Directory.GetFiles("../../../", "*.cs", SearchOption.AllDirectories)) {
// nLines += File.ReadAllLines(v).Length;
//}
//MessageBox.Show(nLines.ToString());
//this.Resize += (s, ea) => this.Text = this.Size.ToString();
//this.BeginInvoke(new MethodInvoker(() => {
// //this.Size = new Size(488, 306);
// this.Size = new Size(488, 246);
// stNodeTreeView1.Visible = false;
// stNodePropertyGrid1.Top = stNodeEditor1.Top;
// stNodePropertyGrid1.Height = stNodeEditor1.Height;
// stNodeTreeView1.Height = stNodeEditor1.Height;
//}));
}
private void btn_open_Click(object sender, EventArgs e) {
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "*.stn|*.stn";
if (ofd.ShowDialog() != DialogResult.OK) return;
stNodeEditor1.Nodes.Clear();
stNodeEditor1.LoadCanvas(ofd.FileName);
}
private void btn_save_Click(object sender, EventArgs e) {
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "*.stn|*.stn";
if (sfd.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
stNodeEditor1.SaveCanvas(sfd.FileName);
}
private void lockConnectionToolStripMenuItem_Click(object sender, EventArgs e) {
stNodeEditor1.ActiveNode.LockOption = !stNodeEditor1.ActiveNode.LockOption;
}
private void lockLocationToolStripMenuItem_Click(object sender, EventArgs e) {
if (stNodeEditor1.ActiveNode == null) return;
stNodeEditor1.ActiveNode.LockLocation = !stNodeEditor1.ActiveNode.LockLocation;
}
private void removeToolStripMenuItem_Click(object sender, EventArgs e) {
if (stNodeEditor1.ActiveNode == null) return;
stNodeEditor1.Nodes.Remove(stNodeEditor1.ActiveNode);
}
}
}

View File

@ -112,9 +112,12 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="contextMenuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo.ImageNode
{
/// <summary>
/// 图片节点基类 用于确定节点风格 标题颜色 以及 数据类型颜色
/// </summary>
public abstract class ImageBaseNode : STNode
{
/// <summary>
/// 需要作为显示绘制的图片
/// </summary>
protected Image m_img_draw;
/// <summary>
/// 输出节点
/// </summary>
protected STNodeOption m_op_img_out;
protected override void OnCreate() {
base.OnCreate();
m_op_img_out = this.OutputOptions.Add("", typeof(Image), false);
this.AutoSize = false; //此节点需要定制UI 所以无需AutoSize
//this.Size = new Size(320,240);
this.Width = 160; //手动设置节点大小
this.Height = 120;
this.TitleColor = Color.FromArgb(200, Color.DarkCyan);
}
protected override void OnOwnerChanged() { //向编辑器提交数据类型颜色
base.OnOwnerChanged();
if (this.Owner == null) return;
this.Owner.SetTypeColor(typeof(Image), Color.DarkCyan);
}
}
}

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
using System.Drawing.Imaging;
namespace WinNodeEditorDemo.ImageNode
{
[STNode("/Image")]
public class ImageChannelNode : ImageBaseNode
{
private STNodeOption m_op_img_in; //输入的节点
private STNodeOption m_op_img_r; //R图 输出节点
private STNodeOption m_op_img_g; //G图 输出节点
private STNodeOption m_op_img_b; //B图 输出节点
protected override void OnCreate() {
base.OnCreate();
this.Title = "ImageChannel";
m_op_img_in = this.InputOptions.Add("", typeof(Image), true);
m_op_img_r = this.OutputOptions.Add("R", typeof(Image), false);
m_op_img_g = this.OutputOptions.Add("G", typeof(Image), false);
m_op_img_b = this.OutputOptions.Add("B", typeof(Image), false);
//当输入节点有数据输入时候
m_op_img_in.DataTransfer += new STNodeOptionEventHandler(m_op_img_in_DataTransfer);
}
void m_op_img_in_DataTransfer(object sender, STNodeOptionEventArgs e) {
//如果当前不是连接状态 或者 接受到的数据为空
if (e.Status != ConnectionStatus.Connected || e.TargetOption.Data == null) {
m_op_img_out.TransferData(null); //向所有输出节点输出空数据
m_op_img_r.TransferData(null);
m_op_img_g.TransferData(null);
m_op_img_b.TransferData(null);
m_img_draw = null; //需要绘制显示的图片置为空
} else {
Bitmap bmp = (Bitmap)e.TargetOption.Data; //否则计算图片的RGB图像
Bitmap bmp_r = new Bitmap(bmp.Width, bmp.Height);
Bitmap bmp_g = new Bitmap(bmp.Width, bmp.Height);
Bitmap bmp_b = new Bitmap(bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData bmpData_r = bmp_r.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
BitmapData bmpData_g = bmp_g.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
BitmapData bmpData_b = bmp_b.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
byte[] byColor = new byte[bmpData.Height * bmpData.Stride];
byte[] byColor_r = new byte[byColor.Length];
byte[] byColor_g = new byte[byColor.Length];
byte[] byColor_b = new byte[byColor.Length];
System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, byColor, 0, byColor.Length);
for (int y = 0; y < bmpData.Height; y++) {
int ny = y * bmpData.Stride;
for (int x = 0; x < bmpData.Width; x++) {
int nx = x << 2;
byColor_b[ny + nx] = byColor[ny + nx];
byColor_g[ny + nx + 1] = byColor[ny + nx + 1];
byColor_r[ny + nx + 2] = byColor[ny + nx + 2];
byColor_r[ny + nx + 3] = byColor_g[ny + nx + 3] = byColor_b[ny + nx + 3] = byColor[ny + nx + 3];
}
}
bmp.UnlockBits(bmpData);
System.Runtime.InteropServices.Marshal.Copy(byColor_r, 0, bmpData_r.Scan0, byColor_r.Length);
System.Runtime.InteropServices.Marshal.Copy(byColor_g, 0, bmpData_g.Scan0, byColor_g.Length);
System.Runtime.InteropServices.Marshal.Copy(byColor_b, 0, bmpData_b.Scan0, byColor_b.Length);
bmp_r.UnlockBits(bmpData_r);
bmp_g.UnlockBits(bmpData_g);
bmp_b.UnlockBits(bmpData_b);
m_op_img_out.TransferData(bmp); //out选项 输出原图
m_op_img_r.TransferData(bmp_r); //R选项输出R图
m_op_img_g.TransferData(bmp_g);
m_op_img_b.TransferData(bmp_b);
m_img_draw = bmp; //需要绘制显示的图片
}
}
protected override void OnDrawBody(DrawingTools dt) {
base.OnDrawBody(dt);
Graphics g = dt.Graphics;
Rectangle rect = new Rectangle(this.Left + 10, this.Top + 30, 120, 80);
g.FillRectangle(Brushes.Gray, rect);
if (m_img_draw != null) g.DrawImage(m_img_draw, rect);
}
}
}

View File

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;
namespace WinNodeEditorDemo.ImageNode
{
[STNode("Image", "Crystal_lz", "2212233137@qq.com", "st233.com", "Image Node")]
public class ImageInputNode : ImageBaseNode
{
private string _FileName;//默认的DescriptorType不支持文件路径的选择 所以需要扩展
[STNodeProperty("InputImage", "Click to select a image", DescriptorType = typeof(OpenFileDescriptor))]
public string FileName {
get { return _FileName; }
set {
Image img = null; //当文件名被设置时 加载图片并 向输出节点输出
if (!string.IsNullOrEmpty(value)) {
img = Image.FromFile(value);
}
if (m_img_draw != null) m_img_draw.Dispose();
m_img_draw = img;
_FileName = value;
m_op_img_out.TransferData(m_img_draw, true);
this.Invalidate();
}
}
protected override void OnCreate() {
base.OnCreate();
this.Title = "ImageInput";
}
protected override void OnDrawBody(DrawingTools dt) {
base.OnDrawBody(dt);
Graphics g = dt.Graphics;
Rectangle rect = new Rectangle(this.Left + 10, this.Top + 30, 140, 80);
g.FillRectangle(Brushes.Gray, rect);
if (m_img_draw != null) g.DrawImage(m_img_draw, rect);
}
}
/// <summary>
/// 对默认Descriptor进行扩展 使得支持文件路径选择
/// </summary>
public class OpenFileDescriptor : STNodePropertyDescriptor
{
private Rectangle m_rect_open; //需要绘制"打开"按钮的区域
private StringFormat m_sf;
public OpenFileDescriptor() {
m_sf = new StringFormat();
m_sf.Alignment = StringAlignment.Center;
m_sf.LineAlignment = StringAlignment.Center;
}
protected override void OnSetItemLocation() { //当在STNodePropertyGrid上确定此属性需要显示的区域时候
base.OnSetItemLocation(); //计算出"打开"按钮需要绘制的区域
m_rect_open = new Rectangle(
this.RectangleR.Right - 20,
this.RectangleR.Top,
20,
this.RectangleR.Height);
}
protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e) {
if (m_rect_open.Contains(e.Location)) { //点击在"打开"区域 则弹出文件选择框
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "*.jpg|*.jpg|*.png|*.png";
if (ofd.ShowDialog() != DialogResult.OK) return;
this.SetValue(ofd.FileName);
} else base.OnMouseClick(e); //否则默认处理方式 弹出文本输入框
}
protected override void OnDrawValueRectangle(DrawingTools dt) {
base.OnDrawValueRectangle(dt); //在STNodePropertyGrid绘制此属性区域时候将"打开"按钮绘制上去
dt.Graphics.FillRectangle(Brushes.Gray, m_rect_open);
dt.Graphics.DrawString("+", this.Control.Font, Brushes.White, m_rect_open, m_sf);
}
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo.NumberNode
{
[STNode("/Number/", "Crystal_lz", "2212233137@qq.com", "www.st233.com", "This node can get two numbers add result")]
public class NumberAddNode : NumberNode
{
private STNodeOption m_in_num1;
private STNodeOption m_in_num2;
private STNodeOption m_out_num;
private int m_nNum1, m_nNum2;
private StringFormat m_sf;
protected override void OnCreate() {
base.OnCreate();
this.Title = "NumberAdd";
m_sf = new StringFormat();
m_sf.LineAlignment = StringAlignment.Center;
m_in_num1 = new STNodeOption("", typeof(int), true);//只能有一个连线
m_in_num2 = new STNodeOption("", typeof(int), true);//只能有一个连线
m_out_num = new STNodeOption("", typeof(int), false);//可以多个连线
this.InputOptions.Add(m_in_num1);
this.InputOptions.Add(m_in_num2);
this.OutputOptions.Add(m_out_num);
m_in_num1.DataTransfer += new STNodeOptionEventHandler(m_in_num_DataTransfer);
m_in_num2.DataTransfer += new STNodeOptionEventHandler(m_in_num_DataTransfer);
}
//当有数据传入时
void m_in_num_DataTransfer(object sender, STNodeOptionEventArgs e) {
//判断连线是否是连接状态(建立连线 断开连线 都会触发该事件)
if (e.Status == ConnectionStatus.Connected) {
if (sender == m_in_num1) {
if (e.TargetOption.Data != null) m_nNum1 = (int)e.TargetOption.Data;//TargetOption为触发此事件的Option
} else {
if (e.TargetOption.Data != null) m_nNum2 = (int)e.TargetOption.Data;
}
} else {
if (sender == m_in_num1) m_nNum1 = 0; else m_nNum2 = 0;
}
//向输出选项上的所有连线传输数据 输出选项上的所有连线都会触发 DataTransfer 事件
m_out_num.TransferData(m_nNum1 + m_nNum2); //m_out_num.Data 将被自动设置
this.Invalidate();
}
/// <summary>
/// 当绘制选项文本时候 将数字绘制 因为STNodeOption.Text被protected修饰 STNode无法进行设置
/// 因为作者并不建议对已经添加在STNode上的选项进行修改 尤其是在AutoSize被设置的情况下
/// 若有需求 应当采用其他方式 比如:重绘 或者添加STNodeControl来显示变化的文本信息
/// </summary>
/// <param name="dt">绘制工具</param>
/// <param name="op">需要绘制的选项</param>
protected override void OnDrawOptionText(DrawingTools dt, STNodeOption op) {
base.OnDrawOptionText(dt, op);
string strText = "";
if (op == m_in_num1) {
m_sf.Alignment = StringAlignment.Near;
strText = m_nNum1.ToString();
} else if (op == m_in_num2) {
m_sf.Alignment = StringAlignment.Near;
strText = m_nNum2.ToString();
} else {
m_sf.Alignment = StringAlignment.Far;
strText = (m_nNum1 + m_nNum2).ToString();
}
dt.Graphics.DrawString(strText, this.Font, Brushes.White, op.TextRectangle, m_sf);
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo.NumberNode
{
/// <summary>
/// 此节点通过Number属性提供一个整数的输入
/// </summary>
[STNode("/Number","Crystal_lz","2212233137@qq.com","st233.com","Number input node")]
public class NumberInputNode : NumberNode
{
private int _Number;
[STNodeProperty("Input","this is input number")]
public int Number {
get { return _Number; }
set {
_Number = value;
m_op_number.TransferData(value); //将数据向下传递
this.Invalidate();
}
}
private STNodeOption m_op_number; //输出选项
private StringFormat m_sf = new StringFormat();
protected override void OnCreate() {
base.OnCreate();
this.Title = "NumberInput";
m_op_number = new STNodeOption("", typeof(int), false);
this.OutputOptions.Add(m_op_number);
m_sf = new StringFormat();
m_sf.LineAlignment = StringAlignment.Center;
m_sf.Alignment = StringAlignment.Far;
}
/// <summary>
/// 当绘制选项文本时候 将数字绘制 因为STNodeOption.Text被protected修饰 STNode无法进行设置
/// 因为作者并不建议对已经添加在STNode上的选项进行修改 尤其是在AutoSize被设置的情况下
/// 若有需求 应当采用其他方式 比如:重绘 或者添加STNodeControl来显示变化的文本信息
/// </summary>
/// <param name="dt">绘制工具</param>
/// <param name="op">需要绘制的选项</param>
protected override void OnDrawOptionText(DrawingTools dt, STNodeOption op) {
base.OnDrawOptionText(dt, op);
dt.Graphics.DrawString(this._Number.ToString(), this.Font, Brushes.White, op.TextRectangle, m_sf);
}
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo.NumberNode
{
/// <summary>
/// Number节点基类 用于确定节点风格 标题颜色 以及 数据类型颜色
/// </summary>
public abstract class NumberNode : STNode
{
protected override void OnCreate() {
base.OnCreate();
this.TitleColor = Color.FromArgb(200, Color.CornflowerBlue);
}
protected override void OnOwnerChanged() {
base.OnOwnerChanged();
if (this.Owner != null) this.Owner.SetTypeColor(typeof(int), Color.CornflowerBlue);
}
}
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace ST.Library.UI
namespace WinNodeEditorDemo
{
static class Program
{
@ -14,7 +14,6 @@ namespace ST.Library.UI
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
new Demo_Image.FrmImage().Show();
Application.Run(new Form1());
}
}

View File

@ -5,12 +5,12 @@ using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WinNodeEditerTest")]
[assembly: AssemblyTitle("WinNodeEditorDemo")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("WinNodeEditerTest")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2020")]
[assembly: AssemblyProduct("WinNodeEditorDemo")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@ -20,7 +20,7 @@ using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("2c0c84a0-fb66-4133-80f5-053356fa0f04")]
[assembly: Guid("04b761ae-c417-4bc8-807c-c009959e188c")]
// Version information for an assembly consists of the following four values:
//

View File

@ -8,8 +8,8 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace ST.Library.UI.Properties {
using System;
namespace WinNodeEditorDemo.Properties
{
/// <summary>
@ -22,7 +22,8 @@ namespace ST.Library.UI.Properties {
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
@ -38,8 +39,8 @@ namespace ST.Library.UI.Properties {
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ST.Library.UI.Properties.Resources", typeof(Resources).Assembly);
if ((resourceMan == null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WinNodeEditorDemo.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;

View File

@ -8,12 +8,14 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace ST.Library.UI.Properties {
namespace WinNodeEditorDemo.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
namespace WinNodeEditorDemo
{
/// <summary>
/// 类库自带的STNodeHub并未被STNodeAttribute标记 无法被STNodeTreeView显示 所以需要扩展
/// </summary>
[STNode("/", "Crystal_lz", "2212233137@qq.com", "st233.com", "This is single Hub")]
public class STNodeHubSingle : STNodeHub
{
public STNodeHubSingle()
: base(true) {
this.Title = "S_HUB";
}
}
/// <summary>
/// 类库自带的STNodeHub并未被STNodeAttribute标记 无法被STNodeTreeView显示 所以需要扩展
/// </summary>
[STNode("/", "Crystal_lz", "2212233137@qq.com", "st233.com", "This multi is Hub")]
public class STNodeHubMulti : STNodeHub
{
public STNodeHubMulti()
: base(false) {
this.Title = "M_HUB";
}
}
}

View File

@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace WinNodeEditorDemo
{
public class ToolStripRendererEx : ToolStripRenderer
{
private SolidBrush m_brush = new SolidBrush(Color.FromArgb(255, 52, 86, 141));
private StringFormat m_sf = new StringFormat();
public ToolStripRendererEx() {
m_sf.LineAlignment = StringAlignment.Center;
}
protected override void InitializeItem(ToolStripItem item) {
base.InitializeItem(item);
item.AutoSize = false;
item.Size = new Size(item.Width, 30);
}
protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e) {
using (SolidBrush sb = new SolidBrush(Color.FromArgb(34, 34, 34))) {
e.Graphics.FillRectangle(sb, e.AffectedBounds);
}
base.OnRenderToolStripBackground(e);
}
protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) {
e.Graphics.DrawRectangle(Pens.Black, e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width - 1, e.AffectedBounds.Height - 1);
base.OnRenderToolStripBorder(e);
}
//protected override void OnRenderImageMargin(ToolStripRenderEventArgs e) {
// using (SolidBrush sb = new SolidBrush(Color.FromArgb(50, 50, 50))) {
// e.Graphics.FillRectangle(sb, e.AffectedBounds);
// }
// base.OnRenderImageMargin(e);
//}
protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) {
e.TextColor = e.Item.Selected ? Color.White : Color.LightGray;
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
e.TextRectangle = new Rectangle(e.TextRectangle.Left, e.TextRectangle.Top, e.TextRectangle.Width, 30);
base.OnRenderItemText(e);
}
protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e) {
e.ArrowColor = e.Item.Selected ? Color.White : Color.LightGray;
base.OnRenderArrow(e);
}
protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e) {
Point ptEnd = new Point(e.Item.ContentRectangle.X + e.Item.Width / 2, e.Item.ContentRectangle.Y);
using (LinearGradientBrush lgb = new LinearGradientBrush(e.Item.ContentRectangle.Location, ptEnd, Color.Transparent, Color.Gray)) {
lgb.WrapMode = WrapMode.TileFlipX;
using (Pen p = new Pen(lgb)) {
e.Graphics.DrawLine(p, e.Item.ContentRectangle.Location, new Point(e.Item.ContentRectangle.Right, ptEnd.Y));
}
}
//e.Graphics.DrawLine(Pens.Gray, e.Item.ContentRectangle.Location, new Point(e.Item.ContentRectangle.Right, ptEnd.Y));
base.OnRenderSeparator(e);
}
protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) {
if (e.Item.Selected)
e.Graphics.FillRectangle(m_brush, e.Item.ContentRectangle);
else
base.OnRenderMenuItemBackground(e);
}
//protected override void OnRenderItemImage(ToolStripItemImageRenderEventArgs e) {
// //base.OnRenderItemImage(e);
// e.Graphics.DrawImage(e.Image, e.ImageRectangle.X, e.ImageRectangle.Y, 17, 17);
//}
}
}

View File

@ -5,17 +5,16 @@
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{8B0A4516-45C9-4DE9-B947-64E8DA8A72A6}</ProjectGuid>
<OutputType>Exe</OutputType>
<ProjectGuid>{4E1829B5-2160-48F5-ABD6-11914A6A25DD}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ST.Library.UI</RootNamespace>
<AssemblyName>WinNodeEditerTest</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<RootNamespace>WinNodeEditorDemo</RootNamespace>
<AssemblyName>WinNodeEditorDemo</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
@ -33,9 +32,6 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
@ -48,40 +44,36 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="DemoNode.cs" />
<Compile Include="Demo_Image\FrmImage.cs">
<Compile Include="AttrTestNode.cs" />
<Compile Include="Blender\BlenderMixColorNode.cs" />
<Compile Include="Blender\FrmEnumSelect.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Demo_Image\FrmImage.Designer.cs">
<DependentUpon>FrmImage.cs</DependentUpon>
</Compile>
<Compile Include="Demo_Image\STNodeImage.cs" />
<Compile Include="Demo_Image\STNodeImageChannel.cs" />
<Compile Include="Demo_Image\STNodeImageInput.cs" />
<Compile Include="Blender\STNodeCheckBox.cs" />
<Compile Include="Blender\STNodeColorButton.cs" />
<Compile Include="Blender\STNodeProgress.cs" />
<Compile Include="Blender\STNodeSelectBox.cs" />
<Compile Include="CalcNode.cs" />
<Compile Include="Form1.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Form1.Designer.cs">
<DependentUpon>Form1.cs</DependentUpon>
</Compile>
<Compile Include="Form2.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Form2.Designer.cs">
<DependentUpon>Form2.cs</DependentUpon>
</Compile>
<Compile Include="NodeNumberAdd.cs" />
<Compile Include="ImageNode\ImageBaseNode.cs" />
<Compile Include="ImageNode\ImageChannelNode.cs" />
<Compile Include="ImageNode\ImageInputNode.cs" />
<Compile Include="EmptyOptionTestNode.cs" />
<Compile Include="NumberNode\NumberAddNode.cs" />
<Compile Include="NumberNode\NumberInputNode.cs" />
<Compile Include="NumberNode\NumberNode.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Demo_Image\FrmImage.resx">
<DependentUpon>FrmImage.cs</DependentUpon>
</EmbeddedResource>
<Compile Include="STNodeHub.cs" />
<Compile Include="ToolStripRendererEx.cs" />
<EmbeddedResource Include="Form1.resx">
<DependentUpon>Form1.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Form2.resx">
<DependentUpon>Form2.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
@ -90,9 +82,7 @@
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<None Include="app.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
@ -109,6 +99,7 @@
<Name>ST.Library.UI</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

BIN
WinNodeEditorTest.7z Normal file

Binary file not shown.

24
WinNodeEditerTest.sln → WinNodeEditorTest.sln Executable file → Normal file
View File

@ -1,10 +1,10 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinNodeEditerTest", "WinNodeEditerTest\WinNodeEditerTest.csproj", "{8B0A4516-45C9-4DE9-B947-64E8DA8A72A6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ST.Library.UI", "ST.Library.UI\ST.Library.UI.csproj", "{EFFCC270-4999-4077-A543-56CCCCE92147}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinNodeEditorDemo", "WinNodeEditorDemo\WinNodeEditorDemo.csproj", "{4E1829B5-2160-48F5-ABD6-11914A6A25DD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -15,16 +15,6 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8B0A4516-45C9-4DE9-B947-64E8DA8A72A6}.Debug|Any CPU.ActiveCfg = Debug|x86
{8B0A4516-45C9-4DE9-B947-64E8DA8A72A6}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{8B0A4516-45C9-4DE9-B947-64E8DA8A72A6}.Debug|Mixed Platforms.Build.0 = Debug|x86
{8B0A4516-45C9-4DE9-B947-64E8DA8A72A6}.Debug|x86.ActiveCfg = Debug|x86
{8B0A4516-45C9-4DE9-B947-64E8DA8A72A6}.Debug|x86.Build.0 = Debug|x86
{8B0A4516-45C9-4DE9-B947-64E8DA8A72A6}.Release|Any CPU.ActiveCfg = Release|x86
{8B0A4516-45C9-4DE9-B947-64E8DA8A72A6}.Release|Mixed Platforms.ActiveCfg = Release|x86
{8B0A4516-45C9-4DE9-B947-64E8DA8A72A6}.Release|Mixed Platforms.Build.0 = Release|x86
{8B0A4516-45C9-4DE9-B947-64E8DA8A72A6}.Release|x86.ActiveCfg = Release|x86
{8B0A4516-45C9-4DE9-B947-64E8DA8A72A6}.Release|x86.Build.0 = Release|x86
{EFFCC270-4999-4077-A543-56CCCCE92147}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EFFCC270-4999-4077-A543-56CCCCE92147}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EFFCC270-4999-4077-A543-56CCCCE92147}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@ -35,6 +25,16 @@ Global
{EFFCC270-4999-4077-A543-56CCCCE92147}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{EFFCC270-4999-4077-A543-56CCCCE92147}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{EFFCC270-4999-4077-A543-56CCCCE92147}.Release|x86.ActiveCfg = Release|Any CPU
{4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Debug|Any CPU.ActiveCfg = Debug|x86
{4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Debug|Mixed Platforms.Build.0 = Debug|x86
{4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Debug|x86.ActiveCfg = Debug|x86
{4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Debug|x86.Build.0 = Debug|x86
{4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Release|Any CPU.ActiveCfg = Release|x86
{4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Release|Mixed Platforms.ActiveCfg = Release|x86
{4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Release|Mixed Platforms.Build.0 = Release|x86
{4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Release|x86.ActiveCfg = Release|x86
{4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

BIN
WinNodeEditorTest.suo Normal file

Binary file not shown.

Binary file not shown.

1681
docs/api.html Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,185 +0,0 @@
@charset "utf-8";
*{
color:rgba(255,255,255,.7);
font-family:"consolas","Menlo","DejaVu Sans Mono","monaco","MonoSpace","courier new","微软雅黑","Microsoft Yahei";
font-weight:300;
background-repeat:no-repeat;
/*text-shadow:0px 1px 1px #DDD;*/
-webkit-font-smoothing:antialiased;
}
::selection { background-color: #5B6B8545; }
::-moz-selection { background-color: #5B6B8545; }
::-webkit-selection { background-color: #5B6B8545; }
hr{
height: 1px;
border: none;
margin: 10px;
border-top: solid 1px #2b2b2b;
}
body{
margin:0px;
position:relative;
font-size:14px;
background-color:#343434;
}
h2{
margin:0px 10px;
padding-left:10px;
border-left:solid 5px #0096C9;
border-top:solid 1px #2b2b2b;
border-bottom:solid 1px #2b2b2b;
font-weight:bold;
background-color:#505050;
height:38px;
line-height:38px;
}
p{margin:10px 25px;}
a{
color:#58b4e8;
cursor:pointer;
outline:none;
text-decoration:none;
}
a:hover{
color:#58b4e8;
outline-style:none;
}
table{
margin:20px;
color: rgba(255,255,255,.7);
font-size: 12px;
border-spacing: 0px;
border-top: solid 1px #1f1f1f;
border-left: solid 1px #1f1f1f;
background-color: #2b2b2b;
}
td{
padding: 5px 20px;
border-right: solid 1px #1f1f1f;
border-bottom: solid 1px #1f1f1f;
}
pre{
background-color: #1a1a1a;
font-size: 12px;
overflow: auto;
padding:10px;
display:inline-block;
margin-top:0px;
min-width:680px;
}
.span_center{
display: inline-block;
background-color: #0096c9;
border-radius: 5px;
padding: 1px 5px;
color: white;
font-size: 12px;
}
.span_title{
margin-left:10px;
display: inline-block;
background-color: dodgerblue;
padding: 2px 10px;
/* border-radius: 5px; */
font-size: 12px;
position: relative;
height: 20pxpx;
line-height: 20px;
}
.span_title:after{
content: ' ';
position: absolute;
border-left: solid 13px #2090ff;
border-top: solid 12px transparent;
border-bottom: solid 12px transparent;
right: -13px;
top: 0px;
}
.span_property{
color:white;
font-size: 12px;
border-radius: 2px;
background-color: gray;
padding: 1px 4px;
margin:0px 4px;
}
#div_body{
}
#div_left{
overflow:auto;
height:100%;
width:220px;
position:fixed;
background-color:#343434;
border-right:solid 1px black;
}
.ul_group_root a{
color:gray;
font-weight:300px;
height:30px;
line-height:30px;
display:block;
padding:5px 15px;
transition:background-color .5s;
}
.ul_group_root a:hover{
color:white;
background-color:deepskyblue;
transition:background-color 0s;
}
.ul_group_root,.ul_group_root ul{
margin:0px;
padding:0px;
list-style:none;
font-size:12px;
background-color:#1A1A1A;
}
.a_node_root{
color:gray;
font-size:14px;
padding:5px;
display:block;
background-color:#343434;
/*border-top:1px solid #1A1A1A;
border-bottom:1px solid #1A1A1A;
background-color:rgba(255,255,255,0.05);*/
}
.anchor_btn{
transition:background-color 1s;
}
.anchor_btn.active{
color:white;
background-color:#0076A0;
transition:background-color 0s;
}
.li_node_sub{
text-align:right;
}
#div_right{
margin-left:220px;
}
#div_right a{
color: white;
margin: 2px;
padding: 1px 2px;
border-radius: 2px;
display: inline-block;
font-size:12px;
background-color: rgba(0,150,201,.2);
border:solid 1px #0096C9;
}
#div_right ul{
list-style:none;
margin:5px;
padding:0px 10px;
}
#div_img_node{
text-align:center;
margin:0px 10px;
background-color:black;
}
#div_img_node img{
width:50%;
max-width:537px;
}

365
docs/css/index.css Executable file → Normal file
View File

@ -3,91 +3,336 @@
font-family:"consolas","Menlo","DejaVu Sans Mono","monaco","MonoSpace","courier new","微软雅黑","Microsoft Yahei";
font-weight:300;
background-repeat:no-repeat;
text-shadow:0px 1px 1px #DDD;
-webkit-font-smoothing:antialiased;
}
::selection { background-color: #5B6B8545; }
::-moz-selection { background-color: #5B6B8545; }
::-webkit-selection { background-color: #5B6B8545; }
h1{
font-size:2.75rem;
}
a{
cursor:pointer;
outline:none;
text-decoration:none;
}
p{line-height:25px;}
img{max-width:100%;}
ul{margin:0px;padding-left:20px;}
li{margin:5px;}
hr{
height: 1px;
border: none;
margin: 30px 0px;
border-top: solid 1px lightgray;
a:hover{
outline-style:none;
}
img{
vertical-align: bottom;
}
span{
display:inline-block;
}
body{
margin:0px;
}
#h1_title{
margin:0px;
position:relative;
font-size:14px;
}
#div_body{
max-width:1074px;
margin: 0 auto;
box-shadow: 0px 0px 20px #222;
}
#div_img_node{
text-align:center;
font-size:0px;
overflow:hidden;
background-color:cornflowerblue;
}
#div_img_node img{
width:50%;
max-width:537px;
box-shadow: 0px 0px 20px #222;
}
.span_title{
font-size:40px;
display:inline-block;
margin:20px;
text-align:center;
position:relative;
}
.span_title:before{
#h1_title:before{
content: 'Copyright© DebugST@Crystal_lz';
position: absolute;
left: 50%;
top: 100%;
font-size: 11px;
color: gray;
width:100%;
transform: translate(-50%, 0px);
font-size: 0.75rem;
color: white;
width: 100%;
text-align:center;
}
.span_title:after{
content: '(V 1.0)';
font-size: 10px;
#h1_title:after{
content: 'V 2.0';
font-size: 0.75rem;
position: absolute;
background-color: hotpink;
border-radius: 5px;
padding: 2px;
border-radius: 3px;
padding: 2px 5px;
color: white;
display: block;
top: 0px;
right: 0px;
transform: translate(50%, -50%);
}
.a_top_btn{
color:white;
padding: 5px 20px;
font-size: 16px;
border-radius: 15px;
background-color: cornflowerblue;
#div_top{
background-color:rgba(20,20,20,1);
background-image:url('../images/top_bg.jpeg');
background-size:cover;
text-shadow: 0px 1px 1px black;
}
#div_top_left{
padding: 30px;
display: inline-block;
left: 0px;
top: 0px;
}
#div_top_right{
text-align:center;
color: white;
display: inline-block;
top: 0px;
right: 0px;
}
#div_top_left img{
width:100%;
}
#a_btn_down{
color: white;
background-color: rgba(255,255,255,.3);
padding: 0px 40px;
display: inline-block;
text-align: center;
border-radius: 5px;
margin-bottom:30px;
height:40px;
line-height:40px;
position:relative;
}
#a_btn_down:hover{
background-color:coral;
}
#a_btn_down:before{
content:' ';
display:block;
width:40px;
height:40px;
background-image:url('../images/download.png');
background-size:cover;
position:absolute;
left:0px;
top:0px;
}
#a_btn_down:after{
content:'MIT LICENSE';
font-size:0.75rem;
height:1rem;
line-height:1rem;
display:block;
}
.a_icon{
width: 40px;
height: 40px;
display: inline-block;
background-color: rgba(255,255,255,.3);
line-height: 40px;
text-align: center;
border-radius: 20px;
background-size: contain;
margin:0px 10px;
}
.quote_symbol{
color:gray;
position:relative;
display:inline-block;
}
.span_note{
color:cornflowerblue;
margin: 2px;
padding: 2px 5px;
border-radius: 5px;
background-color: #EEE;
border:solid 1px #DDD;
.quote_symbol:before{
content: "“";
font-size: 3rem;
color: lightgray;
position: absolute;
transform: translate(-100%, -50%);
left: 0px;
top: 0px;
}
.quote_symbol:after{
content: "”";
font-size: 3rem;
color: lightgray;
position: absolute;
top: 100%;
left:100%;
}
.span_mark{
background-color: gold;
padding: 5px 10px;
border-radius: 5px;
font-size:0.8rem;
margin:10px 10px 0px 10px;
}
.span_key{
background-color: lavender;
color: cornflowerblue;
padding: 0px 5px;
margin: 0px 2px;
border-radius: 5px;
}
/*.div_content_left{
position:absolute;
width:50%;
height:500px;
text-align:right;
display:inline-block;
}
.div_content_body{
height:500px;
}*/
.div_content_img img{
max-width:100%;
}
/*.div_content_img{
line-height:500px;
text-align:center;
}
.div_content_img img{
max-width:100%;
position:absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);
}
.div_content_right{
position:absolute;
left:50%;
height:500px;
display:inline-block;
width:50%;
}
.div_content_text{
margin:0px 50px 0px 30px;
position: absolute;
top: 50%;
transform: translate(0px, -50%);
}*/
.div_content_text h1{
margin-top:1rem;
}
.question_symbol:before{
content: '?';
font-size: 5rem;
position: absolute;
bottom: 0px;
left: 100%;
transform: rotate(45deg);
color: lightgray;
transform-origin:50% 100%;
}
.question_symbol:after{
content: '?';
font-size: 5rem;
position: absolute;
bottom: 0px;
left: 100%;
transform: rotate(90deg);
color: lightgray;
transform-origin:50% 100%;
}
#span_sticker:before{
content: ' ';
display: block;
width: 100px;
height: 100%;
position: absolute;
left: -100px;
background-image: url(../images/sticker.png);
background-size: contain;
background-repeat: no-repeat;
background-position: right;
top:0px;
}
#p_mengbi:after{
content:attr(attr_text);
position: absolute;
font-size: 0.8rem;
width: 100%;
left: 0px;
top: 100%;
color: gray;
}
#div_img_pannel{
position: relative;
width: 926px;
max-width: 100%;
display: inline-block;
}
#div_img_pannel img{
max-width:100%;
}
#a_fork_me{
display: block;
width: 300px;
background-color: green;
position: absolute;
height: 26px;
line-height: 26px;
box-shadow: 0px 0px 3px black;
text-shadow: 0px 0px 3px black;
color: white;
top: 60px;
right: -80px;
transform: rotate(45deg);
}
#a_fork_me:after{
content: ' ';
display: block;
position: absolute;
width: 100%;
top: 2px;
bottom: 2px;
border-top: dashed 1px white;
border-bottom: dashed 1px white;
}
@media screen and (min-width:1080px){
#div_top_left{
margin-right: 400px;
}
#div_top_right{
width:370px;
padding: 30px 30px 0px 0px;
position: absolute;
top: 0px;
right: 0px;
}
.div_content_body{
height:500px;
}
.div_content_left{
position:absolute;
width:50%;
height:500px;
text-align:right;
display:inline-block;
}
.div_content_img{
line-height:500px;
text-align:center;
}
.div_content_img img{
max-width:100%;
position:absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);
}
.div_content_right{
position:absolute;
left:50%;
height:500px;
display:inline-block;
width:50%;
}
.div_content_text{
margin:0px 50px 0px 30px;
position: absolute;
top: 50%;
transform: translate(0px, -50%);
}
}
@media screen and (max-width:1080px){
#div_top_right{
padding: 10px;
}
.div_content_body {
padding: 10px 0px;
overflow:hidden;
}
.div_content_text{
padding:10px;
}
.div_content_img{
text-align:center;
font-size:0px;
}
.div_content_text h1{
font-size:2rem;
display:block;
text-align:center;
}
}

270
docs/css/stdoc.css Normal file
View File

@ -0,0 +1,270 @@
@charset "utf-8";
*{
color:rgba(255,255,255,.7);
font-family:"consolas","Menlo","DejaVu Sans Mono","monaco","MonoSpace","courier new","微软雅黑","Microsoft Yahei";
font-weight:300;
background-repeat:no-repeat;
-webkit-font-smoothing:antialiased;
-webkit-text-size-adjust: none;
}
::selection { background-color: rgba(0,255,255,.2); }
::-moz-selection { background-color: rgba(0,255,255,.2); }
::-webkit-selection { background-color: rgba(0,255,255,.2); }
::-webkit-scrollbar { display: none; }
img{
display:block;
max-width:100%;
}
hr{
height: 1px;
border: none;
border-top: solid 1px #2b2b2b;
}
body{
margin:0px;
position:relative;
font-size:14px;
background-color:#343434;
}
a{
color:#58b4e8;
cursor:pointer;
outline:none;
text-decoration:none;
}
a:hover{
color:#58b4e8;
outline-style:none;
}
table{
margin:10px 0px;
color: rgba(255,255,255,.7);
font-size: 12px;
border-spacing: 0px;
border-top: solid 1px #1f1f1f;
border-left: solid 1px #1f1f1f;
background-color: #2b2b2b;
min-width:500px;
}
th{
padding: 5px 20px;
border-right: solid 1px #1f1f1f;
border-bottom: solid 1px #1f1f1f;
background-color:#505050;
}
.tr_hight{background-color:rgba(255,255,255,.04);}
td{
padding: 5px 20px;
border-right: solid 1px #1f1f1f;
border-bottom: solid 1px #1f1f1f;
}
#div_body{
}
#div_left{
overflow:auto;
height:100%;
width:250px;
position:fixed;
background-color:#343434;
padding-right:10px;
z-index:1;
}
#div_left_list{border-right:solid 1px black;}
#a_btn_left{
display:block;
}
.ul_group_root a{
color:gray;
font-weight:300px;
height:30px;
line-height:30px;
display:block;
padding:5px 15px;
transition:background-color .5s;
}
.ul_group_root a:hover{
color:white;
background-color:deepskyblue;
transition:background-color 0s;
}
.ul_group_root,.ul_group_root ul{
margin:0px;
padding:0px;
list-style:none;
font-size:12px;
background-color:#1A1A1A;
}
.a_node_root{
color:gray;
font-size:14px;
padding:5px;
display:block;
background-color:#343434;
}
.anchor_btn{
transition:background-color 1s;
}
.anchor_btn.active{
color:white;
background-color:#0076A0;
transition:background-color 0s;
}
.li_node_sub{
text-align:right;
}
#div_left:hover{
left:0px;
}
#div_right{
padding:0px 10px;
}
@media screen and (min-width:820px){
#div_right{
margin-left:250px;
}
}
@media screen and (max-width:820px){
#div_left{
left:-250px;
transition:left 1s;
}
#div_left:hover{border-right:solid 1px black;}
#div_left:before{
content: ' ';
position: fixed;
width: 10px;
left: 4px;
top: 0px;
height: 100%;
animation-name: light;
animation-duration: 2s;
animation-iteration-count: 4;
border-left:dashed 2px gray;
transition:left 1s;
}
@keyframes light{
from{opacity: 0;}
50%{opacity: 1;}
to{opacity: 0;}
}
#div_left:hover:before{
left:254px;
transition:left 1s;
}
.h_title{ font-size:1.4rem; }
}
#div_right a{
color: white;
margin: 2px;
padding: 1px 2px;
border-radius: 2px;
display: inline-block;
font-size:12px;
background-color: rgba(0,150,201,.2);
border:solid 1px #0096C9;
}
.h_title{
margin:0px 0px 10px 0px;
padding-left:10px;
border-left:solid 5px dodgerblue;
font-weight:bold;
background-color:#505050;
height:40px;
line-height:40px;
}
.h_option{
margin:0px;
display: inline-block;
background-color: dodgerblue;
padding: 2px 10px;
font-size: 12px;
position: relative;
height: 20pxpx;
line-height: 20px;
}
.h_option:after{
content: ' ';
position: absolute;
border-left: solid 13px #2090ff;
border-top: solid 12px transparent;
border-bottom: solid 12px transparent;
right: -13px;
top: 0px;
}
.span_mark{
color:white;
font-size: 12px;
border-radius: 2px;
background-color: gray;
padding: 1px 4px;
margin:0px 4px;
}
.p_hightlight{
color:hotpink;
}
.div_table{
overflow:auto;
}
.div_code{
overflow: auto;
counter-reset: code_line_num;
border: solid 1px black;
vertical-align: bottom;
font-size: 0px;
display:inline-block;
max-width:100%;
margin-bottom:10px;
}
.span_code_title{
display: inline-block;
background-color: black;
padding: 2px 5px;
margin-top: 10px;
}
.pre_code{
background-color: #1a1a1a;
font-size: 12px;
overflow: auto;
padding:10px 10px 10px 35px;
display:inline-block;
margin:0px;
min-width:680px;
position:relative;
}
.pre_code:before{
content: ' ';
background-color: gray;
width: 30px;
display: block;
position: absolute;
height: 100%;
left: 0px;
top: 0px;
background-color: rgb(43 43 43);
}
.span_code_line{
position:relative;
}
.span_code_line:before{
content: counter(code_line_num);
counter-increment: code_line_num;
position: absolute;
width: 25px;
text-align: right;
left: -35px;
height: 100%;
color:gray;
}
.code_note{ color:gray; }
.code_note_1{ color:bisque; }
.code_key{ color:cornflowerblue; }
.code_class{ color:#0de8e8; }
.code_string{ color:darkorange; }
.span_time{
display: block;
text-align: center;
padding: 2px;
font-size: 12px;
background-color: darkgoldenrod;
}

View File

@ -1,927 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>STNodeEditor Document</title>
<link rel="stylesheet" type="text/css" href="./css/doc.css"/>
<script type="text/javascript" src="./js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="./js/doc.js"></script>
<script>
$(document).ready(function(){
$(".anchor_btn").click(function(){
var nTop = $(".anchor_point[anchor='" + $(this).attr('anchor') + "']").offset().top;
if(!$(this).hasClass("a_node_root")) nTop -= 5;
$('html,body').animate({scrollTop:nTop},500);
});
$(window).scroll(function(){
var nMin = 100000,strName = '';
var nHeight = document.body.clientHeight;
var nHtmlTop = $(this).scrollTop();
var es = $('.anchor_point');
for(i = 0; i < es.length; i++){
var nSub = Math.abs(es[i].offsetTop - nHtmlTop);
if(nSub < nMin){
nMin = nSub;
if(nHtmlTop + (nHeight / 2) >= es[i].offsetTop)
strName = $(es[i]).attr('anchor');
}
}
//if(nMin > 100) return;
$(".anchor_btn").removeClass('active');
$(".anchor_btn[anchor='" + strName + "']").addClass('active');
});
});
</script>
</head>
<body>
<div id="div_body">
<div id="div_left">
<ul class="ul_group_root">
<li>
<a class="a_node_root anchor_btn active" anchor="a">概述</a>
<ul>
<li class="li_node_sub"><a class="anchor_btn" anchor="b">节点编辑器</a></li>
</ul>
</li>
<li>
<a class="a_node_root anchor_btn" anchor="c">STNodeEditor</a>
<ul>
<li class="li_node_sub"><a class="anchor_btn" anchor="d">属性</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="e">受保护字段</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="f">可重载函数</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="g">公开函数</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="h">事件</a></li>
</ul>
</li>
<li>
<a class="a_node_root anchor_btn" anchor="i">STNode</a>
<ul>
<li class="li_node_sub"><a class="anchor_btn" anchor="j">属性</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="k">受保护字段</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="l">可重载函数</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="m">其他重载函数</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="n">公开函数</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="o">关于重绘</a></li>
</ul>
</li>
<li>
<a class="a_node_root anchor_btn" anchor="p">STNodeOption</a>
<ul>
<li class="li_node_sub"><a class="anchor_btn" anchor="q">属性</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="r">受保护字段</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="s">构造器</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="t">可重载函数</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="u">公开函数</a></li>
<li class="li_node_sub"><a class="anchor_btn" anchor="v">事件</a></li>
</ul>
</li>
<li>
<a class="a_node_root anchor_btn" anchor="w">STNodeOptionEventArgs</a>
<ul>
<li class="li_node_sub"><a class="anchor_btn" anchor="x">属性</a></li>
</ul>
</li>
<li>
<a class="a_node_root anchor_btn" anchor="y">STNodeControl</a>
<ul>
<li class="li_node_sub"><a class="anchor_btn" anchor="z">Demo</a></li>
</ul>
</li>
</ul>
<span style="background-color:blueviolet;font-size:12px;text-align:center;width:100%;display:inline-block;">最后编辑时间 2021-01-21</span>
</div>
<div id="div_right">
<h2 class="anchor_point" anchor="a">概述</h2>
<p>那是一个冬季 在研究无线电安全的作者接触到了<a target="_bank" href="https://www.gnuradio.org/">GNURadio</a> 那是作者第一次接触到节点编辑器<br/>&nbsp;&nbsp;"What? Excuse me... What's this?.. 这是什么鬼东西?..."</p>
<p>那是一个春季 不知道为什么 过完年整个世界都变了 大家被迫窝在家里 无聊至极的作者学起了<a target="_bank" href="https://www.blender.org/">Blender</a> 那是作者第二次接触到节点编辑器<br/>&nbsp;&nbsp;"Wo...原来这东西可以这么玩...真方便"<br/>&nbsp;&nbsp;于是一些想法在作者脑中逐渐诞生 让作者有了想做一个这样的东西的想法</p>
<p>那是一个夏季 不知道为什么 作者又玩起了<a target="_bank" href="http://www.blackmagicdesign.com/cn/products/davinciresolve/">Davinci</a> 那是作者第三次接触到节点编辑器 这一次的接触让作者对节点编辑器的好感倍增 作者瞬间觉得 只要是可以模块化流程化的功能 万物皆可节点化</p>
<hr/>
<center><span class="span_center">于是下面的这个东西就诞生了</span></center>
<div id="div_img_node"><img src="./images/node.png"/><img src="./images/node.gif"/></div>
<center><span class="span_center">或者这样的一个东西</span></center>
<div style="text-align:center;margin:0px 10px;background-color:black;"><img style="width:1077px;max-width:100%;" src="./images/channel.png"/></div>
<p>本案例中只是提供了一个节点编辑器控件 并不包含节点列表 属性编辑器等 若后期有空再做一套完整的框架</p>
<hr/>
<span class="span_title anchor_point" anchor="b">节点编辑器</span>
<hr/>
<p>或许你未曾接触过节点编辑 但节点编辑的影子越来越多 尤其是在影视相关的一些设计类软件当中 Blender,C4D,Houdini,Davinci 等</p>
<p>节点编辑最大的好处就是可视化操作 将单一的功能点封装到节点之中 让用户通过节点布线来组合自己需要的逻辑 让整个流程可视化 而不是将你程序的整个执行流程固化在你的程序之中 当然在这之前你需要定义好节点的数据类型 <span style="color:orangered">因为数据类型无法兼容是不允许连线的</span> 通常情况下同颜色的连接点表示数据类型相同</p>
<p>另一个好处 让开发者只需要注重单一的功能点开发 使得功能与功能之间的耦合度降低 开发者在节点中开发完成需要的功能 无需知道应该把数据交给谁或者怎么去传递数据 只需要将你的结果数据打包给你所继承的节点类 节点编辑器会自动完成数据的投递过程
</p>
<h2 class="anchor_point" anchor="c">STNodeEditor</h2>
<p>STNodeEditor为节点容器 其中最重要的一个属性为<span class="span_property">Nodes</span>里面包含了画布中能看到的所有节点</p>
<p>STNodeEditor有两套坐标系统 平常情况下使用最多的就直接是控件的坐标系统 需要在控件的什么位置绘图或者添加东西采用的都是相对控件左上角的坐标 但是在节点编辑器中的情况比较复杂 需要引入一个画布的概念 画布是可以移动缩放的 所以在绘图的时候依旧采用控件的坐标系统将带来很多的麻烦 所以为画布定义了一个坐标原点 在节点以及连线绘制的时候采用的是画布的坐标系统 当需要拖动画布位置的时候 改变画布原点的位置就好了 而内部节点的位置依旧不做变更 因为节点采用的坐标位置是相对画布原点的
<br/><span style="color:orangered">当画布中<span class="span_property">Nodes</span>没有元素时候 画布将处于重置状态无法缩放与移动 因为作者觉得这样的操作没有任何意义</span></p>
<hr/>
<span class="span_title anchor_point" anchor="d">属性</span>
<hr/>
<table>
<tr style="text-align:center;background-color:#505050;">
<td>属性名称</td><td>类型</td><td>描述</td>
</tr>
<tr>
<td>CanvasOffsetX</td><td>float</td><td>获取画布原点相对于控件 X 方向上的偏移位置</td>
</tr>
<tr>
<td>CanvasOffsetY</td><td>float</td><td>获取画布原点相对于控件 Y 方向上的偏移位置</td>
</tr>
<tr>
<td>CanvasOffset</td><td>PointF</td><td>获取画布原点相对于控件偏移位置</td>
</tr>
<tr>
<td>CanvasValidBounds</td><td>Rectangle</td><td>获取画布中的有被用到的有效区域</td>
</tr>
<tr>
<td>CanvasScale</td><td>float</td><td>获取画布的缩放比例</td>
</tr>
<tr>
<td>Curvature</td><td>float</td><td>获取或设置 Option 之间连线的曲度</td>
</tr>
<tr>
<td>Magnet</td><td>bool</td><td>获取或设置移动画布中 Node 时候 是否启用磁铁效果</td>
</tr>
<tr>
<td>ShowBorder</td><td>bool</td><td>获取或设置移动画布中是否显示 Node 边框</td>
</tr>
<tr>
<td>ShowGrid</td><td>bool</td><td>获取或设置画布中是否绘制背景网格线条</td>
</tr>
<tr>
<td>ShowLocation</td><td>bool</td><td>获取或设置是否在画布边缘显示超出视角的 Node 位置信息</td>
</tr>
<tr>
<td>Nodes</td><td>STNodeCollection</td><td>获取画布中 Node 集合</td>
</tr>
<tr>
<td>ActiveNode</td><td>STNode</td><td>获取当前画布中被选中的活动 Node</td>
</tr>
<tr>
<td>HoverNode</td><td>STNode</td><td>获取当前画布中鼠标悬停的 Node</td>
</tr>
<tr>
<td>GridColor</td><td>Color</td><td>获取或设置绘制画布背景时 网格线条颜色</td>
</tr>
<tr>
<td>BorderColor</td><td>Color</td><td>获取或设置画布中 Node 边框颜色</td>
</tr>
<tr>
<td>BorderHoverColor</td><td>Color</td><td>获取或设置画布中悬停 Node 边框颜色</td>
</tr>
<tr>
<td>BorderSelectColor</td><td>Color</td><td>获取或设置画布中选中 Node 边框颜色</td>
</tr>
<tr>
<td>BorderActiveColor</td><td>Color</td><td>获取或设置画布中活动 Node 边框颜色</td>
</tr>
<tr>
<td>MarkForeColor</td><td>Color</td><td>获取或设置画布绘制 Node 标记详情采用的前景色</td>
</tr>
<tr>
<td>MarkBackColor</td><td>Color</td><td>获取或设置画布绘制 Node 标记详情采用的背景色</td>
</tr>
<tr>
<td>MagnetLineColor</td><td>Color</td><td>获取或设置画布中移动 Node 时候 磁铁标记线颜色</td>
</tr>
<tr>
<td>SelectedRectangleColor</td><td>Color</td><td>获取或设置画布中选择矩形区域的颜色</td>
</tr>
<tr>
<td>HighLineColor</td><td>Color</td><td>获取或设置画布中高亮连线的颜色</td>
</tr>
<tr>
<td>LocationForeColor</td><td>Color</td><td>获取或设置画布中边缘位置提示区域前景色</td>
</tr>
<tr>
<td>LocationBackColor</td><td>Color</td><td>获取或设置画布中边缘位置提示区域背景色</td>
</tr>
<tr>
<td>UnknownTypeColor</td><td>Color</td><td>获取或设置画布中当 Node 中 Option 数据类型无法确定时应当使用的颜色</td>
</tr>
<tr>
<td>TypeColor</td><td>Dictionary&lt;Type, Color&gt;</td><td>获取或设置画布中 Node 中 Option 数据类型预设颜色</td>
</tr>
</table>
<span class="span_title anchor_point" anchor="e">受保护字段</span>
<table>
<tr style="text-align:center;background-color:#505050;">
<td>属性名称</td><td>类型</td><td>描述</td>
</tr>
<tr>
<td>m_pt_in_control</td><td>Point</td><td>当前鼠标在控件中的实时位置</td>
</tr>
<tr>
<td>m_pt_in_canvas</td><td>PointF</td><td>当前鼠标在画布中的实时位置</td>
</tr>
<tr>
<td>m_pt_down_in_control</td><td>Point</td><td>鼠标点击时在控件上的位置</td>
</tr>
<tr>
<td>m_pt_down_in_canvas</td><td>PointF</td><td>鼠标点击时在画布中的位置</td>
</tr>
<tr>
<td>m_pt_canvas_old</td><td>PointF</td><td>用于鼠标点击移动画布时候 鼠标点下时候的画布坐标位置</td>
</tr>
<tr>
<td>m_pt_dot_down</td><td>Point</td><td>用于保存连线过程中保存点下 Option 的起点坐标</td>
</tr>
<tr>
<td>m_option_down</td><td>STNodeOption</td><td>用于保存连线过程中鼠标点下的起点Option 当MouseUp时候 确定是否连接此节点</td>
</tr>
</table>
<ul>
<li>
<span class="span_property" style="background-color:#0090c9;">TypeColor</span>
<ul>
<li>此属性为当前<span class="span_property">STNodeEditor</span>中所有的数据类型所对应的颜色 节点中所有的数据类型都应当包含在此集合中 即便有动态需要加载的未知类型的节点 那么对应的节点也应该在节点的<span class="span_property">OnOwnerChanged()</span>中向容器提交自己数据类型以及对应颜色
<br/><span style="color:orangered;">若节点有单独对<span class="span_property">STNodeOption.DotColor</span>设置值 则忽略该对照表中的颜色 即不为默认颜色<span class="span_property">Color.Transparent</span></span></li>
</ul>
</li>
<hr/>
<li>
<span class="span_property" style="background-color:#0090c9">UnknownTypeColor</span>
<ul>
<li>若节点中并没有单独设置数据类型颜色且在<span class="span_property">TypeColor</span>中无法匹配时候采用此值 所以将此特殊类型单独设置</li>
</ul>
</li>
</ul>
<hr/><span class="span_title anchor_point" anchor="f">可重载函数</span><hr/>
<table>
<tr style="background-color:#505050"><td colspan="2">当绘制背景网格线时候发生</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawGrid(DrawingTools dt, int nWidth, int nHeight)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr>
<tr>
<td>nWidth</td><td>需要绘制宽度</td>
</tr>
<tr>
<td>nHeight</td><td>需要绘制高度</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">当绘制 Node 时候发生</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawNode(DrawingTools dt, Rectangle rect)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr>
<tr>
<td>rect</td><td>可视画布区域大小</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">当绘制已连接路径时候发生</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawConnectedLine(DrawingTools dt)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">当绘制 Mark 详情信息时候发生</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawMark(DrawingTools dt)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">当移动 Node 时候 需要显示对齐参考线时候发生</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawMagnetLine(DrawingTools dt, MagnetInfo mi)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr>
<tr>
<td>mi</td><td>匹配的磁铁信息</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">绘制选择的矩形区域</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawSelectedRectangle(DrawingTools dt, RectangleF rectf)</td></tr>
<tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr>
<tr>
<td>rectf</td><td>位于控件上的矩形区域</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">绘制超出视觉区域的 Node 位置提示信息</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawNodeOutLocation(DrawingTools dt, Size sz, List&lt;Point&gt; lstPts)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr>
<tr>
<td>sz</td><td>提示框边距</td>
<tr>
<td>lstPts</td><td>超出视觉区域的 Node 位置信息</td>
</tr>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">绘制提示信息</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawAlert(DrawingTools dt, Rectangle rect, string strText, Color foreColor, Color backColor, AlertLocation al)</td></tr>
<tr><td>dt</td><td>绘制工具</td></tr>
<tr><td>rect</td><td>需要绘制区域</td></tr>
<tr><td>strText</td><td>需要绘制文本</td></tr>
<tr><td>foreColor</td><td>信息前景色</td></tr>
<tr><td>backColor</td><td>信息背景色</td></tr>
<tr><td>al</td><td>信息位置</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">获取提示信息需要绘制的矩形区域</td></tr>
<tr style="background-color:#505050"><td colspan="2">Rectangle GetAlertRectangle(Graphics g, string strText, AlertLocation al)</td></tr>
<tr><td>g</td><td>绘图表面</td></tr>
<tr><td>strText</td><td>需要绘制文本</td></tr>
<tr><td>al</td><td>信息位置</td></tr>
</table>
<span class="span_title anchor_point" anchor="g">公开函数</span>
<table>
<tr style="background-color:#505050"><td colspan="2">通过画布坐标进行寻找</td></tr>
<tr style="background-color:#505050"><td colspan="2">NodeFindInfo FindNodeFromPoint(PointF pt)</td></tr>
<tr>
<td>pt</td><td>画布中的坐标</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">获取已经被选择的 Node 集合</td></tr>
<tr style="background-color:#505050"><td colspan="2">STNode[] GetSelectedNode()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">将画布坐标转换为控件坐标</td></tr>
<tr style="background-color:#505050"><td colspan="2">float CanvasToControl(XXX xxx)</td></tr>
<tr>
<td>xxx</td><td>Rectangle,RectangleF,Point,PointF...</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">将控件坐标转换为画布坐标</td></tr>
<tr style="background-color:#505050"><td colspan="2">float ControlToCanvas(XXX xxx)</td></tr>
<tr>
<td>xxx</td><td>Rectangle,RectangleF,Point,PointF...</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">移动画布原点坐标到指定的控件坐标位置</td></tr>
<tr style="background-color:#505050"><td colspan="2">void MoveCanvas(float x, float y, bool bAnimation, CanvasMoveArgs ma)</td></tr>
<tr>
<td>x</td><td>X 坐标</td>
</tr>
<tr>
<td>y</td><td>Y 坐标</td>
</tr>
<tr>
<td>bAnimation</td><td>移动过程中是否启动动画效果</td>
</tr>
<tr>
<td>ma</td><td>指定需要修改的坐标参数</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">缩放画布</td></tr>
<tr style="background-color:#505050"><td colspan="2">void ScaleCanvas(float f, float x, float y)</td></tr>
<tr>
<td>f</td><td>缩放比例</td>
</tr>
<tr>
<td>x</td><td>以指定控件坐标 X 为中心进行缩放</td>
</tr>
<tr>
<td>y</td><td>以指定控件坐标 Y 为中心进行缩放</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">获取当前已连接的 Option 对应关系</td></tr>
<tr style="background-color:#505050"><td colspan="2">ConnectionInfo[] GetConnectionInfo()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">判断两个 Node 之间是否存在连接路径</td></tr>
<tr style="background-color:#505050"><td colspan="2">static bool CanFindNodePath(STNode nodeStart, STNode nodeFind)</td></tr>
<tr>
<td>nodeStart</td><td>起始 Node</td>
</tr>
<tr>
<td>nodeFind</td><td>目标 Node</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">获取画布中指定矩形区域图像</td></tr>
<tr style="background-color:#505050"><td colspan="2">Image GetCanvasImage(Rectangle rect, float fScale)</td></tr>
<tr>
<td>rect</td><td>画布中指定的矩形区域</td>
</tr>
<tr>
<td>fScale</td><td>缩放比例</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">保存画布中的类容到文件中</td></tr>
<tr style="background-color:#505050"><td colspan="2">void SaveCanvas(string strFileName)</td></tr>
<tr>
<td>strFileName</td><td>文件路径</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">保存画布中的类容到数据流</td></tr>
<tr style="background-color:#505050"><td colspan="2">void SaveCanvas(Stream s)</td></tr>
<tr>
<td>s</td><td>数据流对象</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">获取画布中内容二进制数据</td></tr>
<tr style="background-color:#505050"><td colspan="2">byte[] GetCanvasData()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">加载程序集</td></tr>
<tr style="background-color:#505050"><td colspan="2">bool LoadAssembly(string strFile)</td></tr>
<tr>
<td>返回值</td><td>此文件中是否有类型被加载</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">加载程序集</td></tr>
<tr style="background-color:#505050"><td colspan="2">int LoadAssembly(string[] strFiles)</td></tr>
<tr>
<td>返回值</td><td>存在STNode类型的文件的个数</td>
</tr>
<tr><td colspan="2" style="background-color:#343434"><span style="color:orangered">编辑器中的节点并不一定来自自身程序集 也可能包含在其他程序集中 这时候需提前加载对应程序集 对应的<span class="span_property">STNode</span>才能正确的从文件或者数据中动态加载到画布类容</span></td></tr>
<tr style="background-color:#505050"><td colspan="2">获取当前编辑器中已加载的Node类型</td></tr>
<tr style="background-color:#505050"><td colspan="2">Type[] GetTypes()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">从文件中加载数据</td></tr>
<tr style="background-color:#505050"><td colspan="2">void LoadCanvas(string strFileName)</td></tr>
<tr>
<td>strFileName</td><td>文件路径</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">从二进制加载数据</td></tr>
<tr style="background-color:#505050"><td colspan="2">void LoadCanvas(byte[] byData)</td></tr>
<tr>
<td>byData</td><td>二进制数据</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">从数据流中加载数据</td></tr>
<tr style="background-color:#505050"><td colspan="2">void LoadCanvas(Stream s)</td></tr>
<tr>
<td>s</td><td>数据流对象</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">在画布中显示提示信息</td></tr>
<tr style="background-color:#505050"><td colspan="2">void ShowAlert(string strText, Color foreColor, Color backColor, int nTime, AlertLocation al, bool bRedraw)</td></tr>
<tr><td>strText</td><td>要显示的信息</td></tr>
<tr><td>foreColor</td><td>信息前景色</td></tr>
<tr><td>backColor</td><td>信息背景色</td></tr>
<tr><td>nTime</td><td>信息持续时间</td></tr>
<tr><td>al</td><td>信息要显示的位置</td></tr>
<tr><td>bRedraw</td><td>是否立即重绘</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">设置画布中活动的节点</td></tr>
<tr style="background-color:#505050"><td colspan="2">STNode SetActiveNode(STNode node)</td></tr>
<tr><td>STNode</td><td>需要被设置为活动的节点</td></tr>
<tr><td>返回值</td><td>设置前的活动节点</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">向编辑器中添加默认数据类型颜色</td></tr>
<tr style="background-color:#505050"><td colspan="2">SetTypeColor(Type t, Color clr)</td></tr>
<tr><td>t</td><td>数据类型</td></tr>
<tr><td>clr</td><td>对应颜色</td></tr>
</table>
<hr/><span class="span_title anchor_point" anchor="h">事件</span><hr/>
<table>
<tr><td>SelectedChanged</td><td>选择的节点发生变化时候发生</td><tr/>
<tr><td>HoverChanged</td><td>悬停的节点发生变化时候发生</td><tr/>
<tr><td>NodeAdded</td><td>当节点被添加时候发生</td><tr/>
<tr><td>NodeRemoved</td><td>当节点被移除时候发生</td><tr/>
<tr><td>CanvasMoved</td><td>移动画布原点时候发生</td><tr/>
<tr><td>CanvasScaled</td><td>缩放画布时候发生</td><tr/>
<tr><td>OptionConnected</td><td>连接节点选项时候发生</td><tr/>
<tr><td>OptionDisConnected</td><td>断开节点选项时候发生</td><tr/>
<tr><td>OptionConnecting</td><td>正在连接节点选项时候发生</td><tr/>
<tr><td>OptionDisConnecting</td><td>正在断开节点选项时候发生</td><tr/>
</table>
<ul>
<li>
<span class="span_property" style="background-color:#0090c9;">Option(Dis)Connected</span>
<ul>
<li><span style="color:orangered">此事件触发时并不代表当前画布中的节点连线成功</span> 请通过获取事件参数中的<span class="span_property">Status</span>字段进行判断
<br/>但是<span class="span_property">STNodeOption.Connected</span>事件是成功后才触发 之所以要这样设计是因为考虑到 对于<span class="span_property">STNodeEditor</span>来说 它需要知道一个连接的状态 即使连接不成功也应当告知前端用户为什么会连接不成功 所以失败和成功都会触发事件</li>
</ul>
</li>
<hr/>
<li>
<span class="span_property" style="background-color:#0090c9">Option(Dis)Connecting</span>
<ul>
<li>此事件是在连接或者断开连接正在发生的时候触发 <span style="color:orangered">可通过事件参数<span class="span_property">Continue</span>来决定是否继续或者停止操作</span></li>
</ul>
</li>
</ul>
<h2 class="anchor_point" anchor="i">STNode</h2>
<p><span style="color:orangered"><span class="span_property">STNode</span>为抽象类 不可实例化 需要用户自己继承重写</span></p>
<p><span class="span_property">STNode</span>有三个比较重要的属性<span class="span_property">InputOptions</span><span class="span_property">OutputOptions</span><span class="span_property">Controls</span></p>
<p>若把<span class="span_property">STNodeEditor</span>类比成桌面 那么<span class="span_property">STNode</span>就可以类比成一个窗体 虽然作者在节点中提供了<span class="span_property">Controls</span>集合 但是作者没有提供任何的一个封装好的控件 只提供了一个<span class="span_property">STNodeControl</span>基类 若有需要用户可继承此类绘制自己需要的控件</p>
<p><span style="color:orangered">继承<span class="span_property">STNode</span>必须提供空参构造器 否则对节点进行保存后无法还原节点 因为还原过程并非序列化 而是通过反射重新构造对象</span></p>
<hr/>
<span class="span_title anchor_point" anchor="j">属性</span>
<hr/>
<table>
<tr style="text-align:center;background-color:#505050;">
<td>属性名称</td><td>类型</td><td>描述</td>
</tr>
<tr><td>Owner</td><td>STNodeEditor</td><td>获取当前 Node 所有者</td></tr>
<tr><td>IsSelected</td><td>bool</td><td>获取或设置 Node 是否处于被选中状态</td></tr>
<tr><td>IsActive</td><td>bool</td><td>获取 Node 是否处于活动状态</td></tr>
<tr><td>TitleColor</td><td>Color</td><td>获取或设置标题背景颜色</td></tr>
<tr><td>MarkColor</td><td>Color</td><td>获取或设置标记信息背景颜色</td></tr>
<tr><td>ForeColor</td><td>Color</td><td>获取或设置当前 Node 前景色</td></tr>
<tr><td>BackColor</td><td>Color</td><td>获取或设置当前 Node 背景色</td></tr>
<tr><td>Title</td><td>string</td><td>获取或设置 Node 标题</td></tr>
<tr><td>Mark</td><td>string</td><td>获取或设置 Node 标记信息</td></tr>
<tr><td>MarkLines</td><td>string[]</td><td>获取 Node 标记信息行数据</td></tr>
<tr><td>Left</td><td>int</td><td>获取或设置 Node 左边坐标</td></tr>
<tr><td>Top</td><td>int</td><td>获取或设置 Node 上边坐标</td></tr>
<tr><td>Width</td><td>int</td><td>获取或设置 Node 宽度</td></tr>
<tr><td>Height</td><td>int</td><td>获取或设置 Node 高度</td></tr>
<tr><td>Right</td><td>int</td><td>获取 Node 右边边坐标</td></tr>
<tr><td>Bottom</td><td>int</td><td>获取 Node 下边坐标</td></tr>
<tr><td>Rectangle</td><td>Rectangle</td><td>获取 Node 矩形区域</td></tr>
<tr><td>TitleRectangle</td><td>Rectangle</td><td>获取 Node 标题矩形区域</td></tr>
<tr><td>MarkRectangle</td><td>Rectangle</td><td>获取 Node 标记矩形区域</td></tr>
<tr><td>TitleHeight</td><td>int</td><td>获取或设置 Node 标题高度</td></tr>
<tr><td>InputOptions</td><td>STNodeOptionCollection</td><td>获取输入选项集合</td></tr>
<tr><td>InputOptionsCount</td><td>int</td><td>获取输入选项集合个数</td></tr>
<tr><td>OutputOptions</td><td>STNodeOptionCollection</td><td>获取输出选项</td></tr>
<tr><td>OutputOptionsCount</td><td>int</td><td>获取输出选项个数</td></tr>
<tr><td>Controls</td><td>STNodeControlCollection</td><td>获取 Node 所包含的控件集合</td></tr>
<tr><td>ControlsCount</td><td>int</td><td>获取 Node 所包含的控件集合个数</td></tr>
<tr><td>Location</td><td>Point</td><td>获取 Node 坐标位置</td></tr>
<tr><td>Size</td><td>Size</td><td>获取 Node 大小</td></tr>
<tr><td>Font</td><td>Font</td><td>获取或设置 Node 字体</td></tr>
<tr><td>LockOption</td><td>bool</td><td>获取或设置是否锁定Option选项 锁定后不在接受连接</td></tr>
<tr><td>LockLocation</td><td>bool</td><td>获取或设置是否锁定Node位置 锁定后不可移动</td></tr>
<tr><td>ContextMenuStrip</td><td>ContextMenuStrip</td><td>获取或设置当前Node 上下文菜单</td></tr>
<tr><td>Tag</td><td>object</td><td>获取或设置用户自定义保存的数据</td></tr>
</table>
<ul>
<li>
<span class="span_property" style="background-color:#0090c9;">Lock(Option/Location)</span>
<ul>
<li><img src="./images/lock.gif"/><br/><span class="span_property">LockOption</span>被设定后左上角会出现一把锁 表示节点内所有选项不再接受连接 但可以与其他节点选项断开连接 <span style="color:orangered">但是其他节点也同为被锁定状态 则无法断开</span>
<br/><span class="span_property">LockLocation</span>被设定后右上角会出现一枚图钉 表示节点位置不再接受设置</li>
</ul>
</li>
</ul>
<hr/><span class="span_title anchor_point" anchor="k">受保护字段</span>
<table>
<tr style="text-align:center;background-color:#505050;">
<td>属性名称</td><td>类型</td><td>描述</td>
</tr>
<tr><td>m_ctrl_active</td><td>STNodeControl</td><td>当前Node中 活动的控件</td></tr>
<tr><td>m_ctrl_hover</td><td>STNodeControl</td><td>当前Node中 悬停的控件</td></tr>
</table>
<p><span style="color:orangered">在上述属性中除了<span class="span_property">Mark</span><span class="span_property">Left</span><span class="span_property">Top</span>其余均为只读属性 被<span class="span_property">protected</span>所修饰 仅继承<span class="span_property">STNode</span>后才可访问 不然作者觉得太危险了 比如下面这段代码</span></p>
<p><span class="span_property">NodeXXX.Owner.Nodes[nIndex].InputOptions.RemoveAt(0);</span></p>
<p>节点都是模块化的 可能会有不同的人来做开发 甚至是以插件的方式提供 如果大家都遵守约定那将很好 可如果一旦有人不遵守约定 或者出现意外情况 那么就可能出现跨节点操作 破坏掉原本一个正常的节点 如果确实有需要请更改源代码</p>
<hr/>
<span class="span_title anchor_point" anchor="l">可重载函数</span>
<table>
<tr style="background-color:#505050"><td colspan="2">当Node被构造时候发生</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnCreate()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">当所有者发生改变时候发生</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnOwnerChanged()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">当选中状态改变时候发生</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnSelectedChanged()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">当活动状态改变时候发生</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnActiveChanged()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">绘制整个Node</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawNode(DrawingTools dt)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">绘制Node标题部分</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawTitle(DrawingTools dt)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">绘制Node主体部分 除去标题部分</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawBody(DrawingTools dt)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">绘制标记信息</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawMark(DrawingTools dt)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr><tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">绘制选项连线的点</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawOptionDot(DrawingTools dt, STNodeOption op)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr>
<tr>
<td>op</td><td>需要绘制的Option</td>
</tr></tr><tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">绘制选项的文本</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnDrawOptionText(DrawingTools dt, STNodeOption op)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
<tr/>
<tr>
<td>op</td><td>需要绘制的Option</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">当计算Option连线点位置时候发生</td></tr>
<tr style="background-color:#505050"><td colspan="2">Point OnSetOptionDotLocation(STNodeOption op, Point pt)</td></tr>
<tr>
<td>op</td><td>需要计算的Option</td>
</tr>
<tr>
<td>op</td><td>自动计算出的位置</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">当计算Option文本区域时候发生</td></tr>
<tr style="background-color:#505050"><td colspan="2">Rectangle OnSetOptionTextRectangle(STNodeOption op, Rectangle rect)</td></tr>
<tr>
<td>op</td><td>需要计算的Option</td>
</tr>
<tr>
<td>op</td><td>自动计算出的区域</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">计算当前Node所需要的矩形区域</td></tr>
<tr style="background-color:#505050"><td colspan="2">Size OnBuildNodeSize(DrawingTools dt)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr>
<tr>
<td colspan="2" style="background-color:#343434"><span style="color:orangered">若需要自己重绘Node 则应当重写此函数 以确定绘图区域大小
<br/>返回的大小并不会限制绘制区域 任然可以在此区域之外绘制
<br/>但是并不会被STNodeEditor所接受 并触发对应事件</span></td>
</tr>
<tr style="background-color:#505050"><td colspan="2">计算当前Mark所需要的矩形区域</td></tr>
<tr style="background-color:#505050"><td colspan="2">Rectangle OnBuildMarkRectangle(DrawingTools dt)</td></tr>
<tr>
<td>dt</td><td>绘制工具</td>
</tr>
<tr>
<td colspan="2" style="background-color:#343434"><span style="color:orangered">若需要自己重绘Mark 则应当重写此函数 以确定绘图区域大小
<br/>返回的大小并不会限制绘制区域 任然可以在此区域之外绘制
<br/>但是并不会被STNodeEditor所接受 并触发对应事件</span></td>
</tr>
<tr style="background-color:#505050"><td colspan="2">当需要保存时候 此Node有哪些需要额外保存的数据</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnSaveNode(Dictionary&lt;string, byte[]&gt;)</td></tr>
<tr>
<td>dic</td><td>需要保存的数据</td>
</tr>
<tr><td colspan="2" style="background-color:#343434"><span style="color:orangered">保存时并不会进行序列化 仅自动保存部分(<span class="span_property">Guid,Left,Top,Mark,LockOption,LockLocation</span>)属性 还原时候仅重新通过空参数构造器创建此Node
<br/>然后调用<span class="span_property">OnLoadNode()</span>将保存的数据进行还原</span></td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnLoadNode(Dictionary&lt;string, byte[]&gt; dic)</td></tr>
<tr>
<td>dic</td><td>保存时候的数据</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">当编辑器加载完成所有的节点时候发生</td></tr>
<tr style="background-color:#505050"><td colspan="2">void OnEditorLoadCompleted()</td></tr>
</table>
<div style="overflow:auto;margin:0px 10px;max-width:100%;"><pre><span style="color:cornflowerblue">protected override void</span> OnSaveNode(<span style="color:cyan">Dictionary</span>&lt;<span style="color:cornflowerblue">string</span>, <span style="color:cornflowerblue">byte</span>[]&gt;) {
dic.Add(<span style="color:lime">"count"</span>, <span style="color:cyan">BitConverter</span>.GetBytes(<span style="color:cornflowerblue">this</span>.InputOptionsCount));
}
<span style="color:cornflowerblue">protected</span> <span style="color:cornflowerblue">internal</span> <span style="color:cornflowerblue">override void </span>OnLoadNode(<span style="color:cyan">Dictionary</span>&lt;<span style="color:cornflowerblue">string</span>, <span style="color:cornflowerblue">byte</span>[]&gt; dic) {
<span style="color:cornflowerblue">int</span> nCount = <span style="color:cyan">BitConverter</span>.ToInt32(dic[<span style="color:lime">"count"</span>], 0);
<span style="color:cornflowerblue">while</span> (<span style="color:cornflowerblue">this</span>.InputOptionsCount &lt; nCount && <span style="color:cornflowerblue">this</span>.InputOptionsCount != nCount)
<span style="color:cornflowerblue">this</span>.Addhub();
}
</pre></div>
<p><img style="width:116px;" src="./images/object_type.png"/>
<br/>上面代码片段为<span class="span_property">STNodeHub</span>重载 当需要保存时候此节点需要保存当前已拥有的行数 还原的时候需要将行数还原 因为新构造的节点只有一行 若上面已有连线关系的时候只有一行可能无法正常还原连线关系</p>
<hr/>
<div><pre><span style="color:cornflowerblue">protected override void</span> OnSaveNode(<span style="color:cyan">Dictionary</span>&lt;<span style="color:cornflowerblue">string</span>, <span style="color:cornflowerblue">byte</span>[]&gt;) {
dic.Add(<span style="color:lime">"file"</span>, <span style="color:cyan">Encoding</span>.UTF8.GetBytes(m_str_file));
}
<span style="color:cornflowerblue">protected</span> <span style="color:cornflowerblue">override void </span>OnLoadNode(<span style="color:cyan">Dictionary</span>&lt;<span style="color:cornflowerblue">string</span>, <span style="color:cornflowerblue">byte</span>[]&gt; dic) {
m_str_file = <span style="color:cyan">Encoding</span>.UTF8.GetString(dic[<span style="color:lime">"file"</span>]);
if (System.IO.<span style="color:cyan">File</span>.Exists(m_str_file)) { <span style="color:lime">//如果文件存在加载并投递数据</span>
m_option_out.TransferData(<span style="color:cyan">Image</span>.FromFile(m_str_file));
}
}</pre></div>
<p><img style="width:173px;" src="./images/open_image.png"/>
<br/>上面代码片段为DEMO中<span class="span_property">STNodeImageInput</span>重载 因为保存时候 需要把已经打开的文件路劲一起保存 还原的时候再次打开文件并传递数据</p>
<hr/>
<span class="span_title anchor_point" anchor="m">其他重载函数</span>
<p>
<span class="span_property">OnGotFocus</span><span class="span_property">OnLostFocus</span><span class="span_property">OnMouse***</span><span class="span_property">OnKey***</span><span class="span_property">OnMove</span>
</p>
<span class="span_title anchor_point" anchor="n">公开函数</span>
<table>
<tr style="background-color:#505050"><td colspan="2">重绘Node</td></tr>
<tr style="background-color:#505050"><td colspan="2">void Invalidate()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">重绘 Node 指定区域</td></tr>
<tr style="background-color:#505050"><td colspan="2">void Invalidate(Rectangle rect)</td></tr>
<tr>
<td>rect</td><td>Node 指定区域</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">获取此Node所包含的输入Option集合</td></tr>
<tr style="background-color:#505050"><td colspan="2">STNodeOption[] GetInputOptions()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">获取此Node所包含的输出Option集合</td></tr>
<tr style="background-color:#505050"><td colspan="2">STNodeOption[] GetOutputOptions()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">设置Node的选中状态</td></tr>
<tr style="background-color:#505050"><td colspan="2">void SetSelected(bool bSelected, bool bRedraw)</td></tr>
<tr>
<td>bSelected</td><td>是否选中</td>
</tr>
<tr>
<td>bRedraw</td><td>是否立即重绘</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">此函数参照<span class="span_property">System.Windows.Forms.Control</span></td></tr>
<tr style="background-color:#505050"><td colspan="2">XXX Begin/Invoke(Delegate method, params object[] args)</td></tr>
</table>
<span class="span_title anchor_point" anchor="o">关于重绘</span>
<p><img style="max-width:537px;" src="./images/node.png"/>
<br/>若无特殊需求 仅仅需要的是输入输出点就足够了 那么无需用户进行重绘操作 上图 "Demo_Node" 就是默认的绘制 用户只需要在节点中加入需要的输入输出选项即可</p>
<div style="overflow:auto;margin:0px 10px;max-width:100%"><pre><span style="color:cornflowerblue">protected</span> <span style="color:cornflowerblue">override</span> <span style="color:cornflowerblue">void</span> OnCreate() {
<span style="color:cornflowerblue">base</span>.OnCreate();
<span style="color:cornflowerblue">this</span>.InputOptions.Add(<span style="color:cornflowerblue">new</span> STNodeOption(<span style="color:lime">"Input"</span>, <span style="color:cornflowerblue">typeof</span>(<span style="color:cornflowerblue">string</span>), <span style="color:cornflowerblue">false</span>));
<span style="color:cornflowerblue">this</span>.InputOptions.Add(<span style="color:cornflowerblue">new</span> STNodeOption(<span style="color:lime">"SingleNode"</span>, <span style="color:cornflowerblue">typeof</span>(<span style="color:cyan">Image</span>), <span style="color:cornflowerblue">true</span>));
<span style="color:cornflowerblue">this</span>.InputOptions.Add(<span style="color:cornflowerblue">new</span> STNodeOption(<span style="color:lime">"SingleNode"</span>, <span style="color:cornflowerblue">typeof</span>(<span style="color:cyan">Icon</span>), <span style="color:cornflowerblue">true</span>));
<span style="color:cornflowerblue">this</span>.OutputOptions.Add(<span style="color:cornflowerblue">new</span> STNodeOption(<span style="color:lime">"output"</span>, <span style="color:cornflowerblue">typeof</span>(<span style="color:cornflowerblue">string</span>), <span style="color:cornflowerblue">false</span>));
<span style="color:cornflowerblue">this</span>.OutputOptions.Add(<span style="color:cornflowerblue">new</span> STNodeOption(<span style="color:lime">"Single"</span>, <span style="color:cornflowerblue">typeof</span>(<span style="color:cornflowerblue">string</span>), <span style="color:cornflowerblue">true</span>));
<span style="color:cornflowerblue">this</span>.Title = <span style="color:lime">"Demo_Node"</span>;
}</pre></div>
<p>上面代码片段为 "Demo_Node" 的<span class="span_property">OnCreate</span>重载 加入了三个输入节点和两个输出节点 <span style="color:orangered">(当然上面的代码并没有绑定<span class="span_property">STNodeOption</span>的事件)</span> 若需要自定义绘制参考Demo代码</p>
<h2 class="anchor_point" anchor="p">STNodeOption</h2>
<p><span class="span_property">STNodeOption</span><span class="span_property">STNode</span>下选项的连接点</p>
<p style="color:orangered"><span class="span_property">TransferData(object data)</span>是关键函数 当被调用时数据将自动传递到与它相连的所有选项中 并触发目标选项的<span class="span_property">DataTransfer</span>事件
<br/>此函数是节点选项之间数据传递的核心</p>
<hr/>
<span class="span_title anchor_point" anchor="q">属性</span>
<table>
<tr style="text-align:center;background-color:#505050;">
<td>属性名称</td><td>类型</td><td>描述</td>
</tr>
<tr><td>Owner</td><td>STNode</td><td>获取当前 Option 所属的 Node</td></tr>
<tr><td>IsSingle</td><td>bool</td><td>获取当前 Option 是否仅能被连接一次</td></tr>
<tr><td>IsInput</td><td>bool</td><td>获取当前 Option 是否是输入选项</td></tr>
<tr><td>TextColor</td><td>Color</td><td>获取或设置当前 Option 文本的颜色</td></tr>
<tr><td>DotColor</td><td>Color</td><td>获取或设置当前 Option 连接点的颜色</td></tr>
<tr><td>Text</td><td>string</td><td>获取或设置当前 Option 显示文本</td></tr>
<tr><td>DotLeft</td><td>int</td><td>获取当前 Option 连接点的左边坐标</td></tr>
<tr><td>DotTop</td><td>int</td><td>获取当前 Option 连接点的上边坐标</td></tr>
<tr><td>DotSize</td><td>int</td><td>获取当前 Option 连接点的宽度</td></tr>
<tr><td>Data</td><td>object</td><td>获取或者设置当前 Option 所包含的数据</td></tr>
<tr><td>DataType</td><td>Type</td><td>获取当前 Option 数据类型</td></tr>
<tr><td>DotRectangle</td><td>Rectangle</td><td>获取当前 Option 连接点的区域</td></tr>
<tr><td>ConnectionCount</td><td>int</td><td>获取当前 Option 被连接的个数</td></tr>
</table>
<span class="span_title anchor_point" anchor="r">受保护字段</span>
<table>
<tr style="text-align:center;background-color:#505050;">
<td>属性名称</td><td>类型</td><td>描述</td>
</tr>
<tr><td>m_hs_connected</td><td>HashSet&lt;STNodeOption&gt;</td><td>保存已经被连接的点</td></tr>
</table>
<p><span style="color:orangered">上述中不带 "设置" 二字的都为只读属性 即便是继承至<span class="span_property">STNodeOption</span></span>
<br/>因为作者认为<span class="span_property">STNodeOption</span>最大的用途应该是作为节点之间连线的载体和事件的触发 而对于其他外观需求并不是很重要</p>
<ul>
<li>
<span class="span_property" style="background-color:#0090c9;">DotColor</span>
<ul>
<li>若设置此值 则绘制时候以此值为准 否则查询<span class="span_property">STNodeEditor.TypeColor</span>字段进行颜色匹配</li>
</ul>
</li>
<li>
<span class="span_property" style="background-color:#0090c9;">DataType</span>
<ul>
<li>此选项允许被连接或者输出的数据类型 若类型无法被兼容 无法完成连线
<br/><span style="color:orangered">对于输入类型的节点 每个与其参与连接的输出节点的数据类型必须与输入节点的数据类型相同 或者为输入节点数据类型的子类</span>
<br/>在使用过程中作者并不建议子类和父类混合使用 对于输入或者输出的数据类型应当统一为子类或者父类 若混合使用将在一定程度上产生歧义 比如父类颜色被设定成红色子类颜色被设定成黄色 那么在UI上会给人一种无法被连接的错觉 应当统一数据类型所对应的颜色
<br/><span style="color:orangered">当数据类型为<span class="span_property">object</span>时 则表示可兼容所有数据类型且以 空心 状态绘制</span>
<br/><img style="width:116px;" src="./images/object_type.png"/>
<br/>上图中的 "HUB" 节点为内置节点 其节点类型为<span class="span_property">STNodeHub</span>默认状态下其输入输出点都为<span class="span_property">object</span>类型 表示可兼容所有类型 且重载了<span class="span_property">ConnectOption(STNodeOption op)</span><span class="span_property">CanConnect(STNodeOption op)</span>函数 一旦被接入或者被接入时则会变更为和对应的数据类型进行连接 若连接成功另则会再增加一行类型为<span class="span_property">object</span>的新行
<br/><span style="color:orangered;">但是决定输入类型的<span class="span_property">DataType</span>属性被<span class="span_property">internal</span>修饰 所以若你即使继承至<span class="span_property">STNodeOption</span>也无法改写 只能通过构造器传递 因为处于连线状态突然变更类型会带来一系列的问题 即便编辑器发现变更时候可以选择自动断开所有不相符的连接 但是作者并不建议或者打算这么做 若有此需求请更改源代码</span></li>
</ul>
</li>
</ul>
<hr/>
<span class="span_title anchor_point" anchor="s">构造器</span>
<hr/>
<table>
<tr style="background-color:#505050"><td colspan="2">构造一个 Option</td></tr>
<tr style="background-color:#505050"><td colspan="2">STNodeOption(string strText, Type dataType, bool bSingle)</td></tr>
<tr>
<td>strText</td><td>显示文本</td>
</tr>
<tr>
<td>dataType</td><td>数据类型</td>
</tr>
<tr>
<td>bSingle</td><td>是否为单连接 <span style="color:orangered">若为true则以圆形绘制 否则方形</span></td>
</tr>
</table>
<hr/>
<span class="span_title anchor_point" anchor="t">可重载函数</span>
<p><span class="span_property">OnConnected</span><span class="span_property">OnConnecting</span><span class="span_property">OnDisConnected</span><span class="span_property">OnDisConnecting</span><span class="span_property">OnDataTransfer</span></p>
<hr/>
<span class="span_title anchor_point" anchor="u">公开函数</span>
<hr/>
<table>
<tr style="background-color:#505050"><td colspan="2">当前 Option 连接目标 Option</td></tr>
<tr style="background-color:#505050"><td colspan="2">virtual ConnectionStatus ConnectOption(STNodeOption op)</td></tr>
<tr>
<td>op</td><td>需要连接的 Option</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">检测当前 Option 是否可以连接目标 Option</td></tr>
<tr style="background-color:#505050"><td colspan="2">virtual ConnectionStatus CanConnect(STNodeOption op)</td></tr>
<tr>
<td>op</td><td>需要连接的 Option</td>
</tr>
<tr><td colspan="2" style="background-color:#343434"><span style="color:orangered">当前Option具备连接目标Option的条件 不代表目标Option也具备连当前Option的条件</span></td></tr>
<tr style="background-color:#505050"><td colspan="2">当前 Option 断开目标 Option</td></tr>
<tr style="background-color:#505050"><td colspan="2">virtual bool DisConnection(STNodeOption op)</td></tr>
<tr>
<td>op</td><td>需要断开的 Option</td>
</tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">断开当前 Option 的所有连接</td></tr>
<tr style="background-color:#505050"><td colspan="2">void DisConnectionAll()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2">获取当前 Option 所连接的 Option 集合</td></tr>
<tr style="background-color:#505050"><td colspan="2">List&lt;STNodeOption&gt; GetConnectedOption()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2"><span style="color:orangered">向当前 Option 所连接的所有 Option 投递数据</span></td></tr>
<tr style="background-color:#505050"><td colspan="2">void TransferData()</td></tr>
<tr><td colspan="2" style="background-color:#343434">&nbsp;</td></tr>
<tr style="background-color:#505050"><td colspan="2"><span style="color:orangered">向当前 Option 所连接的所有 Option 投递数据</span></td></tr>
<tr style="background-color:#505050"><td colspan="2">void TransferData(object data)</td></tr>
<tr>
<td>data</td><td>需要投递的数据</td>
</tr>
</table>
<hr/><span class="span_title anchor_point" anchor="v">事件</span><hr/>
<table>
<tr><td>Connected</td><td>当被连接时候发生</td><tr/>
<tr><td>Connecting</td><td>当连接正在被连接时发生</td><tr/>
<tr><td>Disconnected</td><td>当被断开时候发生</td><tr/>
<tr><td>DisConnecting</td><td>当连接正在被断开时发生</td><tr/>
<tr><td>DataTransfer</td><td>当有数据传递时候发生</td><tr/>
</table>
<p><span class="span_property">Connected</span><span class="span_property">DisConnected</span>触发的同时<span class="span_property">DataTransfer</span>也会触发
<br/>所以通常情况下<span class="span_property">DataTransfer</span>事件足够使用 通过对事件参数<span class="span_property">Status</span>可得到 当前事件触发时候目标<span class="span_property">Option</span>与当前<span class="span_property">Option</span>的连线关系</p>
<p><span style="color:orangered">当一个连线完成或者断开时候 输入和输出<span class="span_property">STNodeOption</span>都将触发事件 因为无论连接还是断开都应该是相对的需要两个节点共同参与 而通常情况下只需要对输入节点进行事件绑定即可</span></p>
<h2 class="anchor_point" anchor="w">STNodeOptionEventArgs</h2>
<p><span class="span_property">STNodeOptionEventArgs</span>为包含了<span class="span_property">STNodeOption</span>相关事件的事件参数</p>
<hr/>
<span class="span_title anchor_point" anchor="x">属性</span>
<table>
<tr style="text-align:center;background-color:#505050;">
<td>属性名称</td><td>类型</td><td>描述</td>
</tr>
<tr><td>TargetOption</td><td>STNodeOption</td><td>触发此事件的对应Option</td></tr>
<tr><td>Status</td><td>ConnectionStatus</td><td>Option之间的连线状态</td></tr>
<tr><td>IsSponsor</td><td>bool</td><td>是否为此次行为的发起者</td></tr>
</table>
<hr/>
<h2 class="anchor_point" anchor="y">STNodeControl</h2>
<p>关于此类不做过多介绍 可将其视为<span class="span_property">System.Windows.Forms.Control</span>虽然提供的属性和事件并没有太多 但是作者认为也应当足够了 若还有其他需求请自行修改源代码</p>
<hr/>
<span class="span_title anchor_point" anchor="z">Demo</span>
<hr/>
<div style="overflow:auto;margin:0px 10px;max-width:100%"><pre><span style="color:cornflowerblue">public class</span> <span style="color:cyan">STNodeButton</span> : <span style="color:cyan">STNodeControl</span> //自定义一个Button控件
{
<span style="color:cornflowerblue">private bool</span> m_isHover;
<span style="color:cornflowerblue">protected</span> <span style="color:cornflowerblue">override void</span> OnMouseEnter(<span style="color:cyan">EventArgs</span> e) {
<span style="color:cornflowerblue">base</span>.OnMouseEnter(e);
m_isHover = <span style="color:cornflowerblue">true</span>;
<span style="color:cornflowerblue">this</span>.Invalidate();
}
<span style="color:cornflowerblue">protected</span> <span style="color:cornflowerblue">override void</span> OnMouseLeave(<span style="color:cyan">EventArgs</span> e) {
<span style="color:cornflowerblue">base</span>.OnMouseLeave(e);
m_isHover = <span style="color:cornflowerblue">false</span>;
<span style="color:cornflowerblue">this</span>.Invalidate();
}
<span style="color:cornflowerblue">protected</span> <span style="color:cornflowerblue">override void</span> OnPaint(<span style="color:cyan">DrawingTools</span> dt) {
<span style="color:lime">//base.OnPaint(dt);</span>
<span style="color:cyan">Graphics</span> g = dt.<span style="color:cyan">Graphics</span>;
<span style="color:cyan">SolidBrush</span> brush = dt.<span style="color:cyan">SolidBrush</span>;
brush.Color = m_isHover ? <span style="color:cyan">Color</span>.DodgerBlue : <span style="color:cornflowerblue">this</span>.BackColor;
g.FillRectangle(brush, 0, 0, <span style="color:cornflowerblue">this</span>.Width, <span style="color:cornflowerblue">this</span>.Height);
g.DrawString(<span style="color:cornflowerblue">this</span>.Text, <span style="color:cornflowerblue">this</span>.Font, <span style="color:cyan">Brushes</span>.White, <span style="color:cornflowerblue">this</span>.ClientRectangle, <span style="color:cornflowerblue">base</span>.m_sf);
}
}</pre></div><div style="overflow:auto;margin:0px 10px;max-width:100%;"><pre><span style="color:cornflowerblue">public</span> <span style="color:cornflowerblue">class</span> <span style="color:cyan">STNodeImageInput</span> : <span style="color:cyan">STNode</span>
{
<span style="color:cornflowerblue">private</span> <span style="color:cyan">STNodeOption</span> m_option_out;
<span style="color:cornflowerblue">protected</span> <span style="color:cornflowerblue">override void</span> OnCreate() {
<span style="color:cornflowerblue">base</span>.OnCreate();
<span style="color:cornflowerblue">this</span>.Title = <span style="color:lime">"ImageInput"</span>;
m_option_out = <span style="color:cornflowerblue">new</span> <span style="color:cyan">STNodeOption</span>(<span style="color:lime">""</span>, <span style="color:cornflowerblue">typeof</span>(<span style="color:cyan">Image</span>), <span style="color:cornflowerblue">false</span>);
<span style="color:cornflowerblue">this</span>.OutputOptions.Add(m_option_out);
<span style="color:cyan">STNodeButton</span> btn = <span style="color:cornflowerblue">new</span> <span style="color:cyan">STNodeButton</span>();
btn.Left = 10; btn.Top = 10;
btn.Text = <span style="color:lime">"OpenImage"</span>;
btn.MouseClick += <span style="color:cornflowerblue">new</span> <span style="color:cyan">MouseEventHandler</span>(btn_MouseClick);
<span style="color:cornflowerblue">this</span>.Controls.Add(btn);
}
<span style="color:lime">//...other code</span>
}</pre>
</div>
<p><img style="width:173px;" src="./images/open_image.png"/>
<br/>上面代码片段为 DEMO 中 "ImageInput" 节点与自定义 Button 控件代码 以上代码演示了如何自定义与使用一个控件 可以看到与<span class="span_property">System.Windows.Forms.Control</span>差异并不是很大 多以这里并不做过多介绍</p>
</div>
</div>
</body>
</html>

87
docs/doc_cn.html Normal file
View File

@ -0,0 +1,87 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<title>STNodeEditor - 文档教程</title>
<script type="text/javascript" src="./js/jquery-1.10.2.min.js"></script>
<style type="text/css">
body{margin:0px;}
iframe{border:none;}
#left{
position:absolute;
width:50%;
left:0px;
top:0px;
height:100%;
}
#right{
border-left:solid 1px black;
position:absolute;
width:50%;
left:50%;
top:0px;
height:100%;
}
.a_btn{
position: fixed;
display: block;
width: 30px;
height: 30px;
top: 5px;
background-color: rgba(125,125,125,.8);
z-index: 1;
border-radius: 15px;
background-size: cover;
opacity:0.6;
}
.a_btn:hover{background-color:dodgerblue;}
.active:after{
content: ' ';
display: block;
width: 28px;
height: 28px;
border-radius: 14px;
border: solid 1px dodgerblue;
}
#a_btn_lr{
right:85px;
background-image:url('./images/layout_lr.png');
}
#a_btn_l{
right:50px;
background-image:url('./images/layout_l.png');
}
#a_btn_r{
right:15px;
background-image:url('./images/layout_l.png');
}
</style>
<script type="text/javascript">
$(document).ready(function(){
var e_l = $("#left"), e_r = $("#right");
$("#a_btn_l").click(function(){
e_l.css({opacity:1,width:"100%"});
e_r.css({opacity:0});
});
$("#a_btn_r").click(function(){
e_l.css({opacity:0});
e_r.css({opacity:1,left:0,width:"100%"});
});
$("#a_btn_lr").click(function(){
e_l.css({opacity:1,width:"50%"});
e_r.css({opacity:1,left:"50%",width:"50%"});
});
$(".a_btn").click(function(){
$('.a_btn').removeClass('active');
$(this).addClass('active');
});
});
</script>
</head>
<body>
<a id="a_btn_lr" class="a_btn active"></a><a id="a_btn_l" class="a_btn"></a><a id="a_btn_r" class="a_btn"></a>
<iframe id="left" src="./tutorials_cn.html"></iframe>
<iframe id="right" src="./api.html"></iframe>
</body>
</html>

87
docs/doc_en.html Normal file
View File

@ -0,0 +1,87 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<title>STNodeEditor - Document and tutorials</title>
<script type="text/javascript" src="./js/jquery-1.10.2.min.js"></script>
<style type="text/css">
body{margin:0px;}
iframe{border:none;}
#left{
position:absolute;
width:50%;
left:0px;
top:0px;
height:100%;
}
#right{
border-left:solid 1px black;
position:absolute;
width:50%;
left:50%;
top:0px;
height:100%;
}
.a_btn{
position: fixed;
display: block;
width: 30px;
height: 30px;
top: 5px;
background-color: rgba(125,125,125,.8);
z-index: 1;
border-radius: 15px;
background-size: cover;
opacity:0.6;
}
.a_btn:hover{background-color:dodgerblue;}
.active:after{
content: ' ';
display: block;
width: 28px;
height: 28px;
border-radius: 14px;
border: solid 1px dodgerblue;
}
#a_btn_lr{
right:85px;
background-image:url('./images/layout_lr.png');
}
#a_btn_l{
right:50px;
background-image:url('./images/layout_l.png');
}
#a_btn_r{
right:15px;
background-image:url('./images/layout_l.png');
}
</style>
<script type="text/javascript">
$(document).ready(function(){
var e_l = $("#left"), e_r = $("#right");
$("#a_btn_l").click(function(){
e_l.css({opacity:1,width:"100%"});
e_r.css({opacity:0});
});
$("#a_btn_r").click(function(){
e_l.css({opacity:0});
e_r.css({opacity:1,left:0,width:"100%"});
});
$("#a_btn_lr").click(function(){
e_l.css({opacity:1,width:"50%"});
e_r.css({opacity:1,left:"50%",width:"50%"});
});
$(".a_btn").click(function(){
$('.a_btn').removeClass('active');
$(this).addClass('active');
});
});
</script>
</head>
<body>
<a id="a_btn_lr" class="a_btn active"></a><a id="a_btn_l" class="a_btn"></a><a id="a_btn_r" class="a_btn"></a>
<iframe id="left" src="./tutorials_en.html"></iframe>
<iframe id="right" src="./api.html"></iframe>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/images/api.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

BIN
docs/images/download.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

BIN
docs/images/gdip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

BIN
docs/images/gitee.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/images/github.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/images/layout_l.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
docs/images/layout_lr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
docs/images/layout_r.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 744 KiB

0
docs/images/node.gif Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 742 KiB

After

Width:  |  Height:  |  Size: 742 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/images/node_scan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
docs/images/node_ui.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/images/page_top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

BIN
docs/images/sticker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Some files were not shown because too many files have changed in this diff Show More