在我们的程序中,窗体之间经常要相互访问彼此的成员,调用彼此的方法,或者激发彼此的事件。下面就我的经验,简单谈谈在.NET中窗体间相互访问的几种方式。以下给出了C#.NET和VB.NET的示例代码。用Form1、Form2类分别表示主、从窗体。
1、内部成员访问法,适合于主窗体访问它所创建的从窗体对象中的成员。只需将从窗体允许主窗体访问的那些成员加上internal或Friend限定符。例如:
C#主 | private void ShowForm2Info() { Form 2 f 2 = new Form2(); f2.Show(); MessageBox.Show(f2.MyName); } |
C#从 | internal string MyName = "A Form2 Object"; |
VB主 | Private Sub ShowForm2Info() Dim f2 As New Form2 f2.Show() MsgBox(f2.MyName) End Sub |
VB从 | Friend MyName As String = "A Form2 Object" |
2、将以上方法反过来用,让从窗体访问创建它的主窗体中的成员,需要增加一个步骤:在从窗体的构造函数中传入一个主窗体的引用,以便找到创建它的主窗体(新建的构造函数中不要忘了调用InitializeComponent()函数)。主窗体创建从窗体对象时传入自身的引用。例如:
C#主 | private void ShowForm2() { Form 2 f 2 = new Form2(this); f2.Show(); }
internal string MyName = "A Form1 Object"; |
C#从 | private Form 1 f 1; public Form2(Form1 _f1) { InitializeComponent(); f1 = _f1; }
private void ShowForm1Info() { MessageBox.Show(f1.MyName); } |
VB主 | Private Sub ShowForm2() Dim f2 As New Form2(Me) f2.Show() End Sub
Friend MyName As String = "A Form1 Object" |
VB从 | Private f1 As Form1 Public Sub New(ByVal _f1 As Form1) MyBase.New() InitializeComponent() f1 = _f1 End Sub
Private Sub ShowForm1Info() MsgBox(f1.MyName) End Sub |
注:我的朋友wuzhenzhi指出,对主窗体的引用,通过属性或方法传入要比通过在构造函数中传入好,因为“对于一个窗体,没什么,但如果是一个用户控件,我们经常会像拖动Button控件一样从工具栏里拽下来,这时系统就会调用默认的那个构造函数,而不是你的那个重载版本,这办法就失灵了。”这个建议有道理。
3、从窗体访问主窗体中的成员,如果主窗体只有一个实例,或者被访问对象是全局性的,可以将主窗体中要求访问的成员设置为静态的,加上static或Shared限定符即可:
C#主 | private void ShowForm2() { Form 2 f 2 = new Form2(); f2.Show(); }
internal static string MyName = "A Form1 Object"; |
C#从 | private void ShowForm1Info() { MessageBox.Show(Form1.MyName); } |
VB主 | Private Sub ShowForm2() Dim f2 As New Form2() f2.Show() End Sub
Friend Shared MyName As String = "A Form1 Object" |
VB从 | Private Sub ShowForm1Info() MsgBox(Form1.MyName) End Sub |
4、还可以运用.NET的事件委托机制,实现更加灵活的相互调用。下例是从窗体激发主窗体的事件。事件委托机制的功能事实上远比此例强大,大家可以找书看看它的详细用法。
C#主 | private void ShowForm2() { Form 2 f 2 = new Form2(); f2.Show(); f2.SendMessage += new Form2.DelegateOfSendMessage(ShowMessage); }
private void ShowMessage(string str) { MessageBox.Show(str); } |
C#从 | public delegate void DelegateOfSendMessage(string str); public event DelegateOfSendMessage SendMessage;
private void Send() { SendMessage("A Message From Form2"); } |
VB主 | Private Sub ShowForm2() Dim f2 As New Form2 f2.Show() AddHandler f2.SendMessage, AddressOf ShowMessage End Sub
Private Sub ShowMessage(ByVal str As String) MsgBox(str) End Sub |
VB从 | Public Delegate Sub DelegateOfSendMessage(ByVal str As String) Public Event SendMessage As DelegateOfSendMessage
Private Sub Send() RaiseEvent SendMessage("A Message From Form2") End Sub |
5、同样的道理,参考方法2和方法4可以实现主窗体激发从窗体的事件,这个请读者自己实验。当然这样一来代码会显得比较乱。一个较好的解决方法是将事件封装入一个接口,同时在事件的发布方和订阅方实现。这种技术在.NET Remoting远程对象操作技术中尤为重要,有一篇不错的文章给大家参考:《Remoting事件处理全接触》(http://www.cnblogs.com/wayfarer/articles/75213.html)
6、习惯了VB6中全局变量的朋友可能觉得.NET中不支持全局变量对于窗体间的交互很不方便,那么再请大家参考这篇文章:《.NET窗体之间的交互》(http://blog.csdn.net/warsgrobe/archive/2005/07/29/438013.aspx),里面叙述了用VB.NET模拟全局变量的方法。另外我的朋友wuzhenzhi提出了在全局模块中建立全局变量解决方案,大家可以参考他的文章:《对默认启动窗体操作的方法》(http://wuzhenzhi.blogdriver.com/wuzhenzhi/875776.html)
7、本文发布的第二天,wuzhenzhi提醒我:事实上.NET中有一个现成的方法让从窗体访问主窗体的成员,就是带有owner参数的Form.ShowDialog方法。这个owner便是对主窗体的引用。这个快捷的方法适合于对话框性质的从窗体(也就是说在操作从窗体时不允许操作主窗体),但对诸如MDI子窗体等不适用。注意owner是个IWin32Window,需要强制转化为主窗体类。
C#主 | private void ShowForm2() { Form 2 f 2 = new Form2(); f2.ShowDialog(this); }
internal string MyName = "A Form1 Object"; |
C#从 | private void ShowForm1Info() { MessageBox.Show(((Form1)(this.Owner)).MyName); } |
VB主 | Private Sub ShowForm2() Dim f2 As New Form2() f2.ShowDialog(Me) End Sub
Friend MyName As String = "A Form1 Object" |
VB从 | Private Sub ShowForm1Info() MsgBox(CType(Me.Owner, Form1).MyName) End Sub |
8、那么对于MDI子窗体有什么好方法呢?最怕的就是看了上面的几种方法,形成思维定势,直接就套用了。须知矛盾有普遍性,也有特殊性,对于MDI子窗体照样有现成的方法,就是调用子窗体的MdiParent属性访问主窗体。这个想必大家都知道,放在这里,保持文章的完整性。
C#主 | private void ShowForm2() { Form 2 f 2 = new Form2(); f2.MdiParent = this; f2.Show(); }
internal string MyName = "A Form1 Object"; |
C#从 | private void ShowForm1Info() { MessageBox.Show(((Form1)(this.ParentForm)).MyName); } |
VB主 | Private Sub ShowForm2() Dim f2 As New Form2 f2.MdiParent = Me f2.Show() End Sub
Friend MyName As String = "A Form1 Object" |
VB从 | Private Sub ShowForm1Info() MsgBox(CType(Me.ParentForm, Form1).MyName) End Sub |
OK,就说到这里。.NET是一个强大而灵活的体系,只要肯动脑筋,一定能发现不少这样的技巧。