Виртуализация DataGrid через ListCollectionView

Я использую ListCollectionView поверх пользовательского списка, который обеспечивает доступ для чтения к определенной таблице базы данных. Ниже приведено определение пользовательского списка.

class MaterialCollection : IList
{
    #region Fields
    private Object syncRoot;
    private SQLiteConnection connection;
    #endregion

    #region IList Interface
    public object this[int index]
    {
        get
        {
            using (SQLiteCommand command = connection.CreateCommand())
            {
                command.CommandText = "SELECT * FROM Materials LIMIT 1 OFFSET @Index";
                command.Parameters.AddWithValue("@Index", index);
                using (SQLiteDataReader reader = command.ExecuteReader())
                {
                    if (reader.Read())
                    {
                        return GetMaterial(reader);
                    }
                    else
                    {
                        throw new Exception();
                    }
                }
            }
        }

        set
        {
            throw new NotImplementedException();
        }
    }

    public int Count
    {
        get
        {
            using (SQLiteCommand command = connection.CreateCommand())
            {
                command.CommandText = "SELECT Count(*) FROM Materials";
                return Convert.ToInt32(command.ExecuteScalar());
            }
        }
    }

    public bool IsFixedSize
    {
        get
        {
            return false;
        }
    }

    public bool IsReadOnly
    {
        get
        {
            return true;
        }
    }

    public bool IsSynchronized
    {
        get
        {
            return true;
        }
    }

    public object SyncRoot
    {
        get
        {
            return this.syncRoot;
        }
    }

    public IEnumerator GetEnumerator()
    {
        return Enumerate().GetEnumerator();
    }
    #endregion

    #region Constructor
    public MaterialCollection(SQLiteConnection connection)
    {
        this.connection = connection;
        this.syncRoot = new Object();
    }
    #endregion

    #region Private Methods
    private Material GetMaterial(SQLiteDataReader reader)
    {
        int id = Convert.ToInt32(reader["Id"]);
        string materialNumber = Convert.ToString(reader["MaterialNumber"]);
        string type = Convert.ToString(reader["Type"]);
        string description = Convert.ToString(reader["Description"]);
        string alternateDescription = Convert.ToString(reader["AlternateDescription"]);
        string tags = Convert.ToString(reader["Tags"]);

        Material material = new Material();
        material.Id = id;
        material.MaterialNumber = materialNumber;
        material.Type = (MaterialType)Enum.Parse(typeof(MaterialType), type);
        material.Description = description;
        material.AlternateDescription = alternateDescription;
        material.Tags = tags;

        return material;
    }

    private IEnumerable<Material> Enumerate()
    {
        using (SQLiteCommand command = connection.CreateCommand())
        {
            command.CommandText = "SELECT * FROM Materials";
            using (SQLiteDataReader reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    yield return GetMaterial(reader);
                }
            }
        }
    }
    #endregion

    #region Unimplemented Functions
    public int Add(object value)
    {
        throw new NotImplementedException();
    }

    public void Clear()
    {
        throw new NotImplementedException();
    }

    public bool Contains(object value)
    {
        throw new NotImplementedException();
    }

    public void CopyTo(Array array, int index)
    {
        throw new NotImplementedException();
    }

    public int IndexOf(object value)
    {
        return 0;
    }

    public void Insert(int index, object value)
    {
        throw new NotImplementedException();
    }

    public void Remove(object value)
    {
        throw new NotImplementedException();
    }

    public void RemoveAt(int index)
    {
        throw new NotImplementedException();
    }
    #endregion
}

Проблема в том, что во время начального заполнения DataGrid метод this[int index] вызывается для каждой отдельной строки (т. е. от 0 до Count — 1).

Поскольку таблица содержит потенциально миллионы строк, а операции чтения являются дорогостоящими (например, SQLite на устаревшем жестком диске), такая операция неприемлема. Кроме того, нет смысла загружать все миллионы строк, когда экран может вместить только около 50 элементов.

Я хотел бы сделать так, чтобы DataGrid извлекал только отображаемую строку, а не всю таблицу. Как возможна такая работа?

(P.S. Я убедился, что виртуализация включена для самого DataGrid)


person Yiyuan Lee    schedule 17.12.2015    source источник
comment
Вы уже ознакомились с этой статьей? codeproject.com/Articles/34405/WPF-Data-Virtualization   -  person SnowballTwo    schedule 17.12.2015
comment
@SnowballTwo Да, мой текущий подход был вдохновлен этой статьей.   -  person Yiyuan Lee    schedule 17.12.2015


Ответы (1)


Кажется, проблема была решена после добавления ScrollViewer.CanContentScroll="True" в ListView.

person Yiyuan Lee    schedule 17.12.2015