diff options
Diffstat (limited to 'Xamarin.Forms.Platform.WinRT/NavigationPageRenderer.cs')
-rw-r--r-- | Xamarin.Forms.Platform.WinRT/NavigationPageRenderer.cs | 554 |
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 |