窗体是程序设计最常见,最普通,也是最容易受到程序员忽视的编程对象。一般来说,在Visual Basic .Net针对窗体的编程是不需要人为介入的,因为可视化编程工具Visual Studio .Net已经按照窗体的缺省状态实现了。但是在最近遇到的应用程序中。 我们发现越来越多的应用程序中使用到各种不同规则的窗体,这些不同规则的窗体给应用程序带来异常的情趣和不同平常的效果的同时,也促使使用者思考这样一个问题,如何创建这些窗体?在Visual Basic .Net中提供了一种简单、直接创建不规则窗体的方法,本文的主要内容就是探讨一下这些方法具体的实现过程。下面就来详细介绍在Visual Basic .Net中实现五种不同规格窗体的方法,即:椭圆形、扇形、圆形、环形和三角形。掌握了这五种不同形状窗体的实现方法后,我想对于其他规则的窗体就不应该有什么问题了。
Visual Basic .Net中创建、使用MDI窗体也是本文的重要内容之一,MDI是Multi Document Interface的简称,即:多文档界面,MDI最早出现于Windows 2.0中,最先使用到MDI的应用程序是Excel电子表格。为了方便使用者同时能够操作多份电子表格,Excel就采用了MDI来解决这个问题。到了Windows3.1中,MDI在应用程序中得到了更大范围的应用。其中Windows 3.1中的程序管理器和文件管理器都采用了MDI。目前MDI在应用程序中依然被广泛采用。下面就首先来介绍一下Visual Basic .Net创建不规则窗体的相关知识和具体实现方法。
一.简介Visual Basic .Net中定制不规则窗体时使用的类库: Visual Basic .Net中定制不规则窗体主要使用到Region类和GraphicsPath类,其中以GraphicsPath类最为重要。GraphicsPath类和Region类分别位于.Net FrameWork SDK中的"System.Drawing.Drawing2D"命名空间和"System.Drawing"命名空间。在Visual Basic .Net中具体实现各种不规则窗体的过程中,GraphicsPath主要作用是根据程序员的要求,通过GraphicsPath中提供的方法绘制不同规则的形状。Region的主要作用是依靠GraphicsPath实例来初始化Region对象,从而形成不规则窗体。
其具体的实现过程是,首先通过GraphicsPath类构造函数创建GraphicsPath实例,然后通过GraphicsPath中提供的方法按照程序员的要求绘制形状,最后以GraphicsPath实例来初始化Region对象。完成不规则窗体。可见了解、掌握GraphicsPath类对于理解本文介绍的实现方法是非常必要的。表01和表02分别是GraphicsPath类的常用方法和常用属性及其说明:
| 方法 |
说明 |
| AddArc |
GraphicsPath向当前图形追加一段椭圆弧。 |
| AddBezier |
GraphicsPath向当前图形添加一段立方贝塞尔曲线。 |
| AddBeziers |
GraphicsPath向当前图形添加一系列相互连接的立方贝塞尔曲线。 |
| AddClosedCurve |
GraphicsPath向此路径添加一个闭合曲线。由于曲线经过数组中的每个点,因此使用基数样条曲线。 |
| AddCurve |
GraphicsPath向当前图形添加一段样条曲线。由于曲线经过数组中的每个点,因此使用基数样条曲线。 |
| AddEllipse |
GraphicsPath向当前路径添加一个椭圆。 |
| AddLine |
GraphicsPath向此GraphicsPath实例追加一条线段。 |
| AddLines |
GraphicsPath向此GraphicsPath实例末尾追加一系列相互连接的线段。 |
| AddPath |
将指定的GraphicsPath实例追加到该路径。 |
| AddPie |
GraphicsPath向此路径添加一个扇形轮廓。 |
| AddPolygon |
GraphicsPath向此路径添加多边形。 |
| AddRectangle |
GraphicsPath向此路径添加一个矩形。 |
| AddRectangles |
GraphicsPath向此路径添加一系列矩形。 |
| AddString |
GraphicsPath向此路径添加文本字符串。 |
| ClearMarkers |
清除此路径的所有标记。 |
| Clone |
创建此路径的一个精确副本。 |
| CloseAllFigures |
闭合此路径中所有开放的图形并开始一个新图形。它通过连接一条从图形的终结点到起始点的直线,闭合每一开放的图形。 |
| CloseFigure |
闭合当前图形并开始新的图形。如果当前图形包含一系列相互连接的直线和曲线,该方法通过连接一条从终结点到起始点的直线,闭合该环回。 |
| Flatten |
GraphicsPath将此路径中的各段曲线转换成相连的线段序列。 |
| GetBounds |
GraphicsPath返回限定此GraphicsPath实例的矩形。 |
| GetLastPoint |
获取此GraphicsPath实例PathPoints数组中的最后的点。 |
| IsOutlineVisible |
GraphicsPath指示当使用指定的Pen对象绘制此GraphicsPath实例时,指定点是否包含在后者的轮廓内。 |
| IsVisible |
GraphicsPath指示指定点是否包含在此GraphicsPath实例内。 |
| Reset |
清空PathPoints和PathTypes数组并将FillMode设置为Alternate。 |
| Reverse |
反转此GraphicsPath实例的PathPoints数组中各点的顺序。 |
| SetMarkers |
在此GraphicsPath实例上设置标记。 |
| StartFigure |
不闭合当前图形即开始一个新图形。后面添加到该路径的所有点都被添加到此新图形中。 |
| Transform |
将变形矩阵应用到此GraphicsPath实例。 |
| Warp |
GraphicsPath对此GraphicsPath实例应用由一个矩形和一个平行四边形定义的扭曲变形。 |
| Widen |
GraphicsPath在用指定的画笔绘制此路径时,用包含所填充区域的曲线代替此路径。 |
| AddArc |
GraphicsPath向当前图形追加一段椭圆弧。 |
| AddBezier |
GraphicsPath向当前图形添加一段立方贝塞尔曲线。 |
表01:GraphicsPath类的常用方法及其说明
在本文用Visual Basic .Net实现的椭圆形、扇形、圆形、环形和三角形等形状窗体中就使用到GraphicsPath中的AddEllipse方法(绘制椭圆形)、AddPie方法(绘制扇形)、AddLine方法(绘制直线)等方法。GraphicsPath中提供了丰富的方法,这对实现更复杂的窗体形状是非常有用的,但完全掌握这些方法的使用方法也需要花费大量的时间和精力。
| 属性 |
说明 |
| FillMode |
获取或设置一个FillMode枚举,它确定此 |
| PathData |
获取一个PathData对象,它封装此GraphicsPath实例的点(points)和类型(types)的数组。 |
| PathPoints |
获取路径中的点。 |
| PathTypes |
获取PathPoints数组中相应点的类型。 |
| PointCount |
获取PathPoints或PathTypes数组中的元素数。 |
表02:GraphicsPath类中的常用属性及其说明
二.Visual Basic .Net中定制不规则窗体的具体步骤: 正如前文所说在Visual Basic .Net定制不同规则的窗体是比较容易的,再了解了上面内容后,下面就是在Visual Basic .Net实现椭圆形、扇形、圆形、环形和三角形等五种形状窗体的具体实现步骤:
1. 启动Visual Studio .Net。
2. 选择菜单【文件】|【新建】|【项目】后,弹出【新建项目】对话框。
3. 将【项目类型】设置为【Visual Basic项目】。
4. 将【模板】设置为【Windows应用程序】。
5. 在【名称】文本框中输入【VB.NET中定制不规则窗体】。
6. 在【位置】的文本框中输入【E:\VS.NET项目】,然后单击【确定】按钮,这样在"E:\VS.NET项目"目录中就产生了名称为"VB.NET中定制不规则窗体"的文件夹,并在里面创建了名称为"VB.NET中定制不规则窗体"的项目文件。具体如图01所示:

