c# - WPF DataGrid DataContext 极慢

标签 c# wpf performance wpfdatagrid

我有一个正在处理的简单 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>

最佳答案

变量太多,无法确定地回答这个问题。但是,您需要考虑以下几点:

  1. 您为网格提供的数据量是否必要?您是否可能给它提供了比用户实际使用的数据太多的数据?这会减慢速度。

  2. 您是否使用过多模板渲染列或单元格?这使您的演示文稿变得灵活,我知道,但是过多的模板(或控件)会降低速度。

  3. 您的数据网格中是否有很多值转换器?是否要求每一行或每一列都运行一些稍微昂贵的代码才能呈现?

  4. 您是否有很多嵌套样式(使用 BasedOn),也许更重要的是,这些样式中的许多触发器会占用渲染时间来应用?

  5. 您是否在演示文稿中使用了大量用户控件,尤其是嵌套控件,这可能会导致演示文稿呈现延迟?

  6. 您为细胞使用的绑定(bind)字符串是否复杂?应用许多 StringFormat 或 ElementName 或 Ancestory 查找?这些会导致渲染速度变慢。

  7. 是否有可能使用视觉画笔来显示比用户立即可见的更多的数据?这会使虚拟化逻辑短路。

  8. 您是否考虑过对您的绑定(bind)使用 FallBackValue 并将 IsAsync 设置为 true?将此设置为 tru 将显示 FallBackValue,直到数据准备就绪。

  9. 您是否在 UI 中使用了许多 MultiBindings 或 PriorityBindings,这可能会导致渲染速度变慢,因为它处理多个字段或值?

  10. 您使用的样式是否复杂?尤其是渐变画笔,渲染它们的成本可能很高,尤其是当您在网格中的每一行都进行渲染时。

  11. 您是否考虑过使用分页来减少数据量?最后,奈柔,如果你能让用户接受,这就是解决这类问题的最佳方案。

  12. 您是否正在查看内存和 CPU 使用情况?您使用的硬件是否有可能只是在努力呈现您在此处创建的 UI?

  13. 您是否在观察调试输出以查看是否存在导致性能下降的绑定(bind)错误或其他吞噬错误?

  14. 你有没有对着屏幕微笑,给你的代码一种好感,让它愿意为你更努力?开个玩笑,但变数很多 - 是吗?

  15. 您是否实现过 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/

相关文章:

c# - Linq 中的排序和唯一记录

.net - WPF 控件和 SystemColors - Alpha 混合?

c# - 如何在 wpf 中使用简单的 wifi 连接到 wifi?

java - 在抽象类中持有对多个对象的静态引用可以吗?

java - 如何在 Spring 3.0 应用程序中配置 Hibernate 统计信息?

c# - 在构建 dll 后更新 DLL 元数据

c# - System.Linq.Enumerable.WhereListIterator 和 System.Linq.Enumerable.WhereSelectListIterator 之间有什么区别?

javascript - 从 JavaScript 按钮调用 WPF 应用程序函数

php - MySQL 匹配与喜欢

c# - 如何使用单个变量在单个循环中打印三角形?