summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Core/ObservableWrapper.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Core/ObservableWrapper.cs')
-rw-r--r--Xamarin.Forms.Core/ObservableWrapper.cs256
1 files changed, 256 insertions, 0 deletions
diff --git a/Xamarin.Forms.Core/ObservableWrapper.cs b/Xamarin.Forms.Core/ObservableWrapper.cs
new file mode 100644
index 00000000..e8fc0c5b
--- /dev/null
+++ b/Xamarin.Forms.Core/ObservableWrapper.cs
@@ -0,0 +1,256 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Linq;
+
+namespace Xamarin.Forms
+{
+ internal class ObservableWrapper<TTrack, TRestrict> : IList<TRestrict>, INotifyCollectionChanged where TTrack : Element where TRestrict : TTrack
+ {
+ readonly ObservableCollection<TTrack> _list;
+
+ public ObservableWrapper(ObservableCollection<TTrack> list)
+ {
+ if (list == null)
+ throw new ArgumentNullException("list");
+
+ _list = list;
+
+ list.CollectionChanged += ListOnCollectionChanged;
+ }
+
+ public void Add(TRestrict item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+ if (IsReadOnly)
+ throw new NotSupportedException("The collection is read-only.");
+
+ if (_list.Contains(item))
+ return;
+
+ item.Owned = true;
+ _list.Add(item);
+ }
+
+ public void Clear()
+ {
+ if (IsReadOnly)
+ throw new NotSupportedException("The collection is read-only.");
+
+ foreach (TRestrict item in _list.OfType<TRestrict>().ToArray())
+ {
+ _list.Remove(item);
+ item.Owned = false;
+ }
+ }
+
+ public bool Contains(TRestrict item)
+ {
+ return item.Owned && _list.Contains(item);
+ }
+
+ public void CopyTo(TRestrict[] array, int destIndex)
+ {
+ if (array.Length - destIndex < Count)
+ throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ foreach (TRestrict item in this)
+ {
+ array[destIndex] = item;
+ destIndex++;
+ }
+ }
+
+ public int Count
+ {
+ get { return _list.Where(i => i.Owned).OfType<TRestrict>().Count(); }
+ }
+
+ public bool IsReadOnly { get; internal set; }
+
+ public bool Remove(TRestrict item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+ if (IsReadOnly)
+ throw new NotSupportedException("The collection is read-only.");
+
+ if (!item.Owned)
+ return false;
+
+ if (_list.Remove(item))
+ {
+ item.Owned = false;
+ return true;
+ }
+ return false;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public IEnumerator<TRestrict> GetEnumerator()
+ {
+ return _list.Where(i => i.Owned).OfType<TRestrict>().GetEnumerator();
+ }
+
+ public int IndexOf(TRestrict value)
+ {
+ int innerIndex = _list.IndexOf(value);
+ if (innerIndex == -1)
+ return -1;
+ return ToOuterIndex(innerIndex);
+ }
+
+ public void Insert(int index, TRestrict item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+ if (IsReadOnly)
+ throw new NotSupportedException("The collection is read-only.");
+
+ item.Owned = true;
+ _list.Insert(ToInnerIndex(index), item);
+ }
+
+ public TRestrict this[int index]
+ {
+ get { return (TRestrict)_list[ToInnerIndex(index)]; }
+ set
+ {
+ int innerIndex = ToInnerIndex(index);
+ if (value != null)
+ value.Owned = true;
+ TTrack old = _list[innerIndex];
+ _list[innerIndex] = value;
+
+ if (old != null)
+ old.Owned = false;
+ }
+ }
+
+ public void RemoveAt(int index)
+ {
+ if (IsReadOnly)
+ throw new NotSupportedException("The collection is read-only");
+ int innerIndex = ToInnerIndex(index);
+ TTrack item = _list[innerIndex];
+ if (item.Owned)
+ {
+ _list.RemoveAt(innerIndex);
+ item.Owned = false;
+ }
+ }
+
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+
+ void ListOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ NotifyCollectionChangedEventHandler handler = CollectionChanged;
+ if (handler == null)
+ return;
+
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ if (e.NewStartingIndex == -1 || e.NewItems.Count > 1)
+ goto case NotifyCollectionChangedAction.Reset;
+
+ var newItem = e.NewItems[0] as TRestrict;
+ if (newItem == null || !newItem.Owned)
+ break;
+
+ int outerIndex = ToOuterIndex(e.NewStartingIndex);
+ handler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, e.NewItems, outerIndex));
+ break;
+ case NotifyCollectionChangedAction.Move:
+ if (e.NewStartingIndex == -1 || e.OldStartingIndex == -1 || e.NewItems.Count > 1)
+ goto case NotifyCollectionChangedAction.Reset;
+
+ var movedItem = e.NewItems[0] as TRestrict;
+ if (movedItem == null || !movedItem.Owned)
+ break;
+
+ int outerOldIndex = ToOuterIndex(e.OldStartingIndex);
+ int outerNewIndex = ToOuterIndex(e.NewStartingIndex);
+ handler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, e.NewItems, outerNewIndex, outerOldIndex));
+ break;
+ case NotifyCollectionChangedAction.Remove:
+ if (e.OldStartingIndex == -1 || e.OldItems.Count > 1)
+ goto case NotifyCollectionChangedAction.Reset;
+
+ var removedItem = e.OldItems[0] as TRestrict;
+ if (removedItem == null || !removedItem.Owned)
+ break;
+
+ int outerRemovedIndex = ToOuterIndex(e.OldStartingIndex);
+ var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItem, outerRemovedIndex);
+ handler(this, args);
+ break;
+ case NotifyCollectionChangedAction.Replace:
+ if (e.NewStartingIndex == -1 || e.OldStartingIndex == -1 || e.NewItems.Count > 1)
+ goto case NotifyCollectionChangedAction.Reset;
+
+ var newReplaceItem = e.NewItems[0] as TRestrict;
+ var oldReplaceItem = e.OldItems[0] as TRestrict;
+
+ if ((newReplaceItem == null || !newReplaceItem.Owned) && (oldReplaceItem == null || !oldReplaceItem.Owned))
+ {
+ break;
+ }
+ if (newReplaceItem == null || !newReplaceItem.Owned || oldReplaceItem == null || !oldReplaceItem.Owned)
+ {
+ goto case NotifyCollectionChangedAction.Reset;
+ }
+
+ int index = ToOuterIndex(e.NewStartingIndex);
+
+ var replaceArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newReplaceItem, oldReplaceItem, index);
+ handler(this, replaceArgs);
+ break;
+ case NotifyCollectionChangedAction.Reset:
+ handler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ int ToInnerIndex(int outterIndex)
+ {
+ var outerIndex = 0;
+ int innerIndex;
+ for (innerIndex = 0; innerIndex < _list.Count; innerIndex++)
+ {
+ TTrack item = _list[innerIndex];
+ if (item is TRestrict && item.Owned)
+ {
+ if (outerIndex == outterIndex)
+ return innerIndex;
+ outerIndex++;
+ }
+ }
+
+ return innerIndex;
+ }
+
+ int ToOuterIndex(int innerIndex)
+ {
+ var outerIndex = 0;
+ for (var index = 0; index < innerIndex; index++)
+ {
+ TTrack item = _list[index];
+ if (item is TRestrict && item.Owned)
+ {
+ outerIndex++;
+ }
+ }
+
+ return outerIndex;
+ }
+ }
+} \ No newline at end of file