设为首页收藏本站language 语言切换
查看: 1626|回复: 0
收起左侧

用VS.NET构造异形窗体-从入门到精通

[复制链接]
发表于 2010-2-25 10:29:28 | 显示全部楼层 |阅读模式
<p >  从Windows操作系统诞生的第一天开始,所有的窗口都是矩形的。但是,打破这一戒规标新出异的软件正越来越多,即使是Microsoft这个矩形窗体的鼻祖,也开始使用不规则的异形窗体,Windows Media Player就是一个例子。可惜的是,构造异形窗体向来不是一件轻松的事情,不过现在不同了!有了.NET框架特别是Windows Forms包,即使构造复杂的窗体形状也变得轻而易举。 <p >  既然已经提到Media Player,那就继续用它作为例子说明吧。Media Player运用了许多特殊的技术,足以作为典型的范例。想必大家的Windows系统上都安装了Media Player,你可以按照本文的说明马上打开试试。 <p >  本文将示范异形窗体的构造过程,这个异形窗体拥有与Meida Player相似的外形。但在说明这一复杂异形窗体的构造过程之前,首先我们要了解一些基础知识。 <p >  <b>一、异形窗体基础</b><p >  构造异形窗体的基本思路很简单,只需定义向量形式的窗体轮廓,然后把这个窗体轮廓指定给窗体。 <p >  窗体的外形由.NET框架类Region定义。每一个Windows的Form有一个成员对象Region,但在默认情况下,Form不会带有用户自定义的Region,其对象引用是null(C#)或Nothing(VB.NET),窗体显示为矩形(Windows XP的“主题”功能会修改窗体的外观,不过本文将忽略这一细节)。 <p >  创建一个Region类的实例,填充异形窗体的形状信息,就可以修改窗体的外形。要做到这一点,最简单的办法是使用GraphicsPath对象。GraphicsPath是一个GDI+类,属于System.Drawing.Drawing2D名称空间。GraphicsPath类能够以向量的形式描述形状,用法很简单,只需给出窗体的轮廓定义即可。定义好的向量路径提交给Region对象的构造函数,Region对象自动把路径信息转换成形状定义数据。窗体获得形状数据之后,它的形状就随之改变。 <p >  因此,要简单地改动一下窗体外形简直轻而易举。.NET的GraphicsPath类功能相当强大,部分方法可以说很复杂。不过,本文只需用到直线和弧形组合成的简单路径。 <p >  <b>二、构造椭圆窗体</b><p >  下面先来看一个椭圆窗体的简单例子。首先创建一个VS.NET Windows窗体工程,VS.NET将创建一个默认的矩形窗体。椭圆窗体轮廓的图形路径很简单,只需调用一下GraphicsPath对象的AddEllipse()方法即可得到。GraphicsPath提供了许多方法来构造复杂的向量路径,部分将在本文后面的例子用到,但现在我们只需要一个椭圆。下面这段代码显示了如何创建路径并加入一个椭圆: <p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>Imports System.Drawing.Drawing2D   Dim oPath As New GraphicsPath()   oPath.AddEllipse(0, 0, 200, 100)</ccid_code></pre></td></tr></table></ccid_nobr><p >  这段代码定义的椭圆开始位置是(0,0),也就是窗体(即绘图平面)的左上角,椭圆的大小是200 X 100。注意,为了让这段代码顺利通过编译,必须导入System.Drawing.Drawing2D名称空间。所有的Windows窗体应用自动引用该名称空间,所以不必为它添加工程引用。 <p >  AddEllipse()方法有几种重载的形态,其中一种允许传入一个Rectangle对象替代矩形的坐标。如果椭圆的矩形大小和窗体大小一样,用这个方法就很方便,因为每一个Windows的窗体都有一个ClientRectangle成员: <p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>Dim oPath As New GraphicsPath()   oPath.AddEllipse(Me.ClientRectangle)</ccid_code></pre></td></tr></table></ccid_nobr><p >  利用这个路径创建Region对象,然后再传递给窗体。下面是窗体的Load事件句柄: <p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>rivate Sub Form1_Load( ByVal sender As System.Object, _                         ByVal e As System.EventArgs)  _                          Handles MyBase.Load   Dim oPath As New GraphicsPath()   oPath.AddEllipse(Me.ClientRectangle)   Me.Region = New Region(oPath)   End Sub</ccid_code></pre></td></tr></table></ccid_nobr><p >  对于C#,处理方法也和VB.NET相似。下面是生成椭圆窗体的C#代码: <p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>private void Form1_Load(object sender, System.EventArgs e)   {   GraphicsPath oPath = new GraphicsPath();   oPath.AddEllipse(this.ClientRectangle);   this.Region = new Region(oPath);   }</ccid_code></pre></td></tr></table></ccid_nobr><p ><img  src="http://www.hh010.com/upload_files/article/244/9_fgsnbd140581.gif"><p >  图一:椭圆窗体 <p >  鉴于C#和VB.NET在构造异形窗体方面的差别实在不大,下面的例子将只介绍VB.NET,想必C#开发者也一样能够通过VB.NET版的例子掌握异形窗体设计思路。 <p >  现在可以运行这个VB.NET应用了,图一就是运行结果(加上了一个“关闭”按钮)。大家一眼就可以看出,这个窗口看起来有点古怪,就象其他Windows窗体一样,它也有一个标题,但椭圆切割了窗体标题,看起来特别不专业。 <p ><img  src="http://www.hh010.com/upload_files/article/244/9_mozu6a140582.gif"><p >  图二:设计时异形窗体仍旧显示为矩形 <p >  另一个马上会注意到的细节是在设计模式下,窗体仍旧显示为矩形(图二)。这很正常,只是稍微增加了在异形窗体中放置控件的难度,但除此之外,异形窗体的设计和编程完全与普通窗体一样,包括改变大小、拖放控件、设计事件句柄等。 <p >  首先来解决窗口标题条的问题。一般地,大多数异形窗体不会使用操作系统默认加上的标题条,部分异形窗体会另外制作一个精致的标题条,通常由图形专家设计,以图形的形式放入窗体。有些应用能够以多种不同的模式运行,当它以异形窗体模式运行时,标题条隐藏,只有以普通窗体模式运行时,标题条才会显示出来。Windows Media Player就是一个很典型的例子,参见图三和图四。 <p ><img  src="http://www.hh010.com/upload_files/article/244/9_zjk9it140583.gif"><p >  图三:以异形窗体模式运行的Media Player <p ><img  src="http://www.hh010.com/upload_files/article/244/9_k3d2wn140584.gif"><p >  图四:以标准窗体模式运行的Media Player <p >  从图三和图四可以看出,异形窗体是从矩形窗体标题条下面“切割”出一部分来。对于前面例子中的椭圆窗体,我们也可以按照同样的方式处理。下面的代码片断针对窗体标题条和窗体边框作了调整: <p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>' 计算椭圆的大小   Dim iElTop, iElLeft, iElHeight, iElWidth As Integer   iElTop = SystemInformation.BorderSize.Height + _          SystemInformation.CaptionHeight + 2   iElLeft = SystemInformation.BorderSize.Width + 2   iElHeight = Height - iElTop - SystemInformation.BorderSize.Height - 3   iElWidth = Me.Width - iElLeft - SystemInformation.BorderSize.Width - 3   ' 创建图形路径并设置其大小   oPath = New GraphicsPath()   oPath.AddEllipse(iElLeft, iElTop, iElWidth, iElHeight)</ccid_code></pre></td></tr></table></ccid_nobr><p >  这段代码看起来要比实际情形复杂一些,大多数代码都在和SystemInformation类(及其静态方法)打交道,查询标题条高度之类的信息。 <p >  再次运行这个VB.NET工程,可以看到它仍是一个椭圆,但要比以前的小一点,看起来舒服不少——虽然还不够完美,但至少改进了不少,标题条已经消失不见了。不过现在出现了另一个问题,在Windows中移动窗口最方便的办法就是点住标题条拖动,现在没有了标题条,要移动窗口就很困难了。 <p >  <b>三、实现拖动功能</b><p >  大多数异形窗体采用同样的办法解决窗体移动问题:允许用户点击窗体背景的任意位置移动窗体。Listing 1的代码给出了具体实现。这段代码对于任何异形窗体来说都很有用,所以作为一个新的窗体类ShapedForm实现,本文其余的窗体都将从这个窗体派生。 <p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>Listing 1:ShapedForm类实现所有异形窗体必需的标准功能Imports System.Drawing.Drawing2D   Public Class ShapedForm      Inherits System.Windows.Forms.Form      ' 可以在子类窗体Load事件之前赋值      Public oFormPath As GraphicsPath      Private oOriginalRegion As Region = Nothing      ' 用于窗体移动      Private bFormDragging As Boolean = False      Private oPointClicked As Point      #Region &quot; Windows 窗体设计器生成的代码&quot;      ' 此处略...      Private Sub ShapedForm_Load( _         ByVal sender As System.Object, _               ByVal e As System.EventArgs) Handles MyBase.Load         ' 给子类提供一个设置窗体形状的机会         Me.SetInitialFormShape()         If Not Me.oFormPath Is Nothing Then            Me.AssignShapePath()         End If      End Sub      Public Sub AssignShapePath()         If Me.oOriginalRegion Is Nothing Then            Me.oOriginalRegion = Me.Region         End If         Me.Region = New Region(Me.oFormPath)         Me.Invalidate()      End Sub      Public Sub ResetShape()         Me.Region = Me.oOriginalRegion         Me.Invalidate()      End Sub      Public Overridable Sub SetInitialFormShape()          ' 这个方法用来让子类覆盖      End Sub      Private Sub ShapedForm_MouseDown( _         ByVal sender As Object, _          ByVal e As System.Windows.Forms.MouseEventArgs) _         Handles MyBase.MouseDown         Me.bFormDragging = True         Me.oPointClicked = New Point(e.X, e.Y)      End Sub      Private Sub ShapedForm_MouseUp( _         ByVal sender As Object, _               ByVal e As System.Windows.Forms.MouseEventArgs) _         Handles MyBase.MouseUp         Me.bFormDragging = False      End Sub      Private Sub ShapedForm_MouseMove(ByVal sender As Object, _         ByVal e As System.Windows.Forms.MouseEventArgs) _         Handles MyBase.MouseMove         If Me.bFormDragging Then            Dim oMoveToPoint As Point            ' 以当前鼠标位置为基础,找出目标位置            oMoveToPoint = Me.PointToScreen(New Point(e.X, e.Y))            ' 根据开始位置作出调整            oMoveToPoint.Offset(Me.oPointClicked.X * -1, _               (Me.oPointClicked.Y + _               SystemInformation.CaptionHeight + _               SystemInformation.BorderSize.Height) * -1)            ' 移动窗体            Me.Location = oMoveToPoint         End If      End Sub   End Class</ccid_code></pre></td></tr></table></ccid_nobr><p >  移动窗体的功能通过窗体的鼠标事件句柄实现。当窗体遇到MouseDown事件,程序设置一个标志表示当前正处于“移动状态”,同时记录鼠标在窗体内的位置。MouseMove事件句柄检查窗体是否正处于移动状态,如果是,把窗体移动到新的位置。必须考虑到鼠标在窗体内原始的位置,否则,窗体的左上角将跳转到鼠标所在位置。当MouseUp事件出现时,窗体必须结束其“移动状态”,这样即使鼠标继续移动,窗体的位置也不会再移动。 <p >  ShapedForm不仅实现了移动窗体的功能,而且还提供了一种标准的机制,允许通过覆盖SetInitialFormShape()方法创建简单的路径。SetInitialFormShape()方法的任务是创建窗体的oFormPath成员,即一个GraphicsPath对象。如果这个对象存在,窗体将自动采用它定义的形状。另外,这个窗体还会保留上次的形状(大多数情况下是正规的Windows矩形窗体),还有几个方法用来将窗体恢复成原来的外观。 <p >  <b>四、修饰窗体外观</b><p >  现在椭圆异形窗体已经能够移动,但看起来还不是很专业,其中一个原因是它没有一个相配的边框。为异形窗体加上边框没有什么捷径,Windows本身只能为矩形窗体描绘边框。另外,对于大多数实际应用中的异形窗体,画边框不会象本例的椭圆窗体那么简单。 <p >  图五显示了经过修饰的椭圆窗体,它有蓝色的底色和一个简单的边框,边框有简单的3D风格,为了方便读者模仿本文的示例,所以一切从简。边框在窗体的Paint事件中绘制,用GDI+画出椭圆图形(类似于创建窗体轮廓的椭圆路径,但略小一些),用不同的颜色、大小重复绘制即可得到3D效果。 <p ><img  src="http://www.hh010.com/upload_files/article/244/9_mu5hsb140585.gif"><p >  图五:经过修饰的椭圆窗体 <p >  绘制边框从左上角开始,左上角颜色是亮蓝色。然后,用暗蓝色的笔向右下角绘制,用普通的蓝色画出窗体中间的椭圆形作为窗体底色,最终得到三维的边框效果(仔细观察图五就可以看出来)。Listing 2给出绘制图五窗体的代码。这个窗体是ShapedForm类的子类,继承了前面实现的ShapedForm的功能。 <p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>Listing 2:带有3D边框的椭圆窗体Imports System.Drawing.Drawing2D   Public Class EllipticalForm      Inherits ShapedForm      Private iElTop, iElLeft, iElHeight, iElWidth As Integer      Private iElTopOffset, iElLeftOffset      #Region &quot; Windows 窗体设计器生成的代码&quot;      ' 此处略...      Public Overrides Sub SetInitialFormShape()         ' 计算椭圆的大小         Me.iElTop = SystemInformation.BorderSize.Height _            + SystemInformation.CaptionHeight + 2         Me.iElLeft = SystemInformation.BorderSize.Width + 2         Me.iElHeight = Me.Height - Me.iElTop - _             SystemInformation.BorderSize.Height - 3         Me.iElWidth = Me.Width - Me.iElLeft - _             SystemInformation.BorderSize.Width - 3         ' 记住椭圆的偏移量,以便以后绘制时使用         Me.iElLeftOffset = -2         Me.iElTopOffset = _            (SystemInformation.CaptionHeight + 2) * -1         ' 创建图形路径         Me.oFormPath = New GraphicsPath()         Me.oFormPath.AddEllipse(Me.iElLeft, Me.iElTop, _            Me.iElWidth, Me.iElHeight)      End Sub      Private Sub EllipticalForm_Paint( _         ByVal sender As Object, _               ByVal e As System.Windows.Forms.PaintEventArgs) _         Handles MyBase.Paint         ' 创建另一个椭圆(略小一点)         Dim oInteriorPath As New GraphicsPath()         oInteriorPath.AddEllipse(Me.iElLeft, Me.iElTop, _            Me.iElWidth - 6, Me.iElHeight - 6)         ' 创建画笔,用来绘制边框和背景         Dim oLightPen As New Pen(Color.FromArgb(100, 100, 255), 7)         Dim oDarkPen As New Pen(Color.FromArgb(0, 0, 120), 7)         ' 调整位置,绘制边框和背景         e.Graphics.TranslateTransform(Me.iElLeftOffset - 1, _            Me.iElTopOffset - 1)         e.Graphics.DrawPath(oLightPen, oInteriorPath)         e.Graphics.TranslateTransform(4, 4)         e.Graphics.DrawPath(oDarkPen, oInteriorPath)         e.Graphics.TranslateTransform(-2, -2)         e.Graphics.FillPath(Brushes.Blue, oInteriorPath)         e.Graphics.ResetTransform()         ' 清理         oInteriorPath.Dispose()         oLightPen.Dispose()         oDarkPen.Dispose()      End Sub      Private Sub Button1_Click(ByVal sender As System.Object, _         ByVal e As System.EventArgs) Handles Button1.Click         Me.Dispose()      End Sub      Private Sub Button2_Click(ByVal sender As System.Object, _         ByVal e As System.EventArgs) Handles Button2.Click         Me.ResetShape()      End Sub      Private Sub Button3_Click(ByVal sender As System.Object, _         ByVal e As System.EventArgs) Handles Button3.Click         Me.AssignShapePath()      End Sub   End Class</ccid_code></pre></td></tr></table></ccid_nobr><p >  注意:另一种显示边框的方式是将它作为背景图形,实际上,许多采用异形窗体的应用看来正是采用这种方式,因此本文后面也将探讨这种绘制方式。 <p >  <b>五、构造复杂窗体形状</b><p >  前面介绍了构造异形窗体的基础知识,现在该是构造一个复杂异形窗体的时候了。所谓复杂,其实也只不过是在构造GraphicsPath的时候把许多小线段连接在一起。 <p ><img  src="http://www.hh010.com/upload_files/article/244/9_ewzzil140586.gif"><p >  图六:用文字作为异形窗体的轮廓 <p >  GraphicsPath对象提供了许多这方面的方法,包括:加入直线的方法AddLine(),加入曲线的方法AddArc()、AddCurve()等等,还有很多,甚至还有一个加入指定字体和大小的字符串的方法AddString(),它能够以向量图形的形式表示出指定的字符串。图六显示了一个形状为字符串轮廓的异形窗体,看起来就象是一些直接写在桌面背景上的图形,但它确实是一个异形窗体!“仙”字上面的按钮可以证明这一点。Listing 3显示了构造该异形窗体的代码。 <p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>Listing 3:用文字定义异形窗体的轮廓Imports System.Drawing.Drawing2D   Public Class CodeForm       Inherits ShapedForm      #Region &quot; Windows 窗体设计器生成的代码&quot;      ' 此处略...      Public Overrides Sub SetInitialFormShape()         Me.oFormPath = New GraphicsPath()         oFormPath.AddString(&quot;仙人掌&quot;, _            New FontFamily(&quot;隶书&quot;), _            FontStyle.Bold, 200, Me.ClientRectangle, _            StringFormat.GenericDefault)         Me.Region = New Region(oFormPath)      End Sub   End Class</ccid_code></pre></td></tr></table></ccid_nobr><p >  除了Listing 3之外,到目前为止本文用到的绘图方法还只有AddEllipse()一个。AddEllipse()方法创建一个“自包含”的完整的椭圆,换句话说,AddEllipse()方法画出的图形是封闭的,其起点和终点是同一个点。对于由许多小的基本线段(包括曲线)构成的路径,一个很重要的问题是最终必须让整个图形封闭,为此,GraphicsPath对象专门提供了一个自动封闭图形的CloseAllFigures()方法。 <p ><img  src="http://www.hh010.com/upload_files/article/244/9_50rq9r140587.gif"><p >  图七:应用了Compact外观的Media Player <p >  现在我们再来看看Windows Media Player这个例子,考虑一下如何用.NET Windows窗体来构造出相似的窗体。大家知道,Media Player支持所谓的换“皮肤”功能,允许用户彻底改变应用运行的外观。图七就是Media Player支持的众多皮肤中的一种。 <p >  这个皮肤令人感兴趣的不仅是它的外形,更特别的是,它有可打开和隐藏的面板,图八显示了面板打开后的Media Player外观。 <p ><img  src="http://www.hh010.com/upload_files/article/244/9_fzna2v140588.gif"><p >  图八:打开面板后的Media Player <p >  要构造出类似的异形窗体,我们必须画出一个相当复杂的形状。从图七和图八可以看出,这个窗体轮廓包含了大量的直线和弧线。GraphicsPath对象的优点之一在于它很“聪明”,如果前一线段的终点和后一线段的起点不同,它会用直线把两个线段连接起来。因此,在绘制这个异形窗体时,我们(差不多)只要描述各个弧线就可以了,Listing 4给出了生成图形路径的代码。 <p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>Listing 4:能够动态改变的复杂异形窗体Imports System.Drawing.Drawing2D   Public Class ComplexShape      Inherits ShapedForm      ' 两个面板是否打开(默认隐藏)      Private bRightPanelVisible As Boolean = False      Private bBottomPanelVisible As Boolean = False      ' 这是我们要用到的图形      Private imgCollapsed As _         New Bitmap(&quot;..\Images\CollapsedForm.bmp&quot;)      Private imgRightPanel As _         New Bitmap(&quot;..\Images\RightPanelForm.bmp&quot;)      Private imgBottomPanel As _         New Bitmap(&quot;..\Images\BottomPanelForm.bmp&quot;)      Private imgExpanded As New Bitmap _         (&quot;..\Images\ExpandedForm.bmp&quot;)      #Region &quot; Windows 窗体设计器生成的代码&quot;      ' 此处略...      Public Overrides Sub SetInitialFormShape()         ' 设置初始的背景图形         Me.SetBGImage()         ' 创建窗体的轮廓         Me.CreateShape()      End Sub      Private Sub CreateShape()         ' 处理边框和标题条         Dim iOX As Integer = SystemInformation.BorderSize.Width + 2         Dim iOY As Integer = SystemInformation.CaptionHeight + _             (SystemInformation.BorderSize.Height * 2) + 2         ' 生成窗体外形的图形路径         Me.oFormPath = New GraphicsPath()         ' 左上方         Me.oFormPath.AddArc(42 + iOX, 285 + iOY, 70, 80, 90, 75)         Me.oFormPath.AddArc(15 + iOX, 311 + iOY, 25, 40, -20, -60)         Me.oFormPath.AddArc(0 + iOX, 263 + iOY, 50, 45, 110, 70)         Me.oFormPath.AddArc(1 + iOX, 42 + iOY - 31, _            68, 68, 180, 90)         Me.oFormPath.AddArc(96 + iOX, -28 + iOY, 34, 40, 90, -60)         Me.oFormPath.AddArc(376 + iOX - 34, 0 + iOY, _            68, 68, 270, 90)         ' 右边面板         If Not Me.bRightPanelVisible Then            Me.oFormPath.AddArc(396 + iOX, 102 + iOY, 26, 112, _               270, 180)         Else            Me.oFormPath.AddLine(409 + iOX, 36 + iOY, 581 + iOX, _               36 + iOY)            Me.oFormPath.AddArc(576 + iOX, 36 + iOY, _               10, 10, 270, 90)            Me.oFormPath.AddArc(574 + iOX, 102 + iOY, 26, 112, _               270, 180)            Me.oFormPath.AddArc(576 + iOX, 287 + iOY, 10, 10, 0, 90)            Me.oFormPath.AddLine(580 + iOX, 298 + iOY, 409 + iOX, _               298 + iOY)         End If         ' 右下方         Me.oFormPath.AddArc(409 - 68 + iOX, 333 - 36 + iOY, _            68, 68, 0, 90)         ' 底部面板         If Not Me.bBottomPanelVisible Then            Me.oFormPath.AddArc(165 + iOX, 352 + iOY, _               112, 26, 0, 180)         Else            Me.oFormPath.AddLine(366 + iOX, 365 + iOY, 366 + iOX, _               461 + iOY)            Me.oFormPath.AddArc(355 + iOX, 456 + iOY, 10, 10, 0, 90)            Me.oFormPath.AddArc(165 + iOX, 454 + iOY, _               112, 26, 0, 180)            Me.oFormPath.AddArc(75 + iOX, 456 + iOY, 10, 10, 90, 90)            Me.oFormPath.AddLine(75 + iOX, 461 + iOY, 75 + iOX, _               365 + iOY)         End If         ' 默认窗体形状         Me.oFormPath.CloseAllFigures()      End Sub      Private Sub ComplexShape_MouseUp( _         ByVal sender As Object, _          ByVal e As System.Windows.Forms.MouseEventArgs) _         Handles MyBase.MouseUp         ' 检查是否有人点击了面板的控制开关         If Me.bRightPanelVisible Then            If e.X &gt; 572 And e.X &lt; 600 And _               e.Y &gt; 100 And e.Y &lt; 220 Then               ' 隐藏右边面板               Me.bRightPanelVisible = False               Me.CreateShape()               Me.AssignShapePath()               Me.SetBGImage()            End If         Else            If e.X &gt; 390 And e.X &lt; 420 And _               e.Y &gt; 100 And e.Y &lt; 220 Then               ' 打开右边面板               Me.bRightPanelVisible = True               Me.CreateShape()               Me.AssignShapePath()               Me.SetBGImage()            End If         End If         If Me.bBottomPanelVisible Then            If e.X &gt; 160 And e.X &lt; 285 And _               e.Y &gt; 450 And e.Y &lt; 480 Then               ' 隐藏底部面板               Me.bBottomPanelVisible = False               Me.CreateShape()               Me.AssignShapePath()               Me.SetBGImage()            End If         Else            If e.X &gt; 160 And e.X &lt; 285 And _               e.Y &gt; 350 And e.Y &lt; 380 Then               ' 打开底部面板               Me.bBottomPanelVisible = True               Me.CreateShape()               Me.AssignShapePath()               Me.SetBGImage()            End If         End If      End Sub      Private Sub SetBGImage()         If Not Me.bRightPanelVisible Then            If Not Me.bBottomPanelVisible Then               ' 隐藏面板               Me.BackgroundImage = Me.imgCollapsed            Else               Me.BackgroundImage = Me.imgBottomPanel            End If         Else            If Not Me.bBottomPanelVisible Then               ' 隐藏面板               Me.BackgroundImage = Me.imgRightPanel            Else               Me.BackgroundImage = Me.imgExpanded            End If         End If      End Sub   End Class</ccid_code></pre></td></tr></table></ccid_nobr><p >  默认情况下,该窗体的两个面板都是隐藏的。窗体的两个域(bRightPanelVisible和bBottomPanelVisible)定义了右边面板和底部面板是否显示出来。如果面板是可见的,为了改变窗体的外形,程序创建的图形路径也不同。这样,我们可以在任何时候调用CreateShape()方法构造新的窗体轮廓,然后利用AssignShapePath()方法把修改后的窗体轮廓赋值给窗体。实际上,当用户点击窗体的某些特定区域时,程序就要照此步骤执行,其基本思路是:捕获鼠标点击面板控制开关的事件,在事件句柄中切换bRightPanelVisible和bBottomPanelVisible域的值,重新构造窗体轮廓,并把改变后的窗体轮廓指定给窗体。 <p >  说明:当用户点击Media Player面板的控制开关时,面板从窗体的主体部分缓缓推出或缩进,类似一种滑动效果,但在本文的实现中,面板是直接跳出来的。滑动效果可以通过多次逐步改变窗体轮廓的方式获得,应当说,实现这一效果并不是特别复杂。但是,为简单起见,本文的例子不实现这一效果。 <p >  不错,现在我们已经有了一个与众不同的窗体,但它还算不上漂亮,看起来似乎让人觉得是屏幕中央怪模怪样灰不溜秋的一块。要让它变得好看一些,一种简单的办法是加上背景图形。 <p >  在这个例子中,我们要加上四个图形:其中一个用于所有面板关闭时,一个用于所有面板打开时,另外两个分别对应其中一个面板打开的情形。制作背景图形最简单的办法是:运行不带任何修饰的窗体,截取该窗体的图形,用图形编辑工具创建一个精确匹配该窗体轮廓的背景图形。注意背景图形必须和窗体轮廓精确匹配,否则窗体的周围可能留下空白点,使最后得到的窗体显得很难看。 <p ><img  src="http://www.hh010.com/upload_files/article/244/9_ws0qoz140589.gif"><p >  图九:定义一个类似于Media Player的异形窗体 <p >  大多数图形编辑工具允许使用纹理和3D边框之类的特效。按照前面介绍的步骤,我们可以方便地构造出图九和图十显示的背景图形。窗体启动的时候预先装入这些图形,做好赋值给窗体BackgroundImage成员的准备。然后,当用户点击面板的控制开关时,只要调用SetBGImage()方法设置适当的背景图形就可以了。 <p ><img  src="http://www.hh010.com/upload_files/article/244/9_lvytvg140590.gif"><p >  图十:完成后的复杂异形窗体 <p >  从程序员的角度来看,创建异形窗体其实算不上特别困难的事情,只有当窗体轮廓的向量化描述极其复杂时才会让人感觉棘手。另外,除非你自己有相当好的艺术功底,否则可能需要一个图形专家来帮你制作背景图形。 <p >  现在你已经知道怎样构造异形窗体了,你会发现把标准的Windows矩形控件放入异形窗体是多么格格不入。想象一下Windows Media Player换上一个标准“播放”按钮会成什么样子!接下来,也许你该精心打造匹配异形窗体的按钮和其他控件了。 <p ><center>(责任编辑:<ccid_nobr>西门吹雪</ccid_nobr>)</center><p align="center"></p></p>
您需要登录后才可以回帖 登录 | 论坛注册

本版积分规则

QQ|Archiver|手机版|小黑屋|sitemap|鸿鹄论坛 ( 京ICP备14027439号 )  

GMT+8, 2025-4-8 23:54 , Processed in 0.075534 second(s), 24 queries , Redis On.  

  Powered by Discuz!

  © 2001-2025 HH010.COM

快速回复 返回顶部 返回列表