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

最佳答案

有太多变量无法确定地回答这一问题。但是,您可以考虑以下一些事项:

  • 您是否需要为网格提供数据量?您是否可能给它提供了超出用户实际使用量的过多数据?这会使速度变慢。
  • 您是否使用太多模板渲染列或单元格?我知道,这使您的演示文稿更加灵活,但是太多的模板(或控件)会使速度变慢。
  • 您的数据网格中是否有很多值(value)转换器?是否需要每一行或每一列运行一些稍微昂贵的代码才能呈现?
  • 是否有很多嵌套样式(使用BasedOn),也许更重要的是,这些样式中的许多触发器会占用渲染时间以进行应用?
  • 您是否在演示文稿中使用了很多用户控件(尤其是嵌套控件),这可能会导致演示文稿的渲染延迟?
  • 您用于细胞的绑定(bind)字符串是否复杂?是否应用许多StringFormat或ElementName或Ancestory查找?这些导致渲染缓慢。
  • 是否有可能使用可视笔刷来显示比用户立即可见的更多数据?这将使虚拟化逻辑短路。
  • 您是否考虑过将FallBackValue用于绑定(bind)并将IsAsync设置为true?将此设置为tru将显示FallBackValue,直到数据准备就绪为止。
  • 您是否在UI中使用了许多MultiBindings或PriorityBindings,它们可能会导致渲染处理多个字段或值时变慢?
  • 您使用的样式是否复杂?尤其是渐变笔刷,渲染这些笔刷可能会很昂贵,尤其是在网格中的每一行都进行渲染时。
  • 您是否考虑过使用分页以减少数据量?最后,奈柔,如果您可以让用户接受,那么这是解决此类问题的最佳解决方案。
  • 您在查看内存和CPU使用率吗?您使用的硬件是否可能只是在努力渲染此处创建的UI?
  • 您是否正在查看调试输出,以查看是否存在导致性能下降的绑定(bind)错误或其他可吞并的错误?
  • 您是否在屏幕上微笑并给您的代码一个良好的感觉,以便它愿意为您付出更大的努力?只是在开玩笑,但变量很多-是吗?
  • 您是否实现了经常调用CanExecute处理程序且执行成本可能很高的命令?这些可能是性能的沉默杀手。

  • 同样,此问题没有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)技术。我曾经经历过。

    祝你好运!

    10-07 19:19
    查看更多