首页 > 其他分享 >GDI+画工作流图的一些总结

GDI+画工作流图的一些总结

时间:2024-05-23 09:42:26浏览次数:15  
标签:总结 node new var GDI line Panel public 流图

流程图由节点跟连线组成,先画节点,再根据节点做连线。

1:连线没有可用的控件,而节点可以直接使用Button类充当,使用Panel做画版,拖动button后会自动出现滚动条,且不需要自己做坐标转换。

  1.1:不方便的地方在于如果让节点基础Button,那么属性设置面板里会出现一堆的控件属性,需要再定义个装饰类进行包装,

  1.2:不继承Button类可以通过让节点包含Button控件来实现,将Button的一些必要属性暴露出去,不过跟继承也差不多

2.节点跟连线都自己画出来(通过Panel.Paint 进行渲染),这个方式最灵活,但是需要自己处理节点的坐标变换,与鼠标事件转发,很多坑,但是可以考虑不添加Panel的滚动条

这样就可以避免很多麻烦,现在的27寸显示器,画个几十个节点也还好。

 

一些向量点积,叉积,点到线段距离,向量夹角等。。

    public class Vertex
    {
        public double x;
        public double y;
        public Vertex(double x, double y)
        {
            this.x = x;
            this.y = y;
        }

        public Vertex(Point p)
        {
            this.x = p.X;
            this.y = p.Y;
        }

        //扩展计算两点距离方法(点选查询时会用)
        public double Distance(Vertex another)
        {
            //勾股定理求两点距离
            return Math.Sqrt(Math.Pow(x - another.x, 2) + Math.Pow(y - another.y, 2));
        }



    }
    public class Vector
    {
        public double X;
        public double Y;
        public Vector()
        {

        }
        public Vector(double x, double y)
        {
            this.X = x;
            this.Y = y;

        }
        public  Vector(PointF p0, PointF p1)
        {
            this.X= p1.X - p0.X;
            this.Y = p1.Y - p0.Y;
        }
        public static double Dot(Vector v1, Vector v2)
        {
            return v1.X * v2.X + v1.Y * v2.Y;
        }
        public static double Cross(Vector v1, Vector v2)
        {
            return v1.X * v2.Y - v1.Y * v2.X;
        }
        /// <summary>
        /// 转单位向里
        /// </summary>
        /// <param name="v"></param>
        /// <param name="fac"></param>
        /// <returns></returns>
        public static Vector Unit(Vector v, double fac = 1.0d)
        {
            var v1 = new Vector();
            var a = Math.Sqrt(Math.Pow(v.X, 2) + Math.Pow(v.Y, 2));
            v1.X = (float)((double)v.X * fac / a);
            v1.Y = (float)((double)v.Y * fac / a);
            return v1;
        }
    }


    public class Range
    {
        public int Area { get; set; }
        public double L { get; set; }
        public double H { get; set; }
        public Range(int area, double l, double h)
        {
            Area = area;
            L = l;
            H = h;
        }


    }
    public class Algorithm
    {


        public static double PointToSegment(Point P, Point A, Point B)
        {
            return PointToSegment(new Vertex(P), new Vertex(A), new Vertex(B));
        }

        //点至线段距离
        public static double PointToSegment(
              Vertex P, Vertex A, Vertex B)
        {
            if (A.Equals(B))
                return P.Distance(A);

            //一种钝角情况
            double dp1 = DotProduct(
            CalcVector(B, A), CalcVector(B, P));

            if (dp1 <= 0)
                return P.Distance(B);

            //另一种钝角情况
            double dp2 = DotProduct(
            CalcVector(A, B), CalcVector(A, P));

            if (dp2 <= 0)
                return P.Distance(A);

            //平行四边形面积/底长度
            double signedDistance =
            CrossProduct(A, B, P) / A.Distance(B);

            //平行四边形高(垂线高)
            return Math.Abs(signedDistance);
        }

        //叉积
        public static double CrossProduct(
              Vertex p1, Vertex p2, Vertex p3)
        {
            Vertex v1 = CalcVector(p1, p2);
            Vertex v2 = CalcVector(p1, p3);
            return v1.x * v2.y - v2.x * v1.y;
        }

        //计算向量
        public static Vertex CalcVector(Vertex start, Vertex end)
        {
            return new Vertex(
            end.x - start.x, end.y - start.y);
        }

        public static PointF CalcVector(PointF start, PointF end)
        {
            return new PointF(
            end.X - start.X, end.Y - start.Y);
        }
        public static Point CalcVector(Point start, Point end)
        {
            return new Point(
            end.X - start.X, end.Y - start.Y);
        }

        /// 点乘
        public static double DotProduct(Vertex p1, Vertex p2)
        {
            return p1.x * p2.x + p1.y * p2.y;
        }




        #region 坐标旋转

        /// <summary>
        /// 给定一线段坐标点
        /// 计算p1点两边箭头坐标点
        /// 左右按15度角偏转
        /// </summary>
        /// <param name="p0"></param>
        /// <param name="p1"></param>
        /// <returns></returns>
        public static PointF[] CalcArrowPoint(PointF p0, PointF p1)
        {
            var v1 = CalcVector(p1, p0);
            //计算单位向量
            var v1_1 = CalcUV(v1, 10.0d);//单位向量放大10个像素
            var c=0.9659d;//cos(15)
            var s=0.2588d; //sin(15)
            var  p_s=new PointF();
            p_s.X =(float)( c * v1_1.X - s * v1_1.Y + p1.X);
            p_s.Y =(float)( s * v1_1.X + c * v1_1.Y + p1.Y);

            //sin是奇函数要变符号
            var p_e = new PointF();
            p_e.X = (float)(c * v1_1.X + s * v1_1.Y + p1.X);
            p_e.Y = (float)(-s * v1_1.X + c * v1_1.Y + p1.Y);

            return new PointF[2] { p_s, p_e };


        }

        public static PointF TransCoordinate(PointF p,double theta,double x_offset=0,double y_offset=0 )
        {


                #region
      
                double[,] a =
            {
                {Math.Cos(theta),  -Math.Sin(theta)  ,0 ,x_offset },
                {Math.Sin(theta) ,  Math.Cos(theta)  ,0 ,y_offset },
                {0 ,                0                ,1 , 0},
                {0,                 0                ,0 ,1}

            };
               var  _M = DenseMatrix.OfArray(a);
                #endregion
        

            var B = DenseVector.OfEnumerable(new double[] { p.X, p.Y, 0,1});
            var C = _M.Multiply(B);
            return new PointF((float)C[0], (float)C[1]);
        }
        public static double _theta = Math.PI / 12;//15度角
        private static PointF P(int x, int y)
        {
            return TransCoordinate(new PointF(x, y), _theta);

        }
        public static PointF CalcUV(PointF p,double fac =1.0d)
        {
            var p1 = new PointF();
            var a =Math.Sqrt( Math.Pow( p.X,2) +Math.Pow( p.Y,2));
            p1.X =(float)( (double)p.X * fac/ a);
            p1.Y =(float)( (double)p.Y *fac / a);
            return p1;
        }

        /// <summary>
        /// 长方形,右边:0区,上边:1区,左边:2区,下边3区
        /// </summary>
        /// <param name="theta"></param>
        /// <returns></returns>
        public static List<Range> GenAngleRanges(double theta)
        {
            var list = new List<Range>();
            list.Add(new Range(0, 0, theta));
            list.Add(new Range(1, theta, Math.PI - theta));
            list.Add(new Range(2, Math.PI - theta, Math.PI + theta));
            list.Add(new Range(3, Math.PI + theta, Math.PI * 2 - theta));
            list.Add(new Range(0, Math.PI * 2 - theta, Math.PI * 2 + 0.1));//最后区域再加一点

            return list;
        }


        #endregion


    }
