.NET寻根问底之窗体Resize事件

.NET寻根问底之窗体Resize事件


今天收到一名名为“三十而悟”的中专学校的计算机教师同行的邮件:

=====================================================

我最近正在把我掌握的有关Visual Basic的一些教学和开发经验写成一本书,目前写了20多万字了。在写的过程中,发现存在一个很有意思的问题,一直百思不解。特向您请教一下。

问题描述:在Visual Basic 2005的IDE环境下,我使用的IDE是Visual Studio 2005 Team Suite,环境设置选的是“Visual Basic开发设置”。在窗体的两个事件Resize和Load出现的先后顺序中出现的问题。按理说,这两个事件,Load一定是发生于Resize事件之前的。但是,我在编写代码过程中,有一次偶然的机会,发现Resize事件是发生在Load之前,我百思不得其解。在花了一天的时间找原因时,才重现了这种Resize发生于Load之前的情境。我发现,如果在窗体设计阶段,重新调整一下窗体的大小不按默认的300*300的大小设置,则Resize事件必发生在Load事件之前。我虽然重现了这种情况,但我找不出这种情况出现的原因。
当然如果一定要在Resize事件和Load事件中写代码且要区分他们的先后,用一个开关量是可以解决的。只是问题放在心里特不好受,网上也找了很多资料,就是没办法解决。只能向您发送一个请教了,您毕竟是同行中专家。打扰之处,请原谅!

来自一个中专学校的计算机教师同行

敬上,谢谢!

=================================================

我打开Visual Studio,检测了一下,真的如他所说。这也引发了我的兴趣,产生这种现象的原因何在?

查询MSDN未获直接答案,我觉得必须到.NET源代码中去找谜底。

以下是我的追寻之路:

首先,我发现Form类定义了一个DefaultSize属性:


protected override Size DefaultSize

{

get

{

return new Size(300, 300);

}

}

可以看到,它设定的默认属性就是300*300.


然后,我在Form类的基类Control的构造函数中找到以下代码:


internal Control(bool autoInstallSyncContext)
{
……

Size defaultSize = this.DefaultSize;
this.width = defaultSize.Width;
this.height = defaultSize.Height;
……
}

可以看到,在构造函数中窗体的高度和宽度被定义为默认值。

打开Visual studio自动生成的的Form1.Designer.vb,可以看到以下代码:


Private Sub InitializeComponent()
Me.SuspendLayout()
'
'Form1
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 12.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font

Me.ClientSize = New System.Drawing.Size(562, 415)
Me.Name = "Form1"
Me.Text = "Form1"
Me.ResumeLayout(False)

End Sub

这说明窗体的ClientSize属性已经被改为默认值(300,300)之外的数值 。

ClientSize是一个属性,给它赋值时会调用SetClientSizeCore方法。

public Size ClientSize
{
get
{
return new Size(this.clientWidth, this.clientHeight);
}
set
{
this.SetClientSizeCore(value.Width, value.Height);
}
}

SetClientSizeCore方法内部又调用SizeFromClientSize方法设置窗体的Size属性

protected virtual void SetClientSizeCore(int x, int y)
{
this.Size = this.SizeFromClientSize(x, y);
this.clientWidth = x;
this.clientHeight = y;
this.OnClientSizeChanged(EventArgs.Empty);
}

Size属性内部调用SetBounds方法

public Size Size
{
get
{
return new Size(this.width, this.height);
}
set
{
this.SetBounds(this.x, this.y, value.Width, value.Height, BoundsSpecified.Size);
}
}

SetBounds方法又经过几个方法调用(不再赘述),最终调用UpdateBounds方法。


在Form类的UpdateBounds方法中,可以看到以下代码:




protected void UpdateBounds(int x, int y, int width, int height, int clientWidth, int clientHeight)
{
。。。
bool flag2 = (((this.Width != width) || (this.Height != height)) || (this.clientWidth != clientWidth)) || (this.clientHeight != clientHeight);
this.x = x; this.y = y; this.width = width; this.height = height;
this.clientWidth = clientWidth; this.clientHeight = clientHeight;
。。。
if (flag2) {
this.OnSizeChanged(EventArgs.Empty);
。。。
}
。。。

可以看到,只要尺寸不等于300*300,就会调用OnSizeChanged方法,而此方法负责激发Resize事件。
那么,UpdateBounds方法在什么情况被调用? 
使用Reflector继续跟踪下去,可以看到,位于最顶层的并且间接调用它的语句在WmCreate()方法中,而此方法又位于窗体过程WndProc内。

protected virtual void WndProc(ref Message m)
{
if ((this.controlStyle & ControlStyles.EnableNotifyMessage) == ControlStyles.EnableNotifyMessage)
{
this.OnNotifyMessage(m);
}
switch (m.Msg)
{
case 1:

this. WmCreate(ref m) ;
return;

case 2:
this.WmDestroy(ref m);
return;

。。。
}
。。。
}


在窗体过程WndProc中,响应窗体创建消息(WM_CREATE)时,就会调用WmCreate()方法。WM_CREATE消息在窗体创建时由Windows负责发送给进程。
所以,经过刨根问底的一番寻根之旅,我们弄明白了产生这一现象的根本原因。

感想:
微软平台的技术大都有这样的一个特点:抽象层次较高,使用简易。但封装层次很多,表面的“简单”是由背后的“复杂”所支撑。很多使用微软技术的程序员习惯了“只管用,不管为什么”,其实这种工作与学习方式并不利于技术的进一步提升。

就本文所讨论的问题而言,虽然Resize事件激发顺序问题在实际开发中可能并不十分重要与关键,但这种技术探索精神很可贵。如果学技术、教技术、用技术的人都有这种探索劲头,杜绝浮燥的急功近利的风气,相信国内软件开发者的总体水平绝不会差。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值