Created
June 29, 2016 08:17
-
-
Save alpinskiy/de0fb926f8297163066b3ca32cf0f170 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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