工作流(2):工作流流程图设计

接上篇:

工作流(1):表格设计_斯内科的博客-CSDN博客

使用Winform的GDI进行流程图绘制,实现环节流转。

比如流程图如下:

         A---->B---->C

                            |

         F<----E<----D

新建windows应用程序WorkFlowDemo,将默认的Form1重命名为FormWorkFlow,引入开源ORM框架SqlSugar以及log4net,以及操作mysql的库MySql.Data.dll,然后添加对System.Configuration的引用。

一、默认的应用程序配置文件App.Config源程序如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
 <connectionStrings>
	 <add name="mysql" providerName="MySql.Data.MySqlClient" connectionString="Server=127.0.0.1;Database=workflow_demo;Uid=root;Pwd=root;" />
 </connectionStrings>
</configuration>

二、增加操作mysql的类文件SugarDao,

SugarDao.cs源程序如下:

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using SqlSugar;
namespace WorkFlowDemo
{
    /// <summary>
    /// SqlSugar
    /// </summary>
    public class SugarDao
    {
        //禁止实例化
        private SugarDao()
        {

        }
        public static string ConnectionString(int index = 1)
        {
            string reval = System.Configuration.ConfigurationManager.ConnectionStrings["mysql"].ConnectionString;
            return reval;
        }
        public static SqlSugarClient GetInstance(SqlSugar.DbType dbType= SqlSugar.DbType.MySql, int index = 1)
        {
            return new SqlSugarClient(new ConnectionConfig()
            {
                DbType = dbType,
                ConnectionString = ConnectionString(index),
                InitKeyType = InitKeyType.Attribute,
                IsAutoCloseConnection = true,
                AopEvents = new AopEvents
                {
                    OnLogExecuting = (sql, p) =>
                    {
                        Console.WriteLine(sql);
                        Console.WriteLine(string.Join(",", p?.Select(it => it.ParameterName + ":" + it.Value)));
                    }
                }
            });
        }
        /// <summary>
        /// 检查数据库连接
        /// </summary>
        public static bool CheckConnect()
        {
            SqlSugarClient db = GetInstance();
            string connectionString = db.CurrentConnectionConfig.ConnectionString;//连接字符串
            try
            {
                db.Open();
                return true;
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show($"连接mysql数据库失败,【{ex.Message}】.\n连接字符串【{connectionString}】", "出错");
                return false;
            }
            finally
            {
                db.Close();
            }
        }
    }
}

三、增加操作类RawSql,

RawSql.cs源程序如下

using SqlSugar;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WorkFlowDemo
{
    /// <summary>
    /// 原生的sql增删改查
    /// </summary>
    public class RawSql
    {
        /// <summary>
        /// 执行原生的Sql与sql参数的insert、update、delete等操作,返回受影响的行数
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="dict"></param>
        /// <param name="dbType">数据库类型,一般是mysql</param>
        /// <param name="index">额外的连接数据库,2为新的数据库连接字符串</param>
        /// <returns></returns>
        public static int ExecuteCommand(string sql, Dictionary<string, object> dict, SqlSugar.DbType dbType = SqlSugar.DbType.MySql, int index = 1)
        {
            List<SugarParameter> parameters = DictToList(dict);
            using (var db = SugarDao.GetInstance(dbType, index))
            {
                return db.Ado.ExecuteCommand(sql, parameters);
            }
        }

        /// <summary>
        /// 执行原生的Sql与sql参数的select查询等操作,返回首行首列的数据
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="dict"></param>
        /// <param name="dbType">数据库类型,一般是mysql</param>
        /// <param name="index">额外的连接数据库,2为新的数据库连接字符串</param>
        /// <returns></returns>
        public static object GetScalar(string sql, Dictionary<string, object> dict, SqlSugar.DbType dbType = SqlSugar.DbType.MySql, int index = 1)
        {
            List<SugarParameter> parameters = DictToList(dict);
            using (var db = SugarDao.GetInstance(dbType, index))
            {
                return db.Ado.GetScalar(sql, parameters);
            }
        }

        /// <summary>
        /// 执行原生的Sql与sql参数的select查询等操作,返回一个数据表
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="dict"></param>
        /// <param name="dbType">数据库类型,一般是mysql</param>
        /// <param name="index">额外的连接数据库,2为新的数据库连接字符串</param>
        /// <returns></returns>
        public static DataTable GetDataTable(string sql, Dictionary<string, object> dict, SqlSugar.DbType dbType = SqlSugar.DbType.MySql, int index = 1)
        {
            List<SugarParameter> parameters = DictToList(dict);
            using (var db = SugarDao.GetInstance(dbType, index))
            {
                return db.Ado.GetDataTable(sql, parameters);
            }
        }

        /// <summary>
        /// 字典转参数列表
        /// </summary>
        /// <param name="dict"></param>
        /// <returns></returns>
        private static List<SugarParameter> DictToList(Dictionary<string, object> dict)
        {
            List<SugarParameter> parameters = new List<SugarParameter>();
            for (int i = 0; dict != null && i < dict.Count; i++)
            {
                KeyValuePair<string, object> keyValuePair = dict.ElementAt(i);
                parameters.Add(new SugarParameter(keyValuePair.Key, keyValuePair.Value));
            }
            return parameters;
        }
    }
}

