Winform 拖拽控件流程
1 效果展示,双击节点可以新的页面进行操作

2 代码编写
1、新建一个流程节点用户控件,2个lable 控件组成,如果要lable 可以搞改变大小可以将AutoSize设置为false

后台代码:
public partial class FlowNode : UserControl { /// <summary> /// 上一个节点 /// </summary> public string PreNodeID { get; set; } /// <summary> /// 当前节点 /// </summary> public string NodeID { get; set; } /// <summary> /// 下一个节点 /// </summary> public string NextNodeID { get; set; } /// <summary> /// 输入参数 /// </summary> public HObject InputParms { get; set; } /// <summary> /// 输出参数 /// </summary> public HObject OutputParms { get; set; } /// <summary> /// 保存上一个节点数据 /// </summary> public static FlowNode PreFlowNode { get; set; } /// <summary> /// 节点名称 /// </summary> /// [Category("节点名称")] public string NodeName { get { return labTilt.Text; } set { labTilt.Text = value; } } /// <summary> /// /// </summary> [Category("节点颜色")] public Color LightColor { get { return lblLight.ForeColor; } set { lblLight.ForeColor = value; } } public FlowNode() { InitializeComponent(); NodeID = Guid.NewGuid().ToString("N"); LightColor = Color.LightGray; foreach (Control item in this.Controls) { // 移动相关 item.MouseDown += UserControl1_MouseDown; item.MouseUp += UserControl1_MouseUp; item.MouseMove += UserControl1_MouseMove; //双击打开对应的窗体 item.DoubleClick += Item_DoubleClick; } } /// <summary> /// 双击打开页面 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Item_DoubleClick(object sender, EventArgs e) { var node=(sender as Control).Parent; switch (NodeName) { case "加载图像": new FrmImage(this).ShowDialog(); PreFlowNode = this; break; case "绘制矩形": new FrmROI4(PreFlowNode, this).ShowDialog(); PreFlowNode = this; break; case "创建模版": new FrmCreateModel(PreFlowNode, this).ShowDialog(); PreFlowNode = this; break; case "模版匹配": new FrmResult(PreFlowNode, this).ShowDialog(); break; } } /// <summary> /// 是否移动 /// </summary> bool mMoving = false; Point mStartPoint; /// <summary> /// 按下鼠标左键 我要开始移动了 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UserControl1_MouseDown(object sender, MouseEventArgs e) { // 标识控制准备移动 mMoving = true; //记录开始的位置 mStartPoint=new Point(e.X, e.Y); } /// <summary> /// 拖动节点结束 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UserControl1_MouseUp(object sender, MouseEventArgs e) { if (mMoving) { mMoving = false; Control control= sender as Control; control.Cursor = Cursors.Default; } } /// <summary> /// 移动设置操作 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UserControl1_MouseMove(object sender, MouseEventArgs e) { if (mMoving) { if (e.Button == MouseButtons.Left) { // 这个是label var control = sender as Control; // 这个是用户控件 var node = control.Parent; // 获取控件的偏移量 //将控件当前位置加上鼠标移动的距离得到的新位置 int left = node.Left+(e.X-mStartPoint.X); int top = node.Top+(e.Y-mStartPoint.Y); //控件本身的宽 高 int width=node.Width; int height=node.Height; // 动态获取编辑流程区域大小 var rect = node.Parent.ClientRectangle; //判断拖拽过程中不能超过边界 if (left + width > rect.Width) { left = rect.Width-width; } if (left < 0) { left = 0; } if (top + height > rect.Height) { top = rect.Height-height; } if (top < 0) { top = 0; } //设置控件的新的偏移量 node.Left = left; node.Top = top; //var s= node.Parent; //强制刷新界面 要不然界面成了一个模糊的饼,强制刷新绘图区域 node.Parent.Invalidate(); ; } } } #region 实现绘制节点连续操作 #endregion /// <summary> /// 绘制点到点之间的线 /// </summary> public void DrawPontToPointLine(Control node1, Control node2, Control panel) { Graphics g = panel.CreateGraphics(); // 定义2个点的对象 Point point1, point2; // 左到右 if (Math.Abs(node2.Location.X - node1.Location.X) > Math.Abs(node2.Location.Y - node1.Location.Y) && node2.Location.X >= node1.Location.X) { point1 = new Point(node1.Location.X + node1.Width + 1, node1.Location.Y + node1.Height / 2); point2 = new Point(node2.Location.X - 1, node2.Location.Y + node2.Height / 2); DrawJoinLine(point1, point2, E_DrawLineDirect.L_R, g); } // 右 左 else if (Math.Abs(node2.Location.X - node1.Location.X) > Math.Abs(node2.Location.Y - node1.Location.Y) && node2.Location.X < node1.Location.X) { point1 = new Point(node1.Location.X - 1, node1.Location.Y + node1.Height / 2); point2 = new Point(node2.Location.X + node2.Width + 1, node2.Location.Y + node2.Height / 2); DrawJoinLine(point1, point2, E_DrawLineDirect.R_L, g); } // 上 下 else if (Math.Abs(node2.Location.Y - node1.Location.Y) > Math.Abs(node2.Location.X - node1.Location.X) && node2.Location.Y >= node1.Location.Y) { point1 = new Point(node1.Location.X + node1.Width / 2, node1.Location.Y + node1.Height + 1); point2 = new Point(node2.Location.X + node2.Width / 2, node2.Location.Y - 1); DrawJoinLine(point1, point2, E_DrawLineDirect.U_D, g); } // 下 上 else if (Math.Abs(node2.Location.Y - node1.Location.Y) > Math.Abs(node2.Location.X - node1.Location.X) && node2.Location.Y < node1.Location.Y) { point1 = new Point(node1.Location.X + node1.Width / 2, node1.Location.Y - 1); point2 = new Point(node2.Location.X + node2.Width / 2, node2.Location.Y + node2.Height + 1); DrawJoinLine(point1, point2, E_DrawLineDirect.D_U, g); } } /// <summary> /// 根据2点绘制一条线 /// </summary> /// <param name="p1"></param> /// <param name="p2"></param> /// <param name="forward"></param> /// <param name="g"></param> public void DrawJoinLine(Point p1, Point p2, E_DrawLineDirect forward, Graphics g) { //Graphics g = panel3.CreateGraphics(); g.SmoothingMode = SmoothingMode.HighQuality; Color color = Color.DarkRed; Pen p = new Pen(color, 5); p.DashStyle = DashStyle.Solid; p.StartCap = LineCap.Round; p.EndCap = LineCap.ArrowAnchor; p.LineJoin = LineJoin.Round; Point inflectPoint1; Point inflectPoint2; if (forward == E_DrawLineDirect.L_R || forward == E_DrawLineDirect.L_R) { inflectPoint1 = new Point((p1.X + p2.X) / 2, p1.Y); inflectPoint2 = new Point((p1.X + p2.X) / 2, p2.Y); } else { inflectPoint1 = new Point(p1.X, (p1.Y + p2.Y) / 2); inflectPoint2 = new Point(p2.X, (p1.Y + p2.Y) / 2); } Point[] points = new Point[] { p1, inflectPoint1, inflectPoint2, p2 }; g.DrawLines(p, points); } } /// <summary> /// 是否连线状态 /// </summary> public enum E_DrawStatus { /// <summary> /// 正常状态 /// </summary> Normal, /// <summary> /// 画线 /// </summary> DrawLint } /// <summary> /// 绘制线的方向 /// </summary> public enum E_DrawLineDirect { /// <summary> /// 左到右 /// </summary> L_R, /// <summary> /// 右到左 /// </summary> R_L, /// <summary> /// 上--下 /// </summary> U_D, /// <summary> /// 下--上 /// </summary> D_U }
2 、设置主页面 控件

Label mlabel;
/// <summary>
/// 设置Label 可以移动
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void LabelMouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Control control = sender as Control;
mlabel = (Label)sender;
//设置当前点击上的样式,设置控件可以移动
control.DoDragDrop(sender, DragDropEffects.Copy | DragDropEffects.Move);
}
}
3、设置承载控件Panel 的属性

