DataGridCell和自定义框架元素内容

时间:2012-10-01 14:03:28

标签: c# wpf datagrid

我正在考虑将我的一个应用程序转换为WPF,我的主要关注点之一是DataGrid控件的性能(我的用户的计算机是过时的没有专用图形的XP机器)。我尝试了几千行,滚动性能很糟糕。

我想知道是否有办法将datagrid单元格的内容设置为派生自FrameworkElement的自定义类?

这是一个示例类,当放入虚拟化堆栈面板时似乎具有更好的性能:

public class LiteTextBlock : FrameworkElement
{
    private Size _mySize;
    private string _myText;
    private double totalWidth;
    private GlyphTypeface _typeface;
    private int _fontSize;
    private GlyphRun _run;

    public LiteTextBlock(Size mySize, string myText, GlyphTypeface typeface, int fontSize)
    {
        _mySize = mySize;
        this.Width = _mySize.Width;
        this.Height = _mySize.Height;
        _myText = myText + " additional information";
        _typeface = typeface;
        _fontSize = fontSize;
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        if (_run == null)
        {
            totalWidth = 0;

            ushort[] glyphIndexes = new ushort[_myText.Length];
            double[] advanceWidths = new double[_myText.Length];

            for (int i = 0; i < _myText.Length; i++)
            {
                ushort glyphIndex = _typeface.CharacterToGlyphMap[_myText[i]];
                double width = _typeface.AdvanceWidths[glyphIndex] * _fontSize;

                if (totalWidth + width >= _mySize.Width - 10)
                {
                    Array.Resize(ref glyphIndexes, i);
                    Array.Resize(ref advanceWidths, i);
                    break;
                }

                glyphIndexes[i] = glyphIndex;
                advanceWidths[i] = width;
                totalWidth += width;
            }

            Point origin = new Point(5, 0);

            _run = new GlyphRun(_typeface, 0, false, _fontSize, glyphIndexes
                , origin, advanceWidths, null, null, null, null, null, null);
        }

        drawingContext.DrawGlyphRun(Brushes.Black, _run);
    }
}

有人可以告诉我这是否可能?

免责声明:我已尝试使用轻量级控件,例如ListView,但性能仍然不佳。

3 个答案:

答案 0 :(得分:1)

是的,有办法。

您需要实现自定义DataGridColumn并覆盖其GenerateElement方法,并且需要处理DataGrid.AutoGeneratingColumn事件以将自定义DataGridColumn设置为DataGrid。

以下是自定义DataGridColumn的示例:

public class DataGridLiteTextColumn : DataGridColumn
{
    private readonly PropertyDescriptor property;

    private readonly GlyphTypeface glyphTypeface = new GlyphTypeface(new Uri("file:///C:\\WINDOWS\\Fonts\\Arial.ttf"));

    public DataGridLiteTextColumn(PropertyDescriptor property)
    {
        this.property = property;
    }

    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
    {
        var value = property.GetValue(dataItem);
        return new LiteTextBlock(new Size(100, 20), value != null ? value.ToString() : string.Empty, this.glyphTypeface, 10);
    }

    protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
    {
        throw new NotImplementedException();
    }
}

这是DataGrid.AutoGeneratingColumn事件的处理程序:

private void OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    e.Column = new DataGridLiteTextColumn((PropertyDescriptor)e.PropertyDescriptor);
}

此代码可用于显示值,但LiteTextBlock构造函数大小参数可能未设置为适当的值。要编辑值,您还必须实现GenerateEditingElement方法。

答案 1 :(得分:0)

确保DataGrid的EnableRowVirtualization属性设置为True。默认情况下它应该为True,但请确保它在某个样式中没有设置为False。

答案 2 :(得分:0)

默认情况下启用

UI Virtualization。但它会在以下任一情况下被禁用 -

  1. 在度量排列过程中,DataGrid具有无限高度。 如果DataGrid认为它有足够的空间来排列所有项目,那么它将不会虚拟化任何内容。 当您将DataGrid放在堆栈面板中时,通常会发生这种情况。

  2. datagrid的ItemsPanel。 虚拟化由VirtualizingStackPanel完成。如果更改ItemsPanel,则将删除VirtualizingStackPanel。

  3. 项目分组。 在.NET4.0中,虚拟化不支持分组。在某些情况下,将GroupStyle分配给DataGrid将关闭虚拟化,即使项目未分组也是如此。

  4. 请检查您是否属于上述任何情况。

    作为旁注,您还可以在datagrid的Items Source上实现Data Virtualization。可以在此处找到带有校样的样本 - Data Virtualization