窗体的大小由窗体的Size属性确定,这个大小包括了窗体的标题栏和边框。而窗体中用于显示客户文档的区域称为工作区(ClientRectangle),其大小用窗体的ClientSize 属性表示(如图17-20所示)。
在图17-20中,窗体的大小为308×234像素,其中标题栏的宽度为30像素,边框的宽度为4像素,所以工作区的大小为300×200像素。
如果我们要在300×200像素的工作区内显示一个200×150像素的矩形和一个300×100像素的椭圆,会出现什么情况呢?
图17-20 在滚动窗口中绘图 图17-21 文档大小超出工作区
作者:梁斌玉 摘自《叩响C#之门》,写了将近五年时间,七月初出版出版
BeginnerClassroom@163.com
http://www.cnblogs.com/BeginnerClassroom
为了叙述方便,我们把要显示的文本、图形等内容称为“文档”。因为这时文档的总高度为250像素,而窗口工作区的高度只有200像素,所以总有一部分无法显示(如图17-21所示)。如果文档太大,工作区不能完全显示,就需要在窗口中添加滚动条,以便查看被挡住的部分。
怎样才能显示滚动条呢?这可以通过设置窗体的AutoScrollMinSize属性实现。
this.AutoScrollMinSize = new Size(300, 250);
因为文档的面积为300×250像素,所以我们把AutoScrollMinSize的值设置为300×250,一旦工作区面积小于该值,窗体就会自动显示相应的滚动条。
请新建一个名为“ScrollWindow”的项目,窗体大小设置为308×234像素(除去标题栏和边框,工作区的实际大小为300×200像素),然后重写OnPaint()方法。
public partial class Form1 : Form{ //构造函数 public Form1() { InitializeComponent(); //将窗体的背景色设置为白色 this.BackColor = Color.White; //当工作区小于300×250像素时显示滚动条 this.AutoScrollMinSize = new Size(300, 250); } //重写OnPaint()方法 protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics g = e.Graphics; //绘制矩形和椭圆 g.FillRectangle(Brushes.LightPink, 0, 0, 200, 150); g.FillEllipse(Brushes.LightGreen, 0, 150, 300, 100); } }
运行程序,结果如图17-22所示,出现了滚动条。
但当我们拖动滚动条时,意想不到的事情发生了。窗体并没有绘制椭圆的下半部分,而是又把椭圆的上半部分绘制了一遍(如图17-23所示)。
图17-22 出现滚动条 图17-23拖动滚动条时又把椭圆的上半部分绘制了一遍
为什么会出现这种情况呢?请把窗口最小化,然后恢复,我们发现窗口中的图像变为初始模样了(如图17-24所示)。
原来当重新显示窗体时,发生Point事件,系统调用OnPaint()方法重绘窗体,下面的代码被再次执行。
g.FillRectangle(Brushes.LightPink, 0, 0, 200, 150);
g.FillEllipse(Brushes.LightGreen, 0, 150, 300, 100);
第一条语句要求以点(0,0)为起点,画一个宽200像素、高150像素的矩形;第二条语句要求以点(0,150)为起点,画一个宽300、高100的椭圆。
然而,Graphics对象绘制图形时并不知道滚动条的变化情况,默认情况下它总是以“工作区左上角”为原点绘制图形的,即它描点时的坐标总是参照“工作区左上角”的。形象地说就是,它总是把文档的左上角和工作区的左上角对齐,然后把文档贴在工作区上。于是图17-22所示的图像重新绘制了一遍,图像变为初始模样。
当我们拖动滚动条时,也会触发Paint事件,重新绘制工作区,但系统并不重新绘制整个工作区。当滚动条向下拖动50像素时,系统首先把工作区中的图像整体向上平移50像素,这时工作区下部出现一块大小为300×50像素的空白(如图17-25所示),系统只需补上这块空白区域即可。这种按需绘制的方式可以大大提高绘图效率。
然而这块空白区域纵坐标范围为150~200,在文档中,正好是椭圆上半部分的位置,所以Graphics对象把椭圆上半部分重新绘制了一遍,结果就出现了椭圆上半部分出现两次的情况(如图17-24所示)。
(拖动滚动条时图像向上平移50像素)
图17-24 最小化窗口再恢复,图像变为初始模样 图17-25 按需绘制的方式
实际上椭圆下半部分纵坐标范围为200~250,所以要想正确绘制出空白区域的图形,需要把绘图的坐标原点向上平移50像素,而这一点可以通过坐标的平移变换实现,如图17-25所示。
图17-26 坐标平移
坐标平移的情况如图17-26所示,要绘制从A点开始的区域,就要把坐标系原点由工作区的左上角A平移到文档的左上角O,即始终使坐标系的原点位于文档的左上角。这种变换可以通过下面的语句实现。
g.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);
属性AutoScrollPosition表示的是滚动条的位置,滚动条移动了多少像素,坐标系就要平移多少像素。需要注意的是,AutoScrollPosition.X和AutoScrollPosition.Y均为负数,所以坐标原点实际上是向左上角平移的。
public partial class Form1 : Form{ //构造函数 public Form1() { InitializeComponent(); //将窗体的背景色设置为白色 this.BackColor = Color.White; //当工作区小于300×250像素时显示滚动条 this.AutoScrollMinSize = new Size(300, 250); } //重写OnPaint()方法 protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics g = e.Graphics; //平移坐标系 g.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y); //绘制矩形和椭圆 g.FillRectangle(Brushes.LightPink, 0, 0, 200, 150); g.FillEllipse(Brushes.LightGreen, 0, 150, 300, 100); } }
运行程序,结果如图17-27所示,一切正常。
图17-27 根据滚动条的位置调整坐标系的运行结果