窗体只是Windows窗体中的一个类。窗体类是通过继承实现的。窗体必须继承System.Windows.Forms类,以获得必须的行为和对象接口。
1、通过Sub Main显示窗体
如果使用上面的代码,Form1会在显示之后立即消失,然后应用程序就会退出。这是因为对象变量f超出了作用域,而它是指向当前显示窗体的唯一引用。因此窗体被销毁。
为了解决这个问题,可以把默认的实例作为启动窗体。但是我们有更好的解决方案,该方案遵循了面向对象的设计规划。使用下面突出显示的代码替换上面的窗体显示行:
现在Sub Main会把控制权交给窗体,即使Sub Main结束,窗体也不会消失。
2、设置启动窗体
3、启动位置
下表列出了StartPosition属性的所有值及其含义
StartPosition属性值 | 效果 |
Manual | 根据窗体的Location属性值来显示窗体 |
CenterScreen | 在屏幕中间显示窗体 |
WindowsDefaultLocation | 在窗口的默认位置显示窗体 |
WindowsDefaultBounds | 按照窗口的默认大小,在窗口的默认位置显示窗体 |
CenterParent | 在父窗体的中心显示窗体 |
4、窗体边框
边框选项可以使用FormBorderStyle属性来设置。这些选项能够影响用户操作窗体的方式。FormBorderStyle可用的选项如下:
- None——无边框,用户不能调整窗体的大小。
- FixedSingle——单线3D边框,用户不能调整窗体的大小。
- Fixed3D——3D边框,用户不能调整窗体的大小。
- FixedDialog——对话框样式边框,用户不能调整窗体的大小。
- Sizable——与FixedSingle相同,用户能够调整窗体的大小。
- FixedToolWindow——单线边框,用户不能调整窗体的大小。
- SizableToolWindow——单线边框,用户能够调整窗体的大小。
5、始终置顶——TopMost属性
TopMost设为True的窗体将位于所有应用程序的上面,而不仅仅位于其所在的应用程序上面。如果窗体仅需要位于应用程序中其他窗体的上面,就应使用附属窗体的TopMost属性。
6、附属窗体
与TopMost属性一样,附属窗体在应用程序的上面浮动,但不会影响应用程序的使用。例如“查找和替换”窗口。但是,附属窗体并不位于所有窗体的上面,只是位于拥有它的窗体的顶部。
当一个窗体属于另一个窗体时,所有者窗体和附属窗体会同时最小化或者关闭。附属窗体永远显示在其所有者窗体的前面,但不会阻止所有者窗体获得焦点和使用。尽管如此,如果要单击附属窗体所覆盖的区域,就必须先移动附属窗体。
窗体在某一时刻只能有一个所有者窗体。如果窗体已经被Form1所拥有,当把它添加到Form2的附属窗体集合中时,该窗体就不再被Form1所拥有。
使窗体成为另一个窗体的附属窗体有两种方式:在所有者窗体中或者在附属窗体中进行设置。
(1)AddOwnedForm方法
在所有者窗体中,使用AddOwnedForm方法可以使另一个窗体成为该窗体的附属窗体。下面的代码使Form2实例被Form1所拥有。这些代码可以位于Form1的某个位置,但一般位于把Form2实例显示到屏幕上的代码前面:
(2)Owner属性
这个关系也可以在附属窗体中建立。为此,需要使用窗体的Owner属性。下面的方法在Form2中工作,把一个窗体作为参数传递给该方法,就可以把Form2设置为传入窗体的附属窗体:
这个技术需要在附属窗体中引用所有者窗体,所以不如前一种方法那么常用。
(3)OwnedForms集合
所有者窗体可以用OwnedForms属性访问它的附属窗体集合。下面的代码遍历了该窗体拥有的全部附属窗体:
所有者窗体使用RemoveOwnedForm方法来删除它的附属窗体。可以使用与前面类似的循环,代码如下:
这个循环会使所有者窗体放弃它原来拥有的全部附属窗体。注意,这些不再被其他窗体拥有的窗体不会卸载,它们只是不再是其他窗体的附属窗体。
7、改变窗体的透明度
(1)Opacity属性
注意,取值为0%时将不再能点击窗体。
如果Opacity属性值非常低,如1%或2%,会使窗体基本不可见,但仍允许点击窗体。这说明,Opacity属性可以创建位于其他应用程序前面的应用程序,“抱走”它们的鼠标单击事件和其他事件。
百分数用于在Properties窗口中设置Opacity属性。但是,如果要在代码中设置Opacity属性,必须使用0-1的值。
下面的代码块展示了如何在用户单击Button1按钮时使窗体淡出和淡入。可以按照自己计算机的性能调整数组中step值。
(2)TransparencyKey属性
除了使整个窗体透明之外,TransparencyKey属性还允许指定某种颜色在窗体上是透明的。这样,就可以使窗体的一部分透明,而其他部分不发生变化。
如果TransparencyKey属性设置为红色,则窗体上显示为红色的区域就是透明的。窗体后面的内容就会透过这个区域显示出来。如果在这个区域傻瓜单击,实际上单击的是窗体后面的对象。
TransparencyKey属性可以用于创建形状不规则的“外壳”窗体。窗体可以把它的BackgroundImage属性设置为一个图像,然后把图像的一部分用TransparencyKey颜色绘制,对应的窗体部分就会消失。
(3)Region属性
获得“外壳”的另一种方式是使用窗体的Region属性。Region属性允许把窗体的形状编码为“图形路径”,从而把窗体形状从默认的矩形改为另一种形状。路径可以包含以任何方式组合在一起的直线、曲线、圆弧、字母的轮廓等。
下面的例子将窗体的形状改为一个箭头。创建一个新的Windows应用程序。把Form1的FormBorderStyle属性设为None,然后在Form1的Load事件中添加如下代码:
8、可视化继承
通过从System.Windows.Forms.Form中继承,任何一个类都会自动得到Windows窗体所拥有的全部属性、方法和事件。然而,类要 想成为Windows窗体,除了直接从System.Windows.Forms.Form类中继承外,也可以继承从 System.Windows.Forms.Form派生出的窗体。这样,位于一个窗体内的初始控件就能够被另一个窗体直接继承。这种方法不仅继承了初始 窗体的设计,还继承了与这些控件相关联的所有代码。这意味着可以先创建一个包含许多窗体都需要的处理逻辑的基本窗体,然后创建继承基本控件和功能的其他窗 体。
9、滚动窗体
一些应用程序要求把大量的字段放在一个窗体中。可以把数据分解到多个屏幕上,也可以使用滚动窗体。
当窗体比窗体中的子控件的尺寸小时,可以把这个窗体设置为自动显示滚动条。为此,可将窗体的AutoScroll属性设置为True。运行程序时,调整窗体的尺寸,使之比控件需要的尺寸小,就可以滚动窗体了。
不能将AutoScroll和IsMdiContainer属性同时设置为True。MDI容器有自己的滚动功能。如果将MDI容器的AutoScroll属性设置为True,IsMdiContainer属性设置为false,窗体就不再是MDI容器了。
10、MDI窗体
多文档界面(MDI)窗体能够包含其他窗体。MDI窗体常常称为父窗体,显示在MDI父窗体中的窗体常常称为子窗体。
(1)创建MDI父窗体
在Windows窗体中,把普通窗体的IsMDIContainer属性值设置为True,就会使它成为一个MDI父窗体。
(2)VB6和VB 2008中MDI父窗体的不同
在VB6中,MDI父窗体只能包含有Align属性的控件。这个属性类似于Windows窗体中的Dock属性。
在Windows窗体中,MDI父窗体可以包含常规窗体包含的任何控件。按钮、标签等都可以直接放在MDI窗体上。这些控件显示在MDI子窗体的前面,而MDI子窗体显示在MDI客户区域中。
在Windows窗体的MDI父窗体上,仍可以使用PictureBox等包含其他控件的控件,这些控件可以使用Dock属性停靠在MDI窗体的边上。
(3)MDI子窗体
在Windows窗体中,在运行时把窗体的MDIParent属性设置为指向某个MDI父窗体,就可以把它变成MDI子窗体。实际上,MDIParent属性不能在设计时设置,必须在运行时设置,才能使一般窗体变成MDI子窗体。
在MDI父窗体的客户区域中,可以显示任意多个MDI子窗体。当前激活的子窗体可以使用MDI父窗体的ActiveForm属性来确定。
11、VB2008中的MDI样例
(1)创建一个新的Windows应用程序,它有一个空白的窗体Form1。把该窗体的名称和Text属性都改为MDIParentForm。
(2)在属性窗口中,把MDIParentForm的IsMSIContainer属性设置为True。这就把该窗体指定为子窗口的MDI容器(设置这个属性,还会使窗体使用另一种默认的背景色)。
(3)在工具箱中,把MenuStrip控件拖放到窗体上。创建一个顶级菜单项File,再创建它的子菜单项New MDI Child和Quit。然后,创建另一个顶级菜单项Window。选择File|New MDI Child菜单项会在运行时创建并显示新的MDI子窗体,Window菜单会跟踪打开的MDI子窗体。
(4)在窗体底部的组件面板中,单击MenuStrip项,选择Properties。在属性窗口中,把MDIWindowListItem属性设置为 WindowToolStripMenuItem。这会使Window菜单包含一个已打开的MDI子窗口列表,其中活动的子窗口会带有一个复选标记。
(5)创建一个MDI子窗体,用作多个子窗口实例的模板。为此,选择Project|Add Windows Form菜单项,再在Add New Item对话框中单击Add按钮,得到一个新的空白窗体Form2.在该窗体上放置一些控件。
(6)下面返回MDIParentForm。在菜单编辑栏中,双击File下面的New MDI Child菜单项,打开代码编辑器,光标停在该菜单项的事件例程中。在事件中添加如下代码:
(7)在MDIParentForm的菜单编辑栏中,双击File下面的Quit菜单项,打开代码编辑器,光标停留在该菜单项的事件例程中,在事件中添加如下代码:
(8)现在运行并测试程序。使用File|New MDI Child菜单项,创建几个子窗体。注意Window菜单自动列出这些窗体,其中当前激活的窗体带有标记,并允许激活另一窗体。
排列子窗体
MDI父窗体有一个LayoutMDI方法,能够按照常见的层叠或平铺方式自动排列子窗体。对于上面的例子,在Windows菜单中添加一个Tile Vertical菜单项,并在该菜单项的Click事件中插入下面的代码:
12、对话框窗体
在Windows窗体中,使用ShowDialog方法来显示模态窗体。
DialogResult属性
在使用ShowDialog方法显示窗体时,该窗体有一个属性DialogResult指示其状态。
DialogResult属性可以使用下面的枚举值:
- DialogResult.Abort
- DialogResult.Cancel
- DialogResult.Ignore
- DialogResult.No
- DialogResult.None
- DialogResult.OK
- DialogResult.Retry
- DialogResult.Yes
设置了DialogResult属性后,对话框就会隐藏起来。也就是说,设置DialogResult属性会隐式调用对话框窗体的Hide方法,将控制权返回给调用对话框的窗体。
对话框的DialogResult属性可以用两种方式设置,最常用的方式是给按钮附带一个DialogResult值。接着,在按下该按钮时,相关的值就会自动放在窗体的DialogResult属性中。
要设置与按钮关联的DialogResult值,应使用按钮的DialogResult属性。如果设置了按钮的这个属性,在按下该按钮时,就不需要在代码中设置DialogResult了。
设置窗体的DialogResult属性的第二种方式是在代码中设置。在Button_Click事件中或DialogForm的任何地方,都可以使用下面的代码设置窗体的DialogResult属性,同时隐藏DialogForm,把控制权返回给调用窗体。
13、运行时的窗体
窗体的生存周期和所有的对象一样,先创建,然后销毁。既然窗体有可视化组件,就会使用像句柄这样的系统资源。窗体的创建和销毁是在窗体生命周期的开始和结束时进行的。窗体创建后,就保持着类的状态,不过在激活之前它是不会出现的。同样,关闭窗体也不会销毁它的状态。
下表总结了窗体存在的各种形态,如何使窗体进入这些状态,窗体进入一个状态时发生的事件,以及每个状态的简要说明:
代码 | 触发的事件 | 说明 |
MyForm=New Form1 | Load | 调用窗体的New方法(同时会调用InitializeComponent) |
MyForm.Show或 MyForm.ShowDialog | HandleCreated Load VisibleChanged Activated | 使用Show进行无模态显示 使用ShowDialog进行模态显示 HandleCreated事件仅在第一次显示窗体时或在窗体关闭后触发 |
MyForm.Activate | Activated | 当一个窗体可见但没有焦点时,激活该窗体 |
MyForm.Hide | Deactivate VisibleChanged | 隐藏窗体(将Visible属性的值设置为False) |
MyForm.Close | Deactivate Closing Closed VisibleChanged HandleDestroyed Disposed | 关闭窗体并调用Dispose,来释放窗口资源在Closing事件中,用户能够把CancelEventArgs.Cancel属性设置为True来中 止关闭过程。当用户使用控件框或X按钮关闭窗体时调用仅在窗体处于当前激活状态时,触发Deactive事件注意:不再有Unload事件,使用 Closing或Closed事件来代替它 |
MyForm.Dispose | 无 | 使用Close方法结束使用窗体 |
MyForm=Nothing | 无 | 释放对窗体的引用,为垃圾回收器标记它。垃圾回收器将调用窗体的Finalize方法 |