图01:【VB.NET中定制不规则窗体】新建项目对话框
7. 把Visual Studio .Net的当前窗口切换到【Form1.vb(设计)】窗口,并从【工具箱】中的【Windows窗体组件】选项卡中往Form1窗体中拖入下列组件,并执行相应的操作:
五个Button组件,用以定制五种不同形状窗体,并在这个Button组件拖入Form1的设计窗体后,双击它们,则系统会在Form1.vb文件分别产生这五个组件的Click事件对应的处理代码。
4. 在上述组件拖入窗体后,分别按照图02所示排列并设置窗体中的各组件:
 图02:【VB.NET中定制不规则窗体】项目的设计界面 |
8. 把Visual Basic .Net当前窗口切换到Form1.vb的代码编辑窗口,并用下列代码替换Form1.vb中的Button1的Click事件对应的处理代码,下列代码功能是改变当前窗体形状为椭圆形:
Private Sub Button1_Click ( ByVal sender As System.Object , ByVal e As System.EventArgs ) Handles Button1.Click Dim p As System.Drawing.Drawing2D.GraphicsPath = New System.Drawing.Drawing2D.GraphicsPath ( ) Dim Width As Integer = Me.ClientSize.Width Dim Height As Integer = Me.ClientSize.Height p.AddEllipse ( 0 , 20 , Width - 50 , Height - 100 ) '根据要绘制椭圆的形状来填写AddEllipse方法中椭圆对应的相应参数 Region = New Region ( p ) End Sub |
9. 用下列代码替换Form1.vb中的Button2的Click事件对应的处理代码,下列代码功能是改变当前窗体形状为扇形:
Private Sub Button2_Click ( ByVal sender As System.Object , ByVal e As System.EventArgs ) Handles Button2.Click Dim p As System.Drawing.Drawing2D.GraphicsPath = New System.Drawing.Drawing2D.GraphicsPath ( ) p.AddPie ( 10 , 10 , 250 , 250 , 5 , 150 ) '根据要实现的扇形形状来填写AddPie方法中的相应参数 Me.Region = New Region ( p ) End Sub |
10. 用下列代码替换Form1.vb中的Button3的Click事件对应的处理代码,下列代码功能是改变当前窗体形状为圆形:
Private Sub Button3_Click ( ByVal sender As System.Object , ByVal e As System.EventArgs ) Handles Button3.Click Dim p As System.Drawing.Drawing2D.GraphicsPath = New System.Drawing.Drawing2D.GraphicsPath ( ) Dim Width As Integer = Me.ClientSize.Width Dim Height As Integer = Me.ClientSize.Height p.AddEllipse ( 0 , 0 , Height , Height ) Me.Region = New Region ( p ) '圆形即是椭圆的一种特例, End Sub |
11. 用下列代码替换Form1.vb中的Button4的Click事件对应的处理代码,下列代码功能是改变当前窗体形状为环形:
Private Sub Button4_Click ( ByVal sender As System.Object , ByVal e As System.EventArgs ) Handles Button4.Click Dim p As System.Drawing.Drawing2D.GraphicsPath = New System.Drawing.Drawing2D.GraphicsPath ( ) Dim Height As Integer = Me.ClientSize.Height Dim width As Integer = 100 p.AddEllipse ( 0 , 0 , Height , Height ) p.AddEllipse ( width , width , Height - ( width * 2 ) , Height - ( width * 2 ) ) '根据环形的形状来分别填写AddEllipse方法中相应的参数 Me.Region = New Region ( p ) End Sub |
12. 用下列代码替换Form1.vb中的Button5的Click事件对应的处理代码,下列代码功能是改变当前窗体形状为三角形:
Private Sub Button5_Click ( ByVal sender As System.Object , ByVal e As System.EventArgs ) Handles Button5.Click Dim p As System.Drawing.Drawing2D.GraphicsPath = New System.Drawing.Drawing2D.GraphicsPath ( ) p.AddLine ( 0 , 0 , 250 , 150 ) p.AddLine ( 250 , 150 , 0 , 300 ) p.AddLine ( 0 , 0 , 0 , 300 ) '根据三角形的形状特征来分别填写AddLine方法中相应的参数 Me.Region = New Region ( p ) End Sub |
13. 至此在上述步骤都正确完成后,【VB.NET中定制不规则窗体】项目的全部工作就基本完成了。此时单击快捷键F5运行程序,单击【VB.NET中定制不规则窗体】中的【椭圆形】按钮,则会得到图03所示的运行界面:
 图03:【VB.NET中定制不规则窗体】项目运行界面之一 |
