c#winform控件过多卡顿问题解决方案

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函数,其实是在主线程刷新界面。

 
 

你可能感兴趣的:(C#,Winform,c#,开发语言)