summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.WinRT/NavigationPageRenderer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Platform.WinRT/NavigationPageRenderer.cs')
-rw-r--r--Xamarin.Forms.Platform.WinRT/NavigationPageRenderer.cs554
1 files changed, 554 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.WinRT/NavigationPageRenderer.cs b/Xamarin.Forms.Platform.WinRT/NavigationPageRenderer.cs
new file mode 100644
index 00000000..1e48d7ea
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/NavigationPageRenderer.cs
@@ -0,0 +1,554 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Linq;
+using System.Threading.Tasks;
+using Windows.Devices.Input;
+using Windows.UI.Input;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Automation;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Media.Animation;
+#if WINDOWS_UWP
+using Windows.UI.Xaml.Data;
+using Windows.UI.Core;
+
+#endif
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class NavigationPageRenderer : IVisualElementRenderer, ITitleProvider, IToolbarProvider
+#if WINDOWS_UWP
+ , IToolBarForegroundBinder
+#endif
+ {
+ PageControl _container;
+ Page _currentPage;
+ Page _previousPage;
+
+ bool _disposed;
+#if WINDOWS_UWP
+ SystemNavigationManager _navManager;
+#endif
+ MasterDetailPage _parentMasterDetailPage;
+ TabbedPage _parentTabbedPage;
+ bool _showTitle = true;
+ VisualElementTracker<Page, PageControl> _tracker;
+ ContentThemeTransition _transition;
+
+ public NavigationPage Element { get; private set; }
+
+ protected VisualElementTracker<Page, PageControl> Tracker
+ {
+ get { return _tracker; }
+ set
+ {
+ if (_tracker == value)
+ return;
+
+ if (_tracker != null)
+ _tracker.Dispose();
+
+ _tracker = value;
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ Brush ITitleProvider.BarBackgroundBrush
+ {
+ set
+ {
+ _container.NavigationBarBackground = value;
+ UpdateTitleOnParents();
+ }
+ }
+
+ Brush ITitleProvider.BarForegroundBrush
+ {
+ set
+ {
+ _container.TitleBrush = value;
+ UpdateTitleOnParents();
+ }
+ }
+
+ bool ITitleProvider.ShowTitle
+ {
+ get { return _showTitle; }
+ set
+ {
+ if (_showTitle == value)
+ return;
+
+ _showTitle = value;
+ UpdateNavigationBarVisible();
+ UpdateTitleOnParents();
+ }
+ }
+
+ public string Title
+ {
+ get { return _currentPage?.Title; }
+
+ set { }
+ }
+
+ Task<CommandBar> IToolbarProvider.GetCommandBarAsync()
+ {
+ return ((IToolbarProvider)_container)?.GetCommandBarAsync();
+ }
+
+ public FrameworkElement ContainerElement
+ {
+ get { return _container; }
+ }
+
+ VisualElement IVisualElementRenderer.Element
+ {
+ get { return Element; }
+ }
+
+ public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+ public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ var constraint = new Windows.Foundation.Size(widthConstraint, heightConstraint);
+ IVisualElementRenderer childRenderer = Platform.GetRenderer(Element.CurrentPage);
+ FrameworkElement child = childRenderer.ContainerElement;
+
+ double oldWidth = child.Width;
+ double oldHeight = child.Height;
+
+ child.Height = double.NaN;
+ child.Width = double.NaN;
+
+ child.Measure(constraint);
+ var result = new Size(Math.Ceiling(child.DesiredSize.Width), Math.Ceiling(child.DesiredSize.Height));
+
+ child.Width = oldWidth;
+ child.Height = oldHeight;
+
+ return new SizeRequest(result);
+ }
+
+ public void SetElement(VisualElement element)
+ {
+ if (element != null && !(element is NavigationPage))
+ throw new ArgumentException("Element must be a Page", "element");
+
+ NavigationPage oldElement = Element;
+ Element = (NavigationPage)element;
+
+ if (oldElement != null)
+ {
+ oldElement.PushRequested -= OnPushRequested;
+ oldElement.PopRequested -= OnPopRequested;
+ oldElement.InternalChildren.CollectionChanged -= OnChildrenChanged;
+ oldElement.PropertyChanged -= OnElementPropertyChanged;
+ }
+
+ if (element != null)
+ {
+ if (_container == null)
+ {
+ _container = new PageControl();
+ _container.PointerPressed += OnPointerPressed;
+ _container.SizeChanged += OnNativeSizeChanged;
+ _container.BackClicked += OnBackClicked;
+
+ Tracker = new BackgroundTracker<PageControl>(Control.BackgroundProperty) { Element = (Page)element, Container = _container };
+
+ SetPage(Element.CurrentPage, false, false);
+
+ _container.Loaded += OnLoaded;
+ _container.Unloaded += OnUnloaded;
+ }
+
+ _container.DataContext = Element.CurrentPage;
+
+ UpdatePadding();
+ LookupRelevantParents();
+ UpdateTitleColor();
+ UpdateNavigationBarBackground();
+ Element.PropertyChanged += OnElementPropertyChanged;
+ Element.PushRequested += OnPushRequested;
+ Element.PopRequested += OnPopRequested;
+ Element.InternalChildren.CollectionChanged += OnChildrenChanged;
+
+ if (!string.IsNullOrEmpty(Element.AutomationId))
+ _container.SetValue(AutomationProperties.AutomationIdProperty, Element.AutomationId);
+
+ PushExistingNavigationStack();
+ }
+
+ OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+ }
+
+ protected void Dispose(bool disposing)
+ {
+ if (!disposing || _disposed)
+ return;
+ Element?.SendDisappearing();
+ _disposed = true;
+
+ _container.PointerPressed -= OnPointerPressed;
+ _container.SizeChanged -= OnNativeSizeChanged;
+ _container.BackClicked -= OnBackClicked;
+
+ SetElement(null);
+ SetPage(null, false, true);
+ _previousPage = null;
+
+ if (_parentTabbedPage != null)
+ _parentTabbedPage.PropertyChanged -= MultiPagePropertyChanged;
+
+ if (_parentMasterDetailPage != null)
+ _parentMasterDetailPage.PropertyChanged -= MultiPagePropertyChanged;
+ }
+
+ protected void OnElementChanged(VisualElementChangedEventArgs e)
+ {
+ EventHandler<VisualElementChangedEventArgs> changed = ElementChanged;
+ if (changed != null)
+ changed(this, e);
+ }
+
+ Brush GetBarBackgroundBrush()
+ {
+#if WINDOWS_UWP
+ object defaultColor = Windows.UI.Xaml.Application.Current.Resources["SystemControlBackgroundChromeMediumLowBrush"];
+#else
+ object defaultColor = Windows.UI.Xaml.Application.Current.Resources["ApplicationPageBackgroundThemeBrush"];
+#endif
+ if (Element.BarBackgroundColor.IsDefault && defaultColor != null)
+ return (Brush)defaultColor;
+ return Element.BarBackgroundColor.ToBrush();
+ }
+
+ Brush GetBarForegroundBrush()
+ {
+ object defaultColor = Windows.UI.Xaml.Application.Current.Resources["ApplicationForegroundThemeBrush"];
+ if (Element.BarTextColor.IsDefault)
+ return (Brush)defaultColor;
+ return Element.BarTextColor.ToBrush();
+ }
+
+ Task<CommandBar> GetCommandBarAsync()
+ {
+ var platform = (Platform)Element.Platform;
+ IToolbarProvider toolbarProvider = platform.GetToolbarProvider();
+ if (toolbarProvider == null)
+ return Task.FromResult<CommandBar>(null);
+
+ return toolbarProvider.GetCommandBarAsync();
+ }
+
+ bool GetIsNavBarPossible()
+ {
+ return _showTitle;
+ }
+
+ IToolbarProvider GetToolbarProvider()
+ {
+ var platform = (Platform)Element.Platform;
+ return platform.GetToolbarProvider();
+ }
+
+ void LookupRelevantParents()
+ {
+ IEnumerable<Page> parentPages = Element.GetParentPages();
+
+ if (_parentTabbedPage != null)
+ _parentTabbedPage.PropertyChanged -= MultiPagePropertyChanged;
+ if (_parentMasterDetailPage != null)
+ _parentMasterDetailPage.PropertyChanged -= MultiPagePropertyChanged;
+
+ foreach (Page parentPage in parentPages)
+ {
+ _parentTabbedPage = parentPage as TabbedPage;
+ _parentMasterDetailPage = parentPage as MasterDetailPage;
+ }
+
+ if (_parentTabbedPage != null)
+ _parentTabbedPage.PropertyChanged += MultiPagePropertyChanged;
+ if (_parentMasterDetailPage != null)
+ _parentMasterDetailPage.PropertyChanged += MultiPagePropertyChanged;
+#if WINDOWS_UWP
+ ((ITitleProvider)this).ShowTitle = _parentTabbedPage == null && _parentMasterDetailPage == null;
+#else
+ if (Device.Idiom == TargetIdiom.Phone && _parentTabbedPage != null)
+ ((ITitleProvider)this).ShowTitle = false;
+ else
+ ((ITitleProvider)this).ShowTitle = true;
+#endif
+ UpdateTitleOnParents();
+ }
+
+ void MultiPagePropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "CurrentPage" || e.PropertyName == "Detail")
+ UpdateTitleOnParents();
+ }
+
+ async void OnBackClicked(object sender, RoutedEventArgs e)
+ {
+ await Element.PopAsync();
+ }
+
+ void OnChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ UpdateBackButton();
+ }
+
+ void OnCurrentPagePropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == NavigationPage.HasBackButtonProperty.PropertyName)
+ UpdateBackButton();
+ else if (e.PropertyName == NavigationPage.BackButtonTitleProperty.PropertyName)
+ UpdateBackButtonTitle();
+ else if (e.PropertyName == NavigationPage.HasNavigationBarProperty.PropertyName)
+ UpdateNavigationBarVisible();
+ }
+
+ void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == NavigationPage.BarTextColorProperty.PropertyName)
+ UpdateTitleColor();
+ else if (e.PropertyName == NavigationPage.BarBackgroundColorProperty.PropertyName)
+ UpdateNavigationBarBackground();
+ else if (e.PropertyName == Page.PaddingProperty.PropertyName)
+ UpdatePadding();
+ }
+
+ void OnLoaded(object sender, RoutedEventArgs args)
+ {
+ if (Element == null)
+ return;
+
+#if WINDOWS_UWP
+ _navManager = SystemNavigationManager.GetForCurrentView();
+#endif
+ Element.SendAppearing();
+ UpdateBackButton();
+ UpdateTitleOnParents();
+ }
+
+ void OnNativeSizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ UpdateContainerArea();
+ }
+
+ void OnPointerPressed(object sender, PointerRoutedEventArgs e)
+ {
+ if (e.Handled)
+ return;
+
+ PointerPoint point = e.GetCurrentPoint(_container);
+ if (point == null)
+ return;
+
+ if (point.PointerDevice.PointerDeviceType != PointerDeviceType.Mouse)
+ return;
+
+ if (point.Properties.IsXButton1Pressed)
+ {
+ e.Handled = true;
+ OnBackClicked(_container, e);
+ }
+ }
+
+ void OnPopRequested(object sender, NavigationRequestedEventArgs e)
+ {
+ var newCurrent = (Page)Element.InternalChildren[Element.InternalChildren.Count - 2];
+ SetPage(newCurrent, e.Animated, true);
+ }
+
+ void OnPushRequested(object sender, NavigationRequestedEventArgs e)
+ {
+ SetPage(e.Page, e.Animated, false);
+ }
+
+ void OnUnloaded(object sender, RoutedEventArgs args)
+ {
+ if (Element == null)
+ return;
+
+ Element.SendDisappearing();
+ }
+
+ void PushExistingNavigationStack()
+ {
+ for (int i = Element.StackCopy.Count - 1; i >= 0; i--)
+ SetPage(Element.StackCopy.ElementAt(i), false, false);
+ }
+
+ void SetPage(Page page, bool isAnimated, bool isPopping)
+ {
+ if (_currentPage != null)
+ {
+ if (isPopping)
+ _currentPage.Cleanup();
+
+ _container.Content = null;
+
+ _currentPage.PropertyChanged -= OnCurrentPagePropertyChanged;
+ }
+
+ _previousPage = _currentPage;
+ _currentPage = page;
+
+ if (page == null)
+ return;
+
+ UpdateBackButton();
+ UpdateBackButtonTitle();
+
+ page.PropertyChanged += OnCurrentPagePropertyChanged;
+
+ IVisualElementRenderer renderer = page.GetOrCreateRenderer();
+
+ UpdateNavigationBarVisible();
+ UpdateTitleOnParents();
+
+ if (isAnimated && _transition == null)
+ {
+ _transition = new ContentThemeTransition();
+ _container.ContentTransitions = new TransitionCollection();
+ }
+
+ if (!isAnimated && _transition != null)
+ _container.ContentTransitions.Remove(_transition);
+ else if (isAnimated && _container.ContentTransitions.Count == 0)
+ _container.ContentTransitions.Add(_transition);
+
+ _container.Content = renderer.ContainerElement;
+ _container.DataContext = page;
+ }
+
+ void UpdateBackButton()
+ {
+ bool showBackButton = Element.InternalChildren.Count > 1 && NavigationPage.GetHasBackButton(_currentPage);
+ _container.ShowBackButton = showBackButton;
+
+#if WINDOWS_UWP
+ if (_navManager != null)
+ {
+ _navManager.AppViewBackButtonVisibility = showBackButton ? AppViewBackButtonVisibility.Visible : AppViewBackButtonVisibility.Collapsed;
+ }
+#endif
+ }
+
+ void UpdateBackButtonTitle()
+ {
+ string title = null;
+ if (_previousPage != null)
+ title = NavigationPage.GetBackButtonTitle(_previousPage);
+
+ _container.BackButtonTitle = title;
+ }
+
+ void UpdateContainerArea()
+ {
+ Element.ContainerArea = new Rectangle(0, 0, _container.ContentWidth, _container.ContentHeight);
+ }
+
+ void UpdateNavigationBarBackground()
+ {
+ (this as ITitleProvider).BarBackgroundBrush = GetBarBackgroundBrush();
+ }
+
+ void UpdateNavigationBarVisible()
+ {
+ UpdateTitleOnParents();
+
+ bool showing = _container.ShowNavigationBar;
+ bool newValue = GetIsNavBarPossible() && NavigationPage.GetHasNavigationBar(_currentPage);
+ if (showing == newValue)
+ return;
+
+ _container.ShowNavigationBar = newValue;
+
+ // Force ContentHeight/Width to update, doesn't work from inside PageControl for some reason
+ _container.UpdateLayout();
+ UpdateContainerArea();
+ }
+
+ void UpdatePadding()
+ {
+ _container.TitleInset = Element.Padding.Left;
+ }
+
+ void UpdateTitleColor()
+ {
+ (this as ITitleProvider).BarForegroundBrush = GetBarForegroundBrush();
+ }
+
+ async void UpdateTitleOnParents()
+ {
+ if (Element == null)
+ return;
+
+ ITitleProvider render = null;
+ if (_parentTabbedPage != null)
+ {
+ render = Platform.GetRenderer(_parentTabbedPage) as ITitleProvider;
+ if (render != null)
+ render.ShowTitle = (_parentTabbedPage.CurrentPage == Element) && NavigationPage.GetHasNavigationBar(_currentPage);
+ }
+
+ if (_parentMasterDetailPage != null)
+ {
+ render = Platform.GetRenderer(_parentMasterDetailPage) as ITitleProvider;
+ if (render != null)
+ render.ShowTitle = (_parentMasterDetailPage.Detail == Element) && NavigationPage.GetHasNavigationBar(_currentPage);
+ }
+
+ if (render != null && render.ShowTitle)
+ {
+ render.Title = _currentPage.Title;
+ render.BarBackgroundBrush = GetBarBackgroundBrush();
+ render.BarForegroundBrush = GetBarForegroundBrush();
+#if WINDOWS_UWP
+ await (Element.Platform as Platform).UpdateToolbarItems();
+#endif
+ }
+ else if (_showTitle)
+ {
+#if WINDOWS_UWP
+ await (Element.Platform as Platform).UpdateToolbarItems();
+#endif
+ }
+ }
+
+#if WINDOWS_UWP
+ public void BindForegroundColor(AppBar appBar)
+ {
+ SetAppBarForegroundBinding(appBar);
+ }
+
+ public void BindForegroundColor(AppBarButton button)
+ {
+ SetAppBarForegroundBinding(button);
+ }
+
+ void SetAppBarForegroundBinding(FrameworkElement element)
+ {
+ element.SetBinding(Control.ForegroundProperty,
+ new Windows.UI.Xaml.Data.Binding { Path = new PropertyPath("TitleBrush"), Source = _container, RelativeSource = new RelativeSource { Mode = RelativeSourceMode.TemplatedParent } });
+ }
+#endif
+ }
+} \ No newline at end of file