当前位置: 首页 > news >正文

C# WinForm DataGridView 非常频繁地更新或重新绘制慢问题及解决

非常频繁地更新 DataGridView问题描述:

        在 C# 中无法在合理的时间内刷新我的 DataGridView ,我每秒通过网络发送 20 个数据包,获取数据。我想解析这些数据并将其放入 DataGridView 中。我还想调整 DataGridView 的更新间隔,从 0.1 秒到 1 分钟。

        因此,我创建了一个额外的线程,用于读取包并将其解析为数组。我还创建了一个计时器,用于更改间隔。每次计时器计时,我都会将 DataSource 重新分配给 DataGridView。

        有趣的是,当我这样做时,即使我将计时器设置为 0.1 秒,它也只会每秒触发一次。如果我不刷新 DataGridView,它每秒就会触发 10 次,正如预期的那样。

        所以我认为我更新 DataGridView 的方法太耗时了。但是我该怎么做才能让它更高效,以便每秒更新 10 次而不会出现任何问题呢?

这是使用的代码:

public MyForm()
    {
        InitializeComponent();

        timer = new System.Windows.Forms.Timer();
        timer.Interval = (1 * 1000); // 1 secs
        timer.Tick += new EventHandler(timer_Tick);
        timer.Start();

        readNetworkValues = true;
        networkReader = new Thread(() =>
        {
            Thread.CurrentThread.IsBackground = true;
            byte[] data = new byte[1024];
            IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 49003);
            UdpClient newsock = new UdpClient(ipep);
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);

            while (readNetworkValues)
            {
                data = newsock.Receive(ref sender);
                dataSet = parseData(data); //Decrypts the data
            }
        newsock.Close();
        });
        networkReader.Start();
    }

    private void timer_Tick(object sender, EventArgs e)
    {
        if (dataSet != null)
        {
            lock (dataSet)
            {
                int currentRow = dataGrid.FirstDisplayedScrollingRowIndex;
                dataGrid.DataSource = dataSet;
                dataGrid.FirstDisplayedScrollingRowIndex = currentRow;
            }
        }
    }

解决方法: 

您想要更新的单元数量以及您想要的更新率足够高,从而导致闪烁和延迟。

为了避免这种情况,您可以DoubleBuffering打开DataGridView。

此属性默认不公开。因此,您可以选择创建子类或通过反射访问它,它主要针对滚动闪烁的情况,但也有助于避免更新延迟。类可能看起来像这样:

public class DBDataGridView : DataGridView
{
    public new bool DoubleBuffered
    {
        get { return base.DoubleBuffered; }
        set { base.DoubleBuffered = value; }
    }

    public DBDataGridView()
    {
        DoubleBuffered = true;
    }
}

您可以将此类添加到项目中,或者仅添加到表单类中(在最后一个花括号之前)。编译后它将显示在工具箱中。

另一个选项是使用反射;这是一个适用于任何类型控件的通用函数:

using System.Reflection;

static void SetDoubleBuffer(Control ctl, bool DoubleBuffered)
{
    typeof(Control).InvokeMember("DoubleBuffered", 
        BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, 
        null, ctl, new object[] { DoubleBuffered });
}

两种方式都可以DoubleBuffering随意打开和关闭;前者通过现在公开的属性,后者通过bool方法的参数。

DataGridView 重新绘制慢问题描述:

我知道有几个关于速度慢的 DataGridViews 的问题,确实尝试了那里提到的所有内容,大多数都是关于更复杂的数据绑定。

之前做过的事情:

    测试了 DataTable 作为数据源
    未绑定的 Datagridview,循环填充
    使用 autoColumnResizing 等测试了许多设置,但所有测试都太慢了。

此示例仅是主窗体中的一个 datgridview,来自设计器工具箱的普通库存,已编辑。

即使只有 100 行,滚动和重新绘制时也会感觉非常滞后,并且会占用一个核心的最大值。

我在 datagridview 中滚动时进行了 VS 分析器会话:

想深入挖掘并了解究竟是什么让这个控制如此缓慢,以及可以做些什么来加速它,或者可以使用什么替代方法来获得一个可以通过单击列标题进行排序的快速表。