View Code

 

分类等基本结构:

    public enum NodeTypes { Start, Task, End }
    public enum WFObjectType { Link, Start, Task, End }

    public delegate void LinkSelectedHandler(Link link,EventArgs arg);
    public delegate void NodeSelectedHandler(Node node,EventArgs arg);

    public delegate void WFLinkSelectedHandler(WFLink link, EventArgs arg);
    public delegate void WFNodeSelectedHandler(WFNode node, EventArgs arg);

    public class PtyChangedEventArgs : EventArgs
    {
        public String PropertyName { get; set; }
        public Object Value { get; set; }
        public PtyChangedEventArgs()
        {

        }
        public PtyChangedEventArgs(string ptyName,Object v)
        {
            PropertyName = ptyName;
            Value = v;
        }
    }
View Code

 

基础类定义:

public class WFObject
    {

        public static Font C_Font_Tahoma_9 = new Font("Tahoma", 9.0f);
        public static Font C_Font_Tahoma_8 = new Font("Tahoma", 8.0f);
        public static Pen _Pen1 = new Pen(Color.Tan) { Width = 1 };
        public static Pen _Pen2 = new Pen(Color.Red) { Width = 2 };


        #region 按这样的方式声明事件方便子类调用
        protected EventHandler<PtyChangedEventArgs> OnObjPtyChanged = null;
            
        public event EventHandler<PtyChangedEventArgs> ObjPtyChanged
        {

            add
            {
                OnObjPtyChanged += value;

            }
            remove
            {
                OnObjPtyChanged -= value;
                    
            }
        }
        #endregion



        #region 属性
        #region 不可浏览属性
        [Browsable(false)]
        public String Name { get; set; }

         [Browsable(false)]
        public WFObjectType ObjectType { get; set; }


        [Browsable(false)]
        public bool IsSelected { get; set; }
        #endregion

        #region 可浏览属性
        private String _text = "";
        [Category("Like")]
        [Description("显示的文本")]
        public String Text
        {
            get
            {

                return _text;
            }
            set
            {
                _text = value;
                if (OnObjPtyChanged!= null)
                {

                    OnObjPtyChanged(this, new PtyChangedEventArgs() { PropertyName = "Text" });
                }

            }
        }
        #endregion
        #endregion
        public virtual void Paint(PaintEventArgs pevent)
        {

        }


        #region Util
        public static void DrawLineSymbol(Graphics g)
        {
            g.DrawLine(Pens.Black, new Point(5, 5), new Point(15, 5));

        }

        public static void DrawPointer(Graphics g)
        {
            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            var pen = new Pen(Color.Black, 3);
            AdjustableArrowCap aac = new AdjustableArrowCap(4, 3);
            pen.CustomEndCap = aac;//定义线尾的样式为箭头
            var p0 = new Point(12, 20);
            var p1 = new Point(5, 5);
            g.DrawLine(pen, p0, p1);

        }
        public static void DrawNodeSymbol(Graphics g, WFObjectType objType,PointF offset)
        {
            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            if (objType == WFObjectType.Start)
            {
              
                var path = new GraphicsPath();
                path.AddPolygon(new PointF[] { new PointF(offset.X + 5, offset.Y + 5), new PointF(offset.X + 5, offset.Y + 11), new PointF(offset.X + 10, offset.Y + 8) });
                // 绘制三角形
                g.FillPath(Brushes.Red, path);
                g.DrawPath(Pens.Red, path);
            }
            else if (objType == WFObjectType.End)
            {

                var path = new GraphicsPath();
                path.AddRectangle(new RectangleF(offset.X+5, offset.Y+5, 5, 5));
                // 绘制方块
                g.FillPath(Brushes.Black, path);
                g.DrawPath(Pens.Black, path);

            }
            else
            {
                var path = new GraphicsPath();
                path.AddEllipse(new RectangleF(offset.X + 5, offset.Y + 5, 5, 5));
                // 绘制圆
                g.FillPath(Brushes.Green, path);
                g.DrawPath(Pens.Green, path);
            }
        }

        #endregion

    }
