Skip to content

Instantly share code, notes, and snippets.

@alpinskiy
Created June 29, 2016 08:17
Show Gist options
  • Save alpinskiy/de0fb926f8297163066b3ca32cf0f170 to your computer and use it in GitHub Desktop.
Save alpinskiy/de0fb926f8297163066b3ca32cf0f170 to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
namespace VirtualListLib
{
public sealed class VirtualList : IVirtualList
{
public VirtualList(
int count,
int pageSize,
VirtualListItemLoader virtualListItemLoader,
object defaultValue = null)
{
_pageSize = pageSize;
_virtualListItemLoader = virtualListItemLoader;
_defaultValue = defaultValue;
_pageList = new List<WeakReference>((count + 1)/pageSize);
_emptyPage = new object[_pageSize];
_collectionChangedHelperPage = new object[_pageSize];
Count = count;
}
public event NotifyCollectionChangedEventHandler CollectionChanged = delegate { };
public event PropertyChangedEventHandler PropertyChanged = delegate { };
IEnumerator IEnumerable.GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
yield return this[i];
}
}
public int Count
{
get { return _count; }
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("value", value, string.Empty);
}
var extra = value - Capacity;
if (extra == 0)
{
return;
}
if (extra > 0)
{
_pageList.AddRange(Enumerable.
Range(0, (extra + 1)/_pageSize).
Select(_ => default(WeakReference)));
}
_count = value;
OnPropertyChanged("Count");
}
}
public object SyncRoot
{
get { return this; }
}
public bool IsSynchronized
{
get { return false; }
}
public bool IsReadOnly
{
get { return true; }
}
public bool IsFixedSize
{
get { return true; }
}
public object this[int index]
{
get
{
if (!(0 <= index && index < Count))
{
throw new ArgumentOutOfRangeException("index", index, string.Empty);
}
var pageIndex = index/_pageSize;
var pageWeakReference = _pageList[pageIndex];
var page = pageWeakReference != null
? pageWeakReference.Target as object[]
: null;
if (page != null)
{
return page[index%_pageSize];
}
_virtualListItemLoader.
LoadAsync(pageIndex*_pageSize, _pageSize).
ContinueWith(
task => OnPageLoaded(task.Result, pageIndex),
TaskScheduler.FromCurrentSynchronizationContext());
if (pageWeakReference == null)
{
_pageList[pageIndex] = new WeakReference(_emptyPage);
}
else
{
pageWeakReference.Target = _emptyPage;
}
return _defaultValue;
}
set { throw new NotSupportedException(); }
}
public bool Contains(object value)
{
return _pageList.
SelectMany(page => (page.Target as object[]) ?? Enumerable.Empty<object>()).
Contains(value);
}
public int IndexOf(object item)
{
var index = 0;
foreach (var pageWeakReference in _pageList)
{
var page = pageWeakReference != null
? pageWeakReference.Target as object[]
: null;
if (page == null || ReferenceEquals(page, _emptyPage))
{
index += _pageSize;
}
else
{
foreach (var o in page)
{
if (ReferenceEquals(o, item))
{
return index;
}
index++;
}
}
}
return -1;
}
#region Not Supported
public int Add(object item)
{
throw new NotSupportedException();
}
public void Clear()
{
throw new NotSupportedException();
}
public void CopyTo(Array array, int arrayIndex)
{
throw new NotSupportedException();
}
public void Remove(object item)
{
throw new NotSupportedException();
}
public void Insert(int index, object item)
{
throw new NotSupportedException();
}
public void RemoveAt(int index)
{
throw new NotSupportedException();
}
#endregion
private void OnPageLoaded(IList<IVirtualListPageHolder> page, int pageIndex)
{
for (var i = 0; i < _pageSize; i++)
{
_collectionChangedHelperPage[i] = _defaultValue;
}
_pageList[pageIndex].Target = _collectionChangedHelperPage;
var newItems = new object[1];
var oldItems = new[] {_defaultValue};
for (var i = 0; i < page.Count; i++)
{
var newItem = page[i];
newItem.VirtualListPage = page;
newItems[0] = newItem;
_collectionChangedHelperPage[i] = newItem;
CollectionChanged(
this,
new NotifyCollectionChangedEventArgs(
action: NotifyCollectionChangedAction.Replace,
newItems: newItems,
oldItems: oldItems,
startingIndex: pageIndex*_pageSize + i));
}
_pageList[pageIndex].Target = page;
PropertyChanged(this, new PropertyChangedEventArgs("Items[]"));
}
private void OnPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private int Capacity
{
get { return _pageList.Count*_pageSize; }
}
private int _count;
private readonly int _pageSize;
private readonly VirtualListItemLoader _virtualListItemLoader;
private readonly object _defaultValue;
private readonly List<WeakReference> _pageList;
private readonly object[] _emptyPage;
private readonly object[] _collectionChangedHelperPage;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment