diff options
author | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 13:02:25 -0700 |
---|---|---|
committer | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 16:13:41 -0700 |
commit | 17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch) | |
tree | b5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Core/Page.cs | |
download | xamarin-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.cs | 403 |
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 |