我有一个正在处理的简单 WPF 应用程序,它执行 SQL 查询并将结果数据显示在 DataGrid 中。
一切都按预期工作,除了性能很糟糕。从单击按钮加载数据到实际看到数据显示在 DataGrid 中的时间长度大约为 3-4 秒。打开行虚拟化会快一点,但我不得不将其关闭,因为我需要能够对滚动后不再可见的单元格执行操作。即使启用了虚拟化,显示数据的速度也比我希望的要慢。
我首先假设是 SQL 数据库运行缓慢,但我做了一些测试,发现我在几分之一秒内将 SQL 服务器(数百行)中的所有数据读取到 DataTable 中。直到我将 DataTable 绑定(bind)到 DataGrid 的 DataContext 后,所有内容才锁定了几秒钟。
那么为什么 DataContext 这么慢?我的计算机是全新的,所以我很难理解为什么要花这么长时间来填写 DataGrid,考虑到我首先检索数据的速度有多快。
(我还尝试绑定(bind)到 DataGrid 的 ItemSource 而不是 DataContext,但性能是一样的。)
是否有其他方法可以将数据加载到具有更合理性能的 DataGrid 中?如果需要,甚至 DataGrid 的替代方案也值得探索。
编辑:在 Vlad 的建议下,我尝试了另一个绕过 SQL 查询的测试。我用 1000 行随机生成的数据填充了 DataTable。没变。数据在一秒钟内生成并写入 DataTable。但是,将其附加到 DataGrid 花费了 20 多秒。
下面是我正在使用的 DataGrid XAML。除了绑定(bind)之外,它非常简单,没有附加自定义代码。
<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False" Name="dataGridWellReadings" GridLinesVisibility="None" CanUserResizeRows="False" SelectionUnit="Cell" AlternatingRowBackground="#FFE0E0E0" RowBackground="#FFF0F0F0" HorizontalScrollBarVisibility="Disabled" SelectedCellsChanged="dataGridWellReadings_SelectedCellsChanged" EnableRowVirtualization="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Date" Binding="{Binding readingDate, StringFormat=yyyy-MM-dd}" Width="3*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Pt" Binding="{Binding readingPt, StringFormat=0.#}" Width="2*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Pc" Binding="{Binding readingPc, StringFormat=0.#}" Width="2*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Ppl" Binding="{Binding readingPpl, StringFormat=0.#}" Width="2*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="MCFD" Binding="{Binding readingMCFD, StringFormat=0.#}" Width="2*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Water Produced" Binding="{Binding readingWaterProduced, StringFormat=0.#}" Width="3*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Water Hauled" Binding="{Binding readingWaterHauled, StringFormat=0.#}" Width="3*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Temperature" Binding="{Binding readingTemperature, StringFormat=0.#}" Width="3*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Hours On (actual)" Binding="{Binding readingHoursOnActual, StringFormat=0.#}" Width="3*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Hours On (planned)" Binding="{Binding readingHoursOnPlanned, StringFormat=0.#}" Width="3*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Clock Cycles" Binding="{Binding readingClockCycles, StringFormat=0.#}" Width="3*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
最佳答案
变量太多,无法确定地回答这个问题。但是,您需要考虑以下几点:
您为网格提供的数据量是否必要?您是否可能给它提供了比用户实际使用的数据太多的数据?这会减慢速度。
您是否使用过多模板渲染列或单元格?这使您的演示文稿变得灵活,我知道,但是过多的模板(或控件)会降低速度。
您的数据网格中是否有很多值转换器?是否要求每一行或每一列都运行一些稍微昂贵的代码才能呈现?
您是否有很多嵌套样式(使用 BasedOn),也许更重要的是,这些样式中的许多触发器会占用渲染时间来应用?
您是否在演示文稿中使用了大量用户控件,尤其是嵌套控件,这可能会导致演示文稿呈现延迟?
您为细胞使用的绑定(bind)字符串是否复杂?应用许多 StringFormat 或 ElementName 或 Ancestory 查找?这些会导致渲染速度变慢。
是否有可能使用视觉画笔来显示比用户立即可见的更多的数据?这会使虚拟化逻辑短路。
您是否考虑过对您的绑定(bind)使用 FallBackValue 并将 IsAsync 设置为 true?将此设置为 tru 将显示 FallBackValue,直到数据准备就绪。
您是否在 UI 中使用了许多 MultiBindings 或 PriorityBindings,这可能会导致渲染速度变慢,因为它处理多个字段或值?
您使用的样式是否复杂?尤其是渐变画笔,渲染它们的成本可能很高,尤其是当您在网格中的每一行都进行渲染时。
您是否考虑过使用分页来减少数据量?最后,奈柔,如果你能让用户接受,这就是解决这类问题的最佳方案。
您是否正在查看内存和 CPU 使用情况?您使用的硬件是否有可能只是在努力呈现您在此处创建的 UI?
您是否在观察调试输出以查看是否存在导致性能下降的绑定(bind)错误或其他吞噬错误?
你有没有对着屏幕微笑,给你的代码一种好感,让它愿意为你更努力?开个玩笑,但变数很多 - 是吗?
您是否实现过 CanExecute 处理程序被频繁调用并且执行起来可能代价高昂的命令?这些可能是性能的无声 killer 。
同样,这个问题没有 100% 的答案。但这些可能会有所帮助。
还需要考虑的一件事是您的枚举可以是一个可观察的列表——这将使您可以分段加载数据。如果你想加载第一页,并在异步过程中附加下一页,然后再下一页等等,用户体验应该非常接近于一次加载所有页面,除了它会是一个更快的初始渲染。这可能很复杂,但它是您的另一种选择。可观察列表就是这样的。
像这样:
ObservableCollection<User> Users { get; set; }
void LoadUsers()
{
int _Size = 2;
int _Page = 0;
using (System.ComponentModel.BackgroundWorker _Worker
= new System.ComponentModel.BackgroundWorker())
{
_Worker.WorkerReportsProgress = true;
_Worker.DoWork += (s, arg) =>
{
List<User> _Data = null;
while (_Data == null || _Data.Any())
{
_Data = GetData(_Size, _Page++);
_Worker.ReportProgress(_Page, _Data);
}
};
_Worker.ProgressChanged += (s, e) =>
{
List<User> _Data = null;
_Data = e.UserState as List<User>;
_Data.ForEach(x => Users.Add(x));
};
_Worker.RunWorkerAsync();
}
}
List<User> GetData(int size, int page)
{
// never return null
return m_Context.Users.Take(size).Skip(page).ToList();
}
以下是我希望您带走的内容 - WPF 中的绑定(bind)从来都不是即时的。您将永远不会毫无延迟地呈现和绑定(bind)复杂的表单。您可以使用上述一些技巧来控制这里的疼痛。但是,您永远无法将其全部删除。然而,WPF 中的绑定(bind)是最强大和令人敬畏的绑定(bind)技术。我曾经经历过。
祝你好运!
关于c# - WPF DataGrid DataContext 极慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7083160/