特定のデータベース テーブルへの読み取りアクセスを提供するカスタム リストに対して 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) に対して呼び出されることです。
テーブルには何百万もの行が含まれる可能性があり、読み取り操作にはコストがかかるため (つまり、レガシー HDD での SQLite)、そのような操作は受け入れられません。さらに、画面が 50 程度のアイテムしか表示できない場合、何百万行ものすべてをロードするのは意味がありません。
テーブル全体をフェッチするのではなく、DataGrid が表示されている行のみをフェッチするようにしたいと考えています。そのような作業はどのように可能ですか?
(PS DataGrid自体で仮想化が有効になっていることを確認しました)