当前位置:首页 > C#WinForm编程(3).第33章使用GDI+绘图
using System.Drawing; 在应用程序启动时设置最小尺寸,并保持不变,在这个应用程序中是必要的,因为我们知道屏幕区域一般有多大。在运行该应用程序时,这个\文档\是不会改变大小的。但要记住,如果应用程序显示文件内容的操作,或者执行某些改变屏幕区域的操作,就需要在其他时间设置这个属性(此时,必须手工调整代码,Visual Studio 2008属性窗口只能在构建窗体时设置属性的初始值)。 设置MinScrollSize只是一个开始,仅有它是不够的。如图33-8所示为示例应用程序目前的外观-开始时,让屏幕正确显示图形。 (点击查看大图)图 33-7 (点击查看大图)图 33-8 注意,不仅窗体正确地设置了滚动条,而且它们的大小也正确设置了,以指定文档正确显示的比例。可以试着在运行示例时重新设置窗口的大小,这样就会发现滚动条会正确响应,甚至如果使窗口变得足够大,不再需要滚动条时,滚动条就会消失。
但是,如果使用一个滚动条,并向下滚动它,会发生什么情况呢?如图33-9所示。显然,出现了错误!
出错的原因是我们没有在OnPaint()重写方法的代码中考虑滚动条的位置。如果最小化窗口,再恢复它,重新绘制一遍窗口,就可以很清楚地看出这一点。结果如图33-10所示。
(点击查看大图)图 33-9 (点击查看大图)图 33-10 图形像以前一样进行了绘制,矩形的左上角嵌套在客户区域的左上角,就好像根本没有移动过滚动条一样。
在更正这个问题前,先介绍一下在这些屏幕图上发生了什么。
首先从BigShapes示例开始,如图33-8所示。在这个例子中,整个窗口刚刚重新进行了绘制。看看前面的代码,该代码的作用是使graphics实例用左上角坐标(0,0)(相对于窗口客户区域的左上角)绘制一个矩形-- 它是已经绘制过的。问题是,graphics实例在默认情况下把坐标解释为是相对于客户窗口的,它不知道滚动条的存在。代码还没有尝试为滚动条的位置调整坐标。椭圆也是这样。
下面处理图33-9的问题。在滚动后,注意窗口上半部分显示正确,这是因为它们是在应用程序第一次启动时绘制的。在滚动窗口时,Windows没有要求应用程序重新绘制已经显示在屏幕中的内容。Windows只指出屏幕上目前显示的内容可以平滑地移动,以匹配滚动条的位置。这是一个非常高效的过程,因为它也能使用某些硬件加速来完成。在这个屏幕
图中,有错误的是窗口下部的1/3部分。在应用程序第一次显示时,没有绘制这部分窗口,因为在滚动窗口前,这部分在客户区域的外部。这表示Windows要求BigShapes应用程序绘制这个区域。它引发Paint事件,把这个区域作为剪切的矩形。这也是OnPaint()重载方法完成的任务。 问题的另一种表达方式是我们把坐标表示为相对于文档开头的左上角-- 需要转换它们,使之相对于客户区域的左上角。图33-11说明了这一点。 为了使该图更清晰,我们向下向右扩展了该文档,超出了屏幕的边界,但这不会改变我们的推论,我们还假定其上还有一个水平滚动条和一个垂直滚动条。 在该图中,细矩形标记了屏幕区域的边框和整个文档的边框。粗线条标记了要绘制的矩形和椭圆。P标记要绘制的某个随意点,这个点在后面会作为一个示例。在调用绘图方法时,提供graphics实例和从B点到P点的矢量,这个矢量表示为一个Ponit实例。我们实际上需要给出从点A到点P的矢量。 图 33-11 问题是,我们不知道从A点到P点的矢量。而知道从B点到P点的矢量,这是P相对于文档左上角的坐标-- 我们要在文档的P点绘图。还知道从B点到A点的矢量,这是滚动的距离,它存储在Form类的一个属性AutoScrollPosition中。但是不知道从A点到P点的矢量。 要解决这个问题,只需进行矢量相减即可。例如,要从B点到P点,可以水平移动150个像素,垂直向下移动200个像素。而要从B点到A点,就需要水平移动10个像素,垂直向下移动57个像素。这表示,要从A点到P点,需要水平移动140个像素(=150-10),垂直向下移动143个像素(=200-57)。为了使之更简单,Graphics类执行了一个方法来进行这些计算,这个方法是TranslateTransform,我们给它传送水平和垂直坐标,表示客户区域的左上角相对于文档的左上角(AutoScrollPosition属性,它是图中从B到A的矢量),然后Graphics设备考虑客户区域相对于文档区域的位置,计算出这些坐标。 解释完之后,所需做的是把下面这行代码添加到绘图代码中: dc.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y); 但在本示例中,它有点复杂,因为我们还要测试剪切区域,看看是否需要进行绘制工作。这个测试需要调整,把滚动的位置也考虑在内。完成后,该示例的整个绘图代码如下所示: protected override void OnPaint( PaintEventArgs e ) { base.OnPaint(e); Graphics dc = e.Graphics; Size scrollOffset = new Size(this.AutoScrollPosition); if (e.ClipRectangle.Top+scrollOffset.Width < 350 || e.ClipRectangle.Left+scrollOffset.Height < 250) { Rectangle rectangleArea = new Rectangle (rectangleTopLeft+scrollOffset, rectangleSize); Rectangle ellipseArea = new Rectangle (ellipseTopLeft+scrollOffset, ellipseSize); dc.DrawRectangle(bluePen, rectangleArea); dc.DrawEllipse(redPen, ellipseArea); } } 现在,滚动代码工作正常,最后得到正确的滚动屏幕图,如图33-12所示。 图 33-12 33.5 世界、页面和设备坐标
测量相对于文档区域左上角的位置和测量相对于屏幕(桌面)左上角的位置之间的区别非常重要,GDI+为它们指定了不同的名称:
● 世界坐标(World Coordinate):要测量的点距离文档区域左上角的位置(以像素为单位)。 ● 页面坐标(Page Coordinate):要测量的点距离客户区域左上角的位置(以像素为单位)。 注意:
熟悉GDI的开发人员要注意,世界坐标对应于GDI中的逻辑坐标。页面坐标对应于设备坐标。还要注意,编写逻辑和设备坐标之间的转换代码在GDI+中有了变化。在GDI中,转换是使用Windows API函数LPtoDP()和DPtoLP()通过设备环境进行的,而在GDI+中,由Control类来维护转换过程中所需要的信息,Form和各种Windows 窗体控件设备派生于Control类。
GDI+还有第3种坐标,即设备坐标(Device Coordinate)。设备坐标类似于页面坐标,但其测量单位不是像素,而是用户通过调用Graphics.PageUnit属性指定的单位。它可以使用的单位除了默认的像素外,还包括英寸和毫米。本章虽然没有使用PageUnit属性,但它可用作获取设备的不同像素密度的方式。例如,在大多数监视器上,100像素大约是1英寸。但是,激光打印机可以达到1 200 dpi(点/英寸)-- 这表示一个100像素宽的图形在该激光打
共分享92篇相关文档