<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      [原創]《C#高級GDI+實戰:從零開發一個流程圖》第07章:來吧,自定義“畫布”控件!

      一、前言

      上節課已經抽象出來了形狀和連線,但是沒解決程序復用的問題:現在所有的代碼是寫在窗口中的,如果想在其它程序想實現流程圖,只能重新寫代碼或者復制粘貼代碼,沒辦法簡單復用,而且也無法保證功能的完整性和及時性。所以我們本節就來看一下,如何獨立出一張“畫布”控件,來解決此問題。

      相信看完的你,一定會有所收獲!

      本文地址:http://www.rzrgm.cn/lesliexin/p/18985184

      二、先看效果

      并沒有什么特別的效果可看,主要是演示我們獨立出來的“畫布”控件功能完整性。

      我們下面就來講解如何實現。

      三、創建類庫及自定義控件

      就像上節我們將抽象出來的形狀和連線類都放到獨立的類庫中一樣,我們同樣將畫布控件放到一個單獨的類庫中:

      image

      然后我們添加一個“自定義控件”,注意不是“用戶控件”:

      image

      我們給畫布起個名稱:FCCanvas,就是FlowChartCanvas的簡寫。

      這里為了方便編寫教程,我們在后面增加V1、V2,用來區分。

      創建好的結構如下:

      image

      四、移植代碼到自定義控件

      現在有了單獨的畫布控件,我們就將之前在程序中實現代碼移植過來,我們在FCCanvasV1上右鍵->查看代碼,進入后臺代碼。

      1,雙緩沖

      首要的,我們在構造函數中添加開啟雙緩沖的代碼:

      image

      2,重寫OnPaint

      有過自定義控件的讀者會知道,自定義控件就相當于一個“畫布”,控制所展示的內容全是我們用代碼“畫”上去的,而繪制的方法就是在OnPaint方法中。

      我們將之前代碼里的DrawAll方法里的代碼復制進來:

      image

      因為已經在OnPaint方法中,所以不再需要傳入Graphics對象,直接使用e.Graphics即可,此即當前控件的對象。

      2,重寫鼠標相關事件

      我們之前是在panel控件上操作,現在我們是在整個控件上操作,所以我們需要重寫下相關事件,這些可重寫的方法一般都是以On開頭,如:OnMouseDown等。

      2.1,OnMouseDown

      我們將之前代碼中的MouseDown中的代碼拷貝進來:

      image

      這里的變化有三點:

      一是提示文本我們這里改為了觸發事件的方式,我們定義了一個事件,通知訂閱者使用,至于是否顯示提示內容及如何顯示提示內容我們控件不作管理。

      image

      image

      二是添加連線時,連線的顏色不再是隨機生成,也是觸發一個事件,由調用方決定連線的顏色是什么:

      image

      image

      為了防止調用方不訂閱此事件,我們會默認連線顏色為黑色。

      三是發起重新繪制的方式不一樣了,之前是直接調用繪制所有方法DrawAll:

      image

      而現在我們也沒有了DrawAll方法,DrawAll的實現被我們移植到了OnPaint方法中。所以我們直接調用控件自帶的無效方法Invalidate(),來使窗口重繪:

      image

      內部邏輯簡單而言就是:當我們調用Invalidate()后,系統會自動調用OnPaint方法,進而重繪。而這也是自定義控件的基礎邏輯。

      2.2,OnMouseMove

      同樣的,我們將之前代碼中的MouseMove中的代碼拷貝進來:

      image

      可以看到幾乎一樣,也是最后一步改為調用無效方法Invalidate(),來使窗口重繪。

      2.3,OnMouseUp

      同理:

      image

      3,形狀集合、連線集合等定義

      我們現在基本的實現都有了,那么就把之前的一些私有變量拿過來,像形狀集合、連線集合、連線狀態等:

      image

      4,公共方法

      現在整個FCCanvasV1內部已經自洽了,但是有個問題:如何與外部交互?如何添加形狀?

      我們現在就來開放一些公共方法,來實現與外部的交互。

      4.1,添加形狀方法

      最核心的也是最基本的功能,就是添加形狀的方法:

      image

      我們的方法支持一次添加多個形狀,而且添加形狀時會自動判斷是否已經添加過。

      注:我們看到方法名帶了個前綴:FCC_,這樣寫看似不優雅,但是對于后續的開發和使用卻有很大的便利,我們統一前綴,這樣在寫代碼時敲入前綴就能看到所有的方法,而不需要再去思考,特別是對于其它人而言,不熟悉的情況下只能去看類的定義里有哪些方法才能去調用,而不像現在這樣這么方便。這是經驗之談,當然加不加前綴完全是個人自由,想怎么寫就怎么寫,并不會影響功能。

      image

      4.2,清空方法

      我們添加一個清空當前畫布中所有形狀和連線的方法,用于復原:

      image

      4.3,刷新方法

      我們雖然可以通過調用控件的Invalidate()方法來刷新,但是不夠直觀,我們直接將其封裝為一個方法:

      image

      4.4,添加連線和中止連線方法

      我們目前的程序支持添加連線和中止添加連線,所以我們同樣開放出這兩個方法:

      image

      好了,到此為止,我們的V1版畫布就已經完成了,可以實現之前課程里的所有效果了。下面是完整代碼,大家可查看和嘗試:

      點擊查看代碼
      using Elements;
      using Elements.Links;
      using System;
      using System.Collections.Generic;
      using System.Drawing;
      using System.Linq;
      using System.Text;
      using System.Threading.Tasks;
      using System.Windows.Forms;
      
      namespace FlowChartCanvas
      {
          //注:隨文說明:不是【用戶控件】,直接在類繼承CONTROL
      
          /// <summary>
          /// 流程圖畫布
          /// </summary>
          public class FCCanvasV1:Control
          {
              public FCCanvasV1()
              {
                  SetStyle(ControlStyles.AllPaintingInWmPaint |
                      ControlStyles.UserPaint |
                      ControlStyles.OptimizedDoubleBuffer, true);
              }
      
              #region 公共事件
      
              /// <summary>
              /// 連線時的狀態提示
              /// </summary>
              public event Action<string> FCC_LinkState;
              /// <summary>
              /// 添加連線時,連線的顏色
              /// </summary>
              public event Func<Color> FCC_LinkColor;
      
              #endregion
      
              #region 公共屬性
      
      
      
              #endregion
      
              #region 公共方法
      
              //注:文章中說明,為了方便查看和演示有哪些方法和屬性,所以固定開頭,可依喜好不要此開頭
      
              /// <summary>
              /// 向當前畫布中添加形狀
              /// </summary>
              /// <param name="sps"></param>
              public void FCC_AddShapes(List<ShapeBase> sps)
              {
                  if (sps == null || sps.Count == 0) return;
                  foreach (var item in sps)
                  {
                      //根據ID去重
                      if (!_shapes.Any(a => a.Id == item.Id))
                      {
                          _shapes.Add(item);
                      }
                  }
                  //令當前控件失效以重繪
                  Invalidate();
              }
              
              /// <summary>
              /// 清空畫布中的形狀和連線
              /// </summary>
              public void FCC_Clear()
              {
                  _shapes.Clear();
                  _links.Clear();
                  Invalidate();
              }
      
              /// <summary>
              /// 刷新當前畫布
              /// </summary>
              public void FCC_Refresh()
              {
                  Invalidate();
              }
      
              /// <summary>
              /// 開始連線
              /// </summary>
              public void FCC_StartLink()
              {
                  _isAddLink = true;
                  _selectedStartShape = null;
                  _selectedEndShape = null;
                  FCC_LinkState?.Invoke("請點擊第1個形狀");
              }
      
              /// <summary>
              /// 中止/停止連線
              /// </summary>
              public void FCC_StopLink()
              {
                  _isAddLink = false;
                  _selectedStartShape = null;
                  _selectedEndShape = null;
                  FCC_LinkState?.Invoke("");
                  Invalidate();
              }
      
              #endregion
      
              #region 私有屬性
      
              /// <summary>
              /// 形狀集合
              /// </summary>
              List<ShapeBase> _shapes = new List<ShapeBase>();
              /// <summary>
              /// 連線集合
              /// </summary>
              List<LinkBase> _links = new List<LinkBase>();
              /// <summary>
              /// 當前是否有鼠標按下,且有矩形被選中
              /// </summary>
              bool _isMouseDown = false;
              /// <summary>
              /// 最后一次鼠標的位置
              /// </summary>
              Point _lastMouseLocation = Point.Empty;
              /// <summary>
              /// 當前被鼠標選中的矩形
              /// </summary>
              ShapeBase _selectedShape = null;
      
              /// <summary>
              /// 添加連線時選中的第一個形狀
              /// </summary>
              ShapeBase _selectedStartShape = null;
              /// <summary>
              /// 添加連線時選中的第一個形狀
              /// </summary>
              ShapeBase _selectedEndShape = null;
              /// <summary>
              /// 是否正添加連線
              /// </summary>
              bool _isAddLink = false;
      
              Bitmap _bmp;
              #endregion
      
              #region 私有方法
      
              #endregion
      
              #region 重寫方法
      
              protected override void OnPaint(PaintEventArgs e)
              {
                  _bmp = new Bitmap(Width, Height);
                  var g = Graphics.FromImage(_bmp);
                  //設置顯示質量
                  g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                  g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                  g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                  g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
                  g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
                  g.Clear(BackColor);
                  //繪制所有形狀
                  foreach (var sp in _shapes)
                  {
                      sp.Draw(g);
                  }
                  //繪制所有連線
                  foreach (var ln in _links)
                  {
                      ln.Draw(g);
                  }
                  //繪制內存繪圖到控件上
                  e.Graphics.DrawImage(_bmp, new PointF(0, 0));
                  //釋放資源
                  g.Dispose();
                  base.OnPaint(e);
              }
      
              protected override void OnMouseDown(MouseEventArgs e)
              { //當鼠標按下時
      
                  //取最上方的形狀
                  var sp = _shapes.FindLast(a => a.Rect.Contains(e.Location));
      
                  if (!_isAddLink)
                  {
                      //當前沒有處理連線狀態
                      if (sp != null)
                      {
                          //設置狀態及選中矩形
                          _isMouseDown = true;
                          _lastMouseLocation = e.Location;
                          _selectedShape = sp;
                      }
                  }
                  else
                  {
                      //正在添加連線
      
                      if (_selectedStartShape == null)
                      {
                          //證明沒有矩形和圓形被選中則設置開始形狀
                          if (sp != null)
                          {
                              //設置開始形狀
                              _selectedStartShape = sp;
                          }
                          FCC_LinkState?.Invoke("請點擊第2個形狀");
                      }
                      else
                      {
                          //判斷第2個形狀是否是第1個形狀
                          if (sp != null)
                          {
                              //判斷當前選中的矩形是否是第1步選中的矩形
                              if (_selectedStartShape.Id == sp.Id)
                              {
                                  FCC_LinkState?.Invoke("不可選擇同一個形狀,請重新點擊第2個形狀");
                                  return;
                              }
                          }
      
                          if (sp != null)
                          {
                              //設置結束形狀
                              _selectedEndShape = sp;
                          }
                          else
                          {
                              return;
                          }
      
                          //兩個形狀都設置了,便添加一條新連線
                          _links.Add(new LineLink()
                          {
                              Id = "連線" + Guid.NewGuid().ToString(),//這里就不能用數量了,防止重復
                              BackgroundColor = FCC_LinkColor?.Invoke() ?? Color.Black,
                              StartShape = _selectedStartShape,
                              EndShape = _selectedEndShape,
                          });
                          //兩個形狀都已選擇,結束添加連線狀態
                          _isAddLink = false;
                          FCC_LinkState?.Invoke("");
      
                          //令當前控件失效以重繪
                          Invalidate();
      
                      }
      
                  }
                  base.OnMouseDown(e);
              }
      
              protected override void OnMouseMove(MouseEventArgs e)
              {  
                  //當鼠標移動時
      
                  //如果處于添加連線時,則不移動形狀
                  if (_isAddLink) return;
      
                  if (_isMouseDown)
                  {
                      //當且僅當:有鼠標按下且有矩形被選中時,才進行后續操作
      
                      //改變選中矩形的位置信息,隨著鼠標移動而移動
      
                      //計算鼠標位置變化信息
                      var moveX = e.Location.X - _lastMouseLocation.X;
                      var moveY = e.Location.Y - _lastMouseLocation.Y;
      
                      //將選中形狀的位置進行同樣的變化
                      var oldXY = _selectedShape.Rect.Location;
                      oldXY.Offset(moveX, moveY);
                      _selectedShape.Rect = new Rectangle(oldXY, _selectedShape.Rect.Size);
      
                      //記錄當前鼠標位置
                      _lastMouseLocation.Offset(moveX, moveY);
      
                      //令當前控件失效以重繪
                      Invalidate();
                  }
                  base.OnMouseMove(e);
              }
      
              protected override void OnMouseUp(MouseEventArgs e)
              { 
                  //當鼠標松開時
                  if (_isMouseDown)
                  {
                      //當且僅當:有鼠標按下且有矩形被選中時,才進行后續操作
      
                      //重置相關記錄信息
                      _isMouseDown = false;
                      _lastMouseLocation = Point.Empty;
                      _selectedShape = null;
                  }
                  base.OnMouseUp(e);
              }
      
              #endregion
      
          }
      }
      
      

      五、使用畫布控件

      我們的畫布控件已經完成,下面就來看一下如何去使用它。

      1,引用畫布類庫

      因為我們的畫布在獨立的類庫中,所以我們先引用類庫:

      image

      2,添加畫布控件

      首先,界面與之前并無變化:

      image

      不過我們不再在中間的panel中繪制,而是將我們的畫布控件添加到panel當中。我們在構造函數中使用代碼的方式添加控件:

      image

      當然也可以能通過工具箱拖動添加,不過不太建議,特別當自定義控件復雜的情況下,代碼的方式更好控制和編寫。

      我們訂閱兩個事件,分別用來設置狀態文本和獲取顏色:

      image

      3,按鈕調用畫布方法

      現在這些按鈕不再自行實現了,而是直接調用畫布的對應方法即可。

      3.1,添加矩形按鈕

      image

      3.2,添加圓形按鈕

      image

      3.3,開始連線

      image

      3.4,中止連線

      image

      好了,到此為止我們就已經實現了之前課程里的效果。

      下面是完整代碼,大家可自己查看和編譯:

      點擊查看代碼
      using Elements;
      using Elements.Links;
      using Elements.Shapes;
      using FlowChartCanvas;
      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 FlowChartDemo
      {
          public partial class FormDemo06V1 : FormBase
          {
              public FormDemo06V1()
              {
                  InitializeComponent();
                  DemoTitle = "第08節隨課Demo  Part1";
                  DemoNote = "效果:加載畫布、并添加形狀、連線等。";
      
                  //添加畫布控件
                  _fcc = new FCCanvasV1();
                  _fcc.FCC_LinkColor += _fcc_FCC_LinkColor;
                  _fcc.FCC_LinkState += _fcc_FCC_LinkState;
                  _fcc.Dock = DockStyle.Fill;
                  panel1.Controls.Add(_fcc);
      
              }
      
              private void _fcc_FCC_LinkState(string obj)
              {
                  toolStripStatusLabel1.Text = obj;
              }
      
              private Color _fcc_FCC_LinkColor()
              {
                  return GetColor(_linkColorIndex++);
              }
      
              FCCanvasV1 _fcc;
      
              /// <summary>
              /// 形狀顏色序號
              /// </summary>
              int _shapeColorIndex = 0;
              /// <summary>
              /// 連線顏色序號
              /// </summary>
              int _linkColorIndex = 0;
                     
              /// <summary>
              /// 獲取不同的背景顏色
              /// </summary>
              /// <param name="i"></param>
              /// <returns></returns>
              Color GetColor(int i)
              {
                  switch (i)
                  {
                      case 0: return Color.Red;
                      case 1: return Color.Green;
                      case 2: return Color.Blue;
                      case 3: return Color.Orange;
                      case 4: return Color.Purple;
                      default: return Color.Red;
                  }
              }
      
              private void toolStripButton1_Click(object sender, EventArgs e)
              {
                  var rs = new RectShape()
                  {
                      Id = "矩形" + Guid.NewGuid().ToString(),//這里就不能用數量了,防止重復
                      Rect = new Rectangle()
                      {
                          X = 50,
                          Y = 50,
                          Width = 100,
                          Height = 100,
                      },
                      FontColor = Color.White,
                      BackgroundColor = GetColor(_shapeColorIndex++),
                      Text = "矩形" + _shapeColorIndex,
                      TextFont = Font,
      
                  };
      
                  _fcc.FCC_AddShapes(new List<ShapeBase>() { rs });
                  _fcc.FCC_Refresh();
              }
      
              private void toolStripButton4_Click(object sender, EventArgs e)
              {
                  var rs = new EllipseShape()
                  {
                      Id = "圓形" + Guid.NewGuid().ToString(),//這里就不能用數量了,防止重復
                      Rect = new Rectangle()
                      {
                          X = 50,
                          Y = 50,
                          Width = 100,
                          Height = 100,
                      },
                      FontColor = Color.White,
                      BackgroundColor = GetColor(_shapeColorIndex++),
                      Text = "圓形" + _shapeColorIndex,
                      TextFont = Font,
      
                  };
                  _fcc.FCC_AddShapes(new List<ShapeBase>() { rs });
                  _fcc.FCC_Refresh();
              }
      
              private void toolStripButton2_Click(object sender, EventArgs e)
              {
                  _fcc.FCC_StartLink();
              }
      
              private void toolStripButton3_Click(object sender, EventArgs e)
              {
                  _fcc.FCC_StopLink();
              }
      
          }
      
      
      }
      
      

      六、結語

      可以看到我們更多的是使用,而不是編寫。有了我們自定義的畫布控件,完全不需要過多的考慮,只需要調用畫布的方法就行了,復用性很強。

      現在所有的角色都已登場,后面就要在這個地基上添磚加瓦,構造我們自己的流程圖。

      我們下節課就來添加一些其它的形狀,如:菱形、平行四邊形、圓角矩形等,到時候會發現原來這么的順理成章,敬請期待。

      感謝大家的觀看,本人水平有限,文章不足之處歡迎大家評論指正。

      -[END]-

      posted @ 2025-07-16 10:50  leslie_xin  閱讀(760)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 亚洲男人第一无码av网站| 国产美女69视频免费观看| 欧美黑人巨大videos精品| 隔壁老王国产在线精品| 少妇人妻偷人精品无码视频新浪 | 国产福利微视频一区二区| 国产成人精品亚洲高清在线| 久久久久久免费一区二区三区| 久久精品久久电影免费理论片| 91精品国产免费人成网站| 午夜福利片1000无码免费| 午夜福制92视频| 国产精品麻豆欧美日韩ww | 乱码午夜-极品国产内射| 无码国产欧美一区二区三区不卡 | 国产精品一区二区三区日韩| 国产99视频精品免费视频76| 十八禁午夜福利免费网站| 亚洲日本精品国产第一区| 自拍日韩亚洲一区在线| 国产精品一区在线蜜臀| 亚洲精品入口一区二区乱| 毛片在线看免费| 天堂v亚洲国产v第一次| 亚洲av国产成人精品区| 色伦专区97中文字幕| 国产成人精品18| 99精品国产综合久久久久五月天| 丁香五月亚洲综合在线| 黄色亚洲一区二区在线观看| 亚洲欧美高清在线精品一区二区 | 成年无码av片在线蜜芽| 又大又黄又粗高潮免费| 亚洲高清成人av在线| 毛片亚洲AV无码精品国产午夜| 免费吃奶摸下激烈视频| 激情亚洲专区一区二区三区| 欧美乱码伦视频免费| 亚洲国产精品久久久久久久| 少妇被粗大的猛烈xx动态图| 四虎影视一区二区精品|