C# Winforms 中的 UI 渲染缓慢

Posted

技术标签:

【中文标题】C# Winforms 中的 UI 渲染缓慢【英文标题】:Slow UI rendering in C# Winforms 【发布时间】:2017-01-30 19:45:09 【问题描述】:

我正在制作一个相对基本的WinForms 用户控件,其中包含许多按钮,每个按钮代表数据库中的一个表。

在调用InitializeComponent方法后,每个表中的数据被加载到对应的DataGridView中。

当单击上述按钮之一时,其对应的DataGridView 以及那些通过 FK 链接到主按钮的代表表变得可见,然后 BackgroundWorker 逐渐将它们增加到所需的大小并将控件转移到适当的位置在短时间内。

public uc1()

    InitializeComponent();

    PopulateTables();

    tableResizer.WorkerReportsProgress = true;
    tableResizer.WorkerSupportsCancellation = true;
    tableResizer.DoWork += tableResizer_DoWork;
    tableResizer.ProgressChanged += tableResizer_ProgressChanged;
    tableResizer.RunWorkerCompleted += tableResizer_RunWorkerCompleted;

    dgv5.SelectionChanged += show5Relations;


private void btn5_Click(object sender, EventArgs e)

    mainTable = 5;
    btn5.Visible = false;
    btn4.Visible = false;
    btn3.Visible = false;
    btn2.Visible = false;
    dgv5.Visible = true;
    dgv4.Visible = true;
    dgv3.Visible = true;
    dgv2.Visible = true;
    lbl6.Visible = false;
    lbl7.Visible = false;
    lbl1.Visible = false;
    tableResizer.RunWorkerAsync();


private void tableResizer_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)

    int expansion = 0;
    while (expansion < 30)
    
        tableResizer.ReportProgress(mainTable);
        expansion++;
        System.Threading.Thread.Sleep(16);
    


private void tableResizer_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)

    if (e.ProgressPercentage == 5)
    
        dgv5.Size = new Size(dgv5.Size.Width + (((this.Width / 26) * 10) / 30), dgv5.Size.Height + (((this.Height / 24) * 10) / 30));
        dgv5.Location = new Point(dgv5.Location.X - (((this.Width / 26) * 10) / 60), dgv5.Location.Y - (((this.Height / 24) * 10) / 60));

        dgv4.Size = new Size(dgv4.Size.Width + (((this.Width / 26) * 10) / 30), dgv4.Size.Height + (((this.Height / 24) * 10) / 50));
        dgv4.Location = new Point(dgv4.Location.X - (((this.Width / 26) * 10) / 40), dgv4.Location.Y - (((this.Height / 24) * 10) / 120));

        dgv3.Size = new Size(dgv3.Size.Width + (((this.Width / 26) * 10) / 30), dgv3.Size.Height + (((this.Height / 24) * 10) / 50));
        dgv3.Location = new Point(dgv3.Location.X - (((this.Width / 26) * 10) / 40), dgv3.Location.Y - (((this.Height / 24) * 10) / 120));

        dgv2.Size = new Size(dgv2.Size.Width + (((this.Width / 26) * 10) / 30), dgv2.Size.Height + (((this.Height / 24) * 10) / 50));
        dgv2.Location = new Point(dgv2.Location.X - (((this.Width / 26) * 10) / 40), dgv2.Location.Y - (((this.Height / 24) * 10) / 120));

        btn1.Location = new Point(btn1.Location.X - (((this.Width / 26) * 10) / 130), btn1.Location.Y);
    


private void tableResizer_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)

    if (mainTable == 5)
    
        btn7.Visible = true;
        btn6.Visible = true;
        btn1.Visible = true;
        lbl5.Visible = true;
        lbl5.Location = new Point(dgv5.Location.X, dgv5.Location.Y - lbl5.Size.Height);
        lbl4.Visible = true;
        lbl4.Location = new Point(dgv4.Location.X, dgv4.Location.Y - lbl4.Size.Height);
        lbl3.Visible = true;
        lbl3.Location = new Point(dgv3.Location.X, dgv3.Location.Y - lbl3.Size.Height);
        lbl2.Visible = true;
        lbl2.Location = new Point(dgv2.Location.X, dgv2.Location.Y - lbl1.Size.Height);
    

问题在于,当tableResizer 工作时,UI 的渲染速度不够快,因此,DataGridViews 之一在工作完成之前无法完全可见。

DoWork 方法中增加睡眠的长度仅在 60 毫秒时解决了这个问题,此时渲染太不稳定而无法接受。任何超过 30 毫秒的都不会做。

我对多线程和 C# 比较陌生,所以我很确定有更有效的方法来解决这个问题。

我应该怎么做才能保证流畅的渲染?

【问题讨论】:

如果您使用BackgroundWorker 只是动画,那么请改用System.Timers.TimerSystem.Threading.Timer 【参考方案1】:

我找到了解决问题的方法。虽然我之前曾尝试将用户控件的DoubleBuffered 设置为true,但它没有帮助。问题是 DataGridViews 默认关闭它,因为它是受保护的属性,所以不能设置为 true。

在进一步调查这个问题(我应该早点做,我很抱歉)后,我发现了这个已回答的问题: Horrible redraw performance of the DataGridView on one of my two screens

解决方案非常简单。创建一个继承自默认DataGridView 的类并将其DoubleBuffered 属性分配为true。

class CustomDataGridView: DataGridView

    public CustomDataGridView()
    
        DoubleBuffered = true;
     

【讨论】:

以上是关于C# Winforms 中的 UI 渲染缓慢的主要内容,如果未能解决你的问题,请参考以下文章

GDI+ DrawImage 在 C++ (Win32) 中比在 C# (WinForms) 中慢得多

如何将 UI 与 C# 中的渲染分开?

c# 中的解决方案架构,其中 winforms 和 asp.mvc 共享业务逻辑和数据访问 [关闭]

WinForms 中的每个表单都有自己的线程吗?

在 C# (WinForms) 中拦截应用程序中所有控件的单击事件

另一个关于 Win32 Vs UI 自动化的文章