测试代码: 

public partial class Form1 : Form
{
   List<Record> myData;
    public Form1()
    {
        InitializeComponent();
        myData = getListofRecord();

        dataGridView1.DataSource = myData;
    }

    private static List<Record> getListofRecord()
    {
        var myTable = new List<Record>();
        for (int i = 0; i < 500; i++)
        {
            myTable.Add(new Record() { a = 121+i, b = 123.2434F, c = 2342, d = 312+i*2, e = 123343, f = 12323 });
        }
        return myTable;
    }

}

public class Record
{
    public Single a { get; set; }
    public Single b { get; set; }
    public Single c { get; set; }
    public Single d { get; set; }
    public Single e { get; set; }
    public Single f { get; set; }

}

编辑1:

        两天后,我在同一台机器上重新运行测试应用时得到了不同的结果。它的速度和我预期的一样快,分析结果或许能解释热路径堆栈中缺失的原因。

编辑2:

        无论第一个差异背后的真正原因是什么,通过反射启用双缓冲的效果最为明显,大约提升了 20 倍。 我在其他 DGV 问题中也看到过类似的建议,但我原本只期望获得轻微的视觉效果提升,而不是全新的体验。

分析热路径现在显示完全不同的调用堆栈。

编辑3:

        为了保存我在这个问题中的分析工作,避免被当作重复数据删除,想获取一些以毫秒为单位的目标数据,用于重新绘制DGV。需要把秒表放在哪里才能获取这些数据?

解决方法:

其实与上面闪烁方法一致,方法就这么简单:

C#.NET:

using System.Reflection;

static void SetDoubleBuffer(Control dgv, bool DoubleBuffered)
{
    typeof(Control).InvokeMember("DoubleBuffered", 
        BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, 
        null, dgv, new object[] { DoubleBuffered });
}

VB.NET:

 Public Sub DoubleBuffered(ByVal dgv As DataGridView, ByVal setting As Boolean)
      Dim dgvType As Type = dgv.[GetType]()
      Dim pi As PropertyInfo = dgvType.GetProperty("DoubleBuffered", BindingFlags.Instance Or BindingFlags.NonPublic)
      pi.SetValue(dgv, setting, Nothing)
   End Sub

 如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

相关文章:

  • WPF 性能 UI 虚拟化 软件开发人员的思考
  • gvm安装go报错ERROR: Failed to use installed version
  • C++GO语言微服务之用户信息处理
  • 深圳SMT贴片加工厂制造流程解析
  • 4.分布式锁
  • Pale Moon:速度优化的Firefox定制浏览器
  • vue访问后端接口,实现用户注册
  • 【金仓数据库征文】_金仓数据库在金融行业的两地三中心容灾架构实践
  • Linux 内核链表宏的详细解释
  • 前端开发实战:用React Hooks优化你的组件性能
  • 缓存理论到实战:技术选型与七层架构设计
  • Windows 系统 - Trae 内 终端 无法使用 node (重新配置 nodejs 路径)
  • RT-Thread 深入系列 Part 7:RT-Thread vs 其他 RTOS 对比与选型指南
  • 图像处理篇---opencv实现坐姿检测
  • Kotlin高阶函数多态场景条件判断与子逻辑
  • 腾讯多模态定制化视频生成框架:HunyuanCustom
  • C语言的中断 vs Java/Kotlin的异常:底层机制与高级抽象的对比
  • android HashMap和List该如何选择
  • 05 mysql之DDL
  • 通俗的理解MFC消息机制
  • 美国长滩港货运量因关税暴跌三成,港口负责人:货架要空了
  • 网红街区如厕难,如何多管齐下补缺口?
  • 4月金融数据前瞻:受去年低基数因素影响,社融增量有望同比大幅多增
  • 国家发改委:美芯片药品等领域关税影响全球科技发展,损害人类共同利益
  • 比特币价格时隔三个月再度站上10万美元
  • 马上评|让“贾宝玉是长子长孙”争议回归理性讨论