上一篇博客详细讲到了设计时(DesignTime)和运行时(RunTime)的概念与区别,不过没有给出实际的Demo,今天整理了一下,做了一个例子,贴出来分享一下,巩固前一篇博客讲到的内容。

简单回顾一下:

  1. 组件有两种状态,即设计时和运行时,组件存在设计器中时,它就处于“设计时”;组件存在运行过程时,它就处于“运行时”;
  2. 无论设计器中组件还是运行过程中的组件,它们都是“组件实例”,所谓“实例”,就是new出来了对象,可想而知,无论在设计器中还是运行过程中,组件都会执行一些代码;
  3. 一般情况下,可以通过组件的DesignMode是否为true,来判断当前组件是否处于“设计时”。(注意是一般情况);
  4. 之所以分“设计时”和“运行时”两个状态,主要原因是为了照顾微软的“可视化设计”开发模式,因为任何一个组件都有可能存在于设计器中,有些时候,存在于设计器中的组件与运行中的组件有不同的表现行为。详见上一篇博客中最后举得例子。

为了更为直观地说明“设计时”和“运行时”存在的必要,我做了一个demo,大概描述为:我先设计了一个Ball的控件,它继承自Control,现在我需要让每个Ball受重力的作用,从而能够自由运动,并且能够与容器壁发生碰撞,发生能量损失(速度减小),为了到达这个目的,我从新定义了一个扩展组件(具体含义请参照之前博客),该扩展组件为每个Ball控件扩展出来了一个Gravity属性,当Gravity为true时,Ball就会受重力影响,否则,则不受重力影响。

先看Ball类代码:

 public class Ball : Control
{
public Ball()
{
BackColor = Color.Black;
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
}
protected override void OnResize(EventArgs e)
{
GraphicsPath p = new GraphicsPath();
p.AddEllipse(ClientRectangle);
Region = new Region(p);
base.OnResize(e);
}
}

代码很简单,不做解释。再来看一下扩展组件GravityEngine:

  [ProvideProperty("Gravity",typeof(Ball))]
public partial class GravityEngine : Component,IExtenderProvider
{
public GravityEngine()
{
InitializeComponent();
}
public GravityEngine(IContainer container)
{
container.Add(this);
InitializeComponent();
} Dictionary<Ball, Info> _dic = new Dictionary<Ball, Info>();
Dictionary<Ball, Point> _dic2 = new Dictionary<Ball, Point>();
float _gravity = 9.8f;
public void SetGravity(Ball ball, bool flag)
{
if (_dic.ContainsKey(ball))
{
if (!flag)
{
_dic.Remove(ball);
}
}
else
{
if (flag)
{
_dic.Add(ball, new Info());
ball.MouseDown += new MouseEventHandler(ball_MouseDown);
ball.MouseUp += new MouseEventHandler(ball_MouseUp);
ball.MouseMove += new MouseEventHandler(ball_MouseMove);
}
}
}
public bool GetGravity(Ball ball)
{
if (_dic.ContainsKey(ball))
{
return true;
}
else
{
return false;
}
} #region IExtenderProvider 成员
public bool CanExtend(object extendee)
{
return extendee is Ball;
}
#endregion private void timer1_Tick(object sender, EventArgs e)
{
if (!DesignMode)
{
foreach (KeyValuePair<Ball, Info> pair in _dic)
{
Ball b = pair.Key;
Info info = pair.Value;
if (info.Move) //都Gravity影响
{
info.YSpeed += _gravity; b.Left += (int)info.XSpeed; //移动水平位置
b.Top += (int)info.YSpeed; //移动垂直位置 Control parent = b.Parent;
if (b.Left <= ) //碰撞左壁
{
info.XSpeed = 0.35f * Math.Abs(info.XSpeed); //改变水平速度
b.Left = ;
}
if (b.Top <= ) //碰撞上部
{
info.YSpeed = 0.95f * Math.Abs(info.YSpeed); //改变垂直速度
b.Top = ;
}
if (b.Left + b.ClientRectangle.Width >= parent.ClientRectangle.Width) //碰撞右壁
{
info.XSpeed = (-) * 0.35f * Math.Abs(info.XSpeed); //改变水平速度 为负
b.Left = parent.ClientRectangle.Width - b.ClientRectangle.Width;
}
if (b.Top + b.ClientRectangle.Height >= parent.ClientRectangle.Height) //碰撞底部
{
info.YSpeed = (-) * 0.95f * Math.Abs(info.YSpeed); //改变垂直速度 为负
b.Top = parent.ClientRectangle.Height - b.ClientRectangle.Height;
}
}
}
}
} void ball_MouseMove(object sender, MouseEventArgs e)
{
Ball b = sender as Ball;
if (_dic.ContainsKey(b))
{
if (_dic2.ContainsKey(b)) //
{
Point p = b.PointToScreen(e.Location); //将ball坐标系的值 转换屏幕坐标系的值
Point delta = new Point(p.X - _dic2[b].X, p.Y - _dic2[b].Y);
b.Location = new Point(b.Location.X + delta.X, b.Location.Y + delta.Y);
_dic[b].XSpeed = delta.X;
_dic[b].YSpeed = delta.Y; _dic2[b] = p;
}
}
} void ball_MouseUp(object sender, MouseEventArgs e)
{
Ball b = sender as Ball;
if (_dic.ContainsKey(b))
{
_dic2.Remove(b);
_dic[b].Move = true;
}
} void ball_MouseDown(object sender, MouseEventArgs e)
{
Ball b = sender as Ball;
if (_dic.ContainsKey(b))
{
Point _down = b.PointToScreen(e.Location); //将ball的坐标系的值 转换成屏幕坐标系的值
_dic2.Add(b, _down);
_dic[b].Move = false; //鼠标选中 不受gravity影响
_dic[b].XSpeed = ;
_dic[b].YSpeed = ;
}
}
}

