• 全部
  • C#综合技术
  • C#互联网桌面应用
  • AppLauncher
  • WinForm&WPF
  • C#开发新技术
  • 问答

重绘Panel控件,闪烁过于厉害,怎么解决?

bineon 2006-05-06 11:41:48
最近试用msn8的时候,感觉其界面很清爽的样子,因此想自己绘制。首先是绘制一个自定义的Panel。基本思路是给Panel加上边框,然后里面区域的颜色依据边框颜色进行计算,然后绘制内部区域。
内部区域绘制时,为了模拟水晶效果,将整个panel分为上下两个部分,分别用渐变填充。
效果是实现了,但是不是很理想,尤其是闪烁的问题,过于严重,根本无法接受。代码如下,请各位指点。
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace Bineon.SystemFramework.Windows.Forms.MSNStyle
{
public class PanelEx : Panel
{
private bool m_LeftBorder = true;

public PanelEx()
{
base.ResizeRedraw = true;
//base.DoubleBuffered = true;
}
public bool LeftBorder
{
get { return m_LeftBorder; }
set { m_LeftBorder = value; }
}
private bool m_RightBorder = true;

public bool RightBorder
{
get { return m_RightBorder; }
set { m_RightBorder = value; }
}
private bool m_TopBorder = true;

public bool TopBorder
{
get { return m_TopBorder; }
set { m_TopBorder = value; }
}
private bool m_BottomBorder = true;

public bool BottomBorder
{
get { return m_BottomBorder; }
set { m_BottomBorder = value; }
}

private int m_OnePart = 70;

public int OnePart
{
get { return m_OnePart; }
set
{
m_OnePart = value > 100 ? 100 : value;
m_OnePart = value < 1 ? 1 : value;
}
}

private Color m_BorderColor = Color.Silver; //正常状态下的边框颜色

public Color BorderColor
{
get { return m_BorderColor; }
set
{
m_BorderColor = value;
}
}

public new BorderStyle BorderStyle
{
get { return BorderStyle.None; }
set { base.BorderStyle = BorderStyle.None; }
}

protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (!this.m_Clarity)
{
Color color = this.BorderColor;
Color start = Color.FromArgb(this.m_StartAlpha, color);
Color end = Color.FromArgb(this.m_EndAlpha, color);
this.PaintBorder(color);
this.PaintRegion(start, end);
}
}

/// <summary>
/// 绘制边框
/// </summary>
/// <param name="e"></param>
protected virtual void PaintBorder(Color color)
{
Pen pen = new Pen(color);
Graphics g = this.CreateGraphics();
g.SmoothingMode = SmoothingMode.AntiAlias;
Rectangle border = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
Point[] points = this.GetPoints(border);
if (this.LeftBorder)
{
g.DrawLine(pen, points[0], points[3]);
}
if (this.TopBorder)
{
g.DrawLine(pen, points[0], points[1]);
}
if (this.RightBorder)
{
g.DrawLine(pen, points[1], points[2]);
}
if (this.BottomBorder)
{
g.DrawLine(pen, points[2], points[3]);
}
}

/// <summary>
/// 绘制填充区域
/// </summary>
protected virtual void PaintRegion(Color start, Color end, float angle)
{
Graphics g = this.CreateGraphics();
g.SmoothingMode = SmoothingMode.AntiAlias;
Brush brush;

brush = new LinearGradientBrush(this.ClientRectangle, end, start, angle);
RectangleF top = new RectangleF(1, 1, this.Width - 3, (this.Height - 2) * this.OnePart / 100);
g.FillRectangle(brush, top);

brush = new LinearGradientBrush(this.ClientRectangle, start, end, angle);
RectangleF down = new RectangleF(top.Left, top.Bottom, (float)(this.Width - 3), (float)(this.Height - top.Height - 3));
g.FillRectangle(brush, down);
}

protected virtual void PaintRegion(Color start, Color end)
{
float angle = 90F;
PaintRegion(start, end, angle);
}

/// <summary>
/// 获取矩形顶点数组
/// </summary>
/// <param name="rect"></param>
/// <returns></returns>
protected Point[] GetPoints(Rectangle rect)
{
return new Point[]
{
new Point(rect.Left, rect.Top), //左上顶点
new Point(rect.Right, rect.Top), //右上顶点
new Point(rect.Right, rect.Bottom), //右下顶点
new Point(rect.Left, rect.Bottom) //坐下顶点
};
}

private bool m_Clarity = false;

public bool Clarity
{
get { return m_Clarity; }
set { m_Clarity = value; }
}

private byte m_StartAlpha = 40;
private byte m_EndAlpha = 30;
private Color m_StartColor;
private Color m_EndColor;

}
}
...全文
2108 点赞 收藏 24
写回复
24 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
阿浩No_1 2006-10-27
学习
回复
Knight94 2006-05-08
已发送,注意查收!
回复
bineon 2006-05-08
楼上的大哥你能把03下的弄成dll发送到我邮箱吗?
bineon (#) hotmail.com
回复
甜味牛牛 2006-05-07
如果是.NET2.0的话用DoubleBuffer
回复
Knight94 2006-05-07
这是由于Color.Black和Color.Blue这两种颜色的alpha值为0。
你出现闪烁的原因,是由于alpha值不为0造成。
但是很奇怪,我在vs2003中没有任何问题,不管alpha值是否为0都可以。
回复
webwait 2006-05-07
mark
回复
bineon 2006-05-07
Color start = Color.Black;//Color.FromArgb(this.m_StartAlpha, color);
Color end = Color.Blue;//Color.FromArgb(this.m_EndAlpha, color);
修改为这样的代码后就没有问题了。估计是这里的原因。


楼上两位能解释一下吗?如果用FromArgb方法则闪烁,直接定义为已知颜色则不闪烁。
回复
没看你的代码。

当拖动窗口的时候,应该将背景“成块”地二进制复制,而不是重新按照业务逻辑绘制。

当放大窗口size的时候,也不应该按照业务逻辑重新绘制。特别是选择“渐变”,可能就是在设计时期没有清醒认识到“不能重绘只能复制”这个原则,所以是需求描述到设计的转换中的描述错误。
回复
Knight94 2006-05-07
2003中,没有发现闪动很厉害的现象,每操作一下会闪一下。
回复
bineon 2006-05-07
Color start = Color.Black;//Color.FromArgb(this.m_StartAlpha, color);
Color end = Color.Blue;//Color.FromArgb(this.m_EndAlpha, color);
修改为这样的代码后就没有问题了。估计是这里的原因。
回复
bineon 2006-05-07
问题依旧。但是这个代码应该在03下也可以测试的吧?大哥能否在03下尝试实现一下?很简单的功能哦。
回复
Knight94 2006-05-07
在OnPaint事件中,开头增加一句this.SuspendLayout(),最后增加一句this.ResumeLayout()试试。

没装vs2005,这东西太吃资源了。
回复
bineon 2006-05-07
这个是简洁点的代码,但是依然还是闪烁。
m_OnePart表示将Panel区域分块,上面一块占用的百分比。程序将Panel分两块进行绘制。这在PaintRegion中实现。
另外,区域中的颜色由Border的颜色计算其Alpha来获取。
private byte m_StartAlpha = 40;
private byte m_EndAlpha = 30;
用上面两个变量定义了起始和结束分量。
public class PanelEx : Panel
{
public PanelEx()
{
base.ResizeRedraw = true;
}

private int m_OnePart = 70;

public int OnePart
{
get { return m_OnePart; }
set
{
m_OnePart = value > 100 ? 100 : value;
m_OnePart = value < 1 ? 1 : value;
}
}

private Color m_BorderColor = Color.Silver; //正常状态下的边框颜色

public Color BorderColor
{
get { return m_BorderColor; }
set
{
this.m_StartColor = Color.FromArgb(this.m_StartAlpha, value);
this.m_EndColor = Color.FromArgb(this.m_EndAlpha, value);
m_BorderColor = value;
}
}

public new BorderStyle BorderStyle
{
get { return BorderStyle.None; }
set { base.BorderStyle = BorderStyle.None; }
}

protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
Color color = this.BorderColor;
Color start = Color.FromArgb(this.m_StartAlpha, color);
Color end = Color.FromArgb(this.m_EndAlpha, color);
this.PaintBorder(ref g, color);
this.PaintRegion(ref g, start, end);

}

/// <summary>
/// 绘制边框
/// </summary>
/// <remarks>创建人员(日期): Bineon(060428 12:57)</remarks>
/// <param name="e"></param>
protected virtual void PaintBorder(ref Graphics g, Color color)
{
Pen pen = new Pen(color);
g.SmoothingMode = SmoothingMode.HighQuality;
Rectangle border = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
Point[] points = this.GetPoints(border);
g.DrawRectangle(pen, border);
}

/// <summary>
/// 绘制填充区域
/// </summary>
/// <remarks>创建人员(日期): Bineon(060428 16:55)</remarks>
protected virtual void PaintRegion(ref Graphics g, Color start, Color end, float angle)
{
g.SmoothingMode = SmoothingMode.HighQuality;
Brush brush;

brush = new LinearGradientBrush(this.ClientRectangle, end, start, angle);
RectangleF top = new RectangleF(1, 1, this.Width - 3, (this.Height - 2) * this.OnePart / 100);
g.FillRectangle(brush, top);

brush = new LinearGradientBrush(this.ClientRectangle, start, end, angle);
RectangleF down = new RectangleF(top.Left, top.Bottom, (float)(this.Width - 3), (float)(this.Height - top.Height - 3));
g.FillRectangle(brush, down);
}

protected virtual void PaintRegion(ref Graphics g, Color start, Color end)
{
float angle = 90F;
PaintRegion(ref g, start, end, angle);
}

/// <summary>
/// 获取矩形顶点数组
/// </summary>
/// <param name="rect"></param>
/// <returns></returns>
protected Point[] GetPoints(Rectangle rect)
{
return new Point[]
{
new Point(rect.Left, rect.Top), //左上顶点
new Point(rect.Right, rect.Top), //右上顶点
new Point(rect.Right, rect.Bottom), //右下顶点
new Point(rect.Left, rect.Bottom) //坐下顶点
};
}

private byte m_StartAlpha = 40;
private byte m_EndAlpha = 30;
private Color m_StartColor;
private Color m_EndColor;

}
回复
bineon 2006-05-07
启用doublebuffer就不能绘制了。准确的说,启用doublebuffer只有,调整窗体大小的时候可以看到闪烁的绘制。但是窗体调整大小完毕以后则恢复为默认样式了。
回复
bineon 2006-05-07
对。2005。能够绘制啊。
回复
Knight94 2006-05-07
你用的是vs2005?
回复
Knight94 2006-05-07
很奇怪,我测试了一下你的代码,根本就没画出来,你的代码需要设置哪些参数,除了
private Color m_StartColor;
private Color m_EndColor;
这两个变量外
回复
bineon 2006-05-07
替换以后问题依旧。不过你的指点是很必要的,我看到教程里面说CreateGraphics获取的g最好不要用于绘图。
但是,怎么解决闪烁问题呢?全部源代码在这里,能有人帮助测试一下吗?
回复
Knight94 2006-05-07
把如下三个函数
protected virtual void PaintBorder(Color color)
protected virtual void PaintRegion(Color start, Color end, float angle)
protected virtual void PaintRegion(Color start, Color end)
用这三个来替换
protected virtual void PaintBorder(ref Graphics g, Color color)
protected virtual void PaintRegion( ref Graphics g, Color start, Color end, float angle)
protected virtual void PaintRegion(ref Graphics g, Color start, Color end)

同时要删除创建Graphics对象的部分,在OnPaint事件中如下调用。
Graphics g = e.Graphics;
PaintBorder( ref g, color);
PaintRegion( ref g, start, end);

回复
bineon 2006-05-07
如果是.NET2.0的话用DoubleBuffer
-------------------------------
尝试过,但是结果不能正确绘制图像。于是注释了该语句。

试试
public PanelEx()
{
this.SetStyle(ControlStyles.UserPaint,true);
this.SetStyle(ControlStyles.DoubleBuffer,true);
}
---------------------
测试过,不行。也不知道为什么。。。

to 但是不是很理想,尤其是闪烁的问题,过于严重,根本无法接受

出现闪烁,是一直闪烁,还是做某些操作时候闪烁。
-------------------------
调整大小时闪烁。被其他窗体盖住了,移开其他窗体时重绘,闪烁,。
回复
发帖
C#
创建于2007-09-28

10.5w+

社区成员

.NET技术 C#
申请成为版主
帖子事件
创建了帖子
2006-05-06 11:41
社区公告

让您成为最强悍的C#开发者