VB.NET多窗体实际编写方式讲解

开发 后端
VB.NET多窗体的编程方式将会以本文介绍的这种方式进行操作。初学者在学习的过程中可以依据这些步骤进行实践操作,以增加其的理解程度。

作为一个优秀的编程人员,能够熟练的运用两种以上的编程语言是必要的基本技能。那么VB.NET这样一款功能强大的开发语言应该是程序员们***之一。在这里先从一个VB.NET多窗体编程的例子来体验一下它的强大性。#t#

前言

在微软 Visual Basic 6.0 中,一条简单的 “Form2.Show” 语句就能显示项目中的第二窗体 (Form2)。然而,它在 Visaul Basic .NET 中却行不通了,因为 .NET 版在窗体处理机制上有了很大的变化。刚刚转向 .NET 版的 Visaul Basic 程序员实在难以接受这么大的变化,因为现在连“显示第二窗体”这么简单的任务都无从下手。我希望能够通过本文向大家介绍 Visaul Basic .NET 与早期的 Visual Basic 在窗体处理机制上有哪些不同之处,以及如何按照 .NET 的模式进行VB.NET多窗体编程。

Visual Basic 6.0 对 Visual Basic .NET

窗体(窗体类)正如其它类一样,无论在哪个版本的 Visual Basic 中都是必不可少的。VB.NET多窗体也有属性、方法和事件,且在同一个项目中也允许创建多个窗体实例。例如:假设你在 Visual Basic 6.0 项目中定义了一个窗体 Form2 ,则你可以创建它的 3 个实例并同时显示出来。代码如下:

  1. Dim myFirstForm As Form2   
  2. Dim mySecondForm As Form2   
  3. Dim myThirdForm As Form2   
  4. Set myFirstForm = New Form2   
  5. Set mySecondForm = New Form2   
  6. Set myThirdForm = New Form2   
  7. myFirstForm.Show   
  8. mySecondForm.Show   
  9. 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 项目自动创建的默认窗体实例都能被当成全局变量使用,也就是说,项目中的任何代码都能直接引用VB.NET多窗体,并且每次被引用的都是该窗体的同一个实例。例如:你可以在窗体中 button 控件的 Click 事件处理程序里用代码 “Form2.Show” 显示 Form2 窗体,然后用下列代码改变 Form2 中某个 textbox 控件 (TextBox1)的内容:

  1. Form2.TextBox1.Text = "Fred"  

可是,你在 Visual Basic .NET 中运行它却会得到一条错误消息:“Reference to a Non-Shared Member Requires an Object Reference”(引用非共享类成员必须使用对象指针)。这是在提醒你:你正在访问的类尚未进行实例化。有一个简便的解决方案:当你在调试过程中得到上述错误消息时,就把相应的语句:

  1. Form2.Show()  

 

改成:

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

 

此方案适用于大多数场合。然而,当项目中还有其它代码访问同一个 Form2 实例 (比如改变其中 TextBox1 的文本) 时,你可能会考虑把下列语句:

  1. Form2.TextBox1.Text =
     
    "Fred"  

 

改成:

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

 

不幸的是,这段代码创建了一个新的 Form2 实例,结果你所访问的VB.NET多窗体不再是原先的 Form2 ,这岂不麻烦了!更坏的是,你不会因此而得到任何错误消息提示,同时你先前调用 Show() 显示的 Form2 窗体也不会发生任何变化。

升级向导如何解决它

如果你用升级向导 (Upgrade Wizard) 把 Visual Basic 6.0 项目升级为 Visual Basic .NET 版,则它会在每个窗体中自动添加一段特殊代码,通过显式创建窗体实例来模拟早期 Visual Basic 版本中的默认实例化机制。此段代码被包裹于标号为 “Upgrade Support”的代码区块内,借助一个新增的 Shared 属性来生成当前窗体的实例:

  1. Private Shared m_vb6FormDef
    Instance As Form1   
  2. Private Shared m_Initializing
    DefInstance As Boolean   
  3. Public Shared Property 
    DefInstance() As Form1   
  4. Get   
  5. If m_vb6FormDefInstance Is 
    Nothing _   
  6. OrElse m_vb6FormDefInstance.
    IsDisposed Then   
  7. m_InitializingDefInstance = True   
  8. m_vb6FormDefInstance = New Form1()   
  9. m_InitializingDefInstance = False   
  10. End If   
  11. DefInstance = m_vb6FormDefInstance   
  12. End Get   
  13. Set(ByVal Value As Form1)   
  14. m_vb6FormDefInstance = Value   
  15. End Set   
  16. End Property  

 

代码中的 DefInstance 是一个 Shared 属性,它能以 “窗体名.DefInstance” 的形式直接访问。它所在项目中的任何代码访问它都将得到同一个窗体实例。这样,你就能模拟 Visual Basic 6.0 项目对VB.NET多窗体的直接引用了,只不过在代码中以 “Form2.DefInstance” 代替 “Form2” 而已。

