当前位置:首页 > WinForm

线性渐变笔刷LinearGradientBrush的陷阱

发表于 2015-11-21 16:17

不知道你使用LinearGradientBrush的时候,是否会遇到这样的情况,在图形的顶部出现一条横线,颜色为渐变色底部的颜色,如图:

线性渐变笔刷LinearGradientBrush的陷阱 图1

下面我们将详细的了解一下LinearGradientBrush,并解释为何会存在上面的问题。


关于LinearGradientBrush的两个Rectangle

我们以构造函数LinearGradientBrush(Rectangle, Color, Color, LinearGradientMode)为例,两个Rectangle是指New LinearGradientBrush时传入的Rectangle和Graphics.FillRectangle时传入的Rectangle。我们很多时候都会写这样的代码,在事先计算好的rect中来填充渐变色:

            Rectangle rect = new Rectangle(0, 10, 100, 24);
            LinearGradientBrush brush = new LinearGradientBrush(rect, Color.LightGreen, Color.Blue, LinearGradientMode.Vertical);
            g.FillRectangle(brush, rect);

这样并看不出来这两个Rectangle有什么区别。


那么我们可以把后者Rectangle放大,宽度x2,高度x4,来看看区别

            int top = 10;
            int height = 24;
            Rectangle rect = new Rectangle(0, top, 150, height);
            LinearGradientBrush brush = new LinearGradientBrush(rect, Color.LightGreen, Color.Blue, LinearGradientMode.Vertical);

            rect.Width = 300;
            rect.Height = height * 4;
            g.FillRectangle(brush, rect);

            brush.Dispose();

效果如下:

线性渐变笔刷LinearGradientBrush的陷阱 图2

可以看到,当要填充的区域大于笔刷的区域时,会以平铺的效果显示。

(水平效果好像是拉伸了,请其实也是平铺的,只不过两个一样的笔刷连着放在一个水平线上,之间没有间隔,看起来是一个而已,其实是两个)

结论1:使用FillRectangle方法时,是使用平铺的效果来显示笔刷的,不是拉伸。

假如我们将上述的代码稍微修改,FillRectangle时将rect的Y减小1个像素:

        private void panel2_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            int top = 10;
            int height = 24;
            Rectangle rect = new Rectangle(0, top, 150, height);
            LinearGradientBrush brush = new LinearGradientBrush(rect, Color.LightGreen, Color.Blue, LinearGradientMode.Vertical);

            rect.Width = 300;
            rect.Height = height * 4;
            rect.Y -= 1;
            g.FillRectangle(brush, rect);

            brush.Dispose();

        }

得到结果:

线性渐变笔刷LinearGradientBrush的陷阱 图3

文章开头提出的现象出现了。

结论2:当FillRectangle的rect和LinearGradientBrush的rect不一致的时候,如果FillRectangle的rect的顶部多一些内容,这部分内容将用渐变的底部颜色填充。

这个是一种情况,很好理解,那么接下来的一种情况就未必了,请看下文。


能不能定义一个LinearGradientBrush在多处使用呢

假如我们想实现下图的效果,能不能用同一个LinearGradientBrush呢?

线性渐变笔刷LinearGradientBrush的陷阱 图4

我们用如下的代码实现:

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            int top = 0;
            int height = 23;
            Rectangle rect = new Rectangle(0, top, this.panel1.Width, height);
            LinearGradientBrush brush = new LinearGradientBrush(rect, Color.LightGreen, Color.Blue, LinearGradientMode.Vertical);
            g.FillRectangle(brush, rect);

            rect.Offset(0, height * 2);
            g.FillRectangle(brush, rect);

            rect.Offset(0, height * 2);
            g.FillRectangle(brush, rect);

            rect.Offset(0, height * 2);
            g.FillRectangle(brush, rect);

            brush.Dispose();

        }

可是我们却得到的是这样的效果:

线性渐变笔刷LinearGradientBrush的陷阱 图5

这个问题又出现了!

