滚动 DataGridView 似乎会大大降低性能
Posted
技术标签:
【中文标题】滚动 DataGridView 似乎会大大降低性能【英文标题】:Scrolling DataGridView seems to bring down performance drastically 【发布时间】:2021-04-05 10:16:03 【问题描述】:我正在使用 Microsoft Visual Studio 2019 用 C# 编写一个应用程序。该应用程序与多个 Arduino 板通信。使用 TAP 模型异步发送和接收工作,并且工作正常。
该应用程序基于 Windows 窗体应用程序,使用 .NET Framework 4.7.2。我将 DataGridView 添加到表单中,使用 DataTable 作为 DataSource。目的是将此 DataGridView 用作数据记录器,显示 5 列:TimeStamp、DeviceID、Direction、Command 和 ErrorStatus。
如果我禁用 DataGridView,我会在中文 Arduino 克隆上达到每秒 500 条命令。在真正的 Arduino 上,我似乎每秒最多只能获得 244 个命令 - 请参阅下面关于 *** 的其他问题 - 但这不是现在的问题:
Communication speed over USB (PC/Arduino) using SerialPort in C# seems to “clip”
当我启用我的 DataGridView 时,我看到通信速度下降到大约 25 个命令/秒,这纯粹是因为更新了 DataGridView 中的行。但这似乎只是 DataGridView 开始滚动时的情况。
见下面代码sn-p:
dt.Rows.Add(new string[]
DateTime.Now.ToString("HH:mm:ss:fff"),
device._FMGSDevice,
action,
notifyData.Command,
notifyData.notifyError.ToString()
);
if (dt.Rows.Count > maxLines)
dt.Rows.RemoveAt(0);
dt 是用作 DataGridView 的 DataSource 的 DataTable。对于每次通信,都会在 DataTable 的末尾添加一行,然后在 DataGridView 中自动更新。
maxLines 是当前设置为 500 的常数。为了避免我的 DataGridView 获得太多行,我将其限制为 500 行。如果达到限制,我会在添加新行后使用“RemoveAt(0)”删除第一行,以使其保持在最多 500 行。
我现在看到,一旦 DataGridView 开始滚动(“RemoveAt(0)”导致所有行都向上移动一行),速度会急剧下降。
有人知道如何加快滚动速度吗? 或者还有其他项目可以用来记录吗? (虽然,我也需要过滤)。
【问题讨论】:
DGV 不会是我实现此日志记录的首选。你是在窗口线程上发送命令吗? 也许你必须有一个列表/表格来保存数据,但在网格中只显示 20 或 30 行。滚动时,然后更新网格。 试试DataGridView.VirtualMode和Implementing Virtual Mode in the Windows Forms DataGridView Control 尝试双缓冲 DGV! @CaiusJard 不,通信使用基于异步任务的实现。 【参考方案1】:我不完全了解您的应用程序,但您可以尝试使用 delete 方法而不是 RemoveAt,看看性能是否有所提高。应该是:
DataRow dr = dtName.Rows[0];
dr.Delete();
dtName.AcceptChanges();
或者,您可以增加要删除的记录数,例如删除前 10 或 20 条记录,而不是仅删除第一个。
【讨论】:
我尝试了你的两个建议。使用 Delete() 和 AcceptChanges 而不是 RemoveAt() 似乎不会带来任何性能提升。我还使用这两种方法一次尝试了 10 行和 20 行,这也没有给我任何改变。无论如何感谢您的反馈,非常感谢。【参考方案2】:@TaW 的建议到目前为止效果最好。至少,它是最容易实现的。我还没有尝试过虚拟模式,也没有尝试过 ListView,因为这些都需要相当长的时间来实现。
我从 DataGridView 创建了一个派生类,如下所示(请参阅我找到这个的链接):
Horrible redraw performance of the DataGridView on one of my two screens
class CustomDataGridView : DataGridView
public CustomDataGridView()
// if not remote desktop session then enable double-buffering optimization
if (!System.Windows.Forms.SystemInformation.TerminalServerSession)
DoubleBuffered = true;
因为我使用的是 Designer,所以我手动更改了那里的代码以使用 CustomDataGridView 类。
this.dgLogView = new HW_Hub.CustomDataGridView();
虽然我不确定,是否允许更改“...designer.cs”中的代码?我感觉 Visual Studio“拥有”这个文件,并自动进行更改。我是否需要另一种方式来否决标准实施?
【讨论】:
"...designer.cs"?我感觉 Visual Studio“拥有”这个文件有一个部分是保留的,但其余部分可以更改,只要您不破坏语法即可。保留区域标记为这样。 Visual Studio 将您添加到表单设计器的内容序列化。如果CustomDataGridView
自定义控件是可访问的(序列化程序可以找到它),您在Designer.cs
文件中所做的更改将保持不变,并且替换可视化设计器中的控件。顺便说一句,这种用另一个替换控件的方式很常见。该警告与添加到 designer.cs
的代码有关,该代码不受序列化的影响:当然,该代码不会持续存在,因此当序列化程序启动时,所有更改都将丢失。【参考方案3】:
考虑不要自动更新数据。人们将阅读数据,也许会选择其中的一部分。如果您刚刚选择的数据滚动消失,那是相当烦人的。
这类似于编辑文档,您正在阅读或刚刚选择的部分会突然消失。
添加一个“刷新”按钮,它将刷新数据。
如果您认为数据的实时更新很重要,以便操作员可以看到哪些数据发生了变化,请考虑使用计时器。如果计时器到时,请刷新数据。
类似这样的:
// the data in one Row:
class ArduinoLogData
... // properties that will be shown in the columns
BindingList<ArduinoLogData> DisplayedArduinoData
get => (BindingList<LoggedArduinoRow>)this.DataGridView1.DataSource;
set => this.DataGridView1.DataSource = value;
// Fetch ArduinoData
IEnumerable<ArduinoLogData> FetchArduinoLogdata()
...
考虑创建一个重载,在其中声明必须获取哪些 arduino,因此您只能更新当前可见的 arduino。使用 DataGridViewRow.Displayed 获取当前显示的 ArduinoLogData:
IEnumerable<ArduinoLogData> DisplayedData => this.DataGridView1.Rows
.Where(row => row.Displayed)
.Select(row => row.DataBoundItem)
.Cast<ArduinoLogData>();
还有一个方法:
void RefreshArduinoLogData(ICollection<ArduinoLogData> itemsToRefresh)
... // update only these items.
void RefreshDisplayedArduinoLogData()
this.RefreshArduinoLogData(this.DisplayedData.ToList());
这将只更新可见项目,大约 40 行左右。
每秒执行一次:
Timer refreshTime = new Timer
AutoReset = true,
Interval = TimeSpan.FromSeconds(1).TotalMilliseconds,
;
timer.Elapsed += OnTimerTick;
timer.Enabled = true;
void OnTimerTick(object sender, ...)
this.RefreshDisplayedArduinoLogData();
不要忘记在您的表单关闭时或最后在表单被处置时处置您的计时器。
【讨论】:
以上是关于滚动 DataGridView 似乎会大大降低性能的主要内容,如果未能解决你的问题,请参考以下文章
UITableViewCell 中嵌套的 UIStackViews,会减慢滚动速度,降低性能。如何提高性能?
将 UIScrollView 添加到表格单元格似乎会影响表格滚动性能