View Code

节点类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.Drawing.Drawing2D;

namespace WindowsFormsApplication3
{

    public class WFNode : WFObject
    {


     


        #region 鼠标事件
        //
        // 摘要:
        //     在鼠标单击该控件时发生。
        [Category("CatAction")]
        [Description("ControlOnMouseClickDescr")]
        public event MouseEventHandler MouseClick;

        public void MouseClickAction(object sender, MouseEventArgs e)
        {
            if (MouseClick != null)
            {
                MouseClick(this, e);
            }
        }

        //
        // 摘要:
        //     当鼠标指针位于控件上并按下鼠标键时发生。
        [Category("CatMouse")]
        [Description("ControlOnMouseDownDescr")]
        public event MouseEventHandler MouseDown;

        public void MouseDownAction(object sender, MouseEventArgs e)
        {
            if (MouseDown != null)
            {
                MouseDown(this, e);
            }
        }

        //
        // 摘要:
        //     在鼠标指针移到控件上时发生。
        [Description("ControlOnMouseMoveDescr")]
        [Category("CatMouse")]
        public event MouseEventHandler MouseMove;

        public void MouseMoveAction(object sender, MouseEventArgs e)
        {
            if (MouseMove != null)
            {
                MouseMove(this, e);
            }
        }

        //
        // 摘要:
        //     在鼠标指针在控件上并释放鼠标键时发生。
        [Description("ControlOnMouseUpDescr")]
        [Category("CatMouse")]
        public event MouseEventHandler MouseUp;

        public void MouseUpAction(object sender, MouseEventArgs e)
        {
            if (MouseUp != null)
            {
                MouseUp(this, e);
            }
        }

        #endregion

        [Browsable(false)]
        public bool MoveFlag { get; set; }
        [Browsable(false)]
        public Point MoveClickPoint { get; set; }

        private Size _size;
        public Size Size
        {
            get
            {
                return _size;
            }
            set
            {
                _size = value;
                if (OnObjPtyChanged!= null)
                {
                    OnObjPtyChanged(this, new PtyChangedEventArgs("Size", value));
                }
            }
        }
        public int Width
        {
            get { return Size.Width; }
            set
            {
                _size.Width = value;
                if (OnObjPtyChanged != null)
                {
                    OnObjPtyChanged(this, new PtyChangedEventArgs("Size", value));
                }
            }
        }

        public int Height
        {
            get { return Size.Height; }
            set
            {
                _size.Height = value;
                if (OnObjPtyChanged != null)
                {
                    OnObjPtyChanged(this, new PtyChangedEventArgs("Size", value));
                }
            }
        }

        private Point _location;
        public Point Location
        {
            get
            {
                return _location;
            }
            set
            {
                _location = value;
                if (OnObjPtyChanged != null)
                {
                    OnObjPtyChanged(this, new PtyChangedEventArgs("Location", value));
                }
            }
        }
        private Color _backColor=Color.FromArgb(0xF0,0xF0,0xF0);
        public Color BackColor
        {
            get
            {
                return _backColor;
            }
            set
            {
                _backColor = value;
                if (OnObjPtyChanged != null)
                {
                    OnObjPtyChanged(this, new PtyChangedEventArgs("BackColor", value));
                }
            }
        }

        public WFNode()
        {
            this.Size = new Size(100, 40);
            ObjectType = WFObjectType.Task;

        }





        public override void Paint(PaintEventArgs pevent)
        {

            var g = pevent.Graphics;


            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            var brush = new SolidBrush(this.BackColor);
            var clientRect = new Rectangle(Location, Size);
            g.FillRectangle(brush, clientRect);
            g.DrawRectangle(Pens.LightGray, clientRect);
            var outSize = EnlargeSize(Size, 3);
            if (IsSelected)
            {
                var outLoc = Location;
                outLoc.X = outLoc.X - 3;
                outLoc.Y = outLoc.Y - 3;
                g.DrawRectangle(_Pen2, new Rectangle(outLoc, outSize));
            }
            if (!string.IsNullOrWhiteSpace(Text))
            {
                var textSize=g.MeasureString(Text,WFObject.C_Font_Tahoma_8);
                var p=new PointF();
                p.X=Location.X + (Size.Width- textSize.Width)/2;
                p.Y=Location.Y + (Size.Height - textSize.Height)/2;
                
                g.DrawString(Text,WFObject.C_Font_Tahoma_8,Brushes.Black,p);
            }
            DrawNodeSymbol(g, ObjectType,(PointF)Location);
        }

        public static Size EnlargeSize(Size size, int v)
        {

            size.Height = size.Height + v*2;
            size.Width = size.Width + v*2;
            return size;
        }


    }


}
View Code

连线类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing.Drawing2D;

namespace WindowsFormsApplication3
{
    public class WFLink:WFObject
    {

    




        public Point P0 { get; set; }
        public Point P1 { get; set; }

        [TypeConverter(typeof(ExpandableObjectConverter))]
        [EditorBrowsable(EditorBrowsableState.Always)]
        public WFNode P0Node { get; set; }

        [TypeConverter(typeof(ExpandableObjectConverter))]
        [EditorBrowsable(EditorBrowsableState.Always)]
        public WFNode P1Node { get; set; }

        [Browsable(false)]
        public string Code
        {
            get
            {
                if (string.Compare(P0Node.Name, P1Node.Name) > 0)
                {
                    return P0Node.Name + "_" + P1Node.Name;
                }
                else
                {
                    return P1Node.Name + "_" + P0Node.Name;
                }
            }
        }

