.net开发笔记(十二) 设计时与运行时的区别(续)
.net开发笔记(十二) 设计时与运行时的区别(续)
IT周见智 发表于3年前
.net开发笔记(十二) 设计时与运行时的区别(续)
  • 发表于 3年前
  • 阅读 8
  • 收藏 0
  • 点赞 0
  • 评论 0

【腾讯云】新注册用户域名抢购1元起>>>   

    上一篇博客详细讲到了设计时(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,还有操作麦克风、摄像头等等,甚至鼠标键盘这些我们不常用到(我指的是不需要我们开发人员直接操作)都是,我找机会整理总结一下,包含很多知识,比如读取数据、判断数据完整性、分析数据、提高底层数据接收效率等等等。

     希望对诸位有帮助。

  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
IT周见智
粉丝 10
博文 61
码字总数 185891
×
IT周见智
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: