• 设计时与运行时的区别


     设计时与运行时的区别

    设计时和运行时的概念网上的中文资料很少,如果不是做第三方组件开发的或者跟设计器开发有关的,几乎用不到这些概念。为了迎合之前几篇博客,我想有必要说一下它两的概念和区别,以及用在什么地方。博客好久没更新,比较忙。

        首先,我个人认为“设计时”和“运行时”是针对组件而言的(包括控件),也就是说,一个组件可以处在“运行时”也可以处在“设计时”两个状态,从字面上的意思来看,“设计时”当然是指处在开发阶段,开发人员在设计器中开发组件的时候,而“运行时”指程序处在运行阶段,比如一个button控件,在设计器中的窗体中时,它就处于“设计时”,当程序运行起来之后,这个button还是在窗体中,但它处于“运行时”了。呵,字面上的意思很好理解,那么这两个状态到底有什么用呢?也就是说,微软为什么规定组件有两个状态?想要了解这个问题的答案,我们先得清楚“可视化设计器”(比如VS  IDE)的作用,简单的总结一下:可视化设计器提供一个WYSIWYG(所见即所得)的开发环境,为开发人员提供一个更直观的操作平台。可是,就是这么两句简单的话,却迷惑了多少人。我们来看一张图:

    如图所示,任何组件都是可以放在“设计器”中设计的(详见前面几篇博客),也就是说,组件具备“可视化设计”的特性,开发人员通过向设计器中添加组件,就可以快速地设计出一个类型(Form1),然后生成代码文档,保存。整个过程需要注意一下几点:

    1)  组件具备“可视化设计”的特性,这里的设计并不代表我们可以改变原有组件的代码,比如向设计器中拖进一个Button控件,默认命名“button1”,接着我们编辑它的属性、绑定事件等等,这些所有的操作都只发生在button1身上,并不会影响原来Button类代码。换句话说,只设计“组件实例“。

    2)  开发人员向设计器中添加组件(包括控件),设计器中的组件都是“实例”,也就是说图中的label1、textbox1、button1这些东西都是实实在在地“控件实例”,什么叫“控件实例”?就是说它们都有自己的句柄(Handle)、窗口过程(WndProc)(控件即窗体,窗体即控件,窗体都有句柄和窗口过程,不清楚的请看前面的博客),如果诸位不相信,可以用Spy++查看设计器中的这些控件,可以查找出它们的Handle以及其他信息。关于这一点,我前面博客已经解释过,这里我再提一下。

    3)  不管我们怎么操作设计器,我们在设计器中看到的东西确实跟程序运行后的界面大概差不多,但是需要明白的是,设计器中的东西全部都是“虚”的,为什么说它是虚的?因为我们一关闭设计器之后,设计器中的东西全部销毁了,而真正有用的东西就是图中③生成的代码文档!对,你没有看错,我更没有写错,代码文档才是我们最终需要的东西,我们可以将它编译生成exe或者dll文件,不是吗?

    4)  接3),我们可以一句话来概括一下“设计器”的作用:为了更快的生成想要的代码。是的,设计器最终的目的就是为开发人员快速生成代码,换句话说,假如没有设计器,开发人员完全可以自己去写代码(比如图中的Form1.cs和Form1.Designer.cs),可是没有哪个sb会这样去做,因为那样实在太麻烦了。

    5)  我们将生成的代码文档编译生成exe文件,运行之,我们会看见什么?毫无疑问,我们会看见与设计器中差不多类似的界面,有一个label、一个textbox、一个button。这个是为什么?因为生成的代码都是由设计器生成的,我们拖进去一个Label,设计器就会生成类似如下的代码:

    Label  label1 = new Label();

    label1.Location = new Point(10,10);

    label1.Text = “label1”;

    this.Controls.Add(label1);

    //大概类似这样,可能不相同

    同理,textbox1和button1也一样。如果我们在属性列表中修改label1的Text属性为“test”,那么,立即有这样的代码生成:

    label1.Text = “test”;

    注意,以上自动生成的代码都在InitializeComponent方法中,我们将生成的代码编译运行,当然会按照原来设计时的样子一一还原。

         如果看懂以上5点的童鞋,那么基本上就能理解本篇博客所要说明的问题了。我们再来回忆一下文章开始提出来的问题:组件为啥要有两个状态,设计时(Design-Time)和运行时(Run-Time)?答案在2)和5)中,已经说过,设计器中的任何组件都是实例,换句话说,你往设计器中拖放组件的时候,会调用它的构造方法,如果该组件恰好是一个控件,那么肯定会激发它的Load事件,当它在设计器中重绘时,肯定会激发它的Paint事件等等等。也就是说,如果我自定义一个控件:

     View Code

    本来设计这个自定义控件的开发人员想让控件加载时,弹出一个消息框,现在问题来了,使用这个自定义控件的coder,只要将该控件拖进设计器时,就会弹出一个MessageBox,毫无疑问,这不是我们希望的。如下图:

    这只是一个简单例子,如果我们在开发第三方组建时,需要在某些场合进行IO操作、访问数据库或者操作注册表等等,那么就有必要考虑这些会不会在设计器中发生,比如修改一下上面的代码:

     View Code

    问题就显而易见了,只要你将该控件拖进设计器,那么就会发生“操作注册表”或者“连接数据库”这样的行为。

         想要解决以上问题,微软就提出来了“设计时”和“运行时”的概念,其实就我个人理解,同一个组件,它出现在设计器中,它就是处于“设计时”,否则它就是处于“运行时”,每个组件都有一个DesinMode属性,如果该属性为true,那么它就是处于“设计时”,否则,它就是处于“运行时”。(注:该方法判断不太准确,只适合大部分场合,使用所有场合的判断方法现在不必提及

    那么,原有代码就可以修改为:

     View Code

        问题完美解决。设计时和运行时的概念,就像同一个人,出生在官宦之家,那么他就是“官二代”,如果出生在普通家庭,那么他很有可能是所谓的“屌丝”。官二代和屌丝有很大的区别,就像设计器中的Button和运行期间的Button,差别很大。

        另外,至于为什么设计器中的组件与运行时的组件能有不同的表现,比如设计器中的组件可以通过属性列表编辑它的属性,可以改变它的位置、大小等等等等,甚至为什么会自动生成代码,这些东西都属于设计器所必须具备的功能,本篇文章不讨论。

    代码为word中所写,如有错误,望见谅!希望对诸位有帮助。

    上一篇博客详细讲到了设计时(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 }
    复制代码

    代码很简单,不做解释。再来看一下扩展组件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,还有操作麦克风、摄像头等等,甚至鼠标键盘这些我们不常用到(我指的是不需要我们开发人员直接操作)都是,我找机会整理总结一下,包含很多知识,比如读取数据、判断数据完整性、分析数据、提高底层数据接收效率等等等。

         希望对诸位有帮助。

     
     

    当前标签: c#

     
    xiaozhi_5638 2013-06-22 17:48 阅读:236 评论:0
     
    xiaozhi_5638 2013-04-12 16:25 阅读:826 评论:2
     
    xiaozhi_5638 2013-03-27 10:29 阅读:2093 评论:16
     
    xiaozhi_5638 2013-03-23 12:07 阅读:1582 评论:11
     
    xiaozhi_5638 2013-03-01 18:04 阅读:1458 评论:6
     
    xiaozhi_5638 2013-01-17 18:01 阅读:1094 评论:2
     
    xiaozhi_5638 2013-01-14 15:36 阅读:1544 评论:3
     
    xiaozhi_5638 2013-01-03 21:24 阅读:1827 评论:8
     
    xiaozhi_5638 2012-12-21 20:52 阅读:2429 评论:31
     
    xiaozhi_5638 2012-12-19 14:25 阅读:3493 评论:39
     
    xiaozhi_5638 2010-04-20 17:19 阅读:126 评论:1
     
    xiaozhi_5638 2010-01-13 20:34 阅读:345 评论:0
     
    标签: c#Winform
  • 相关阅读:
    7.9学习日志
    7.8学习日志
    7.7学习日志
    未命名 1
    未命名 1
    未命名 1
    【转】搭建Mac OS X下cocos2d-x的Android开发环境
    【转】如何高效利用GitHub——2013-08-28 22
    【转】GitHub删除一个仓库——2013-08-27 21
    【转】Cocos2d-x 2.x CCSprite 灰白图的生成(利用shader设置)——2013-08-27 21
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3150081.html
Copyright © 2020-2023  润新知