1

我有一个ObservableCollection位于 WPF 中的 ViewModel DataGrid。具有DataGrid三列:

  • 职位栏;这是在运行时由一个显示行在我的 DataGrid 中的位置的 UserControl 呈现的
  • 名称栏;这是在运行时由显示列名称的 UserControl 呈现的(是的,我需要一个 UserControl,这取决于名称需要如何显示,但这是一个旁白)
  • 数据栏;这是在运行时由另一个 UserControl 呈现的。

我的列定义如下:

        <toolkit:DataGrid.Columns>
            <toolkit:DataGridTemplateColumn Header="" MinWidth="35" MaxWidth="35" SortMemberPath="Position.PositionIndex" CanUserSort="True">
                <toolkit:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ContentPresenter Content="{Binding Path=Position}"/>
                    </DataTemplate>
                </toolkit:DataGridTemplateColumn.CellTemplate>
            </toolkit:DataGridTemplateColumn>
            <toolkit:DataGridTemplateColumn Header="Name" MinWidth="150" Width="150" SortMemberPath="Name" CanUserSort="True">
                <toolkit:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ContentPresenter Content="{Binding Path=Name}"/>
                    </DataTemplate>
                </toolkit:DataGridTemplateColumn.CellTemplate>
            </toolkit:DataGridTemplateColumn>
            <toolkit:DataGridTemplateColumn Header="Data" Width="Auto" CanUserSort="False">
                <toolkit:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ContentPresenter Content="{Binding Path=Data}"/>
                    </DataTemplate>
                </toolkit:DataGridTemplateColumn.CellTemplate>
            </toolkit:DataGridTemplateColumn>
        </toolkit:DataGrid.Columns>

因此,因为我的 Row 和 Name 列是 UserControls,所以 WPFDataGrid不能对它们进行本地排序。所以为了方便排序,当点击一个列标题时,我做了一些ListCollectionView.CustomSort魔术。

这是我的“名称”列的自定义排序器的样子:

// Customized sorter, by name, ascending.
public class AscendingNameSorter : IComparer
{
    public int Compare(object x, object y)
    {
        var lhs = (MyViewModel)x;
        var rhs = (MyViewModel)y;

        return lhs.Name.CompareTo(rhs.Name);
    }
}

// Customized sorter, by name, descending.
public class DescendingNameSorter : IComparer
{
    public int Compare(object x, object y)
    {
        var lhs = (MyViewModel)x;
        var rhs = (MyViewModel)y;

        return rhs.Name.CompareTo(lhs.Name);
    }
}

问题是这非常慢。我不知道为什么。在 10 项中DataGrid,我的应用程序在使用时会停止 3-4 秒。我认为ListCollectionView.CustomSort应该是最有效的排序方式ObservableCollection......我哪里出错了?

4

1 回答 1

3

每次排序更改时,WPF 都会重新创建所有用户控件,所以我的猜测是这些控件的构建过程很慢。但这只是一个猜测。

您应该从缩小问题范围开始。以下是您可以采取的一些步骤:

  1. 找出哪个操作需要 3-4 秒。您没有说明延迟是仅在将值分配给 CustomSort 时发生,还是在设置 CustomSort 后每次列表更改时发生。这会有所不同。

  2. 尝试添加一个常规文本列并使用内置排序对其进行排序,以查看它是否快速。也许您已经这样做了,但是您没有在问题中说。

  3. 出于诊断目的,暂时停止设置 CustomSort 并改为设置 ListCollectionView.Filter。将其设置为始终返回 true 的过滤器。如果仍然出现减速,则问题与 ListCollectionView 尝试重新组织项目有关。

  4. 暂时编辑您的模板并用一些琐碎的东西(例如)替换您的自定义用户控件,<CheckBox/>看看事情是否加快了速度。

  5. 在 UserControls 的构造函数中设置断点以查看它们是否被调用了预期的次数(即,如果列表中有 10 个项目,则调用 10 个构造函数)。如果它们被调用的次数比预期的多,请查看堆栈跟踪以查看额外调用的来源。

  6. 将代码添加到您的 UserControl 构造函数以编写 DateTime。现在构造函数被调用到输出窗口(或日志,或其他)。这会让你知道每个需要多长时间。

  7. 将数百个项目添加到您的 ObservableCollection,与 VS.NET 并行运行您的应用程序,单击排序按钮(或其他),然后单击 VS.NET 中的 Break All 按钮并查看堆栈跟踪。点击 Continue 并立即再次点击 Break All,然后再次查看堆栈跟踪。重复多次。这将使您对花费所有额外时间的原因有一个很好的了解。

如果,正如我所怀疑的,问题是 UserControls 创建和绑定缓慢,您会发现:问题发生在每次列表更改时,并且在您更改过滤器时也会发生,当您将 UserControls 替换为 时,事情会加速<CheckBox/>,您的构造函数只会是每个项目调用一次,调用之间的时间会比较长。

请注意,我并不是说 UserControls 的构造函数很慢 - 可能是 UserControl 在数据绑定时实例化了许多子对象,或者它包含缓慢或复杂的对象,子对象加载一个文件,或许多其他可能的原因。底线是在对象上实例化 DataTemplate 并将其添加到可视化树中的速度很慢。堆栈跟踪应该让您知道在哪里查看。

如果结果是其他问题或者您无法弄清楚,只需更新您的问题以提供有关上述测试所揭示内容的更多信息,我们会尽力帮助您。

于 2010-01-08T20:40:00.250 回答