四、新建枚举类ArrowDirection

ArrowDirection.cs源程序如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WorkFlowDemo
{
    /// <summary>
    /// 箭头方向:分水平方向Horizontal 和垂直方向Vertical
    /// </summary>
    [Flags]
    public enum ArrowDirection
    {
        /// <summary>
        /// 空,不绘制箭头
        /// </summary>
        None = 0,
        /// <summary>
        /// 垂直方向:向上
        /// </summary>
        Up = 1,
        /// <summary>
        /// 垂直方向:向下
        /// </summary>
        Down = 2,
        /// <summary>
        /// 水平方向:向左
        /// </summary>
        Left = 4,
        /// <summary>
        /// 水平方向:向右
        /// </summary>
        Right = 8
    }
}

五、窗体FormWorkFlow.Designer.cs设计器源程序如下:

namespace WorkFlowDemo
{
    partial class FormWorkFlow
    {
        /// <summary>
        /// 必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

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

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

        /// <summary>
        /// 设计器支持所需的方法 - 不要修改
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // FormWorkFlow
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(884, 695);
            this.Name = "FormWorkFlow";
            this.Text = "工作流节点(环节)流转图";
            this.Load += new System.EventHandler(this.FormWorkFlow_Load);
            this.ResumeLayout(false);

        }

        #endregion
    }
}