添加事件
/// <summary>
/// 设置鼠标样式
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void panel3_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(Label)))
{
e.Effect = DragDropEffects.Copy | DragDropEffects.Move;
};
}
/// <summary>
/// 拖动完成时候
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void panel3_DragDrop(object sender, DragEventArgs e)
{
Control contrainer = sender as Control;
FlowNode btn = new FlowNode();
foreach (Control item in btn.Controls)
{
item.Click += btn_Click;
}
btn.Size = new Size(btn.Width, btn.Height);
btn.NodeName = mlabel.Text;
btn.Location = contrainer.PointToClient(new Point(e.X, e.Y));
contrainer.Controls.Add(btn);
}
/// <summary>
/// 鼠标点击
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void btn_Click(object sender, EventArgs e)
{
if (DrawStuas == E_DrawStatus.DrawLint)//绘制线的状态
{
if (NumNo == 0)//点击第一控件
{
Node1 = (FlowNode)(sender as Control).Parent;
NumNo = 1;
}
else if (NumNo == 1)//点击第二个节点 绘制2个连线操作
{
if (Node1.Equals(Node2))
{
MessageBox.Show("不能选择同一个控件");
}
else
{
NumNo = 0;
Node2 = (FlowNode)(sender as Control).Parent;
Node1.NextNodeID = Node2.NodeID;
Node2.PreNodeID= Node1.NodeID;
// 绘制连线
Node1.DrawPontToPointLine(Node1,Node2,panel3);
DrawStuas = E_DrawStatus.Normal;
}
}
}
}
/// <summary>
/// 重绘时发生
/// 当拖动节点时候,需要重新绘制
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void panel3_Paint(object sender, PaintEventArgs e)
{
Control cont=(Control)sender;
//找出流程编辑节点所有的空间
foreach (var item in cont.Controls)
{
if (item is FlowNode)//这个根据自己到时候定义
{
var node1 = (FlowNode)item;
foreach (var item2 in cont.Controls)
{
var node2 = (FlowNode)item2;
if (node1.NextNodeID != null && node1.NextNodeID == node2.NodeID)
{
node1.DrawPontToPointLine(node1, node2,panel3);
}
}
}
}
}
4 、设置开始连线的导火线,上下文,这里的上下文控件可以增加删除 或者其他业务

/// <summary> /// 上下文 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void toolStripMenuItem1_Click(object sender, EventArgs e) { // MessageBox.Show("上下文"); DrawStuas = E_DrawStatus.DrawLint; //注销节点的所有事件 Node1 = null; Node2 = null; NumNo = 0; }
5 当双击节点弹出每个节点对应的设置页面

开始节点,没有输入参数
public partial class FrmImage : Form { FlowNode mFlowNode; public FrmImage(FlowNode flowNode) { InitializeComponent(); mFlowNode = flowNode; }
中间节点有输入参数和输出参数
public partial class FrmCreateModel : Form { FlowNode mPreFlowNode; FlowNode mCurrentFlowNode; //HWindow ttttHWindow; public FrmCreateModel(FlowNode preNode, FlowNode flowNode) { InitializeComponent(); mPreFlowNode = preNode; mCurrentFlowNode = flowNode; }
private void FrmCreateModel_Load(object sender, EventArgs e) { halconUC1.DispImage(mPreFlowNode.OutputParms); }
总结: 大致流程代码都在这里了,可以扩展做自动化测试,流程化操作

浙公网安备 33010602011771号