正如诸位所见,扩展属性为Gravity,目标位Ball([ProvideProperty("Gravity",typeof(Ball))]),为了存储每个Ball的信息,我还定义了一个Info类,代码如下:

  class Info
{
float _xSpeed = ; //水平速度
float _ySpeed = ; //垂直速度
bool _move = true; //是否受gravity影响 public float XSpeed
{
get
{
return _xSpeed;
}
set
{
_xSpeed = value;
}
}
public float YSpeed
{
get
{
return _ySpeed;
}
set
{
_ySpeed = value;
}
}
public bool Move
{
get
{
return _move;
}
set
{
_move = value;
}
}
}

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

希望对诸位有帮助。

.net开发笔记(十二) 设计时与运行时的区别(续)的更多相关文章

  1. 《C++游戏开发》笔记十二 战争迷雾:初步实现

    本系列文章由七十一雾央编写,转载请注明出处.  http://blog.csdn.net/u011371356/article/details/9475979 作者:七十一雾央 新浪微博:http:/ ...

  2. python3.4学习笔记(十二) python正则表达式的使用,使用pyspider匹配输出带.html结尾的URL

    python3.4学习笔记(十二) python正则表达式的使用,使用pyspider匹配输出带.html结尾的URL实战例子:使用pyspider匹配输出带.html结尾的URL:@config(a ...

  3. Go语言学习笔记十二: 范围(Range)

    Go语言学习笔记十二: 范围(Range) rang这个关键字主要用来遍历数组,切片,通道或Map.在数组和切片中返回索引值,在Map中返回key. 这个特别像python的方式.不过写法上比较怪异使 ...

  4. DirectX11笔记(十二)--Direct3D渲染8--EFFECTS

    原文:DirectX11笔记(十二)--Direct3D渲染8--EFFECTS 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010333737 ...

  5. Windows Phone 8初学者开发—第14部分:在运行时绑定到真实的数据

    原文 Windows Phone 8初学者开发—第14部分:在运行时绑定到真实的数据 第14部分:在运行时绑定到真实的数据 原文地址: http://channel9.msdn.com/Series/ ...

  6. FreeSql (十二)更新数据时指定列

    var connstr = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;" + "Initia ...

  7. 编译器设计-RunTime运行时环境

    编译器设计-RunTime运行时环境 Compiler Design - Run-Time Environment 作为源代码的程序仅仅是文本(代码.语句等)的集合,要使其活动,它需要在目标计算机上执 ...

  8. .Net开发笔记(二十)创建一个需要授权的第三方组件

    在使用需要授权的软件时,注册付费的目标是软件的使用者,我们开发人员平时用到的一些第三方组件也是需要授权付费的,也就是说,付费者是开发人员,并不是系统(使用了该第三方组件)的最终使用者. 以上两者的区别 ...

  9. Java开发笔记(二十)一维数组的用法

    之前介绍的各类变量都是单独声明的,倘若要求定义相同类型的一组变量,则需定义许多同类型的变量,显然耗时耗力且不宜维护.为此,编程语言引入了数组的概念,每个数组都由一组相同类型的数据构成,对外有统一的数组 ...

随机推荐

  1. eclipse配置javaee环境

    笔者开发javaee项目时惯用myeclipse,但由于个人笔记本性能较低,myeclipse对内存的消耗极大,所以考虑换成eclipse开发.本文介绍eclipse配置javaee开发环境的一些体会 ...

  2. springMVC 相对于 Structs 的优势

    智者说,没有经过自己的思考和估量,就不能接受别人的东西.资料只能是一个参考,至于是否正确,还得自己去分辨 SpringMVC相对于Structs的几个优势: 1.springMVC安全性更高,stru ...

  3. windows下mongodb权限设置解决方法

    mongodb简介: MongoDB 是一个跨平台的,面向文档的数据库,提供高性能,高可用性和可扩展性方便. MongoDB工作在收集和文件的概念. MongoDB默认设置为无权限访问限制,所以可以直 ...

  4. Android手机编程初学遇到的问题及解决方法

    对高手来讲不值一提,可是对我这个初学来讲却是因为这些问题费了老长时间,有的不是编程问题,但不注意也会浪费不少宝贵时间!随时遇到随时更新... 引入第三方类库的问题,开始引用后没什么问题,但发现了该类库 ...

  5. 纯脚本组装Json格式字符串

    var answerStr = "["; for (var i in answer) { var data = $("input[name=QuestionItem_&q ...

  6. python入门简介

    Python前世今生 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC ...

  7. Php 笔记

    php基本简介 为何要学习php 通过上网查资料,了解了基本的php知识,并知道了php的优缺点.php是一种通用开源脚本语言.语法吸收了C语言.Java和Perl的特点,利于学习,使用广泛,主要适用 ...

  8. Redis多机常用架构-sentinel

    哨兵经典架构 sentinel结构 哨兵工作原理 Sentinel 通过配置文件发现Master,如下: Sentinel 通过向Master发送 INFO 命令来自动获得所有Slave的地址 跟Ma ...

  9. node.js之path

    说到node.js,可能实际中用到node进行后台开发的公司不多,大部分人都没有开发后台的经验.但是也要了解node相关模块的用法,因为现在前端自动化脚本的构建,模块的打包越来越离不开node.特别是 ...

  10. centos下postgresql的安装与配置[转]

    本文摘自:http://blog.chinaunix.net/uid-24846094-id-78490.html 一.安装(以root身份进行) 1.检出最新的postgresql的yum配置从ht ...