.net開發筆記(十二) 設計時與執行時的區別(續)

周見智發表於2013-06-22

    上一篇部落格詳細講到了設計時(DesignTime)和執行時(RunTime)的概念與區別,不過沒有給出實際的Demo,今天整理了一下,做了一個例子,貼出來分享一下,鞏固前一篇部落格講到的內容。

    簡單回顧一下:

  1. 元件有兩種狀態,即設計時和執行時,元件存在設計器中時,它就處於“設計時”;元件存在執行過程時,它就處於“執行時”;
  2. 無論設計器中元件還是執行過程中的元件,它們都是“元件例項”,所謂“例項”,就是new出來了物件,可想而知,無論在設計器中還是執行過程中,元件都會執行一些程式碼;
  3. 一般情況下,可以通過元件的DesignMode是否為true,來判斷當前元件是否處於“設計時”。(注意是一般情況);
  4. 之所以分“設計時”和“執行時”兩個狀態,主要原因是為了照顧微軟的“視覺化設計”開發模式,因為任何一個元件都有可能存在於設計器中,有些時候,存在於設計器中的元件與執行中的元件有不同的表現行為。詳見上一篇部落格中最後舉得例子。

     為了更為直觀地說明“設計時”和“執行時”存在的必要,我做了一個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 }
View Code

程式碼很簡單,不做解釋。再來看一下擴充套件元件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 }
View Code

正如諸位所見,擴充套件屬性為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 }
View Code

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,還有操作麥克風、攝像頭等等,甚至滑鼠鍵盤這些我們不常用到(我指的是不需要我們開發人員直接操作)都是,我找機會整理總結一下,包含很多知識,比如讀取資料、判斷資料完整性、分析資料、提高底層資料接收效率等等等。

     希望對諸位有幫助。

相關文章