既然用同一个LinearGradientBrush不行,那我们用不同的行不行呢,再看代码:

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            int top = 0;
            int height = 23;
            Rectangle rect = new Rectangle(0, top, this.panel1.Width, height);
            LinearGradientBrush brush = new LinearGradientBrush(rect, Color.LightGreen, Color.Blue, LinearGradientMode.Vertical);
            g.FillRectangle(brush, rect);

            rect.Offset(0, height * 2);
            LinearGradientBrush brush2 = new LinearGradientBrush(rect, Color.LightGreen, Color.Blue, LinearGradientMode.Vertical);
            g.FillRectangle(brush2, rect);

            rect.Offset(0, height * 2);
            LinearGradientBrush brush3 = new LinearGradientBrush(rect, Color.LightGreen, Color.Blue, LinearGradientMode.Vertical);
            g.FillRectangle(brush3, rect);

            rect.Offset(0, height * 2);
            LinearGradientBrush brush4 = new LinearGradientBrush(rect, Color.LightGreen, Color.Blue, LinearGradientMode.Vertical);
            g.FillRectangle(brush4, rect);

            brush.Dispose();
            brush2.Dispose();
            brush3.Dispose();
            brush4.Dispose();
        }

效果如何呢?

线性渐变笔刷LinearGradientBrush的陷阱 图6

同样是不行!那么问题究竟出在哪里?

上面的代码中创建了4个笔刷,后3个有问题,为了更简便我们提取第二个来分析问题:

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            int top = 0;
            int height = 23;
            Rectangle rect = new Rectangle(0, top + height * 2, this.panel1.Width, height);

            LinearGradientBrush brush2 = new LinearGradientBrush(rect, Color.LightGreen, Color.Blue, LinearGradientMode.Vertical);
            g.FillRectangle(brush2, rect);

            brush2.Dispose();

        }

即上面的代码会得到如下的结果:

线性渐变笔刷LinearGradientBrush的陷阱 图7

我们把上面的代码改一点点,高度从23变为24

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            int top = 0;
            int height = 24;
            Rectangle rect = new Rectangle(0, top + height * 2, this.panel1.Width, height);

            LinearGradientBrush brush2 = new LinearGradientBrush(rect, Color.LightGreen, Color.Blue, LinearGradientMode.Vertical);
            g.FillRectangle(brush2, rect);

            brush2.Dispose();

        }

一下子就能正常显示了:

线性渐变笔刷LinearGradientBrush的陷阱 图8

没错,就是高度的问题引起的。


使用LinearGradientBrush的最佳做法

经测试发现,当创建LinearGradientBrush的时候如果rect高度为偶数,就不会有问题,如果是奇数,就会出现问题。

根据结论2我们可以这样理解:某些情况下,当创建高度为奇数的LinearGradientBrush笔刷时,其实创建的是高度-1的笔刷,当我们再往原来的Rectangle中填充的时候,填充区域比笔刷区域大,在顶部多了一块,那么这一块将会用渐变的底部颜色填充,所以上面有一条蓝色的线。

为什么上面要说在“某些情况下”呢?我们将代码再改一点点,把rect的Y坐标改为0:

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            int top = 0;
            int height = 23;
            Rectangle rect = new Rectangle(0, top + height * 2, this.panel1.Width, height);
            rect.Y = 0;

            LinearGradientBrush brush2 = new LinearGradientBrush(rect, Color.LightGreen, Color.Blue, LinearGradientMode.Vertical);
            g.FillRectangle(brush2, rect);

            brush2.Dispose();

        }

发现问题不存在了:

线性渐变笔刷LinearGradientBrush的陷阱 图9

实际上,把Y坐标改为1,也不会有问题。

结论3:

当我们在使用LinearGradientBrush填充rectangle的时候,最好把高度设置为偶数。

如果非要是奇数,最好把创建LinearGradientBrush的rectangle高度加1变为偶数,这样在任何情况下都不会有问题。

如下面的代码,即使在Y坐标不为0或1的时候,也能准确的画出渐变:

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            int top = 0;
            int height = 23;
            Rectangle rect = new Rectangle(0, top + height * 2, this.panel1.Width, height);

            Rectangle rectBrush = rect;
            if (rectBrush.Height % 2 == 1)
            {
                rectBrush.Height += 1;
            }

            LinearGradientBrush brush2 = new LinearGradientBrush(rectBrush, Color.LightGreen, Color.Blue, LinearGradientMode.Vertical);
            g.FillRectangle(brush2, rect);

            brush2.Dispose();

        }

效果图:

线性渐变笔刷LinearGradientBrush的陷阱 图10



本文章由创风网原创,转载请注明出处:http://www.windite.com/article/details/qti25q8