        public override void Paint(PaintEventArgs pevent)
        {
            base.Paint(pevent);
            var g = pevent.Graphics;

            var line = this;
            var p0 = line.P0Node.Location;
            var p1 = line.P1Node.Location;



            #region 方案1

            WFDocDesign.AdjLineLink(line, ref p0, ref p1);


            g.DrawLine(line.IsSelected ? WFObject._Pen2 : WFObject._Pen1, p0, p1);

            var arrowPoints = Algorithm.CalcArrowPoint(p0, p1);
            g.DrawLine(line.IsSelected ? WFObject._Pen2 : WFObject._Pen1, arrowPoints[0], p1);
            g.DrawLine(line.IsSelected ? WFObject._Pen2 : WFObject._Pen1, arrowPoints[1], p1);
            #endregion


            #region 连线文字

            if (!string.IsNullOrWhiteSpace(line.Text))
            {
                var lambda = 0.7f;
                var pm = new PointF(0, 0);

                var v_Link_s = Vector.Unit(new Vector(p0, p1));
                var v_base = new Vector(1, 0);

                var theta_r = Math.Acos(Vector.Dot(v_base, v_Link_s));
                var theta_d = theta_r * 180 / Math.PI;

                if (Vector.Cross(v_base, v_Link_s) < 0.0d)
                {
                    theta_d = (360 - theta_d);
                }
                Console.WriteLine(theta_d);
                #region 旋转角度
                var state = g.Save();

                #endregion


                if (theta_d > 100 && theta_d <= 250)
                {
                    pm = new PointF(((float)p1.X * lambda + (float)p0.X * (1 - lambda)), ((float)p1.Y * lambda + (float)p0.Y * (1 - lambda)));
                    Matrix matrix = g.Transform;
                    matrix.RotateAt((float)theta_d + 180, pm);
                    g.Transform = matrix;
                }
                else
                {
                    pm = new PointF(((float)p0.X * lambda + (float)p1.X * (1 - lambda)), ((float)p0.Y * lambda + (float)p1.Y * (1 - lambda)));
                    Matrix matrix = g.Transform;
                    matrix.RotateAt((float)theta_d, pm);
                    g.Transform = matrix;
                }
                g.DrawString(line.Text, WFObject.C_Font_Tahoma_9, Brushes.Black, pm);

                g.Restore(state);
            }


            #endregion
        }


    }
}
View Code

