|
<p >这篇文章我将讲2个方面的技术,第一个是在.net中使用新的gdi+技术,第二个是讲使用静态变量对gdi+对象的移动,这也是本文的关键,因为GDI+对象的移动在msdn里没讲,但是它又有很大的用途,同时它的移动方法又和控件的移动方法不一样,至于不一样在什么地方,答案就在文中!<p ><b>什么是gdi+</b><p >GDI+ 是 Microsoft Windows XP以及后续windows操作系统的子系统,同时又是.net提供的一种新的简单、快速的图形图象开发技术。顾名思义,GDI+ 是 GDI(Windows 早期版本提供的图形设备接口)的后续版本。GDI+ 是一种应用程序编程接口 (API),通过一套部署为托管代码的类来展现。这套类被称为 GDI+ 的“托管类接口”。<p >程序员可利用GDI+这样的图形设备接口在屏幕或打印机上显示信息,而不需要考虑特定显示设备的具体情况。应用程序的程序员调用GDI+类提供的方法,而这些方法又反过来调用特定的设备驱动程序。GDI+ 将应用程序与图形硬件隔离,而正是这种隔离允许开发人员创建设备无关的应用程序。<p ><b>使用gdi+和gdi在编程时有什么区别?</b><p >使用 GDI(Windows 的以前版本中包括的图形设备接口)编写程序,就会熟悉设备上下文的知识。设备上下文是Windows 使用的一种结构,用于存储与特殊显示设备的功能和指定如何在该设备上绘制项目的属性相关的信息。用于视频显示的设备上下文还与显示的特定窗口关联。首先,您获得一个设备上下文的句柄 (HDC),然后将该句柄作为参数传递至实际进行绘制的 GDI 函数。您还可将此句柄作为参数传递给获取或设置设备上下文属性的 GDI 函数。 <p >使用GDI+,您不需要再使用句柄或设备上下文,而只需创建一个Graphics对象,然后在您熟悉的面向对象样式 myGraphicsObject.Drawx(参数)(drawx代表要显示的图形)中调用其方法。Graphics 对象是 GDI+ 的核心。设备上下文和 Graphics 对象的作用相似,但在使用设备上下文 (GDI) 的、基于句柄的编程模式和使用Graphics 对象 (GDI+) 的、面向对象的编程模型之间存在一些基本的差异。<p >Graphics 对象(像设备上下文一样)与屏幕上的特定窗口关联,并具有指定如何绘制项目的属性。但是,Graphics 对象不受钢笔、画笔、路径、图像或字体的约束,这与设备上下文不同。例如,使用设备上下文绘制线条之前,必须先调用SelectObject以使钢笔对象和设备上下文关联。这是指将钢笔选入设备上下文中。在设备上下文中绘制的所有线条均使用该钢笔,直到您选择另一支不同的钢笔为止。在 GDI+ 中,将 Pen 对象作为参数传递给 Graphics 类的 DrawLine 方法。您可以在一系列 DrawLine 调用的每个调用中使用不同的 Pen 对象,而不必使给定的 Pen 对象与 Graphics 对象关联。<p ><b>编写程序的思路:</b>将窗口分成7个矩形,使这7个矩形有不同的颜色,分别在这7个矩形中再建立一个小的矩形,使小的矩形每隔一段时间向前移动移动一段距离,并且覆盖前一时间段的小的矩形。再在第7个矩形的顶部再建立一个小的矩形,用向上键控制他向上移动,每按一次键,就移动到上一个矩形中去。<p >本文的重点是移动gdi+,所以对使用gdi+本身只做一个简单的讲解,为的是方便大家阅读代码。首先要创建Graphics对象和创建绘画工具对象如钢笔、刷子、路径、图像或字体等,在绘画工具中可以指定颜色,字体,图片等,本文使用的是刷子。完成以上两个步骤后通过Graphics对象的绘图属性在指定的位置(由x,y的坐标给出)画出图形和给图形添上颜色如要画直线请用drewline属性,本文将讲画矩形和文字。(对于更复杂的操作请见msdn)<p >对于第二问提我将在原代码以后给出讲解。<p >代码如下:timer1的interval=100每01.秒运行一次Timer1_Tick事件<p ><ccid_nobr><table align="center" border="1" bordercolordark="#FFFFFF" bordercolorlight="black" cellpadding="2" cellspacing="0" width="550"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code> ublic Class Form1 Inherits System.Windows.Forms.Form Public myGraphics As Graphics Public myBrush As New SolidBrush(Color.MediumTurquoise) Dim hi, we As Integer Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint Dim formGraphics As Graphics'初始化Graphics对象formGraphics = Me.CreateGraphics'创建Graphics对象 Dim h, w As Integer h = Me.Height / 7'y点的位置,同时是矩形的高 w = Me.Width'举行的宽 '将窗口分成7个部分,为7个部分添上不同的颜色 Dim redBrush As New SolidBrush(Color.Red) '初始化SolidBrush对象,SolidBrush表示使用刷子来画,Color.Red表示刷子颜色是红色 formGraphics.FillRectangle(redBrush, 0, 0, w, h) '在指定的位置画出以w,h为宽和高矩形 Dim yelBrush As New SolidBrush(Color.Yellow) formGraphics.FillRectangle(yelBrush, 0, h, w, h) Dim bluBrush As New SolidBrush(Color.Blue) formGraphics.FillRectangle(bluBrush, 0, 2 * h, w, h) Dim orcBrush As New SolidBrush(Color.Orchid) formGraphics.FillRectangle(orcBrush, 0, 3 * h, w, h) Dim cranBrush As New SolidBrush(Color.Black) formGraphics.FillRectangle(cranBrush, 0, 4 * h, w, h) Dim Brush As New SolidBrush(Color.AliceBlue) formGraphics.FillRectangle(Brush, 0, 5 * h, w, h) Dim firBrush As New SolidBrush(Color.Firebrick) formGraphics.FillRectangle(firBrush, 0, 6 * h, w, h)'在7个部分中的第一个部分中显示exit文本 Dim drawFont As New Font(&quot;Arial&quot;, 16)'初始化字体和字体大小 Dim drawBrush As New SolidBrush(Color.White)'使用刷子画字体,颜色是白色 Dim drawPoint As New PointF(Me.Width / 2.0F, 0.0F)'文字出现的位置,即x,y的坐标 formGraphics.DrawString(&quot;EXIT&quot;, drawFont, drawBrush, drawPoint)'画出文字exit End Sub Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick '在每个部分中设计一个新的Graphics对象,让他做横向移动,如果他移动到窗口外,让他重回起点。 Dim formGraphics As Graphics Dim w As Integer : w = Me.Width / 200 Dim h As Integer : h = Me.Height / 7 formGraphics = Me.CreateGraphics Static k As Integer If k &lt; Me.Width Then Dim aquBrush As New SolidBrush(Color.Aqua) Dim redBrush As New SolidBrush(Color.Yellow) formGraphics.FillRectangle(redBrush, k, h, w, h)'覆盖前一时间段的矩形 Dim p As Integer p = k : k = p + 2'每1秒移动两个相素单位 p = k formGraphics.FillRectangle(aquBrush, p, h, w, h)'移动后的矩形 Else : k = 0 End If ' *************************** Static k2 As Integer If k2 &lt; Me.Width Then Dim aquBrush As New SolidBrush(Color.Aqua) Dim redBrush As New SolidBrush(Color.Blue) formGraphics.FillRectangle(redBrush, k2, 2 * h, w, h) Dim p As Integer p = k2 : k2 = p + 11 : p = k2 formGraphics.FillRectangle(aquBrush, p, 2 * h, w, h) Else : k2 = 0 End If Static k3 As Integer If k3 &lt; Me.Width Then Dim aquBrush As New SolidBrush(Color.Aqua) Dim redBrush As New SolidBrush(Color.Orchid) '取消覆盖前一时间的矩形例子formGraphics.FillRectangle(redBrush, k3, 3 * h, w, h) Dim p As Integer p = k3 : k3 = p + 5 : p = k3 formGraphics.FillRectangle(aquBrush, p, 3 * h, w, h) Else : k3 = 0 End If Static k4 As Integer If k4 &lt; Me.Width Then Dim aquBrush As New SolidBrush(Color.Aqua) Dim redBrush As New SolidBrush(Color.Black) formGraphics.FillRectangle(redBrush, k4, 4 * h, w, h) Dim p As Integer p = k4 : k4 = p + 7 : p = k4 formGraphics.FillRectangle(aquBrush, p, 4 * h, w, h) Else : k4 = 0 End If Static k5 As Integer If k5 &lt; Me.Width Then Dim aquBrush As New SolidBrush(Color.Aqua) Dim redBrush As New SolidBrush(Color.AliceBlue) formGraphics.FillRectangle(redBrush, k5, 5 * h, w, h) Dim p As Integer p = k5 : k5 = p + 9 : p = k5 formGraphics.FillRectangle(aquBrush, p, 5 * h, w, h) Else : k5 = 0 End If End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Timer1.Enabled = True End Sub Private Sub Form1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles MyBase.KeyPress Dim kup As New KeyEventArgs(Keys.Up) Dim myGraphics As Graphics Dim myBrush As New SolidBrush(Color.MediumTurquoise) myGraphics = Me.CreateGraphics If kup.KeyCode = Keys.Up Then '向上运动 Static k5, k As Integer : Dim p, n As Integer we = Me.Width / 2 Dim hi As Integer : hi = Me.Height / 7 * 6'************************请见后文********************** k5 = k5 + hi k5 = k5 + hi / 6 n = k k = k + 1 n = k p = k5 \ n - n * hi / 6'************************************************ If n&lt;7 Then'如果运动到窗口外面就释放k5和k,并重回起点这样就可以不停的按向上键了。 myGraphics.FillRectangle(myBrush, we, p, 50, 50) Else k5 = Nothing k = Nothing End If End If End SubEnd Class</ccid_code></pre></td></tr></table></ccid_nobr><p >现在就来讲一下使用静态变量将gdi+对象移动问题,因为这两个问题是相互联系,所以我将他放在这里一起讲。<p >如果你希望让控件每隔一定时间移动一段距离或是每按一下方向箭头让他移动一段距离,你会怎么做?答案是使用控件的left、top的两个属性(表明控件距离窗口的左边,窗口的顶部),让这两个属性加上某个值或减某个值,因为这两个属性的值会一直保存在内存中。但是Graphics没有这两个属性,因为要使用Graphics对象在窗口上画图,就要指定所画图形在窗口的位置,而Graphics的位置是用坐标表示的,那你可能会想让坐标加上某个值或减某个值不就可以移动吗?理论上将是对的,但是Graphics坐标在使用一次后会被释放,而且又没有获得当前坐标方法,那吗唯一保存坐标的方法就是使用静态变量。<p >请看Timer1_Tick事件,这个事件是让一个Graphics对象从左到右的按照指定的一个值每隔一定时间移动一段距离。现在就来解释这段代码是如何里用静态变量使Graphics对象移动的。<p >首先定义一个静态变量k,此时k只有一个内存空间,if语句中再定义一个p,p只是一个普通变量,它做为中间变量使用,当第一次画矩形时是在k,h(此时k是0)点用指定的SolidBrush背景色画出矩形,覆盖先前的矩形。画完后将当前的k负值给p,让p加上一个值得到新的k(此时的k以被换成了新值),在把新k负值给p,进行第二次用指定SolidBrush画矩形,此时矩形已经向前移动了一个指定距离。(现在的SolidBrush颜色是指定色)。当第4次画图时k又是一个新值,并且画的矩形又移动了一段距离。这样周而复始的就实现了移动。奇数次都是覆盖前一矩形,使原有颜色不变。<p >但是在Form1_KeyPress事件中你回发现上面的方法无法使用了,如果你仔细观察你会发现在使用静态变量时多了很多代码,你会发现我定义了k5后在给k5附了初值,而正是这个初值将上面的方法改变了。并且使用附值语句不是"k5=hi"而是"k5=k5+hi",但是为什么在Timer1_Tick事件中又没给静态变量附初值?为什么什么要使用"k5=k5+hi"尼?他又代表什么意思?其他的附值语句又是怎么会事?而这就是本文中的最关键的了。Timer1_Tick事件为理解Form1_KeyPress中的着写语句打下了基础,但在理解Form1_KeyPress中的移动对象代码前我门必须更深的理解静态变量和一个数学模型。<p >静态变量:在定义静态变量时,其实只是给他定义了一个内存空间,他没有任何值,但是如果你要用他的话他会将自己初始化0,但是这个0和k=0是不一样的,前一个0表示k有一个空间,这个空间指向含有0这个值的地址,它的语法表示为k=k+0,而k=0表示将0放入k所在的空间,而后一种做法将不可以改变静态变量k的值。其实k=k+0中的第二个k相当于c语言中的指针,想想c语言中是如何使用指针改变内存值的,你就可以很容易的理解第二个k了!所以要使静态变量的初值不为0,就必须使用"k=k+值"的方式,这就是为什么要使用"k5=k5+hi"的原因!<p >定义一个一般内存p做中间量是用来保存改变后的k的。n是用来记录按下向上按钮的次数,每按一次加1, k5 = k5 + hi / 6和p = k5 \ n - n * hi / 6是要移动的距离。这两条语句是本段的关键,我们必须借用数学模型才可以理解,请先看下面这道数学题,然后讲解文中代码:<p ><ccid_nobr><table align="center" border="1" bordercolordark="#FFFFFF" bordercolorlight="black" cellpadding="2" cellspacing="0" width="550"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>已知:x=x+170,y=x+10,x的初值是0,求3次叠加后的y------------------------------------ 1 2 3 最后的y值x=x+170 170 350 530 y=x+10 180 360 540y=: 540------------------------------------分析: 第1次y值 第2次y值 第3次y值y=: 170+10 170+10+170+10 170+10+170+10+170+10------------------------------------ y值的结论: y=(170*n)+n*10 注:n是次数------------------------------------</ccid_code></pre></td></tr></table></ccid_nobr><p >现在将代码和y值的结论联系起来:y=(170*n)+n*10中的n可以看成按下向上按钮的次数,由于窗体的高是已知的,所以170可以看成窗口的高。而y可以看成是按下向上按钮n次后(n=1,2,3,....)后的新坐标点。但是你会发现他是向下移动的,而不是我们希望的向上移动。原来windows的坐标原点是在窗口的左上角,x的正方向是向右,y的正方向是向下的,要向上移动,其实是向y的负方向移动,所以y值的结论该改成y=(170*n)-n*10。新的一个问题又来了除第1次外,以后的移动都看不见矩形了,原来他移动到窗体外面去了,而且移动的距离也不是一个指定的值,那吗要指定每次移动一个固定的值,就必须把结论改一下为y=(170*n)/n-n*10(由于(170*n)/n的结果必须取整数,所以是要改成(170*n)\n)<p >现在,大家就理解了k5 = k5 + hi是给初值 ,k5 = k5 + hi / 6 要移动的值,p = k5 \ n - n * hi / 6实现移动指定的值。如果你希望改变移动的值就要将k5 = k5 + hi / 6 和p = k5 \ n - n * hi / 6中的hi/6都改成一样的值或有效表达式。<p >讲了这么多,那它有什么应用价值?某音频播放器中均衡器随频率的高低的起伏情况,win2000中的碎片整理时显示的碎片整理的进行情况,还有就是游戏,例如台球游戏,通过同时改变x和y的坐标来实现(本文只是改变了y的值)......<p >程序在vb.net和win2000中通过。<p ><center><img src="http://www.hh010.com/upload_files/article/244/9_mrer1o36015.jpg"></center><p >如图,第四行是未覆盖前一时间段的矩形样子,小方块是用向上键控制的。<p >(责任编辑 <ccid_nobr>Sunny</ccid_nobr>) <p align="center"></p></p> |
|