工作流(2):工作流流程图设计
创始人
2025-05-31 19:34:47

接上篇:

工作流(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源程序如下:


 

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

SugarDao.cs源程序如下:

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using SqlSugar;
namespace WorkFlowDemo
{/// /// SqlSugar/// 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)));}}});}/// /// 检查数据库连接/// 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
{/// /// 原生的sql增删改查/// public class RawSql{/// /// 执行原生的Sql与sql参数的insert、update、delete等操作,返回受影响的行数/// /// /// /// 数据库类型,一般是mysql/// 额外的连接数据库,2为新的数据库连接字符串/// public static int ExecuteCommand(string sql, Dictionary dict, SqlSugar.DbType dbType = SqlSugar.DbType.MySql, int index = 1){List parameters = DictToList(dict);using (var db = SugarDao.GetInstance(dbType, index)){return db.Ado.ExecuteCommand(sql, parameters);}}/// /// 执行原生的Sql与sql参数的select查询等操作,返回首行首列的数据/// /// /// /// 数据库类型,一般是mysql/// 额外的连接数据库,2为新的数据库连接字符串/// public static object GetScalar(string sql, Dictionary dict, SqlSugar.DbType dbType = SqlSugar.DbType.MySql, int index = 1){List parameters = DictToList(dict);using (var db = SugarDao.GetInstance(dbType, index)){return db.Ado.GetScalar(sql, parameters);}}/// /// 执行原生的Sql与sql参数的select查询等操作,返回一个数据表/// /// /// /// 数据库类型,一般是mysql/// 额外的连接数据库,2为新的数据库连接字符串/// public static DataTable GetDataTable(string sql, Dictionary dict, SqlSugar.DbType dbType = SqlSugar.DbType.MySql, int index = 1){List parameters = DictToList(dict);using (var db = SugarDao.GetInstance(dbType, index)){return db.Ado.GetDataTable(sql, parameters);}}/// /// 字典转参数列表/// /// /// private static List DictToList(Dictionary dict){List parameters = new List();for (int i = 0; dict != null && i < dict.Count; i++){KeyValuePair 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
{/// /// 箭头方向:分水平方向Horizontal 和垂直方向Vertical/// [Flags]public enum ArrowDirection{/// /// 空,不绘制箭头/// None = 0,/// /// 垂直方向:向上/// Up = 1,/// /// 垂直方向:向下/// Down = 2,/// /// 水平方向:向左/// Left = 4,/// /// 水平方向:向右/// Right = 8}
}

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

namespace WorkFlowDemo
{partial class FormWorkFlow{/// /// 必需的设计器变量。/// private System.ComponentModel.IContainer components = null;/// /// 清理所有正在使用的资源。/// /// 如果应释放托管资源,为 true;否则为 false。protected override void Dispose(bool disposing){if (disposing && (components != null)){components.Dispose();}base.Dispose(disposing);}#region Windows 窗体设计器生成的代码/// /// 设计器支持所需的方法 - 不要修改/// 使用代码编辑器修改此方法的内容。/// 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);}/// /// 绘制起始环节,返回是否查找到起始环节/// /// /// 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;}/// /// 连续绘制工作流的当前环节与下一环节,以及最后一个终结环节/// /// private void PaintNextNode(string operationName){int index = 1;do{//获取当前节点(环节)的下一环节DataTable dt = RawSql.GetDataTable("select * from process_router where PreviousOperationName=@OperationName", new Dictionary() { { "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() { { "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);}/// /// 绘制环节节点以及流转箭头/// /// /// /// /// 是否是完成环节(最后一道工序)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,高度为60Bitmap 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;}/// /// 将显示的文字放在矩形的中间/// /// /// /// /// 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);}}
}

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

 

相关内容

热门资讯

实测交流!pokerrrr开挂... 实测交流!pokerrrr开挂(透视)外挂透明挂辅助工具(有挂方法)-哔哩哔哩;科技安装教程;136...
实操分享!(wpk德州测试外挂... 1、实操分享!(wpk德州测试外挂)软件透明挂,(WepOke)果真真的有挂,详细教程(有挂模拟器)...
2分钟细说!(德州免费辅助神器... 2分钟细说!(德州免费辅助神器app)软件透明挂,(WEPOke)果真是真的有挂,详细教程(有挂真的...
2分钟细说!cloud辅助(辅... 2分钟细说!cloud辅助(辅助挂)外挂透明挂辅助工具(有挂方法)-哔哩哔哩,支持语音通讯、好友开房...
发现一款!微扑克wpk(透视)... WePoke赢率提升策略‌;发现一款!微扑克wpk(透视)其实是真的有挂(2022已更新)(哔哩哔哩...