文档类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace WindowsFormsApplication3
{

    public class WFDocDesign
    {


        public event WFLinkSelectedHandler LinkSelected;
        public event WFNodeSelectedHandler NodeSelected;

        public List<WFNode> _NodeList = new List<WFNode>();
        public List<WFLink> _LinkList = new List<WFLink>();
        public List<WFNode> _DrawingLinkNodes = new List<WFNode>();
        public static List<Range> _AngleRanges = new List<Range>();

        public System.Windows.Forms.Cursor Start_Cursor;
        public System.Windows.Forms.Cursor Task_Cursor;
        public System.Windows.Forms.Cursor End_Cursor;
        public System.Windows.Forms.Cursor Line_Cursor;

        public Form MainForm { get; set; }

        //最后选择节点
        public WFNode _LastSelectedNode = null;
        public Panel _Panel = null;

        public WFNode SelectedNode
        {
            get
            {
                return _LastSelectedNode;
            }
        }
        public WFLink SelectedLink
        {
            get
            {
                return _LinkList.FirstOrDefault(ent => ent.IsSelected == true);
            }

        }
         static WFDocDesign()
        {

            #region 划分区间
            {
                var node = new WFNode();
                var p_s = Algorithm.CalcUV(new PointF(node.Width / 2, 0));//水平向量
                var p_x = Algorithm.CalcUV(new PointF(node.Width / 2, node.Height / 2)); //右上半对角线
                var theta = Math.Acos(p_s.X * p_x.X + p_s.Y * p_x.Y);
                WFDocDesign._AngleRanges = Algorithm.GenAngleRanges(theta);
            }
            #endregion
        }
        public WFDocDesign(Panel panel)
        {

            #region 加载光标文件
            this.Start_Cursor = new Cursor(GetType(), "Cursors.Start.cur");
            this.Task_Cursor = new Cursor(GetType(), "Cursors.Rectangle.cur");
            this.End_Cursor = new Cursor(GetType(), "Cursors.End.cur");
            this.Line_Cursor = new Cursor(GetType(), "Cursors.Line.cur");
            #endregion




            _Panel = panel;
            #region 终结点
            //var anchor = new UserControl() { Text = "终结点" };
            //anchor.Enabled = false;
            //anchor.Location = new Point(2000, 1500);
            //_Panel.Controls.Add(anchor);
            #endregion
            _Panel.Paint += _Paint;
            
            

            _Panel.MouseClick += _MouseClick;
            _Panel.MouseMove += _MouseMove;
            _Panel.MouseDown += _MouseDown;
            _Panel.MouseUp +=_MouseUp;
            _Panel.Scroll += (s, e) =>
            {

            };
        }

        private Point TransPoint(Point p)
        {
            p.X = p.X + _Panel.AutoScrollPosition.X;
            p.Y = p.Y + _Panel.AutoScrollPosition.Y;
            return p;
        }
        private WFNode TryGetEventTarget(MouseEventArgs e)
        {
            foreach (var node in _NodeList)
            {
                var loc = TransPoint(node.Location);

                var rect = new Rectangle(loc, node.Size);
                if (rect.Contains(e.Location))
                {
                    return node;
                }
            }
            return null;
        }

       private void _MouseUp(object sender, MouseEventArgs e)
        {
            _FlyAway = null;

            var node = TryGetEventTarget(e);
            if (node != null)
            {
                var ex = TransEventArgs(e, node);
                node.MouseUpAction(this, ex);
                return;
            }
        }

       private MouseEventArgs TransEventArgs(MouseEventArgs e, WFNode node)
       {
            //var ex = new MouseEventArgs(e.Button, e.Clicks, e.Location.X - node.Location.X, e.Location.Y - node.Location.Y, e.Delta);
           var ex = new MouseEventArgs(e.Button, e.Clicks, e.Location.X - node.Location.X + _Panel.AutoScrollPosition.X, e.Location.Y - node.Location.Y + _Panel.AutoScrollPosition.Y, e.Delta);
           return ex;
       }

       /// <summary>
       /// 快速移动鼠标时会出现这样的情况
       /// 鼠标已经离开Node的区域了
       /// </summary>
       private WFNode _FlyAway = null;
       private void _MouseDown(object sender, MouseEventArgs e)
        {
            var node = TryGetEventTarget(e);
            if (node != null)
            {

                var ex = TransEventArgs(e, node);
                node.MouseDownAction(this, ex);
                _FlyAway = node;
                return;
            }
        }

        private void _MouseMove(object sender, MouseEventArgs e)
        {

            var node = TryGetEventTarget(e);
            if (node == null)
            {
                node = _FlyAway;
            }
            if (node != null)
            {
                var ex = TransEventArgs(e, node);
                node.MouseMoveAction(this, ex);
                return;
            }

            if (HasLineDrawing())
            {
                _Panel.Invalidate();
            }
        }
        private void _MouseClick(object sender, MouseEventArgs e)
        {
            #region 绘制面版事件处理
            Console.WriteLine("_Panel.MouseClick" + e.Location);

            #region 识别 Node的_MouseClick事件
            {
                var node = TryGetEventTarget(e);
                if (node != null)
                {
                    var ex = TransEventArgs(e, node);
                    node.MouseClickAction(this, ex);
                    return;
                }
            }
            #endregion

            try
            {



                #region 左键单击,并且光标是[Start,Task,End,Line]
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    if (_Panel.Cursor == Task_Cursor)
                    {
                        var node = NewNode(WFObjectType.Task);
                        node.Location =TransPoint( e.Location);
                        _Panel.Invalidate();
                        return;
                    }
                    else if (_Panel.Cursor == Start_Cursor)
                    {
                        var node = NewNode(WFObjectType.Start);
                        node.Location =TransPoint( e.Location);
                        _Panel.Cursor = Cursors.Default;
                        _Panel.Invalidate();
                        return;
                    }
                    else if (_Panel.Cursor == End_Cursor)
                    {
                        var node = NewNode(WFObjectType.End);
                        node.Location =TransPoint( e.Location);
                        _Panel.Cursor = Cursors.Default;
                        _Panel.Invalidate();
                        return;
                    }
                    else
                    {
           

                    }
                }
                #endregion

                #region 点击选中连线
                var thisTimeSelected = false;

                if (e.Button == System.Windows.Forms.MouseButtons.Right || e.Button == System.Windows.Forms.MouseButtons.Left)
                {

                    
                    var p = _Panel.PointToClient(Cursor.Position);
                    foreach (var line in _LinkList)
                    {
                        var p0 = line.P0Node.Location;
                        var p1 = line.P1Node.Location;

                        AdjLineLink(line, ref p0, ref p1);

                        var dist = Algorithm.PointToSegment(p, p0, p1);
                        Console.WriteLine("dist:" + dist);
                        if (dist <= 5.0)
                        {

                            _NodeList.ForEach(ent => ent.IsSelected = false);
                            _LinkList.ForEach(ent => ent.IsSelected = false);
                            thisTimeSelected = true;
                            line.IsSelected = true;
                            if (LinkSelected != null)
                            {
 
                                LinkSelected(line, null);
                                _Panel.Invalidate();
                            }
                            break;
                        }
                    }
                    _Panel.Invalidate();
                }
                #endregion

                #region 右键菜单
                if (e.Button == System.Windows.Forms.MouseButtons.Right)
                {
                    var line = _LinkList.FirstOrDefault(ent => ent.IsSelected);
                    if (line != null)
                    {
                        ShowLinkContextMenu();
                        return;
                    }
                }
                #endregion

                #region 左右点击空白区域取消画连线与取消选择
                if (thisTimeSelected) return;
                if (e.Button == System.Windows.Forms.MouseButtons.Right || e.Button==MouseButtons.Left)
                {
                    if (HasLineDrawing())
                    {
                        _DrawingLinkNodes.Clear();
                        _Panel.Invalidate();
                        return;
                    }
                    else
                    {
                        //取消当前操作
                        CancelCurrentOpt();
                    }
                }
                #endregion

            }
            catch (Exception ex)
            {
                ErrMsg(ex.Message);
            }
            #endregion
        }
        private void _Paint(object sender, PaintEventArgs e)
        {
            var g = e.Graphics;
            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            g.TranslateTransform(_Panel.AutoScrollPosition.X, _Panel.AutoScrollPosition.Y);

            #region 画方块
            foreach (var node in _NodeList)
            {
                node.Paint(e);
            }
            #endregion

            #region 画连接中的线

            if (HasLineDrawing())
            {
                var pen = new Pen(Color.Tan);
                AdjustableArrowCap aac = new AdjustableArrowCap(2, 5);
                pen.CustomEndCap = aac;//定义线尾的样式为箭头
                var node0 = _DrawingLinkNodes[0];
                var p0 = node0.Location;
                p0.X = p0.X + node0.Width / 2;
                p0.Y = p0.Y + node0.Height / 2;

                var p1 = _Panel.PointToClient(Cursor.Position);
                Console.WriteLine("{0},{1}", p0, p1);
                g.DrawLine(pen, p0, p1);

            }

            #endregion

            #region 画已连接的线
            foreach (var link in _LinkList)
            {
                link.Paint(e);

            }
            #endregion
        }


        public bool HasLineDrawing()
        {
            return (_DrawingLinkNodes.Count == 1 && GetPanelCursor() == Line_Cursor);
        }

        public Cursor GetPanelCursor()
        {
            return _Panel.Cursor;
        }

        public void SetPanelCursor(System.Windows.Forms.Cursor cursor)
        {
            _Panel.Cursor = cursor;
            _Panel.Invalidate();
        }

        public Cursor GetCursorByObjectType(WFObjectType oType)
        {
            if (oType == WFObjectType.Start)
            {
                return Start_Cursor;
            }
            else if (oType == WFObjectType.Task)
            {
                return Task_Cursor;
            }
            else if (oType == WFObjectType.End)
            {
                return End_Cursor;
            }
            return Cursors.Default;
        }

        public static void AdjLineLink(WFLink line, ref Point p0, ref Point p1)
        {
            //方块中点对中点
            p0.X = p0.X + line.P0Node.Width / 2;
            p0.Y = p0.Y + line.P0Node.Height / 2;
            p1.X = p1.X + line.P1Node.Width / 2;
            p1.Y = p1.Y + line.P1Node.Height / 2;


            var v_Link_s = Vector.Unit(new Vector(p0, p1));
            v_Link_s.Y = -v_Link_s.Y; //需要乘以一个绕x轴旋转180度的变换矩阵,相当于y分量取负
            var v_base = new Vector(1, 0);

            var v_Link_t = Vector.Unit(new Vector(p1, p0));
            v_Link_t.Y = -v_Link_t.Y;

            var theta_s = Math.Acos(Vector.Dot(v_base, v_Link_s));
            var theta_t = Math.Acos(Vector.Dot(v_base, v_Link_t));



            #region 源头端调整
            {
                if (Vector.Cross(v_base, v_Link_s) < 0.0d)
                {
                    theta_s = Math.PI * 2 - theta_s;
                }

                var area = 3;
                var fIt = _AngleRanges.FirstOrDefault(ent => theta_s >= ent.L && theta_s < ent.H);
                if (fIt != null)
                {
                    area = fIt.Area;
                }
                if (area == 0)
                {
                    p0.X = line.P0Node.Location.X + line.P0Node.Width;
                    p0.Y = line.P0Node.Location.Y + line.P0Node.Height / 2;
                }
                else if (area == 1)
                {

                    p0.X = line.P0Node.Location.X + line.P0Node.Width / 2; ;
                    p0.Y = line.P0Node.Location.Y;
                }
                else if (area == 2)
                {
                    p0.X = line.P0Node.Location.X;
                    p0.Y = line.P0Node.Location.Y + line.P0Node.Height / 2;
                }
                else if (area == 3)
                {



                    p0.X = line.P0Node.Location.X + line.P0Node.Width / 2;
                    p0.Y = line.P0Node.Location.Y + line.P0Node.Height;
                }

            }
            #endregion

            #region 目标端调整
            {
                if (Vector.Cross(v_base, v_Link_t) < 0.0d)
                {
                    theta_t = Math.PI * 2 - theta_t;
                }

                var area = 3;
                var fIt = _AngleRanges.FirstOrDefault(ent => theta_t >= ent.L && theta_t < ent.H);
                if (fIt != null)
                {
                    area = fIt.Area;
                }
                if (area == 0)
                {
                    p1.X = line.P1Node.Location.X + line.P1Node.Width;
                    p1.Y = line.P1Node.Location.Y + line.P1Node.Height / 2;
                }
                else if (area == 1)
                {
                    p1.X = line.P1Node.Location.X + line.P1Node.Width / 2; ;
                    p1.Y = line.P1Node.Location.Y;
                }
                else if (area == 2)
                {
                    p1.X = line.P1Node.Location.X;
                    p1.Y = line.P1Node.Location.Y + line.P1Node.Height / 2;
                }
                else if (area == 3)
                {

                    p1.X = line.P1Node.Location.X + line.P1Node.Width / 2;
                    p1.Y = line.P1Node.Location.Y + line.P1Node.Height;

                }

            }
            #endregion
        }


        public void BeginDrawStartNode()
        {
            if (_NodeList.Any(ent => ent.ObjectType == WFObjectType.Start)) throw new Exception("开始节点只允许添加一次");

            SetPanelCursor(GetCursorByObjectType(WFObjectType.Start));
        }
        public void BeginDrawEndNode()
        {
            if (_NodeList.Any(ent => ent.ObjectType == WFObjectType.End)) throw new Exception("结束节点只允许添加一次");
            SetPanelCursor(GetCursorByObjectType(WFObjectType.End));
        }
        public void BeginDrawTaskNode()
        {
            SetPanelCursor(GetCursorByObjectType(WFObjectType.Task));
        }
        public void BeginDrawLink()
        {
            _DrawingLinkNodes.Clear();
            SetPanelCursor(Line_Cursor);
        }

        public void RemoveLink(WFLink link)
        {
            _LinkList.Remove(link);
            _Panel.Invalidate();
        }
        public void RemoveNode(WFNode selectedNode)
        {
            var lines4Remove = new List<WFLink>();
            foreach (var line in _LinkList)
            {
                if (line.P0Node.Equals(selectedNode) || line.P1Node.Equals(selectedNode))
                {
                    lines4Remove.Add(line);
                }
            }
            foreach (var line in lines4Remove)
            {
                _LinkList.Remove(line);
            }

            _NodeList.Remove(selectedNode);
            _Panel.Invalidate();
        }
        public void CancelCurrentOpt()
        {

            _DrawingLinkNodes.Clear();
            //需要其他处理
            _NodeList.ForEach(ent => ent.IsSelected = false);
            _LinkList.ForEach(ent => ent.IsSelected = false);
            SetPanelCursor(Cursors.Default);
            _Panel.Invalidate();
        }


        public WFNode NewNode(WFObjectType oType = WFObjectType.Task)
        {




            if (oType != WFObjectType.Task)
            {
                if (_NodeList.Any(ent => ent.ObjectType == oType)) throw new Exception("开始与结束节点只允许添加一次");
            }

            var node = new WFNode() { Name = "node_" + DateTime.Now.Ticks };

            node.ObjPtyChanged += (s,e) =>
            {
                _Panel.Invalidate();
            };
            node.MouseClick += (s, e) =>
            {


                #region 无论左右都会选中
                {

                    _NodeList.ForEach(ent => ent.IsSelected = false);
                    _LinkList.ForEach(ent => ent.IsSelected = false);

                    node.IsSelected = true;

                    if (NodeSelected != null)
                    {

                        NodeSelected(node, null);
                        _Panel.Invalidate();
                    }
                }
                #endregion


                if (GetPanelCursor() == Line_Cursor && e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    try
                    {

                        #region 画连线


                        if (_DrawingLinkNodes.Any(ent => ent.Equals(node))) return;
                        if (_DrawingLinkNodes.Count == 0 && node.ObjectType == WFObjectType.End)
                        {
                            throw new Exception("结束节点不能有连出现");
                        }
                        _DrawingLinkNodes.Add(node);

                        if (_DrawingLinkNodes.Count >= 2)
                        {
                            if (_DrawingLinkNodes[1].ObjectType == WFObjectType.Start)
                            {
                                _DrawingLinkNodes.Remove(_DrawingLinkNodes[1]);
                                throw new Exception("不能指向开始节点");

                            }
                            var line = new WFLink() { P0Node = _DrawingLinkNodes[0], P1Node = _DrawingLinkNodes[1] };

                            if (_LinkList.Any(ent => ent.Code == line.Code))
                            {
                                Console.WriteLine("两个节点已经有连线了!");
                                _DrawingLinkNodes.Clear();
                                _Panel.Invalidate();

                                return;
                            }
                            line.Name = line.P0Node.Name + "--" + line.P1Node.Name;
                            line.IsSelected = false;
                            line.ObjPtyChanged += (sx, ex) =>
                            {
                                _Panel.Invalidate();
                            };
                            _LinkList.Add(line);
                            _Panel.Invalidate();
                            Console.WriteLine("加入列线条:" + line.Name);
                            // checkBox1.Checked = false;
                            _DrawingLinkNodes.Clear();
                        }
                        #endregion
                    }
                    catch (Exception ex)
                    {
                        ErrMsg(ex.Message);
                        _Panel.Invalidate();

                    }

                    return;
                }




                //Console.WriteLine(node.Name + "be Clicked");
            };
            node.MouseDown += (s, e) =>
            {
                if (e.Button == System.Windows.Forms.MouseButtons.Left && GetPanelCursor() == Cursors.Default)
                {
                    Console.WriteLine(node.Name + "MouseDown");
                    node.MoveFlag = true;
                    node.MoveClickPoint = e.Location;
                }
            };
            node.MouseMove += (s, e) =>
            {
                Console.WriteLine("Node.MouseMove" + e.Location);
                if (e.Button == System.Windows.Forms.MouseButtons.Left && GetPanelCursor() == Cursors.Default)
                {
                    if (node.MoveFlag)
                    {
                        var curNode = s as WFNode;
                        var point4Parent = _Panel.PointToClient(Cursor.Position);
                        point4Parent.X = point4Parent.X - node.MoveClickPoint.X;
                        point4Parent.Y = point4Parent.Y - node.MoveClickPoint.Y;
                        if (point4Parent.X < -5) point4Parent.X = -5;
                        if (point4Parent.Y < -5) point4Parent.Y = -5;
                        if (point4Parent.X > (_Panel.Width-node.Width)) point4Parent.X = _Panel.Width - node.Width;
                        if (point4Parent.Y > (_Panel.Height-node.Height)) point4Parent.Y = _Panel.Height - node.Height;
                        curNode.Location =TransPoint( point4Parent);
                        _Panel.Invalidate();
                        
                    }
                }
            };
            node.MouseUp += (s, e) =>
            {

                var sNode = s as WFNode;
                if (e.Button == System.Windows.Forms.MouseButtons.Right && sNode.ObjectType == WFObjectType.Task)
                {
                    _LastSelectedNode = sNode;
                    ShowNodeContextMenu();
                   
                    return;

                }

                if (e.Button == System.Windows.Forms.MouseButtons.Left && GetPanelCursor() == Cursors.Default)
                {
                    node.MoveFlag = false;
                }
            };
            node.Location = new Point(-100, -100);
            node.ObjectType = oType;
            if (oType == WFObjectType.Task) node.Text = "任务节点" + _NodeList.Count;
            if (oType == WFObjectType.Start) node.Text = "开始";
            if (oType == WFObjectType.End) node.Text = "结束";
            _NodeList.Add(node);
            return node;
        }




        #region Helper

        public void ErrMsg(string errStr)
        {
            dynamic dy = MainForm;
            if (dy!=null)
            {
                dy.ErrMsg(errStr);
            }

        }
        public void ShowNodeContextMenu()
        {
            dynamic dy = MainForm;
            if (dy!=null)
            {
                dy.ShowNodeContextMenu();
            }
        }
        public void ShowLinkContextMenu()
        {
            dynamic dy = MainForm;
            if (dy!=null)
            {
                dy.ShowLinkContextMenu();
            }
        }
        #endregion
    }
}
View Code

 

