summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Core/MultiPage.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Core/MultiPage.cs')
-rw-r--r--Xamarin.Forms.Core/MultiPage.cs359
1 files changed, 359 insertions, 0 deletions
diff --git a/Xamarin.Forms.Core/MultiPage.cs b/Xamarin.Forms.Core/MultiPage.cs
new file mode 100644
index 00000000..89fd7e9c
--- /dev/null
+++ b/Xamarin.Forms.Core/MultiPage.cs
@@ -0,0 +1,359 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace Xamarin.Forms
+{
+ [ContentProperty("Children")]
+ public abstract class MultiPage<T> : Page, IViewContainer<T>, IPageContainer<T>, IItemsView<T> where T : Page
+ {
+ public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(MultiPage<>), null);
+
+ public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(MultiPage<>), null);
+
+ public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create("SelectedItem", typeof(object), typeof(MultiPage<>), null, BindingMode.TwoWay);
+
+ internal static readonly BindableProperty IndexProperty = BindableProperty.Create("Index", typeof(int), typeof(Page), -1);
+
+ readonly ElementCollection<T> _children;
+ readonly TemplatedItemsList<MultiPage<T>, T> _templatedItems;
+
+ T _current;
+
+ protected MultiPage()
+ {
+ _templatedItems = new TemplatedItemsList<MultiPage<T>, T>(this, ItemsSourceProperty, ItemTemplateProperty);
+ _templatedItems.CollectionChanged += OnTemplatedItemsChanged;
+
+ _children = new ElementCollection<T>(InternalChildren);
+ InternalChildren.CollectionChanged += OnChildrenChanged;
+ }
+
+ public IEnumerable ItemsSource
+ {
+ get { return (IEnumerable)GetValue(ItemsSourceProperty); }
+ set { SetValue(ItemsSourceProperty, value); }
+ }
+
+ public DataTemplate ItemTemplate
+ {
+ get { return (DataTemplate)GetValue(ItemTemplateProperty); }
+ set { SetValue(ItemTemplateProperty, value); }
+ }
+
+ public object SelectedItem
+ {
+ get { return GetValue(SelectedItemProperty); }
+ set { SetValue(SelectedItemProperty, value); }
+ }
+
+ T IItemsView<T>.CreateDefault(object item)
+ {
+ return CreateDefault(item);
+ }
+
+ void IItemsView<T>.SetupContent(T content, int index)
+ {
+ SetupContent(content, index);
+ }
+
+ void IItemsView<T>.UnhookContent(T content)
+ {
+ UnhookContent(content);
+ }
+
+ public T CurrentPage
+ {
+ get { return _current; }
+ set
+ {
+ if (_current == value)
+ return;
+
+ OnPropertyChanging();
+ _current = value;
+ OnPropertyChanged();
+ OnCurrentPageChanged();
+ }
+ }
+
+ public IList<T> Children
+ {
+ get { return _children; }
+ }
+
+ public event EventHandler CurrentPageChanged;
+
+ public event NotifyCollectionChangedEventHandler PagesChanged;
+
+ protected abstract T CreateDefault(object item);
+
+ protected override bool OnBackButtonPressed()
+ {
+ if (CurrentPage != null)
+ {
+ bool handled = CurrentPage.SendBackButtonPressed();
+ if (handled)
+ return true;
+ }
+
+ return base.OnBackButtonPressed();
+ }
+
+ protected override void OnChildAdded(Element child)
+ {
+ base.OnChildAdded(child);
+
+ ForceLayout();
+ }
+
+ protected virtual void OnCurrentPageChanged()
+ {
+ EventHandler changed = CurrentPageChanged;
+ if (changed != null)
+ changed(this, EventArgs.Empty);
+ }
+
+ protected virtual void OnPagesChanged(NotifyCollectionChangedEventArgs e)
+ {
+ NotifyCollectionChangedEventHandler handler = PagesChanged;
+ if (handler != null)
+ handler(this, e);
+ }
+
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ if (propertyName == ItemsSourceProperty.PropertyName)
+ _children.IsReadOnly = ItemsSource != null;
+ else if (propertyName == SelectedItemProperty.PropertyName)
+ {
+ UpdateCurrentPage();
+ }
+ else if (propertyName == "CurrentPage" && ItemsSource != null)
+ {
+ if (CurrentPage == null)
+ {
+ SelectedItem = null;
+ }
+ else
+ {
+ int index = _templatedItems.IndexOf(CurrentPage);
+ SelectedItem = index != -1 ? _templatedItems.ListProxy[index] : null;
+ }
+ }
+
+ base.OnPropertyChanged(propertyName);
+ }
+
+ protected virtual void SetupContent(T content, int index)
+ {
+ }
+
+ protected virtual void UnhookContent(T content)
+ {
+ }
+
+ internal static int GetIndex(T page)
+ {
+ if (page == null)
+ throw new ArgumentNullException("page");
+
+ return (int)page.GetValue(IndexProperty);
+ }
+
+ internal T GetPageByIndex(int index)
+ {
+ foreach (T page in InternalChildren)
+ {
+ if (index == GetIndex(page))
+ return page;
+ }
+ return null;
+ }
+
+ internal static void SetIndex(Page page, int index)
+ {
+ if (page == null)
+ throw new ArgumentNullException("page");
+
+ page.SetValue(IndexProperty, index);
+ }
+
+ void OnChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ if (Children.IsReadOnly)
+ return;
+
+ var i = 0;
+ foreach (T page in Children)
+ SetIndex(page, i++);
+
+ OnPagesChanged(e);
+
+ if (CurrentPage == null || Children.IndexOf(CurrentPage) == -1)
+ CurrentPage = Children.FirstOrDefault();
+ }
+
+ void OnTemplatedItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ if (e.NewStartingIndex < 0)
+ goto case NotifyCollectionChangedAction.Reset;
+
+ for (int i = e.NewStartingIndex; i < Children.Count; i++)
+ SetIndex((T)InternalChildren[i], i + e.NewItems.Count);
+
+ for (var i = 0; i < e.NewItems.Count; i++)
+ {
+ var page = (T)e.NewItems[i];
+ page.Owned = true;
+ int index = i + e.NewStartingIndex;
+ SetIndex(page, index);
+ InternalChildren.Insert(index, (T)e.NewItems[i]);
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ if (e.OldStartingIndex < 0)
+ goto case NotifyCollectionChangedAction.Reset;
+
+ int removeIndex = e.OldStartingIndex;
+ for (int i = removeIndex + e.OldItems.Count; i < Children.Count; i++)
+ SetIndex((T)InternalChildren[i], removeIndex++);
+
+ for (var i = 0; i < e.OldItems.Count; i++)
+ {
+ Element element = InternalChildren[e.OldStartingIndex];
+ InternalChildren.RemoveAt(e.OldStartingIndex);
+ element.Owned = false;
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Move:
+ if (e.NewStartingIndex < 0 || e.OldStartingIndex < 0)
+ goto case NotifyCollectionChangedAction.Reset;
+
+ if (e.NewStartingIndex == e.OldStartingIndex)
+ return;
+
+ bool movingForward = e.OldStartingIndex < e.NewStartingIndex;
+
+ if (movingForward)
+ {
+ int moveIndex = e.OldStartingIndex;
+ for (int i = moveIndex + e.OldItems.Count; i <= e.NewStartingIndex; i++)
+ SetIndex((T)InternalChildren[i], moveIndex++);
+ }
+ else
+ {
+ for (var i = 0; i < e.OldStartingIndex - e.NewStartingIndex; i++)
+ {
+ var page = (T)InternalChildren[i + e.NewStartingIndex];
+ SetIndex(page, GetIndex(page) + e.OldItems.Count);
+ }
+ }
+
+ for (var i = 0; i < e.OldItems.Count; i++)
+ InternalChildren.RemoveAt(e.OldStartingIndex);
+
+ int insertIndex = e.NewStartingIndex;
+ if (movingForward)
+ insertIndex -= e.OldItems.Count - 1;
+
+ for (var i = 0; i < e.OldItems.Count; i++)
+ {
+ var page = (T)e.OldItems[i];
+ SetIndex(page, insertIndex + i);
+ InternalChildren.Insert(insertIndex + i, page);
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Replace:
+ if (e.OldStartingIndex < 0)
+ goto case NotifyCollectionChangedAction.Reset;
+
+ for (int i = e.OldStartingIndex; i - e.OldStartingIndex < e.OldItems.Count; i++)
+ {
+ Element element = InternalChildren[i];
+ InternalChildren.RemoveAt(i);
+ element.Owned = false;
+
+ T page = _templatedItems.GetOrCreateContent(i, e.NewItems[i - e.OldStartingIndex]);
+ page.Owned = true;
+ SetIndex(page, i);
+ InternalChildren.Insert(i, page);
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Reset:
+ Reset();
+ return;
+ }
+
+ OnPagesChanged(e);
+ UpdateCurrentPage();
+ }
+
+ void Reset()
+ {
+ List<Element> snapshot = InternalChildren.ToList();
+
+ InternalChildren.Clear();
+
+ foreach (Element element in snapshot)
+ element.Owned = false;
+
+ for (var i = 0; i < _templatedItems.Count; i++)
+ {
+ T page = _templatedItems.GetOrCreateContent(i, _templatedItems.ListProxy[i]);
+ page.Owned = true;
+ SetIndex(page, i);
+ InternalChildren.Add(page);
+ }
+
+ var currentNeedsUpdate = true;
+
+ BatchBegin();
+
+ if (ItemsSource != null)
+ {
+ object selected = SelectedItem;
+ if (selected == null || !ItemsSource.Cast<object>().Contains(selected))
+ {
+ SelectedItem = ItemsSource.Cast<object>().FirstOrDefault();
+ currentNeedsUpdate = false;
+ }
+ }
+
+ if (currentNeedsUpdate)
+ UpdateCurrentPage();
+
+ OnPagesChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+
+ BatchCommit();
+ }
+
+ void UpdateCurrentPage()
+ {
+ if (ItemsSource != null)
+ {
+ int index = _templatedItems.ListProxy.IndexOf(SelectedItem);
+ if (index == -1)
+ CurrentPage = (T)InternalChildren.FirstOrDefault();
+ else
+ CurrentPage = _templatedItems.GetOrCreateContent(index, SelectedItem);
+ }
+ else if (SelectedItem is T)
+ CurrentPage = (T)SelectedItem;
+ }
+ }
+} \ No newline at end of file