summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Core/Page.cs
diff options
context:
space:
mode:
authorJason Smith <jason.smith@xamarin.com>2016-03-22 13:02:25 -0700
committerJason Smith <jason.smith@xamarin.com>2016-03-22 16:13:41 -0700
commit17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch)
treeb5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Core/Page.cs
downloadxamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.gz
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.bz2
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.zip
Initial import
Diffstat (limited to 'Xamarin.Forms.Core/Page.cs')
-rw-r--r--Xamarin.Forms.Core/Page.cs403
1 files changed, 403 insertions, 0 deletions
diff --git a/Xamarin.Forms.Core/Page.cs b/Xamarin.Forms.Core/Page.cs
new file mode 100644
index 00000000..903f4b53
--- /dev/null
+++ b/Xamarin.Forms.Core/Page.cs
@@ -0,0 +1,403 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_PageRenderer))]
+ public class Page : VisualElement, ILayout
+ {
+ internal const string BusySetSignalName = "Xamarin.BusySet";
+
+ internal const string AlertSignalName = "Xamarin.SendAlert";
+
+ internal const string ActionSheetSignalName = "Xamarin.ShowActionSheet";
+
+ internal static readonly BindableProperty IgnoresContainerAreaProperty = BindableProperty.Create("IgnoresContainerArea", typeof(bool), typeof(Page), false);
+
+ public static readonly BindableProperty BackgroundImageProperty = BindableProperty.Create("BackgroundImage", typeof(string), typeof(Page), default(string));
+
+ public static readonly BindableProperty IsBusyProperty = BindableProperty.Create("IsBusy", typeof(bool), typeof(Page), false, propertyChanged: (bo, o, n) => ((Page)bo).OnPageBusyChanged());
+
+ public static readonly BindableProperty PaddingProperty = BindableProperty.Create("Padding", typeof(Thickness), typeof(Page), default(Thickness), propertyChanged: (bindable, old, newValue) =>
+ {
+ var layout = (Page)bindable;
+ layout.UpdateChildrenLayout();
+ });
+
+ public static readonly BindableProperty TitleProperty = BindableProperty.Create("Title", typeof(string), typeof(Page), null);
+
+ public static readonly BindableProperty IconProperty = BindableProperty.Create("Icon", typeof(FileImageSource), typeof(Page), default(FileImageSource));
+
+ bool _allocatedFlag;
+ Rectangle _containerArea;
+
+ bool _containerAreaSet;
+
+ bool _hasAppeared;
+
+ ReadOnlyCollection<Element> _logicalChildren;
+
+ public Page()
+ {
+ var toolbarItems = new ObservableCollection<ToolbarItem>();
+ toolbarItems.CollectionChanged += OnToolbarItemsCollectionChanged;
+ ToolbarItems = toolbarItems;
+ InternalChildren.CollectionChanged += InternalChildrenOnCollectionChanged;
+ }
+
+ public string BackgroundImage
+ {
+ get { return (string)GetValue(BackgroundImageProperty); }
+ set { SetValue(BackgroundImageProperty, value); }
+ }
+
+ public FileImageSource Icon
+ {
+ get { return (FileImageSource)GetValue(IconProperty); }
+ set { SetValue(IconProperty, value); }
+ }
+
+ public bool IsBusy
+ {
+ get { return (bool)GetValue(IsBusyProperty); }
+ set { SetValue(IsBusyProperty, value); }
+ }
+
+ public Thickness Padding
+ {
+ get { return (Thickness)GetValue(PaddingProperty); }
+ set { SetValue(PaddingProperty, value); }
+ }
+
+ public string Title
+ {
+ get { return (string)GetValue(TitleProperty); }
+ set { SetValue(TitleProperty, value); }
+ }
+
+ public IList<ToolbarItem> ToolbarItems { get; internal set; }
+
+ internal Rectangle ContainerArea
+ {
+ get { return _containerArea; }
+ set
+ {
+ if (_containerArea == value)
+ return;
+ _containerAreaSet = true;
+ _containerArea = value;
+ ForceLayout();
+ }
+ }
+
+ internal bool IgnoresContainerArea
+ {
+ get { return (bool)GetValue(IgnoresContainerAreaProperty); }
+ set { SetValue(IgnoresContainerAreaProperty, value); }
+ }
+
+ internal ObservableCollection<Element> InternalChildren { get; } = new ObservableCollection<Element>();
+
+ internal override ReadOnlyCollection<Element> LogicalChildren
+ {
+ get { return _logicalChildren ?? (_logicalChildren = new ReadOnlyCollection<Element>(InternalChildren)); }
+ }
+
+ public event EventHandler LayoutChanged;
+
+ public event EventHandler Appearing;
+
+ public event EventHandler Disappearing;
+
+ public Task<string> DisplayActionSheet(string title, string cancel, string destruction, params string[] buttons)
+ {
+ var args = new ActionSheetArguments(title, cancel, destruction, buttons);
+ MessagingCenter.Send(this, ActionSheetSignalName, args);
+ return args.Result.Task;
+ }
+
+ public Task DisplayAlert(string title, string message, string cancel)
+ {
+ return DisplayAlert(title, message, null, cancel);
+ }
+
+ public Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
+ {
+ if (string.IsNullOrEmpty(cancel))
+ throw new ArgumentNullException("cancel");
+
+ var args = new AlertArguments(title, message, accept, cancel);
+ MessagingCenter.Send(this, AlertSignalName, args);
+ return args.Result.Task;
+ }
+
+ public void ForceLayout()
+ {
+ SizeAllocated(Width, Height);
+ }
+
+ public bool SendBackButtonPressed()
+ {
+ return OnBackButtonPressed();
+ }
+
+ protected virtual void LayoutChildren(double x, double y, double width, double height)
+ {
+ var area = new Rectangle(x, y, width, height);
+ Rectangle originalArea = area;
+ if (_containerAreaSet)
+ {
+ area = ContainerArea;
+ area.X += Padding.Left;
+ area.Y += Padding.Right;
+ area.Width -= Padding.HorizontalThickness;
+ area.Height -= Padding.VerticalThickness;
+ area.Width = Math.Max(0, area.Width);
+ area.Height = Math.Max(0, area.Height);
+ }
+
+ foreach (Element element in LogicalChildren)
+ {
+ var child = element as VisualElement;
+ if (child == null)
+ continue;
+ var page = child as Page;
+ if (page != null && page.IgnoresContainerArea)
+ {
+ Forms.Layout.LayoutChildIntoBoundingRegion(child, originalArea);
+ }
+ else
+ {
+ Forms.Layout.LayoutChildIntoBoundingRegion(child, area);
+ }
+ }
+ }
+
+ protected virtual void OnAppearing()
+ {
+ }
+
+ protected virtual bool OnBackButtonPressed()
+ {
+ var application = RealParent as Application;
+ if (application == null || this == application.MainPage)
+ return false;
+
+ var canceled = false;
+ EventHandler handler = (sender, args) => { canceled = true; };
+ application.PopCanceled += handler;
+ Navigation.PopModalAsync().ContinueWith(t => { throw t.Exception; }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
+
+ application.PopCanceled -= handler;
+ return !canceled;
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ base.OnBindingContextChanged();
+ foreach (ToolbarItem toolbarItem in ToolbarItems)
+ {
+ SetInheritedBindingContext(toolbarItem, BindingContext);
+ }
+ }
+
+ protected virtual void OnChildMeasureInvalidated(object sender, EventArgs e)
+ {
+ InvalidationTrigger trigger = (e as InvalidationEventArgs)?.Trigger ?? InvalidationTrigger.Undefined;
+ OnChildMeasureInvalidated((VisualElement)sender, trigger);
+ }
+
+ protected virtual void OnDisappearing()
+ {
+ }
+
+ protected override void OnParentSet()
+ {
+ if (!Application.IsApplicationOrNull(RealParent) && !(RealParent is Page))
+ throw new InvalidOperationException("Parent of a Page must also be a Page");
+ base.OnParentSet();
+ }
+
+ protected override void OnSizeAllocated(double width, double height)
+ {
+ _allocatedFlag = true;
+ base.OnSizeAllocated(width, height);
+ UpdateChildrenLayout();
+ }
+
+ protected void UpdateChildrenLayout()
+ {
+ if (!ShouldLayoutChildren())
+ return;
+
+ var startingLayout = new List<Rectangle>(LogicalChildren.Count);
+ foreach (VisualElement c in LogicalChildren)
+ {
+ startingLayout.Add(c.Bounds);
+ }
+
+ double x = Padding.Left;
+ double y = Padding.Top;
+ double w = Math.Max(0, Width - Padding.HorizontalThickness);
+ double h = Math.Max(0, Height - Padding.VerticalThickness);
+
+ LayoutChildren(x, y, w, h);
+
+ for (var i = 0; i < LogicalChildren.Count; i++)
+ {
+ var c = (VisualElement)LogicalChildren[i];
+
+ if (c.Bounds != startingLayout[i])
+ {
+ EventHandler handler = LayoutChanged;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ return;
+ }
+ }
+ }
+
+ internal virtual void OnChildMeasureInvalidated(VisualElement child, InvalidationTrigger trigger)
+ {
+ var container = this as IPageContainer<Page>;
+ if (container != null)
+ {
+ Page page = container.CurrentPage;
+ if (page != null && page.IsVisible && (!page.IsPlatformEnabled || !page.IsNativeStateConsistent))
+ return;
+ }
+ else
+ {
+ for (var i = 0; i < LogicalChildren.Count; i++)
+ {
+ var v = LogicalChildren[i] as VisualElement;
+ if (v != null && v.IsVisible && (!v.IsPlatformEnabled || !v.IsNativeStateConsistent))
+ return;
+ }
+ }
+
+ _allocatedFlag = false;
+ InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ if (!_allocatedFlag && Width >= 0 && Height >= 0)
+ {
+ SizeAllocated(Width, Height);
+ }
+ }
+
+ internal void SendAppearing()
+ {
+ if (_hasAppeared)
+ return;
+
+ _hasAppeared = true;
+
+ if (IsBusy)
+ MessagingCenter.Send(this, BusySetSignalName, true);
+
+ OnAppearing();
+ EventHandler handler = Appearing;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+
+ var pageContainer = this as IPageContainer<Page>;
+ pageContainer?.CurrentPage?.SendAppearing();
+ }
+
+ internal void SendDisappearing()
+ {
+ if (!_hasAppeared)
+ return;
+
+ _hasAppeared = false;
+
+ if (IsBusy)
+ MessagingCenter.Send(this, BusySetSignalName, false);
+
+ var pageContainer = this as IPageContainer<Page>;
+ pageContainer?.CurrentPage?.SendDisappearing();
+
+ OnDisappearing();
+ EventHandler handler = Disappearing;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+
+ void InternalChildrenOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ if (e.OldItems != null)
+ {
+ foreach (VisualElement item in e.OldItems.OfType<VisualElement>())
+ OnInternalRemoved(item);
+ }
+
+ if (e.NewItems != null)
+ {
+ foreach (VisualElement item in e.NewItems.OfType<VisualElement>())
+ OnInternalAdded(item);
+ }
+ }
+
+ void OnInternalAdded(VisualElement view)
+ {
+ view.MeasureInvalidated += OnChildMeasureInvalidated;
+
+ OnChildAdded(view);
+ InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+
+ void OnInternalRemoved(VisualElement view)
+ {
+ view.MeasureInvalidated -= OnChildMeasureInvalidated;
+
+ OnChildRemoved(view);
+ }
+
+ void OnPageBusyChanged()
+ {
+ if (!_hasAppeared)
+ return;
+
+ MessagingCenter.Send(this, BusySetSignalName, IsBusy);
+ }
+
+ void OnToolbarItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
+ {
+ if (args.Action != NotifyCollectionChangedAction.Add)
+ return;
+ foreach (IElement item in args.NewItems)
+ item.Parent = this;
+ }
+
+ bool ShouldLayoutChildren()
+ {
+ if (!LogicalChildren.Any() || Width <= 0 || Height <= 0 || !IsNativeStateConsistent)
+ return false;
+
+ var container = this as IPageContainer<Page>;
+ if (container != null && container.CurrentPage != null)
+ {
+ if (InternalChildren.Contains(container.CurrentPage))
+ return container.CurrentPage.IsPlatformEnabled && container.CurrentPage.IsNativeStateConsistent;
+ return true;
+ }
+
+ var any = false;
+ for (var i = 0; i < LogicalChildren.Count; i++)
+ {
+ var v = LogicalChildren[i] as VisualElement;
+ if (v != null && (!v.IsPlatformEnabled || !v.IsNativeStateConsistent))
+ {
+ any = true;
+ break;
+ }
+ }
+ return !any;
+ }
+ }
+} \ No newline at end of file