VB.NET中的多窗體編程:升級到 .NET

發表于:2008-04-10來源:作者:點擊數: 標簽:
一、前言 在微軟 Visual Basic6.0 中,一條簡單的 “Form2.Show” 語句就能顯示項目中的第二窗體 (Form2)。然而,它在 Visaul Basic .NET 中卻行不通了,因為 .NET 版在窗體處理機制上有了很大的變化。剛剛轉向 .NET 版的 Visaul Basic 程序員 實在難以接受這
一、前言

  在微軟 Visual Basic 6.0 中,一條簡單的 “Form2.Show” 語句就能顯示項目中的第二窗體 (Form2)。然而,它在 Visaul Basic .NET 中卻行不通了,因為 .NET 版在窗體處理機制上有了很大的變化。剛剛轉向 .NET 版的 Visaul Basic 程序員實在難以接受這么大的變化,因為現在連“顯示第二窗體”這么簡單的任務都無從下手。我希望能夠通過本文向大家介紹 Visaul Basic .NET 與早期的 Visual Basic 在窗體處理機制上有哪些不同之處,以及如何按照 .NET 的模式進行多窗體編程。

  二、Visual Basic 6.0 對 Visual Basic .NET

  窗體(窗體類)正如其它類一樣,無論在哪個版本的 Visual Basic 中都是必不可少的。窗體也有屬性、方法和事件,且在同一個項目中也允許創建多個窗體實例 (參見 http://msdn.microsoft.com/library/en-us/off2000/html/defInstance.asp)。例如:假設你在 Visual Basic 6.0 項目中定義了一個窗體 Form2 ,則你可以創建它的 3 個實例并同時顯示出來。代碼如下:

Dim myFirstForm As Form2
Dim mySecondForm As Form2
Dim myThirdForm As Form2

Set myFirstForm = New Form2
Set mySecondForm = New Form2
Set myThirdForm = New Form2

myFirstForm.Show
mySecondForm.Show
myThirdForm.Show

  以上代碼用 3 條 Set 語句生成了 3 個 Form2 實例。你可以把它原封不動地搬到 Visual Basic .NET 中運行,它照樣能夠正確顯示 3 個 Form2 窗體。在這里,“Form2” 其實相當于一個普通的類。Visual Basic 6.0 允許代碼直接訪問尚未實例化的窗體類;然而Visual Basic .NET 卻規定在訪問任何類之前都要進行實例化,而且必須借助實例來訪問類。這種變化當然有可能造成許多疑惑。Visual Basic 6.0 等早期版本能自動生成每個窗體的默認實例,從而允許直接通過窗體名稱來訪問窗體。例如:在Visual Basic 6.0項目中,可以直接用代碼“Form2.Show ”顯示 Form2 的默認實例;然而在 Visual Basic .NET 中,這么做只會引發錯誤,因為 Visual Basic .NET 既不會創建默認的窗體實例,也不允許直接訪問尚未實例化的窗體類。

  這就是 Visual Basic .NET 與早期 Visual Basic 在窗體處理機制上的關鍵區別——你只有先創建窗體實例,然后才可以顯示窗體外觀、訪問窗體屬性及其控件。它們還有另一個區別:Visual Basic 6.0 項目自動創建的默認窗體實例都能被當成全局變量使用,也就是說,項目中的任何代碼都能直接引用窗體,并且每次被引用的都是該窗體的同一個實例。例如:你可以在窗體中 button 控件的 Click 事件處理程序里用代碼 “Form2.Show” 顯示 Form2 窗體,然后用下列代碼改變 Form2 中某個 textbox 控件 (TextBox1)的內容:

Form2.TextBox1.Text = "Fred"

  可是,你在 Visual Basic .NET 中運行它卻會得到一條錯誤消息:“Reference to a Non-Shared Member Requires an Object Reference”(引用非共享類成員必須使用對象指針)。這是在提醒你:你正在訪問的類尚未進行實例化。有一個簡便的解決方案:當你在調試過程中得到上述錯誤消息時,就把相應的語句:

Form2.Show()

改成:

Dim myForm2 As New Form2()
myForm2.Show()

  此方案適用于大多數場合。然而,當項目中還有其它代碼訪問同一個 Form2 實例 (比如改變其中 TextBox1 的文本) 時,你可能會考慮把下列語句:

Form2.TextBox1.Text = "Fred"

改成:

Dim myForm2 As New Form2()
myForm2.TextBox1.Text = "Fred"

  不幸的是,這段代碼創建了一個新的 Form2 實例,結果你所訪問的窗體不再是原先的 Form2 ,這豈不麻煩了!更壞的是,你不會因此而得到任何錯誤消息提示,同時你先前調用 Show() 顯示的 Form2 窗體也不會發生任何變化。

  三、升級向導如何解決它

  如果你用升級向導 (Upgrade Wizard) 把 Visual Basic 6.0 項目升級為 Visual Basic .NET 版,則它會在每個窗體中自動添加一段特殊代碼,通過顯式創建窗體實例來模擬早期 Visual Basic 版本中的默認實例化機制。此段代碼被包裹于標號為 “Upgrade Support”的代碼區塊內,借助一個新增的 Shared 屬性來生成當前窗體的實例:

Private Shared m_vb6FormDefInstance As Form1
Private Shared m_InitializingDefInstance As Boolean
Public Shared Property DefInstance() As Form1
Get
If m_vb6FormDefInstance Is Nothing _
OrElse m_vb6FormDefInstance.IsDisposed Then
m_InitializingDefInstance = True
m_vb6FormDefInstance = New Form1()
m_InitializingDefInstance = False
End If
DefInstance = m_vb6FormDefInstance
End Get
Set(ByVal Value As Form1)
m_vb6FormDefInstance = Value
End Set
End Property

  代碼中的 DefInstance 是一個 Shared 屬性,它能以 “窗體名.DefInstance” 的形式直接訪問。它所在項目中的任何代碼訪問它都將得到同一個窗體實例。這樣,你就能模擬 Visual Basic 6.0 項目對窗體的直接引用了,只不過在代碼中以“Form2.DefInstance”代替“Form2” 而已。

  這時,你只需用 Form2.DefInstance.Show() 和Form2.DefInstance.TextBox1.Text = "Fred" 分別替換原先對 Form2 相應的直接引用就大功告成了。假如你不用升級向導,而是在 Visual Basic .NET 窗體中手工插入上述代碼 (以及升級向導在窗體的 New過程中自動添加的代碼),也行。當然了,你并不一定非要修改窗體代碼,因為有一種編程模式可以在 .NET 項目中模擬默認窗體實例的創建。本文將用余下的篇幅來介紹這種編程模式。

  四、.NET 窗體之間的交互

  在 Visual Basic 6.0 等早期版本中,多個窗體之間的交互通常需要借助默認窗體實例來完成。下面我將結合某些具體的編程任務來講解如何在 .NET 下實現多窗體交互,希望它能對你的開發任務有所幫助。

 ?。?、保持窗體引用的全局性

  前面提到,進行 .NET 窗體編程時應該牢牢把握下列原則:在訪問窗體之前,你必須進行窗體實例化;如果在項目中有多處代碼訪問同一窗體,則你必須把它的同一實例指針傳遞給這些代碼。對于早已習慣了直接把默認窗體實例當成全局變量來使用的 Visual Basic 6.0 程序員來說,這可是個嚴重的挑戰。好在 .NET 為你提供了兩條出路:其一,把窗體實例指針保存在全局變量中;其二,把窗體實例指針傳遞給任何需要訪問它的窗體、類、模塊或者過程。

 ?。?、.NET 中的數值全局化

  我以前曾經指出,Visual Basic .NET 不支持全局變量,現在我又要說,在 .NET 中可以在某種程度上實現數值全局化。這算不算此一時,彼一時?不,我不是那種人。Visual Basic .NET 確實不支持全局變量,然而它借助 Shared (相當于 C# 中的 static) 變量卻能模擬全局變量。事實上,前面介紹的 Visual Basic 升級向導自動添加到窗體代碼中的 DefInstance 屬性就是 Shared 類成員。無論容納 DefInstance 屬性的窗體類是否已經實例化,它都能被項目中的任何代碼所引用。象這樣的 Shared 屬性不就相當于全局變量嗎?因此,你可以創建這樣的類:

Public Class myForms
Private Shared m_CustomerForm As CustomerForm
Public Shared Property CustomerForm() As CustomerForm
Get
Return m_CustomerForm
End Get
Set(ByVal Value As CustomerForm)
m_CustomerForm = Value
End Set
End Property
End Class

  你需要在首次實例化一個窗體時,把該窗體的實例保存到一個類中:

Dim myNewCust As New CustomerForm()
myNewCust.Show()
myForms.CustomerForm = myNewCust

  這里的 CustomerForm 屬性值就是你的窗體實例。于是,其它代碼就能從項目的任何地方通過它來間接訪問你的窗體了:

Module DoingStuffWithForms
Sub DoExcitingThings()
myForms.CustomerForm.Text = _
DateTime.Now().ToLongTimeString
End Sub
End Module

  象這樣把窗體實例保存為屬性值就能按照你的要求模擬 Visual Basic 6.0 中的全局變量。如此模擬的“全局變量”其作用域比類域 (class scope) 高一個層次。所謂類域,是指變量僅僅在定義它的類(確切地說,應該包括模塊、類或窗體)中有效。比類域還低一層次的是過程域 (procedure scope),即變量僅僅在定義它的例程中有效。

 ?。?、窗體指針在項目中的傳遞

  除了把窗體實例全局化以外,你還可以把窗體類指針保存在變量中傳遞給需要訪問該窗體的例程。假設你有一個窗體 Form1,并希望在點擊 Form1 中某個按鈕 (Button1) 時打開另第二窗體 Form2 ,然后在點擊第二窗體 Form2 中的另一個按鈕 (Button2) 時進行某項計算。你可以把整個代碼都寫在 Form1 中,即:

Public Class Form1
Inherits System.Windows.Forms.Form
Dim myForm2 As Form2

Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
myForm2 = New Form2()
myForm2.Show()
End Sub

Private Sub Button2_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button2.Click
Calculations.CompoundInterestCalc(myForm2)
End Sub
End Class

  無論是把窗體指針全局化,還是把它以參數的形式傳遞,都是可行的。然而,你必須根據項目的需要選擇最佳方案。當 .NET 項目中只有少數幾個過程需要訪問特定窗體時,我建議你給這些過程增加一個參數,以在必要時接受窗體指針。當你的項目有太多過程需要訪問該窗體時,你就應該考慮設置一個全局窗體指針變量。當然了,你最好還是考慮調整項目代碼結構,使得真正訪問該窗體的類或者過程只有一個。如果你希望用窗體來顯示登錄信息,則你可以先創建一個類,把窗體實例保存為它的 Shared 類成員,然后添加一個 Shared 方法 WriteToLogWindow 來完成實際的窗體訪問。于是,項目中的任何代碼只需調用此 WriteToLogWindow 方法就能間接訪問顯示登錄信息的窗體了:

Public Class Log
Private Shared m_LogForm As Form2
Public Shared Property LogForm() As Form2
Get
Return m_LogForm
End Get
Set(ByVal Value As Form2)
m_LogForm = Value
End Set
End Property

Public Shared Sub WriteToLogWindow(ByVal Message As String)
Dim sb As New _
StringBuilder(m_LogForm.txtLogInfo.Text)
sb.Append(Environment.NewLine)
sb.Append(Message)
m_LogForm.txtLogInfo.Text = sb.ToString()
End Sub
End Class

 ?。?、讀取和改變窗體內的信息

  到現在為止,我們討論的只是如何創建和訪問窗體實例,而沒有涉及如何讀取或改變窗體內的信息。如果你的窗體已經按照前述方法實例化,并且訪問窗體的代碼都位于窗體所在的項目中,則你可以直接操作窗體中的任何控件來讀取和改變窗體內的信息。但我覺得這樣并不理想。與其直接訪問窗體中的文本框、按鈕等控件,還不如增加一個 Public 屬性,通過它來控制窗體中的控件。如果你有意嘗試這種特殊的窗體訪問方式,請跟我來:

 ?。ǎ保┰?Visual Basic .NET 中新建一個 Windows 應用程序項目。 此時項目中已經自動生成了一個窗體 Form1 。
  
 ?。ǎ玻┈F在添加另一個窗體 Form2 :在“解決方案資源管理器”中按右鍵單擊項目名稱 -> “添加” -> “添加 Windows 窗體” -> 點擊“打開”以接受默認名稱 Form2.vb 。

 ?。ǎ常┰?Form1 中添加兩個按鈕,分別按照默認值命名為 Button1 和 Button2 ,并且調整它們在窗體中的位置以免重疊。

 ?。ǎ矗┰?Form2 中添加一個簡單文本框,按照默認值命名為 TextBox1。

  把下列代碼添加到 Form2 的“End Class”前面 (在“解決方案資源管理器”中按右鍵單擊 “Form2”-> “查看代碼”,再粘貼下列代碼):

Public Property CustomerName() As String
Get
Return TextBox1.Text
End Get
Set(ByVal Value As String)
TextBox1.Text = Value
End Set
End Property

  接下來要做的是:

  a. 切換到 Form1 的代碼,在 “Inherits System.Windows.Forms.Form” 后面增加一行:
Dim myForm2 As New Form2()

  b. 在 Form1 中雙擊Button1 按鈕,在它的 Click 事件處理程序代碼中輸入下列代碼:
myForm2.CustomerName = "Fred"
myForm2.Show()

  c. 在 Form1 中雙擊Button2 按鈕,在它的 Click 事件處理程序代碼中輸入下列代碼:
MessageBox.Show(myForm2.CustomerName)
myForm2.CustomerName = "Joe"

  d. 按 F5 運行項目,并點擊窗體中的 Button1 和 Button2 按鈕,以觀察代碼運行情況。

  表面看來,通過 CustomerName 屬性來訪問 Form2 與直接訪問 Form2 非常相似。然而,這種間接的窗體訪問方式能夠帶來很多好處,其中最重要的一點就在于它實現了更高的抽象性。換言之,哪怕你不知道 Form2 中控件的任何細節 (比如:窗體中是否包含 textbox 控件) ,也能與 Form2 交換數據;你所要做的只是讀取或設置 CustomerName 屬性值而已。有了這種抽象,你就能在修改 Form2 的實現時不影響項目中的其它代碼,因而大大簡化了整個項目代碼的維護。單從本文的例子來看,這種基于屬性的窗體編程模式似乎并不比常規方式簡單。然而,它以屬性的形式隱藏了窗體的全部細節,故能用簡潔、一致的代碼來訪問窗體。所以,它在一些相當復雜的用戶界面編程中能夠大顯身手??偠灾?,通過屬性值來訪問窗體及其控件的編程模式雖然不太直觀,卻對程序員很有價值:它不但比直接訪問窗體的編程模式來得更專業,而且讓整個項目的代碼清晰易讀。

  五、結論

  Visual Basic .NET 取消了早期版本中的“默認窗體實例”,卻引起了不少 .NET 編程新手的困惑。Visual Basic .NET 規定,只有通過引用窗體實例,才能訪問窗體的屬性、方法及其控件。你所保存的窗體實例指針應該盡量讓整個項目都能直接訪問到它。Visual Basic .NET 的窗體處理機制已經變得更合理、更強大,可對于剛接觸 .NET 的程序員來說,它的改進偏偏是造成許多困惑的根源。

原文轉自:http://www.anti-gravitydesign.com

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97