这时,你只需用 Form2.DefInstance.Show() 和Form2.DefInstance.TextBox1.Text = "Fred" 分别替换原先对 Form2 相应的直接引用就大功告成了。假如你不用升级向导,而是在 Visual Basic .NET 窗体中手工插入上述代码 (以及升级向导在窗体的 New过程中自动添加的代码),也行。当然了,你并不一定非要修改窗体代码,因为有一种编程模式可以在 .NET 项目中模拟默认窗体实例的创建。本文将用余下的篇幅来介绍这种编程模式。

VB.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 属性不就相当于全局变量吗?因此,你可以创建这样的类:

  1. Public Class myForms   
  2. Private Shared m_CustomerForm 
    As CustomerForm   
  3. Public Shared Property 
    CustomerForm() As CustomerForm   
  4. Get   
  5. Return m_CustomerForm   
  6. End Get   
  7. Set(ByVal Value As CustomerForm)   
  8. m_CustomerForm = Value   
  9. End Set   
  10. End Property   
  11. End Class  

 

#p#

你需要在***实例化一个VB.NET多窗体时,把该窗体的实例保存到一个类中:

  1. Dim myNewCust As New 
    CustomerForm()   
  2. myNewCust.Show()   
  3. myForms.CustomerForm = 
    myNewCust  

 

这里的 CustomerForm 属性值就是你的窗体实例。于是,其它代码就能从项目的任何地方通过它来间接访问你的窗体了:

 

  1. Module DoingStuffWithForms   
  2. Sub DoExcitingThings()   
  3. myForms.CustomerForm.Text = _   
  4. DateTime.Now().
    ToLongTimeString   
  5. End Sub   
  6. End Module  

 

象这样把VB.NET多窗体实例保存为属性值就能按照你的要求模拟 Visual Basic 6.0 中的全局变量。如此模拟的“全局变量”其作用域比类域 (class scope) 高一个层次。所谓类域,是指变量仅仅在定义它的类(确切地说,应该包括模块、类或窗体)中有效。比类域还低一层次的是过程域 (procedure scope),即变量仅仅在定义它的例程中有效。
窗体指针在项目中的传递

除了把窗体实例全局化以外,你还可以把窗体类指针保存在变量中传递给需要访问该窗体的例程。假设你有一个窗体 Form1,并希望在点击 Form1 中某个按钮 (Button1) 时打开另第二窗体 Form2 ,然后在点击第二窗体 Form2 中的另一个按钮 (Button2) 时进行某项计算。你可以把整个代码都写在 Form1 中,即:

 

  1. Public Class Form1   
  2. Inherits System.Windows.Forms.Form   
  3. Dim myForm2 As Form2   
  4. Private Sub Button1_Click
    (ByVal sender As System.
    Object,_ ByVal e As System.
    EventArgs) Handles Button1.Click   
  5. myForm2 = New Form2()   
  6. myForm2.Show()   
  7. End Sub   
  8. Private Sub Button2_Click
    (ByVal sender As System.Object, 
    _ByVal e As System.EventArgs) 
    Handles Button2.Click   
  9. Calculations.CompoundInterest
    Calc(myForm2)   
  10. End Sub   
  11. End Class  

 

无论是把窗体指针全局化,还是把它以参数的形式传递,都是可行的。然而,你必须根据项目的需要选择***方案。当 .NET 项目中只有少数几个过程需要访问特定窗体时,我建议你给这些过程增加一个参数,以在必要时接受窗体指针。当你的项目有太多过程需要访问该窗体时,你就应该考虑设置一个全局窗体指针变量。当然了,你***还是考虑调整项目代码结构,使得真正访问该窗体的类或者过程只有一个。如果你希望用窗体来显示登录信息,则你可以先创建一个类,把VB.NET多窗体实例保存为它的 Shared 类成员,然后添加一个 Shared 方法 WriteToLogWindow 来完成实际的窗体访问。于是,项目中的任何代码只需调用此 WriteToLogWindow 方法就能间接访问显示登录信息的窗体了:

 

  1. Public Class Log   
  2. Private Shared m_LogForm As Form2   
  3. Public Shared Property LogForm() 
    As Form2   
  4. Get   
  5. Return m_LogForm   
  6. End Get   
  7. Set(ByVal Value As Form2)   
  8. m_LogForm = Value   
  9. End Set   
  10. End Property   
  11. Public Shared Sub WriteToLogWindow
    (ByVal Message As String)   
  12. Dim sb As New _   
  13. StringBuilder(m_LogForm.txtLog
    Info.Text)   
  14. sb.Append(Environment.NewLine)   
  15. sb.Append(Message)   
  16. m_LogForm.txtLogInfo.Text = 
    sb.ToString()   
  17. End Sub   
  18. End Class  

 

读取和改变VB.NET多窗体内的信息

