玩轉控制元件:GDI+動態繪製流程圖

何以解憂唯有擼碼發表於2020-10-13
  •    前言

  今天,要跟大家一起分享是“GDI+動態生成流程圖”的功能。別看名字高大上(也就那樣兒--!),其實就是動態生成控制元件,然後GDI+繪製直線連線控制元件罷了。實際專案效果圖如下:

 

  • Talk is Cheap,Show me the Code

  首先,人靠衣裝馬靠鞍!在繪製流程圖之前,我們得有個高大上的背景來襯托,比如網格背景:

程式碼如下:

  /// <summary>
  /// 初始化網格
  /// </summary>
  private void InitGridLine()
  {
      pictureBox1.BorderStyle = BorderStyle.Fixed3D;
      pictureBox1.Focus();
      m_picture = pictureBox1.CreateGraphics();
      Bitmap canvas = new Bitmap(pictureBox1.Width, pictureBox1.Height);
      Graphics gp = Graphics.FromImage(canvas);
      DrawGrid(gp);
      pictureBox1.BackgroundImage = canvas;
      pictureBox1.Refresh();
  }
  //繪製網格
  private void DrawGrid(Graphics gp)
  {
      for (int i = 0; i < Row; i++)
      {
          gp.DrawLine(new Pen(Color.LightCyan), (i + 1) * pictureBox1.Width / Row, 0, (i + 1) * pictureBox1.Width / Row, pictureBox1.Height);
      }
      for (int i = 0; i < colums; i++)
      {
          gp.DrawLine(new Pen(Color.LightCyan), 0, (i + 1) * pictureBox1.Height / colums, pictureBox1.Width, (i + 1) * pictureBox1.Height / colums);
      }
  }

     我們此處以PictureBox為畫布,初始化好網格背景後,就可以開始建立流程標籤了,效果如下:

  程式碼如下:

/// <summary>
/// 繪製元素,此處以Label為例
/// </summary>
/// <returns></returns>
private Label createBlock(string lblName)
{
    try
    {
        Label label = new Label();
        label.AutoSize = false;
        //TODO:如需動態生成每個標籤元素位置,請根據實際情況,初始化標籤的Location即可。此處預設X=150,Y 以75間隔遞增
        label.Location = new Point(150, iPosition); 
        iPosition = iPosition + 75;
        label.Size = new Size(89, 36);
        label.BackColor = Color.DarkOliveGreen;
        label.ForeColor = Color.Black;
        label.FlatStyle = FlatStyle.Flat;
        label.TextAlign = ContentAlignment.MiddleCenter;
        label.Text = lblName;
        //TODO;可以繫結標籤元素的右鍵事件
        //label.ContextMenuStrip = contextBlock;
        pictureBox1.Controls.Add(label);
        //拖拽移動
        MoveBlock(label);
        return label;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    return null;
}

    實現動態生成的標籤拖拽移動效果,方法如下:

 //標籤移動效果
private void MoveBlock(Label block, Label endBlock = null)
{
    block.MouseDown += (ss, ee) =>
    {
        if (ee.Button == System.Windows.Forms.MouseButtons.Left)
            fPoint = Control.MousePosition;
    };
    block.MouseMove += (ss, ee) =>
    {
        if (ee.Button == System.Windows.Forms.MouseButtons.Left)
        {
            Point temp = Control.MousePosition;
            Point res = new Point(fPoint.X - temp.X, fPoint.Y - temp.Y);

            block.Location = new Point(block.Location.X - res.X,
                                       block.Location.Y - res.Y);
            fPoint = temp;
            pictureBox1.Invalidate();   // <------- draw the new lines
        }
    };
}

      生成好背景網格和標籤,以及實現標籤的拖拽後,就需要繪製直線按自己需求,實現連線了本文我們用 Tuple 來實現兩個標籤的連線關係。

//用於儲存需要直線連線的元素
List<Tuple<Label, Label>> lines = new List<Tuple<Label, Label>>();

    繫結PictureBox的Paint事件,利用GDI+的DrawLine實現繪製直線。

private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
    foreach (Tuple<Label, Label> t in lines)
    {
        Point p1 = new Point(t.Item1.Left + t.Item1.Width / 2,
                             t.Item1.Top + t.Item1.Height / 2);
        Point p2 = new Point(t.Item2.Left + t.Item2.Width / 2,
                             t.Item2.Top + t.Item2.Height / 2);

        e.Graphics.DrawLine(Pens.Black, p1, p2);
    }
}

      好了,所有工作都已完成,此時,只需要把想要連線的兩個標籤新增到當前集合中,即可完成直線的連線功能。效果如圖

  參考文獻:

     https://docs.microsoft.com/zh-cn/dotnet/api/system.tuple-2?view=netcore-3.1

  https://stackoverflow.com/questions/31626027/how-to-connect-with-line-shapes-labels-on-runtime/31642448#31642448?newreg=de162494b077460383555e4da76bdd18

  

  • 結束語

   由於後續所有重寫/重繪控制元件都在同一個專案使用,而且Dev系統引用檔案較多,壓縮後原始碼檔案仍然很大,如果有需要原始碼的朋友,可以微信公眾號回覆:erp,即可獲取Fucking ERP所有原始碼示例~!有疑問的也可以CALL我一起探討。

        最後,感謝您的耐心陪伴!如果覺得本篇博文對您或者身邊朋友有幫助的,麻煩點個關注!贈人玫瑰,手留餘香,您的支援就是我寫作最大的動力,感謝您的關注,期待和您一起探討!再會!

 

相關文章