单击【VB.NET中定制不规则窗体】中的【扇形】按钮,则会得到图04所示的运行界面:
 图04:【VB.NET中定制不规则窗体】项目运行界面之二 |
单击【VB.NET中定制不规则窗体】中的【圆形】按钮,则会得到图05所示的运行界面:
 图05:【VB.NET中定制不规则窗体】项目运行界面之三 |
单击【VB.NET中定制不规则窗体】中的【环形】按钮,则会得到图06所示的运行界面:
 图06:【VB.NET中定制不规则窗体】项目运行界面之四 |
单击【VB.NET中定制不规则窗体】中的【三角形】按钮,则会得到图07所示的运行界面:
 图07:【VB.NET中定制不规则窗体】项目运行界面之五 |
三.Form类和MenuItem类中和MDI相关的属性和方法: 接下来就来介绍了Visual Basic .Net中创建、使用MDI窗体的相关知识和具体的实现方法。Visual Basic .Net为编写MDI程序提供了强大支持,使得程序员编写一个MDI程序是那么的方便,最能表现Visual Basic .Net对MDI支持的地方是窗体(Form组件)和菜单(MenuItem组件),只需对这二个组件的某些属性进行设置,就能够完成其他语言在编写MDI程序时要花费很多精力才能够实现的工作。首先简要介绍一下这二个组件对应.Net FrameWork SDK中的System.Windows.Forms命名空间中的Form类和MenuItem类中提供了的实现MDI的方法和属性。也正是由于在Form类和MenuItem类中提供了很多和MDI相关的属性和方法,才使得在Visual Basic .Net编写MDI应用程序那么方便,下面是这二个类中和MDI相关的属性、方法及其说明。
| 属性 |
说明 |
| IsMdiChild |
获取一个值,该值指示该窗体是否为MDI子窗体。 |
| IsMdiContainer |
获取或设置一个值,该值指示窗体是否为MDI子窗体的容器。 |
| MdiChildren |
获取窗体的数组,这些窗体表示以此窗体作为父级的MDI子窗体。 |
| MdiParent |
获取或设置此窗体的当前MDI父窗体。 |
表03:Form类中和MDI相关的属性及其说明
其中和MDI编程相关最为重要的属性是"IsMdiChild"和"MdiParent"。
Form类中虽然只提供了一个和MDI编程相关的方法,但此方法功能比较强大,它能够方便的实现对MDI父窗体中的所有MDI子窗体进行四种不同的排列:"层叠"、"水平平铺"、"垂直平铺"、"排列图标",这个方法就是"LayoutMdi"。
"LayoutMdi"方法只有一个参数,此参数类型是MdiLayout枚举,MdiLayout枚举有四个值分别对应MDI父窗体中所有MDI子窗体的四种排列方式,MdiLayout枚举成员、其对应数值和说明如表04所示:
| MdiLayout枚举成员 |
数值 |
说明 |
| Cascade |
0 |
层叠排列 |
| TileHorizontal |
1 |
横行平铺 |
| TileVertical |
2 |
纵向平铺 |
| ArrangeIcons |
3 |
排列图标 |
表04:MdiLayout枚举成员及其说明
MenuItem类中可用以实现MDI程序的组成成员虽然非常少,但所起的作用却是非常强大,表05所示是MenuItem类中用以MDI编程的属性及其说明:
| 属性 |
说明 |
| MdiList |
获取或设置一个值,通过该值指示是否使用在相关联窗体内显示的MDI子窗口列表来填充菜单项。 |
| MdiListItem |
获取一个值,通过该值指示用于显示MDI子窗体列表的MenuItem。 |
表05:MenuItem类中用以MDI编程的属性及其说明
其中"MdiList"属性在MDI编程中非常重要,这一点将在后面介绍的示例中得以体现。
四.Visual Basic .Net编写MDI应用程序的基本步骤: 在下面介绍的示例基本囊括了MDI应用程序的主要特征,其实现步骤也可以说是Visual Basic .Net编写MDI程序基本步骤。在下面的实现步骤中,没有烦杂的代码,更多的是对组件属性值的设置,提供设置组件的属性,有时能够实现看似需要很多代码才能够实现的工作。当然设置组件的属性对编写MDI应用程序也是非常重要的,缺一步都可能会影响后面程序的正确运行。
下面是Visual Basic .Net编写MDI应用程序的基本步骤:
5. 启动Visual Studio .Net。
6. 选择菜单【文件】|【新建】|【项目】后,弹出【新建项目】对话框。
7. 将【项目类型】设置为【Visual Basic项目】。
8. 将【模板】设置为【Windows应用程序】。
9. 在【名称】文本框中输入【MDI编程】。
10. 在【位置】的文本框中输入【E:\VS.NET项目】,然后单击【确定】按钮,这样在"E:\VS.NET项目"目录中就产生了名称为"MDI编程"的文件夹,并在里面创建了名称为"MDI编程"的项目文件。
11. 把Visual Studio .Net的当前窗口切换到【Form1.vb(设计)】窗口,并从【工具箱】中的【Windows窗体组件】选项卡中往Form1窗体中拖入下列组件:
一个MainMenu组件。
12. 结合上一节介绍的方法,通过Visual Basic .Net的菜单设计器,按照图08和图09所示的菜单结构设计菜单。
 图08:【MDI编程】项目的菜单结构之一 |
 图09:【MDI编程】项目的菜单结构之二 |