到现在为止,我们讨论的只是如何创建和访问窗体实例,而没有涉及如何读取或改变窗体内的信息。如果你的窗体已经按照前述方法实例化,并且访问窗体的代码都位于窗体所在的项目中,则你可以直接操作窗体中的任何控件来读取和改变窗体内的信息。但我觉得这样并不理想。与其直接访问窗体中的文本框、按钮等控件,还不如增加一个 Public 属性,通过它来控制窗体中的控件。如果你有意尝试这种特殊的窗体访问方式,请跟我来:

在 Visual Basic .NET 中新建一个 Windows 应用程序项目。

此时项目中已经自动生成了一个窗体 Form1 。现在添加另一个窗体 Form2 :在“解决方案资源管理器”中按右键单击项目名称 -> “添加” -> “添加 Windows 窗体” -> 点击“打开”以接受默认名称 Form2.vb 。

在 Form1 中添加两个按钮,分别按照默认值命名为 Button1 和 Button2 ,并且调整它们在窗体中的位置以免重叠。

在 Form2 中添加一个简单文本框,按照默认值命名为 TextBox1

把下列代码添加到 Form2 的“End Class”前面 (在“解决方案资源管理器”中按右键单击 “Form2”-> “查看代码”,再粘贴下列代码):

 

  1. Public Property CustomerName()
     As String   
  2. Get   
  3. Return TextBox1.Text   
  4. End Get   
  5. Set(ByVal Value As String)   
  6. TextBox1.Text = Value   
  7. End Set   
  8. End Property  

 

接下来要做的是:

a. 切换到 Form1 的代码,在 “Inherits System.Windows.Forms.Form” 后面增加一行:

 

  1. Dim myForm2 As New Form2()  

b. 在 Form1 中双击Button1 按钮,在它的 Click 事件处理程序代码中输入下列代码:

 

  1. myForm2.CustomerName = 
    "Fred"   
  2. myForm2.Show()  

 

c. 在 Form1 中双击Button2 按钮,在它的 Click 事件处理程序代码中输入下列代码:

 

  1. MessageBox.Show
    (myForm2.CustomerName)   
  2. myForm2.CustomerName = 
    "Joe"  

d. 按 F5 运行项目,并点击窗体中的 Button1 和 Button2 按钮,以观察代码运行情况。

表面看来,通过 CustomerName 属性来访问 Form2 与直接访问 Form2 非常相似。然而,这种间接的窗体访问方式能够带来很多好处,其中最重要的一点就在于它实现了更高的抽象性。换言之,哪怕你不知道 Form2 中控件的任何细节 (比如:窗体中是否包含 textbox 控件) ,也能与 Form2 交换数据;你所要做的只是读取或设置 CustomerName 属性值而已。有了这种抽象,你就能在修改 Form2 的实现时不影响项目中的其它代码,因而大大简化了整个项目代码的维护。

单从本文的例子来看,这种基于属性的窗体编程模式似乎并不比常规方式简单。然而,它以属性的形式隐藏了窗体的全部细节,故能用简洁、一致的代码来访问窗体。所以,它在一些相当复杂的用户界面编程中能够大显身手。总而言之,通过属性值来访问窗体及其控件的编程模式虽然不太直观,却对程序员很有价值:它不但比直接访问窗体的编程模式来得更专业,而且让整个项目的代码清晰易读。

结论

Visual Basic .NET 取消了早期版本中的“默认窗体实例”,却引起了不少 .NET 编程新手的困惑。Visual Basic .NET 规定,只有通过引用窗体实例,才能访问窗体的属性、方法及其控件。你所保存的窗体实例指针应该尽量让整个项目都能直接访问到它。VB.NET多窗体处理机制已经变得更合理、更强大,可对于刚接触 .NET 的程序员来说,它的改进偏偏是造

责任编辑:曹凯 来源: 博客园
相关推荐

2010-01-08 14:07:47

VB.NET窗体传值

2010-01-07 18:22:40

VB.NET声音播放

2010-01-11 11:37:08

VB.NET操作CSV

2010-01-11 17:58:36

VB.NET压缩ZIP

2010-01-15 11:31:02

VB.NET接口实现多

2010-01-21 15:10:37

VB.NET静态托盘程

2010-01-07 17:57:22

VB.NET构造函数

2010-01-11 10:19:18

VB.NET启动外部程

2009-10-14 13:56:05

VB.NET数据窗体

2009-10-16 09:35:24

VB.NET制作透明窗

2010-01-13 09:31:39

VB.NET窗体打印

2009-11-10 14:30:33

VB.NET多窗体

2009-10-14 15:20:21

VB.NET窗体指针

2010-01-11 15:12:30

VB.NET特殊窗体

2009-10-14 15:34:29

VB.NET窗体编程模

2009-10-09 16:54:03

VB.NET窗体

2010-01-11 16:04:10

VB.NET使用wit

2009-10-20 10:16:24

VB.NET COMB

2010-01-14 14:46:57

2009-10-26 17:53:50

VB.NET新窗体
点赞
收藏

51CTO技术栈公众号