熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

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

2013-11-13 12:21:44  來源: .NET編程 
 前言

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

  Visual Basic 對 Visual Basic NET

  窗體(窗體類)正如其它類一樣無論在哪個版本的 Visual Basic 中都是必不可少的窗體也有屬性方法和事件且在同一個項目中也允許創建多個窗體實例 (參見 http://msdnmicrosoftcom/library/enus/off/html/defInstanceasp)例如假設你在 Visual Basic 項目中定義了一個窗體 Form 則你可以創建它的 個實例並同時顯示出來代碼如下

Dim myFirstForm As Form
Dim mySecondForm As Form
Dim myThirdForm As Form

Set myFirstForm = New Form
Set mySecondForm = New Form
Set myThirdForm = New Form

myFirstFormShow
mySecondFormShow
myThirdFormShow

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

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

FormTextBoxText = Fred

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

FormShow()

改成

Dim myForm As New Form()
myFormShow()

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

FormTextBoxText = Fred

改成

Dim myForm As New Form()
myFormTextBoxText = Fred

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

  升級向導如何解決它

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

Private Shared m_vbFormDefInstance As Form
Private Shared m_InitializingDefInstance As Boolean
Public Shared Property DefInstance() As Form
Get
If m_vbFormDefInstance Is Nothing _
OrElse m_vbFormDefInstanceIsDisposed Then
m_InitializingDefInstance = True
m_vbFormDefInstance = New Form()
m_InitializingDefInstance = False
End If
DefInstance = m_vbFormDefInstance
End Get
Set(ByVal Value As Form)
m_vbFormDefInstance = Value
End Set
End Property

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

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

  NET 窗體之間的交互

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

  1保持窗體引用的全局性

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

  2NET 中的數值全局化

  我以前曾經指出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()
myNewCustShow()
myFormsCustomerForm = myNewCust

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

Module DoingStuffWithForms
Sub DoExcitingThings()
myFormsCustomerFormText = _
DateTimeNow()ToLongTimeString
End Sub
End Module

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

  3窗體指針在項目中的傳遞

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

Public Class Form
Inherits SystemWindowsFormsForm
Dim myForm As Form

Private Sub Button_Click(ByVal sender As SystemObject _
ByVal e As SystemEventArgs) Handles ButtonClick
myForm = New Form()
myFormShow()
End Sub

Private Sub Button_Click(ByVal sender As SystemObject _
ByVal e As SystemEventArgs) Handles ButtonClick
CalculationsCompoundInterestCalc(myForm)
End Sub
End Class

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

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

Public Shared Sub WriteToLogWindow(ByVal Message As String)
Dim sb As New _
StringBuilder(m_LogFormtxtLogInfoText)
sbAppend(EnvironmentNewLine)
sbAppend(Message)
m_LogFormtxtLogInfoText = sbToString()
End Sub
End Class

  4讀取和改變窗體內的信息

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

  (1)在 Visual Basic NET 中新建一個 Windows 應用程序項目 此時項目中已經自動生成了一個窗體 Form
  
  (2)現在添加另一個窗體 Form 解決方案資源管理器中按右鍵單擊項目名稱 > 添加 > 添加 Windows 窗體 > 點擊打開以接受默認名稱 Formvb

  (3)在 Form 中添加兩個按鈕分別按照默認值命名為 Button 和 Button 並且調整它們在窗體中的位置以免重疊

  (4)在 Form 中添加一個簡單文本框按照默認值命名為 TextBox

  把下列代碼添加到 FormEnd Class前面 (在解決方案資源管理器中按右鍵單擊 Form> 查看代碼再粘貼下列代碼)

Public Property CustomerName() As String
Get
Return TextBoxText
End Get
Set(ByVal Value As String)
TextBoxText = Value
End Set
End Property

  接下來要做的是

  a 切換到 Form 的代碼Inherits SystemWindowsFormsForm 後面增加一行
Dim myForm As New Form()

  b 在 Form 中雙擊Button 按鈕在它的 Click 事件處理程序代碼中輸入下列代碼
myFormCustomerName = Fred
myFormShow()

  c 在 Form 中雙擊Button 按鈕在它的 Click 事件處理程序代碼中輸入下列代碼
MessageBoxShow(myFormCustomerName)
myFormCustomerName = Joe

  d 按 F 運行項目並點擊窗體中的 Button 和 Button 按鈕以觀察代碼運行情況

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

  結論

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


From:http://tw.wingwit.com/Article/program/net/201311/15749.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.