13. 在利用菜单设计器设计完菜单后,分别在窗体中双击"新建"、"退出"、"层叠显示"、"横行平铺"、"纵向平铺"和"排列图标"菜单项,这样Visual Basic .Net会在Form1.vb文件中分别产生这些菜单项的Click事件对应的处理代码。
14. 设定Form1的"IsMdiContainer"属性值为"True",这把Form1窗体定义为MDI窗体容器,这一步设置是后面编程的基础,当设置此属性值后,Form1的窗体也发生了改变,图10是设置此属性后的窗体模样:
 图10:把Form1窗体设置为MDI窗体容器后的模样 |
15. 选中图03中的"窗口"菜单项,并设置此菜单项的"MdiList"属性值为"True"。其作用是当程序运行后,如果新建了一个MDI窗体,就会在"窗口"菜单项后,自动加入一个以新建MDI窗体名称命名的菜单项,如果新建了多个MDI窗体,则会在"窗口"菜单项后,会加入以这些MDI窗体名称命名的菜单项。当MDI窗体被关闭,那些以此MDI窗体命名的菜单项也会自动删除。具体可图06所示。
16. 把Visua Basic .Net的当前窗口切换到Form1.vb的代码编辑窗口,并在定义Form1中组件的代码区加入下列代码,下列代码是定义一个变量,此变量用以统计程序中新建MDI窗体的数量:
| Private iFormCount As Integer = 1 |
17. 用下列代码替换Form1.vb中的MenuItem2的Click事件对应的处理代码,MenuItem2即为图01中的"新建"菜单项,下列代码的作用是在新建一个MDI窗体:
Private Sub MenuItem2_Click ( ByVal sender As System.Object , ByVal e As System.EventArgs ) Handles MenuItem2.Click Dim frmTemp As Form = New Form ( ) '新建一个窗体 frmTemp.MdiParent = Me '为当前创建的窗体设置MDI父窗体 frmTemp.Text = "MDI窗体" + iFormCount.ToString ( ) iFormCount = iFormCount + 1 frmTemp.Show ( ) '把此MDI窗体显示出来 End Sub |
18. 用下列代码替换Form1.vb中的MenuItem3的Click事件对应的处理代码,MenuItem3即为图08中的"退出"菜单项,下列代码的作用是退出应用程序:
Private Sub MenuItem3_Click ( ByVal sender As System.Object , ByVal e As System.EventArgs ) Handles MenuItem3.Click Application.Exit ( ) '退出应用程序 End Sub |
19. 用下列代码替换Form1.vb中的MenuItem5的Click事件对应的处理代码,MenuItem5即为图09中的"层叠显示"菜单项,下列代码的作用是层叠排列窗体中的所有MDI窗体:
Private Sub MenuItem5_Click ( ByVal sender As System.Object , ByVal e As System.EventArgs ) Handles MenuItem5.Click Me.LayoutMdi ( MdiLayout.Cascade ) '层叠排列窗体中的所有MDI窗体 End Sub |
20. 用下列代码替换Form1.vb中的MenuItem6的Click事件对应的处理代码,MenuItem6即为图09中的"水平平铺"菜单项,下列代码的作用是水平平铺窗体中的所有MDI窗体:
Private Sub MenuItem6_Click ( ByVal sender As System.Object , ByVal e As System.EventArgs ) Handles MenuItem6.Click Me.LayoutMdi ( MdiLayout.TileHorizontal ) '水平平铺窗体中的所有MDI窗体 End Sub |
21. 用下列代码替换Form1.vb中的MenuItem7的Click事件对应的处理代码,MenuItem7即为图09中的"垂直平铺"菜单项,下列代码的作用是垂直平铺窗体中的所有MDI窗体:
Private Sub MenuItem7_Click ( ByVal sender As System.Object , ByVal e As System.EventArgs ) Handles MenuItem7.Click Me.LayoutMdi ( MdiLayout.TileVertical ) '垂直平铺窗体中的所有MDI窗体 End Sub |
22. 用下列代码替换Form1.vb中的MenuItem9的Click事件对应的处理代码,MenuItem9即为图09中的"排列图标"菜单项,下列代码的作用是窗体中的所有MDI窗体排列图标:
Private Sub MenuItem9_Click ( ByVal sender As System.Object , ByVal e As System.EventArgs ) Handles MenuItem9.Click Me.LayoutMdi ( MdiLayout.ArrangeIcons ) '窗体中的所有MDI窗体排列图标 End Sub |
至此【MDI编程】项目的全部工作就完成了,图11、图12和图13是程序运行后的界面:
 图11:【MDI编程】运行界面之一 |
 图12:【MDI编程】运行界面之二 |
 图13:【MDI编程】运行界面之三 |
此时我们注意到图13所示的"窗口"菜单中增加许多子菜单,而在程序设计时,并没有处理这些功能的代码。这是由于第十一步中设置"窗口"菜单项的 "MdiList"属性值为"True"造成的,可见正确的设置组件的属性值有时的确能够代替烦杂的代码编写工作。
五:总结: 本文主要内容有二个,其一是介绍的在Visual Basic .Net定制不规则窗体的实现方法。在Visual Basic .Net中创建不规则窗体主要使用的是Region类和GraphicsPath类。GraphicsPath主要是绘制窗体的形状,Region主要是以GraphicsPath来构建窗体。结合本文的实现思路和具体方法,我想对于其他形状的窗体,在掌握GraphicsPath类提供的使用方法的前提下,加上对所要创建不规则窗体的规划设计,用Visual Basic .Net实现起来应该不算很困难。
本文的另外一个主要内容就是介绍在Visual Basic .Net编写MDI应用程序的基本过程、技巧和注意事项。通过本文的介绍可见Visual Basic .Net的MDI编程并没有什么复杂的地方,只需了解一些对MDI窗体处理的方法及其调用方式,当然更重要的是要了解并掌握某些组件的针对MDI的属性及其设置方式。