上一篇部落格詳細講到了設計時(DesignTime)和執行時(RunTime)的概念與區別,不過沒有給出實際的Demo,今天整理了一下,做了一個例子,貼出來分享一下,鞏固前一篇部落格講到的內容。
簡單回顧一下:
- 元件有兩種狀態,即設計時和執行時,元件存在設計器中時,它就處於“設計時”;元件存在執行過程時,它就處於“執行時”;
- 無論設計器中元件還是執行過程中的元件,它們都是“元件例項”,所謂“例項”,就是new出來了物件,可想而知,無論在設計器中還是執行過程中,元件都會執行一些程式碼;
- 一般情況下,可以透過元件的DesignMode是否為true,來判斷當前元件是否處於“設計時”。(注意是一般情況);
- 之所以分“設計時”和“執行時”兩個狀態,主要原因是為了照顧微軟的“視覺化設計”開發模式,因為任何一個元件都有可能存在於設計器中,有些時候,存在於設計器中的元件與執行中的元件有不同的表現行為。詳見上一篇部落格中最後舉得例子。
為了更為直觀地說明“設計時”和“執行時”存在的必要,我做了一個demo,大概描述為:我先設計了一個Ball的控制元件,它繼承自Control,現在我需要讓每個Ball受重力的作用,從而能夠自由運動,並且能夠與容器壁發生碰撞,發生能量損失(速度減小),為了到達這個目的,我從新定義了一個擴充套件元件(具體含義請參照之前部落格),該擴充套件元件為每個Ball控制元件擴充套件出來了一個Gravity屬性,當Gravity為true時,Ball就會受重力影響,否則,則不受重力影響。
先看Ball類程式碼:
1 public class Ball : Control 2 { 3 public Ball() 4 { 5 BackColor = Color.Black; 6 SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true); 7 } 8 protected override void OnResize(EventArgs e) 9 { 10 GraphicsPath p = new GraphicsPath(); 11 p.AddEllipse(ClientRectangle); 12 Region = new Region(p); 13 base.OnResize(e); 14 } 15 }
程式碼很簡單,不做解釋。再來看一下擴充套件元件GravityEngine:
1 [ProvideProperty("Gravity",typeof(Ball))] 2 public partial class GravityEngine : Component,IExtenderProvider 3 { 4 public GravityEngine() 5 { 6 InitializeComponent(); 7 } 8 public GravityEngine(IContainer container) 9 { 10 container.Add(this); 11 InitializeComponent(); 12 } 13 14 Dictionary<Ball, Info> _dic = new Dictionary<Ball, Info>(); 15 Dictionary<Ball, Point> _dic2 = new Dictionary<Ball, Point>(); 16 float _gravity = 9.8f; 17 public void SetGravity(Ball ball, bool flag) 18 { 19 if (_dic.ContainsKey(ball)) 20 { 21 if (!flag) 22 { 23 _dic.Remove(ball); 24 } 25 } 26 else 27 { 28 if (flag) 29 { 30 _dic.Add(ball, new Info()); 31 ball.MouseDown += new MouseEventHandler(ball_MouseDown); 32 ball.MouseUp += new MouseEventHandler(ball_MouseUp); 33 ball.MouseMove += new MouseEventHandler(ball_MouseMove); 34 } 35 } 36 } 37 public bool GetGravity(Ball ball) 38 { 39 if (_dic.ContainsKey(ball)) 40 { 41 return true; 42 } 43 else 44 { 45 return false; 46 } 47 } 48 49 #region IExtenderProvider 成員 50 public bool CanExtend(object extendee) 51 { 52 return extendee is Ball; 53 } 54 #endregion 55 56 private void timer1_Tick(object sender, EventArgs e) 57 { 58 if (!DesignMode) 59 { 60 foreach (KeyValuePair<Ball, Info> pair in _dic) 61 { 62 Ball b = pair.Key; 63 Info info = pair.Value; 64 if (info.Move) //都Gravity影響 65 { 66 info.YSpeed += _gravity; 67 68 b.Left += (int)info.XSpeed; //移動水平位置 69 b.Top += (int)info.YSpeed; //移動垂直位置 70 71 Control parent = b.Parent; 72 if (b.Left <= 0) //碰撞左壁 73 { 74 info.XSpeed = 0.35f * Math.Abs(info.XSpeed); //改變水平速度 75 b.Left = 0; 76 } 77 if (b.Top <= 0) //碰撞上部 78 { 79 info.YSpeed = 0.95f * Math.Abs(info.YSpeed); //改變垂直速度 80 b.Top = 0; 81 } 82 if (b.Left + b.ClientRectangle.Width >= parent.ClientRectangle.Width) //碰撞右壁 83 { 84 info.XSpeed = (-1) * 0.35f * Math.Abs(info.XSpeed); //改變水平速度 為負 85 b.Left = parent.ClientRectangle.Width - b.ClientRectangle.Width; 86 } 87 if (b.Top + b.ClientRectangle.Height >= parent.ClientRectangle.Height) //碰撞底部 88 { 89 info.YSpeed = (-1) * 0.95f * Math.Abs(info.YSpeed); //改變垂直速度 為負 90 b.Top = parent.ClientRectangle.Height - b.ClientRectangle.Height; 91 } 92 } 93 } 94 } 95 } 96 97 void ball_MouseMove(object sender, MouseEventArgs e) 98 { 99 Ball b = sender as Ball; 100 if (_dic.ContainsKey(b)) 101 { 102 if (_dic2.ContainsKey(b)) // 103 { 104 Point p = b.PointToScreen(e.Location); //將ball座標系的值 轉換螢幕座標系的值 105 Point delta = new Point(p.X - _dic2[b].X, p.Y - _dic2[b].Y); 106 b.Location = new Point(b.Location.X + delta.X, b.Location.Y + delta.Y); 107 _dic[b].XSpeed = delta.X; 108 _dic[b].YSpeed = delta.Y; 109 110 _dic2[b] = p; 111 } 112 } 113 } 114 115 void ball_MouseUp(object sender, MouseEventArgs e) 116 { 117 Ball b = sender as Ball; 118 if (_dic.ContainsKey(b)) 119 { 120 _dic2.Remove(b); 121 _dic[b].Move = true; 122 } 123 } 124 125 void ball_MouseDown(object sender, MouseEventArgs e) 126 { 127 Ball b = sender as Ball; 128 if (_dic.ContainsKey(b)) 129 { 130 Point _down = b.PointToScreen(e.Location); //將ball的座標系的值 轉換成螢幕座標系的值 131 _dic2.Add(b, _down); 132 _dic[b].Move = false; //滑鼠選中 不受gravity影響 133 _dic[b].XSpeed = 0; 134 _dic[b].YSpeed = 0; 135 } 136 } 137 }
正如諸位所見,擴充套件屬性為Gravity,目標位Ball([ProvideProperty("Gravity",typeof(Ball))]),為了儲存每個Ball的資訊,我還定義了一個Info類,程式碼如下:
1 class Info 2 { 3 float _xSpeed = 0; //水平速度 4 float _ySpeed = 0; //垂直速度 5 bool _move = true; //是否受gravity影響 6 7 public float XSpeed 8 { 9 get 10 { 11 return _xSpeed; 12 } 13 set 14 { 15 _xSpeed = value; 16 } 17 } 18 public float YSpeed 19 { 20 get 21 { 22 return _ySpeed; 23 } 24 set 25 { 26 _ySpeed = value; 27 } 28 } 29 public bool Move 30 { 31 get 32 { 33 return _move; 34 } 35 set 36 { 37 _move = value; 38 } 39 } 40 }
Info類記錄每個Ball當前的水平速度、垂直速度以及是否受重力影響(當滑鼠選中Ball時,不受重力影響)。
編譯之後,生成一個Ball控制元件和一個GravityEngine擴充套件元件,你可以再ToolBox看到。將Ball拖進設計器中的窗體中,然後將GravityEngine拖進設計器,Ball的屬性欄就多一個擴充套件屬性“gravityEngine1上的Gravity”,型別為bool。你可以透過設定該屬性為true,從而使該Ball受重力作用。編譯透過後,介面效果為:
gif截圖效果不太好,所以看著不連貫。如果文章到這兒就完了,那就體現不了本篇部落格的任何價值,本文開始就表明本文需要說明“設計時”和“執行時”存在的必要性。
我們回過頭來看一下GravityEngine的程式碼,其中Timer元件Tick事件處理程式Timer1_Tick中,一開始,就判斷了DesignMode的值(if(!DesignMode))也就是說,如果元件(GravityEngine)不處於“設計時”,才開始執行下面的程式碼(讓Ball受重力作用),如果GravityEngine處於“設計時”(也就是存在於設計器中),那麼就不會執行下面的程式碼,是的!這個判斷很重要,因為如果沒有該判斷,無論GravityEngine元件處於設計器中還是實際執行過程中,都會執行Timer1_Tick中那部分程式碼,這就出現問題了,在你設計的時候,也就是在設計器中,就可以看到Ball受重力作用運動,這個太可怕了,你根本固定不了Ball的位置!我去掉判斷,看一下設計器中的截圖效果:
如圖,設計器中的Ball控制元件從矩形中掉下來了。分析主要原因,就是之前講到的,無論設計器中的元件還是實際執行過程中的元件,都是“元件例項”,都執行了程式碼,因此,就算在設計器中,Ball也難逃GravityEngine元件的重力控制。
前幾天看見網上有人問讀取IO資料的問題,尤其像是串列埠、Socket通訊之類的,需要迴圈接收外來資料的場合,這些時候最好用到APM(非同步程式設計模型),.net中一般以Begin開頭的方法基本都屬於該範疇,大多數都是操作IO的,當然也有例外,比如BeginInvoke。很多都屬於操作IO,比如上面提到的串列埠、Socket,還有操作麥克風、攝像頭等等,甚至滑鼠鍵盤這些我們不常用到(我指的是不需要我們開發人員直接操作)都是,我找機會整理總結一下,包含很多知識,比如讀取資料、判斷資料完整性、分析資料、提高底層資料接收效率等等等。
希望對諸位有幫助。