1.窗体打开时防止窗体闪烁
//C# 窗体程序,窗体上控件过多,会导致打开程序时窗体闪烁,下面有个不错的方法
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000;
return cp;
}
}
2.禁掉清除背景消息
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0014) // 禁掉清除背景消息
return;
base.WndProc(ref m);
}
3.设置双缓存
public Form1()
{
InitializeComponent();
//根据我的理解,每个窗体的这地方加上以下几行代码就行了
this.DoubleBuffered = true;//设置本窗体
//采用双缓冲技术的控件必需的设置
//SetStyle(ControlStyles.UserPaint, true);
//SetStyle(ControlStyles.ResizeRedraw, true);
//SetStyle(ControlStyles.DoubleBuffer, true); // 双缓冲
//SetStyle(ControlStyles.SupportsTransparentBackColor, true);
//SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景.
//SetStyle(ControlStyles.EnableNotifyMessage, true);
//SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(
ControlStyles.UserPaint//使用自定义的绘制方式
| ControlStyles.ResizeRedraw//当控件大小发生变化时就重新绘制
| ControlStyles.DoubleBuffer// 双缓冲
| ControlStyles.SupportsTransparentBackColor//则控件接受 alpha 组件数小于 255 个的 BackColor 来模拟透明度
| ControlStyles.AllPaintingInWmPaint//禁止擦除背景.则控件忽略窗口消息 WM_ERASEBKGND 以减少闪烁
| ControlStyles.EnableNotifyMessage//
// Enable the OnNotifyMessage event so we get a chance to filter out
// Windows messages before they get to the form's WndProc
| ControlStyles.OptimizedDoubleBuffer//则控件将首先绘制到缓冲区而不是直接绘制到屏幕,这可以减少闪烁
, true);
}
4.界面重绘
Image curChartImage;//为当前控件/窗体生成截图
private void DrawRectBackImage(Control c, int xStart, int yStart, int Width, int Height)
{
Bitmap bit = new Bitmap(c.Width, c.Height);//实例化一个和窗体一样大的bitmap
Graphics g = Graphics.FromImage(bit);
g.CompositingQuality = CompositingQuality.HighQuality;//质量设为最高
g.CopyFromScreen(this.Location.X +8+ c.Left, this.Location.Y+30 + c.Top, 0, 0, new Size(c.Width, c.Height));//保存整个窗体为图片
curChartImage = bit;
}
在内存中绘好图之后,绘图到界面
int xStart = 150, yStart = 150, xWidth = 100, yHeight = 100;
//虚拟时钟
//在内存中保存图像,直接刷新到界面
double num = 0;
private void timer1_Tick(object sender, EventArgs e)
{
if (curChartImage == null)
{
DrawRectBackImage(panel1, xStart, yStart, 2 * xWidth, 2 * yHeight);
}
if (panel1.Width == 0)
return;
Bitmap memoryCanvas = new Bitmap(panel1.Width, panel1.Height);
double x = xStart + xWidth * Math.Sin(num * Math.PI / 180);
double y = yStart - yHeight * Math.Cos(num * Math.PI / 180);
tsmi_tip.Text = Convert.ToString((num = num + 0.3));
Graphics memDc = Graphics.FromImage(memoryCanvas);
memDc.SmoothingMode = SmoothingMode.HighQuality;
memDc.DrawImage(curChartImage, 0, 0, panel1.Width, panel1.Height);
Pen p1 = new Pen(Color.Black, 1);
memDc.DrawLine(p1, (float)xStart, (float)yStart, (float)x, (float)y);
Graphics g = panel1.CreateGraphics();
g.DrawImage(memoryCanvas, xStart, yStart - 20, 2 * xWidth, 2 * yHeight);
panel1.BackgroundImage = memoryCanvas;
memDc.Dispose();
}
最近项目中使用Chat控件,根据接收到的串口数据实时绘制数据曲线,但是在绘制曲线时,绘图区闪烁严重,网上找了很多方法,都不起作用,双缓存也不起作用,最后使用
protected override void WndProc(ref Message m) {
if (m.Msg == 0x0014) // 禁掉清除背景消息
return;
base.WndProc(ref m);
}
成功。
使用该方法时也不能用双缓存,如果使用双缓存,该方法也不起作用,具体什么原因,还不能理解。
5、C# winform 局部刷新
做winform界面程序时,经常会遇到后台处理占用大量时间的情况,这就会造成界面假死状态。一般解决界面假死有两种方式:要么把占用大量时间的处理方式放入其他线程;要么把界面显示放入其他线程。第一种方式应该比较简单,开单独的线程,处理数据,将处理数据显示到界面就好。但是我们经常需要在主程序运算一些内容,否则可能会改动比较大。因此,这里讲讲第二种方式。
同样是使用多线程,但是c#在其他线程刷新有一点点问题,即不能跨线程操作界面。这可以使用控件的Invoke方法解决:
复制代码
private delegate void CrossThread();
Control control = ....;
CrossThread cross = delegate()
{
control.Refresh();
};
control.Invoke(cross);
复制代码
这样可以让控件在其它线程刷新界面。
再加上开新线程后的通用方法:
复制代码
private void InvaliateControl(Control control)
{
Thread t = new Thread(
new ThreadStart(delegate()
{
CrossThread cross = delegate()
{
control.Refresh();
};
control.Invoke(cross);
}
));
}
复制代码
这样就可以在任何时候,调用此方法对控件进行刷新,而不将整个界面刷新。如果对于同一个控件,连续多次刷新,可以添加一个成员变量作为标记,以免同一控件连续多次刷新,提升部分性能。
补充:在主线程调用耗时操作用此方法可能会有问题,经过验证调用Invoke函数,其实是在主线程刷新界面。