天天看點

.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

  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

  return new Size(this.width, this.height);

  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);

  在窗體過程WndProc中,響應窗體建立消息(WM_CREATE)時,就會調用WmCreate()方法。WM_CREATE消息在窗體建立時由Windows負責發送給程序。

  是以,經過刨根問底的一番尋根之旅,我們弄明白了産生這一現象的根本原因。

  感想:

  微軟平台的技術大都有這樣的一個特點:抽象層次較高,使用簡易。但封裝層次很多,表面的“簡單”是由背後的“複雜”所支撐。很多使用微軟技術的程式員習慣了“隻管用,不管為什麼”,其實這種工作與學習方式并不利于技術的進一步提升。

  就本文所讨論的問題而言,雖然Resize事件激發順序問題在實際開發中可能并不十分重要與關鍵,但這種技術探索精神很可貴。如果學技術、教技術、用技術的人都有這種探索勁頭,杜絕浮燥的急功近利的風氣,相信國内軟體開發者的總體水準絕不會差。

本文轉自 wws5201985 51CTO部落格,原文連結:http://blog.51cto.com/wws5201985/777172,如需轉載請自行聯系原作者