六、核心流程图绘制FormWorkFlow.cs程序如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WorkFlowDemo
{
    public partial class FormWorkFlow : Form
    {
        public FormWorkFlow()
        {
            InitializeComponent();
        }

        private void FormWorkFlow_Load(object sender, EventArgs e)
        {
            //DataTable dt = RawSql.GetDataTable("select * from process_router where PreviousOperationName='' or PreviousOperationName is null", null);
            //if (dt == null || dt.Rows.Count == 0) 
            //{
            //    MessageBox.Show("没有配置工艺路线【process_router】的起始工序,请检查工艺路线配置", "出错");
            //    return;
            //}
            //string nodeText = dt.Rows[0]["OperationName"].ToString();
            //Panel panel = new Panel();
            //panel.Location = new Point(10, 10);
            //panel.Name = "panel1";
            //panel.Size = new Size(160, 60);
            //this.Controls.Add(panel);
            //AddWorkFlowNode(panel, Color.Red, nodeText, ArrowDirection.Right);
            string nodeText;
            if (!PaintStartNode(out nodeText)) 
            {
                return;
            }
            PaintNextNode(nodeText);
        }

        /// <summary>
        /// 绘制起始环节,返回是否查找到起始环节
        /// </summary>
        /// <param name="nodeText"></param>
        /// <returns></returns>
        private bool PaintStartNode(out string nodeText) 
        {
            nodeText = string.Empty;
            DataTable dt = RawSql.GetDataTable("select * from process_router where PreviousOperationName='' or PreviousOperationName is null", null);
            if (dt == null || dt.Rows.Count == 0)
            {
                MessageBox.Show("没有配置工艺路线【process_router】的起始工序,请检查工艺路线配置", "出错");
                return false;
            }
            nodeText = dt.Rows[0]["OperationName"].ToString();
            Panel panel = new Panel();
            panel.Location = new Point(10, 10);
            panel.Name = "panel1";
            panel.Size = new Size(160, 60);
            this.Controls.Add(panel);
            AddWorkFlowNode(panel, Color.Green, nodeText, ArrowDirection.Right, 0);
            //PaintNodeAndArrowPanel(0, Color.Red, nodeText, false);
            return true;
        }

        /// <summary>
        /// 连续绘制工作流的当前环节与下一环节,以及最后一个终结环节
        /// </summary>
        /// <param name="operationName"></param>
        private void PaintNextNode(string operationName)
        {
            int index = 1;
            do
            {
                //获取当前节点(环节)的下一环节
                DataTable dt = RawSql.GetDataTable("select * from process_router where PreviousOperationName=@OperationName", new Dictionary<string, object>() { { "OperationName", operationName } });
                if (dt != null && dt.Rows.Count > 0)
                {
                    operationName = dt.Rows[0]["OperationName"].ToString();
                    //查找是否是终结环节
                    dt = RawSql.GetDataTable("select * from process_router where PreviousOperationName=@OperationName", new Dictionary<string, object>() { { "OperationName", operationName } });
                    if (dt == null || dt.Rows.Count == 0)
                    {
                        //当前环节 没有下一个环节 ,则认为是工序完成环节,不显示向右 或向下箭头
                        PaintNodeAndArrowPanel(index, Color.Red, operationName, true);
                        break;
                    }
                    PaintNodeAndArrowPanel(index, Color.Blue, operationName, false);
                }
                index++;
            } while (true);
        }

        /// <summary>
        /// 绘制环节节点以及流转箭头
        /// </summary>
        /// <param name="index"></param>
        /// <param name="color"></param>
        /// <param name="operationName"></param>
        /// <param name="isCompletedNode">是否是完成环节(最后一道工序)</param>
        private void PaintNodeAndArrowPanel(int index, Color color, string operationName, bool isCompletedNode)
        {
            Panel panel = new Panel();
            panel.Name = $"panel{index + 1}";
            int rowNumber = index / 3 * 2;
            switch (index % 6)
            {
                case 1:
                    panel.Location = new Point(10 + 160 * 2, 10 + 60 * rowNumber);
                    panel.Size = new Size(160, 60);
                    AddWorkFlowNode(panel, color, operationName, isCompletedNode ? ArrowDirection.None : ArrowDirection.Right, index);
                    break;
                case 2:
                    panel.Location = new Point(10 + 160 * 4, 10 + 60 * rowNumber);
                    panel.Size = new Size(160, 60);
                    AddWorkFlowNode(panel, color, operationName, isCompletedNode ? ArrowDirection.None : ArrowDirection.Down, index);
                    break;
                case 3:
                    panel.Location = new Point(10 + 160 * 2, 10 + 60 * rowNumber);
                    panel.Size = new Size(160 * 2, 60);
                    AddWorkFlowNode(panel, color, operationName, isCompletedNode ? ArrowDirection.None : ArrowDirection.Left, index);
                    break;
                case 4:
                    panel.Location = new Point(10 + 160 * 0, 10 + 60 * rowNumber);
                    panel.Size = new Size(160 * 2, 60);
                    AddWorkFlowNode(panel, color, operationName, isCompletedNode ? ArrowDirection.None : ArrowDirection.Left, index);
                    break;
                case 5:
                    panel.Location = new Point(10 + 160 * 0, 10 + 60 * rowNumber);
                    panel.Size = new Size(160, 60);
                    AddWorkFlowNode(panel, color, operationName, isCompletedNode ? ArrowDirection.None : ArrowDirection.Down, index);
                    break;
                case 0:
                    panel.Location = new Point(10 + 160 * 0, 10 + 60 * rowNumber);
                    panel.Size = new Size(160, 60);
                    AddWorkFlowNode(panel, color, operationName, isCompletedNode ? ArrowDirection.None : ArrowDirection.Right, index);
                    break;
            }
            this.Controls.Add(panel);
        }

        private void AddWorkFlowNode(Panel panel, Color color, string text, ArrowDirection arrowDirection, int index) 
        {
            panel.BackgroundImage = null;//清除背景
            //面板宽度为160,高度为60
            Bitmap bitmap = new Bitmap(panel.Width * 2, panel.Height);
            if (arrowDirection == ArrowDirection.Up || arrowDirection == ArrowDirection.Down) 
            {
                bitmap = new Bitmap(panel.Width, panel.Height * 2);
            }
            Graphics graphics = Graphics.FromImage(bitmap);
            Rectangle rect = new Rectangle(0, 0, panel.Width, panel.Height);
            if (index % 6 == 3 || index % 6 == 4)
            {
                //需要向左的环节,矩形绘制需要更新
                rect = new Rectangle(panel.Width / 2, 0, panel.Width / 2, panel.Height);
            }
            //Color color = Color.Blue;
            //string text = "Busbar焊接";
            graphics.FillRectangle(new SolidBrush(color), rect);
            graphics.DrawRectangle(new Pen(color), rect);
            AddTextAlignCenter(graphics, text, new Font("华文楷体", 16), rect);

            Pen pen = new Pen(Color.OrangeRed, 3);//画笔:线条颜色、粗细
            //DashStyle.Dot 代表虚线
            pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;//实线
            pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;//设置线尾的样式为箭头

            switch (arrowDirection)
            {
                case ArrowDirection.None:
                    if (index % 6 == 3 || index % 6 == 4)
                    {
                        panel.Location = new Point(panel.Location.X + 160, panel.Location.Y);
                    }
                    break;
                case ArrowDirection.Up:
                    break;
                case ArrowDirection.Down:
                    graphics.DrawLine(pen, new Point(panel.Width / 2, panel.Height), new Point(panel.Width / 2, panel.Height * 2));
                    //将面板高度放大到两倍,下边部分用于显示线条
                    panel.Height = panel.Height * 2;
                    break;
                case ArrowDirection.Left:
                    graphics.DrawLine(pen, new Point(panel.Width / 2, panel.Height / 2), new Point(0, panel.Height / 2));
                    //将面板宽度放大到两倍,右边部分用于显示线条
                    //panel.Width = panel.Width * 2;
                    panel.Location = new Point(panel.Location.X + 160, panel.Location.Y);
                    break;
                case ArrowDirection.Right:
                    graphics.DrawLine(pen, new Point(panel.Width, panel.Height / 2), new Point(panel.Width * 2, panel.Height / 2));
                    //将面板宽度放大到两倍,右边部分用于显示线条
                    panel.Width = panel.Width * 2;
                    break;
            }
            graphics.Dispose();
            pen.Dispose();
            panel.BackgroundImage = bitmap;
        }

        /// <summary>
        /// 将显示的文字放在矩形的中间
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="text"></param>
        /// <param name="font"></param>
        /// <param name="rect"></param>
        private void AddTextAlignCenter(Graphics graphics, string text, Font font, Rectangle rect)
        {
            SizeF sizeF = graphics.MeasureString(text, font);
            float destX = rect.X + (rect.Width - sizeF.Width) / 2;
            float destY = rect.Y + (rect.Height - sizeF.Height) / 2;
            graphics.DrawString(text, font, Brushes.Black, destX, destY);
        }
    }
}

 七、环节流程图运行如下:

猜你喜欢

转载自blog.csdn.net/ylq1045/article/details/127985863