另外自己继承个Panel开启一下双缓冲设置

Form应用

public partial class Form3 : Form
    {

        public WFDocDesign _Doc=null;

      



        public Form3()
        {

            InitializeComponent();

            _Doc = new WFDocDesign(panel1);
            _Doc.MainForm = this;
   

            #region 按钮标志
            {
                btnStartNode.Paint += (s, e) =>
                {
                    WFObject.DrawNodeSymbol(e.Graphics, WFObjectType.Start,new PointF());
                };
                btnEndNode.Paint += (s, e) =>
                {
                    WFObject.DrawNodeSymbol(e.Graphics, WFObjectType.End, new PointF());
                };
                btnTaskNode.Paint += (s, e) =>
                {
                    WFObject.DrawNodeSymbol(e.Graphics, WFObjectType.Task, new PointF());
                };
                btnLine.Paint += (s, e) =>
                {
                    WFObject.DrawLineSymbol(e.Graphics);
                };
                btnPointer.Paint += (s, e) =>
                {
                    WFObject.DrawPointer(e.Graphics);
                };
             
            }

            #endregion

            #region 对象选中事件处理
            _Doc.LinkSelected += (line, e) =>
            {

                propertyGrid1.SelectedObject = line;
              
            };
            _Doc.NodeSelected += (node, e) =>
            {
                
                propertyGrid1.SelectedObject = node;

            };
            #endregion

            #region 弹出菜单
            barButtonItem1.ItemClick += (s, e) =>
            {
                try
                {
                    var selectedNode = _Doc.SelectedNode;
                    

                    if (selectedNode == null) return;
                    if (MessageBox.Show("确认删除选中节点吗?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) != System.Windows.Forms.DialogResult.OK) return;

                    _Doc.RemoveNode(selectedNode);
                }
                catch (Exception ex)
                {
                    ErrMsg(ex.Message);
                }
            };
            barButtonItem2.ItemClick += (s, e) =>
            {
                try
                {
                    var link = _Doc.SelectedLink;
                    if (link == null) return;
                    if (MessageBox.Show("确认删除选中连线吗?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) != System.Windows.Forms.DialogResult.OK) return;

                    _Doc.RemoveLink(link);
                }
                catch (Exception ex)
                {
                    ErrMsg(ex.Message);
                }
            };

            #endregion
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void btnStartNode_Click(object sender, EventArgs e)
        {
            try
            {


                _Doc.BeginDrawStartNode();

            }
            catch (Exception ex)
            {
                ErrMsg(ex.Message);
            }
        }

        private void btnEndNode_Click(object sender, EventArgs e)
        {
            try
            {

                _Doc.BeginDrawEndNode();

            }
            catch (Exception ex)
            {
                ErrMsg(ex.Message);
            }
        }

        private void btnTaskNode_Click(object sender, EventArgs e)
        {
            try
            {


                _Doc.BeginDrawTaskNode();
            }
            catch (Exception ex)
            {
                ErrMsg(ex.Message);
            }
        }

        private void btnLine_Click(object sender, EventArgs e)
        {
            try
            {

                _Doc.BeginDrawLink();
            }
            catch (Exception ex)
            {
                ErrMsg(ex.Message);
            }
        }

        private void btnPointer_Click(object sender, EventArgs e)
        {

            _Doc.CancelCurrentOpt();
        }

        public void ErrMsg(string errStr)
        {
            MessageBox.Show(errStr, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        public void ShowNodeContextMenu()
        {
            popupMenu1.ShowPopup(System.Windows.Forms.Control.MousePosition);
        }
        public void ShowLinkContextMenu()
        {
            popupMenu2.ShowPopup(System.Windows.Forms.Control.MousePosition);
        }

    
    }
View Code

 

标签:总结,node,new,var,GDI,line,Panel,public,流图
From: https://www.cnblogs.com/wdfrog/p/18207660

相关文章

  • 每日总结
    PreparedStatement可以防止sql注入问题,效率更高先进行预编译sql,将要设置的字段值使用占位符本质:预编译会将传递进来的参数包裹成字符,而单引号会被转义字符转换为空内容,有效的防止sql注入的问题CRUD--SELECTStringsql="select*fromuserswhereid=?";st=......
  • 【学习整理】编程知识点总结
    编程知识点总结查询接口实现查询接口示例查询排序@SortDefault查询参数配置@ApiIgnore@Param消息发送常量定义......
  • 相似标准型 总结
    ......
  • 3/25数仓面试总结
    数据采集模块 主要采集什么 业务数据和用户行为数据 包括 页面浏览记录,启动记录,错误记录,曝光记录,动作记录格式包括页面日志和启动日志JSON格式?flume怎么采集   第一次见没有sink的flume   业务数据怎么说?sku商品表 平台表流水表 退单表订......
  • 总结全网C#取随机数方法(整型,浮点型,字符串)
    原文链接:https://blog.csdn.net/m0_65636467/article/details/127770112C#取随机数(Random篇)一、整数随机数//10以内的随机整数Randomrd=newRandom();intn=ran.Next(10);//1-100的随机整数intp=rd.Next(1,100);//大于等于1小于100的整数intNext(intmi......
  • 站在岸上学不会游泳 | 算法校招生的高效成长总结
       在这个由数据编织、由算法驱动的时代,AI大模型正成为推动社会进步的重要力量。我们不仅是变革的见证者,更是推动者和塑造者。感谢零售UP技术人栏目的邀请,本文借此机会回顾一下自己的算法之路上的一些故事和思考,希望能带给读者一些帮助。介绍自己我是2020届校招生,INFJ提倡......
  • 定位辽宁:种植玉米一周后的经验总结 —— 关键因素:浇水、浅覆土,种子泡水并不关键
    地点定位:辽宁,辽南;时间定位:上周;本文记录时间为2024年5月22日,玉米种子时间大致为一周前。说来也是惭愧,一家农村人,没有一个会种地的。我今年也是突发奇想,想看看玉米发芽的影响因素有那些,注意,这里不包括自然的气温和降雨,因为这两个因素和种植时间、地点有关,这两个因素已交代。......
  • salesforce零基础学习(一百三十八)零碎知识点小总结(十)
    本篇参考: https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_5level_SOQLqueries.htm&release=250&type=5https://developer.salesforce.com/tools/vscode/en/einstein/einstein-overviewhttps://developer.salesforce.com/tools/vscode/en/user-g......
  • MQ的一些总结
    原代码地址快速掌握RabbitMQ(二)——四种Exchange介绍及代码演示-捞月亮的猴子-博客园(cnblogs.com)1四种交换规则1direct类型根据不同的routingKey和推送到不同的消费者2fanout类型生产和消费定义对应交换机和队列,然后根据这两个来确认关系和推送......
  • SonarLint实践总结
    SonarLint实践总结1、集合要用isEmpty()判空。MinorcodesmellUseisEmpty()tocheckwhetherthecollectionisemptyornot.问题代码:   Rule:UsingCollection.size()totestforemptinessworks,butusingCollection.isEmpty()makesthecodemorer......