[叩响C#之门]写给初学者:滚动条原理

时间:2023-01-26 22:44:04

17.9.4 在滚动窗口中绘图

窗体的大小由窗体的Size属性确定,这个大小包括了窗体的标题栏和边框。而窗体中用于显示客户文档的区域称为工作区(ClientRectangle),其大小用窗体的ClientSize 属性表示(如图17-20所示)。

 

在图17-20中,窗体的大小为308×234像素,其中标题栏的宽度为30像素,边框的宽度为4像素,所以工作区的大小为300×200像素。

如果我们要在300×200像素的工作区内显示一个200×150像素的矩形和一个300×100像素的椭圆,会出现什么情况呢?

[叩响C#之门]写给初学者:滚动条原理 [叩响C#之门]写给初学者:滚动条原理

图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()方法。

[叩响C#之门]写给初学者:滚动条原理 试一试:在滚动窗口中绘图

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所示)。

[叩响C#之门]写给初学者:滚动条原理

图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所示)。

[叩响C#之门]写给初学者:滚动条原理

(拖动滚动条时图像向上平移50像素)

图17-24 最小化窗口再恢复,图像变为初始模样 图17-25 按需绘制的方式

实际上椭圆下半部分纵坐标范围为200~250,所以要想正确绘制出空白区域的图形,需要把绘图的坐标原点向上平移50像素,而这一点可以通过坐标的平移变换实现,如图17-25所示。

[叩响C#之门]写给初学者:滚动条原理 

 

 

图17-26 坐标平移

坐标平移的情况如图17-26所示,要绘制从A点开始的区域,就要把坐标系原点由工作区的左上角A平移到文档的左上角O,即始终使坐标系的原点位于文档的左上角。这种变换可以通过下面的语句实现。

g.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);

属性AutoScrollPosition表示的是滚动条的位置,滚动条移动了多少像素,坐标系就要平移多少像素。需要注意的是,AutoScrollPosition.X和AutoScrollPosition.Y均为负数,所以坐标原点实际上是向左上角平移的。

[叩响C#之门]写给初学者:滚动条原理 试一试:根据滚动条的位置调整坐标系

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所示,一切正常。

[叩响C#之门]写给初学者:滚动条原理

图17-27 根据滚动条的位置调整坐标系的运行结果

作者:梁斌玉 摘自《叩响C#之门》,写了将近五年时间,七月初出版出版
BeginnerClassroom@163.com
http://www.cnblogs.com/BeginnerClassroom