VB.NET学习笔记:窗体显示及互相传值
测试环境:windows 7和Microsoft Visual Studio 2015
很多文章里都说在Visaul Basic .NET中显示窗体之前必须先实例化,经测试,也可以与旧Visual Basic 6.0 一样不经实例化直接Form1.Show显示窗体。
一、正确显示窗体
新建一个窗体应用程序,再添加一个模块和一个窗体。如图:
在项目属性页进行如下图设置。
在模块添加如下代码:
Module Module1 Public Sub Main() 'IF语句,符合条件则显示窗体Form1,这里略过直接显示窗体 Form1.Show() End Sub End Module
测试项目,发现窗体一闪而过,为什么没有长时间显示窗体呢?
在网友uruseibest的热心帮助下才知道应该使用Form1.ShowDialog()将窗体显示为模式对话框。
修改代码如下:
Module Module1 Public Sub Main() 'IF语句,符合条件则显示窗体Form1,这里略过直接显示窗体 Form1.ShowDialog() End Sub End Module
测试项目,代码执行到 Form1.ShowDialog()这句就不再继续向下执行,直到关闭窗体Form1后才往下执行余下代码。
也可以使用Application.Run(Form1)语句代替rm1.ShowDialog()来将窗体显示为模式对话框。
对于旧Visual Basic 6.0程序员来说还是很容易理解的,但作为面向对象编程的Visaul Basic .NET语言,最好先把窗体类给实例化,再显示窗体。即应该是这样:
Module Module1 Public Sub Main() 'IF语句,符合条件则显示窗体Form1,这里略过直接显示窗体 '声明变量 Dim frm As Form1 '实例化窗体 frm = New Form1 '显示窗体 frm.ShowDialog() End Sub End Module
二、窗体间互相传值
分别在两个窗体上添加3个按钮和一个Label控件。如图:
为按钮添加事件代码:
Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Form2.Show() End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click Form2.Label1.Text = "Form2,你好!" End Sub Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click Form2.Label1.Text = "" End Sub End Class
Public Class Form2 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Form1.Label1.Text = "Form1,感谢你!" End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click Form1.Label1.Text = "" End Sub End Class
以上代码是按旧Visual Basic 6.0语言编写的,但并没有出现其他文章里所说不实例化就会报错,代码能够执行并很好完成窗体间相互传值的任务。
后续:经多方阅读后了解到,上述是使用窗体的共享方法Show来显示窗体,用于与VB6兼容,但不推荐使用。即:
Form1.Show()
共享方法可以不用先实例化窗体就进行了显示,这样显示的是默认的窗体实例。在包含窗体的项目中的任何地方都可以使用这个默认窗体。默认的窗体实例只有一个,因此任何指向它的引用实际上使用的都是同一个底层窗体实例。
三、测试实例化后再显示和传值
模块代码修改为:
Module Module1 Public Sub Main() 'IF语句,符合条件则显示窗体Form1,这里略过直接显示窗体 '声明变量 Dim frm As Form1 '实例化窗体 frm = New Form1 '显示窗体 frm.ShowDialog() End Sub End Module
窗体代码修改为:
Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim frm2 As Form2 frm2 = New Form2 frm2.Show() End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click Dim frm2 As Form2 frm2 = New Form2 frm2.Label1.Text = "Form2,你好!" End Sub Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click Dim frm2 As Form2 frm2 = New Form2 frm2.Label1.Text = "" End Sub End Class
测试一下项目,点击Button1可以正确显示窗体 Form2,可是Button2和Button3却无法向Form1传值,是不是也要加一句frm2.Show()?试试看!
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click Dim frm2 As Form2 frm2 = New Form2 frm2.Label1.Text = "Form2,你好!" frm2.Show() End Sub
测试项目,可以传值了,只是并不是向原来的Form1传值,而程序反而又蹦出了一个Form1,再点一次按钮又蹦出一个Form1,再点再蹦……,如图:
原来窗体其实是一个类,一个类可以实例化成多个对象,我们两个按钮就生成了两个Form1窗体对象了,看来还是Visual Basic 6.0语言好理解。什么是类?笔者在这里暂不过多解释!
怎么办?在网友uruseibest的博文里发现,可以这样处理:
1、保持窗体引用的全局性
在访问窗体之前,最好进行窗体实例化,如果在项目中有多处代码访问同一窗体,可以把它的同一实例指针传递给这些代码,即把窗体实例指针保存在全局变量中。
模块代码再次修改:
Module Module1 '声明一个全局变量 Public frm1 As Form1 Public Sub Main() 'IF语句,符合条件则显示窗体Form1,这里略过直接显示窗体 frm1 = New Form1 frm1.ShowDialog() End Sub End Module
窗体Form1代码修改为:
Public Class Form1 Private frm2 As Form2 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load '加载窗体时实例化 frm2 = New Form2 End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click frm2.Show() End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click frm2.Label1.Text = "Form2,你好!" frm2.Show() End Sub Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click frm2.Label1.Text = "" frm2.Show() End Sub End Class
窗体Form2代码修改为:
Public Class Form2 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click frm1.Label1.Text = "Form1,感谢你!" End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click frm1.Label1.Text = "" End Sub End Class
OK,现在可以正确传值了,测试结果如图:
2、重写窗体构造函数
什么是重写?就是不要原来的无参数构造函数了,改成自己写的带参数构造函数。
回到Form2窗体的代码窗口,输入:Public Sub new,按回车,即可添加如下代码:
Public Sub New() ' 此调用是设计器所必需的。 InitializeComponent() ' 在 InitializeComponent() 调用之后添加任何初始化。 End Sub
这个是窗体默认的无参数构造函数,现在我们重写一个有参数的构造函数。如下代码:
Private Frm1 As Form1 Public Sub New(ByVal frm As Form1) MyBase.New() ' 此调用是设计器所必需的。 InitializeComponent() ' 在 InitializeComponent() 调用之后添加任何初始化。 Me.Frm1 = frm End Sub
这里传入的参数是一个对象,即Form1窗体,当然你也可以把参数改成字符串等其他数据类型。
现在,就可以通过重写的构造函数把Form1对象传入Form2中,在Form2中直接使用变量Frm1就是Form1窗体啦。
然后到Form1代码窗口,修改实例化Form2的代码为:
Frm2 = New Form2(Me)
原来是不带参数的,因为上面已经修改了Form2的构造函数,所以此时实例化Form2时不再调用默认构造函数,而是需要我们指明参数再实例化。
这里的Me指的就是Form1当前的实例,也就是把当前Form1的实例通过Form2的构造函数传递给Form2类。
完整代码如下:
模块代码:
Module Module1 '声明一个全局变量 Public frm1 As Form1 Public Sub Main() 'IF语句,符合条件则显示窗体Form1,这里略过直接显示窗体 frm1 = New Form1 frm1.ShowDialog() End Sub End Module
Form1代码:
Public Class Form1 Private frm2 As Form2 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load '加载窗体时实例化 Frm2 = New Form2(Me) End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click frm2.Show() End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click frm2.Label1.Text = "Form2,你好!" frm2.Show() End Sub Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click frm2.Label1.Text = "" frm2.Show() End Sub End Class
Form2代码:
Public Class Form2 Private Frm1 As Form1 Public Sub New(ByVal frm As Form1) MyBase.New() ' 此调用是设计器所必需的。 InitializeComponent() ' 在 InitializeComponent() 调用之后添加任何初始化。 Me.Frm1 = frm End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Frm1.Label1.Text = "Form1,感谢你!" End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click Frm1.Label1.Text = "" End Sub End Class
经测试,正确实现相互传值。
还有说使用委托机制,笔者没有测试,感兴趣的你也可以查阅相关博文了解了解。
四、迷惑
不用实例化就可以很方便的调用窗体和向窗体传值,为什么Visaul Basic .NET要如此多事,非要整个实例化才显得更高大上吗?就因为是面向对象编程?那为什么还可以象旧Visual Basic 6.0 那样不实例化调用窗体呢?
更多阅读:再谈WinForm窗体间相互传值(单值、多值)——属性、委托和事件
本文参考了以下博文:
1、vb.net 教程 3-2 窗体编程之窗体 5
2、vb.net 教程 3-2 窗体编程之窗体 4
3、如何在窗体之间通讯的几种方法 ? VB.NET方案
4、vb.net的多窗体操作