314 строки
8.2 KiB
C#
314 строки
8.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Windows.UI.Popups;
|
|
using Windows.UI.Xaml;
|
|
using Windows.UI.Xaml.Controls;
|
|
using Xamarin.Forms.Internals;
|
|
|
|
namespace Xamarin.Forms.Platform.UWP
|
|
{
|
|
public abstract partial class Platform : IPlatform, INavigation, IToolbarProvider
|
|
{
|
|
internal static readonly BindableProperty RendererProperty = BindableProperty.CreateAttached("Renderer",
|
|
typeof(IVisualElementRenderer), typeof(Platform), default(IVisualElementRenderer));
|
|
|
|
public static IVisualElementRenderer GetRenderer(VisualElement element)
|
|
{
|
|
return (IVisualElementRenderer)element.GetValue(RendererProperty);
|
|
}
|
|
|
|
public static void SetRenderer(VisualElement element, IVisualElementRenderer value)
|
|
{
|
|
element.SetValue(RendererProperty, value);
|
|
element.IsPlatformEnabled = value != null;
|
|
}
|
|
|
|
public static IVisualElementRenderer CreateRenderer(VisualElement element)
|
|
{
|
|
if (element == null)
|
|
throw new ArgumentNullException(nameof(element));
|
|
|
|
IVisualElementRenderer renderer = Registrar.Registered.GetHandlerForObject<IVisualElementRenderer>(element) ??
|
|
new DefaultRenderer();
|
|
renderer.SetElement(element);
|
|
return renderer;
|
|
}
|
|
|
|
internal Platform(Windows.UI.Xaml.Controls.Page page)
|
|
{
|
|
if (page == null)
|
|
throw new ArgumentNullException(nameof(page));
|
|
|
|
_page = page;
|
|
|
|
_container = new Canvas
|
|
{
|
|
Style = (Windows.UI.Xaml.Style)Windows.UI.Xaml.Application.Current.Resources["RootContainerStyle"]
|
|
};
|
|
|
|
_page.Content = _container;
|
|
|
|
_container.SizeChanged += OnRendererSizeChanged;
|
|
|
|
MessagingCenter.Subscribe(this, Page.BusySetSignalName, (Page sender, bool enabled) =>
|
|
{
|
|
Windows.UI.Xaml.Controls.ProgressBar indicator = GetBusyIndicator();
|
|
indicator.Visibility = enabled ? Visibility.Visible : Visibility.Collapsed;
|
|
});
|
|
|
|
_toolbarTracker.CollectionChanged += OnToolbarItemsChanged;
|
|
|
|
UpdateBounds();
|
|
|
|
InitializeStatusBar();
|
|
}
|
|
|
|
internal void SetPage(Page newRoot)
|
|
{
|
|
if (newRoot == null)
|
|
throw new ArgumentNullException(nameof(newRoot));
|
|
|
|
_navModel.Clear();
|
|
|
|
_navModel.Push(newRoot, null);
|
|
SetCurrent(newRoot, true);
|
|
Application.Current.NavigationProxy.Inner = this;
|
|
}
|
|
|
|
internal void SetPlatformDisconnected(VisualElement visualElement)
|
|
{
|
|
visualElement.Platform = this;
|
|
}
|
|
|
|
public IReadOnlyList<Page> NavigationStack
|
|
{
|
|
get { return _navModel.Tree.Last(); }
|
|
}
|
|
|
|
public IReadOnlyList<Page> ModalStack
|
|
{
|
|
get { return _navModel.Modals.ToList(); }
|
|
}
|
|
|
|
Task INavigation.PushAsync(Page root)
|
|
{
|
|
return ((INavigation)this).PushAsync(root, true);
|
|
}
|
|
|
|
Task<Page> INavigation.PopAsync()
|
|
{
|
|
return ((INavigation)this).PopAsync(true);
|
|
}
|
|
|
|
Task INavigation.PopToRootAsync()
|
|
{
|
|
return ((INavigation)this).PopToRootAsync(true);
|
|
}
|
|
|
|
Task INavigation.PushAsync(Page root, bool animated)
|
|
{
|
|
throw new InvalidOperationException("PushAsync is not supported globally on Windows, please use a NavigationPage.");
|
|
}
|
|
|
|
Task<Page> INavigation.PopAsync(bool animated)
|
|
{
|
|
throw new InvalidOperationException("PopAsync is not supported globally on Windows, please use a NavigationPage.");
|
|
}
|
|
|
|
Task INavigation.PopToRootAsync(bool animated)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"PopToRootAsync is not supported globally on Windows, please use a NavigationPage.");
|
|
}
|
|
|
|
void INavigation.RemovePage(Page page)
|
|
{
|
|
throw new InvalidOperationException("RemovePage is not supported globally on Windows, please use a NavigationPage.");
|
|
}
|
|
|
|
void INavigation.InsertPageBefore(Page page, Page before)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"InsertPageBefore is not supported globally on Windows, please use a NavigationPage.");
|
|
}
|
|
|
|
Task INavigation.PushModalAsync(Page page)
|
|
{
|
|
return ((INavigation)this).PushModalAsync(page, true);
|
|
}
|
|
|
|
Task<Page> INavigation.PopModalAsync()
|
|
{
|
|
return ((INavigation)this).PopModalAsync(true);
|
|
}
|
|
|
|
Task INavigation.PushModalAsync(Page page, bool animated)
|
|
{
|
|
if (page == null)
|
|
throw new ArgumentNullException(nameof(page));
|
|
|
|
var tcs = new TaskCompletionSource<bool>();
|
|
_navModel.PushModal(page);
|
|
SetCurrent(page, completedCallback: () => tcs.SetResult(true));
|
|
return tcs.Task;
|
|
}
|
|
|
|
Task<Page> INavigation.PopModalAsync(bool animated)
|
|
{
|
|
var tcs = new TaskCompletionSource<Page>();
|
|
Page result = _navModel.PopModal();
|
|
SetCurrent(_navModel.CurrentPage, true, () => tcs.SetResult(result));
|
|
return tcs.Task;
|
|
}
|
|
|
|
SizeRequest IPlatform.GetNativeSize(VisualElement element, double widthConstraint, double heightConstraint)
|
|
{
|
|
// Hack around the fact that Canvas ignores the child constraints.
|
|
// It is entirely possible using Canvas as our base class is not wise.
|
|
// FIXME: This should not be an if statement. Probably need to define an interface here.
|
|
if (widthConstraint > 0 && heightConstraint > 0)
|
|
{
|
|
IVisualElementRenderer elementRenderer = GetRenderer(element);
|
|
if (elementRenderer != null)
|
|
return elementRenderer.GetDesiredSize(widthConstraint, heightConstraint);
|
|
}
|
|
|
|
return new SizeRequest();
|
|
}
|
|
|
|
internal virtual Rectangle ContainerBounds
|
|
{
|
|
get { return _bounds; }
|
|
}
|
|
|
|
internal void UpdatePageSizes()
|
|
{
|
|
Rectangle bounds = ContainerBounds;
|
|
if (bounds.IsEmpty)
|
|
return;
|
|
foreach (Page root in _navModel.Roots)
|
|
{
|
|
root.Layout(bounds);
|
|
IVisualElementRenderer renderer = GetRenderer(root);
|
|
if (renderer != null)
|
|
{
|
|
renderer.ContainerElement.Width = _container.ActualWidth;
|
|
renderer.ContainerElement.Height = _container.ActualHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle _bounds;
|
|
readonly Canvas _container;
|
|
readonly Windows.UI.Xaml.Controls.Page _page;
|
|
Windows.UI.Xaml.Controls.ProgressBar _busyIndicator;
|
|
Page _currentPage;
|
|
readonly NavigationModel _navModel = new NavigationModel();
|
|
readonly ToolbarTracker _toolbarTracker = new ToolbarTracker();
|
|
readonly FileImageSourcePathConverter _fileImageSourcePathConverter = new FileImageSourcePathConverter();
|
|
|
|
|
|
Windows.UI.Xaml.Controls.ProgressBar GetBusyIndicator()
|
|
{
|
|
if (_busyIndicator == null)
|
|
{
|
|
_busyIndicator = new Windows.UI.Xaml.Controls.ProgressBar
|
|
{
|
|
IsIndeterminate = true,
|
|
Visibility = Visibility.Collapsed,
|
|
VerticalAlignment = VerticalAlignment.Top
|
|
};
|
|
|
|
Canvas.SetZIndex(_busyIndicator, 1);
|
|
_container.Children.Add(_busyIndicator);
|
|
}
|
|
|
|
return _busyIndicator;
|
|
}
|
|
|
|
internal bool BackButtonPressed()
|
|
{
|
|
Page lastRoot = _navModel.Roots.Last();
|
|
|
|
bool handled = lastRoot.SendBackButtonPressed();
|
|
|
|
if (!handled && _navModel.Tree.Count > 1)
|
|
{
|
|
Page removed = _navModel.PopModal();
|
|
if (removed != null)
|
|
{
|
|
SetCurrent(_navModel.CurrentPage, true);
|
|
handled = true;
|
|
}
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
void OnRendererSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs)
|
|
{
|
|
UpdateBounds();
|
|
UpdatePageSizes();
|
|
}
|
|
|
|
async void SetCurrent(Page newPage, bool popping = false, Action completedCallback = null)
|
|
{
|
|
if (newPage == _currentPage)
|
|
return;
|
|
|
|
newPage.Platform = this;
|
|
|
|
if (_currentPage != null)
|
|
{
|
|
Page previousPage = _currentPage;
|
|
IVisualElementRenderer previousRenderer = GetRenderer(previousPage);
|
|
_container.Children.Remove(previousRenderer.ContainerElement);
|
|
|
|
if (popping)
|
|
{
|
|
previousPage.Cleanup();
|
|
// Un-parent the page; otherwise the Resources Changed Listeners won't be unhooked and the
|
|
// page will leak
|
|
previousPage.Parent = null;
|
|
}
|
|
}
|
|
|
|
newPage.Layout(ContainerBounds);
|
|
|
|
IVisualElementRenderer pageRenderer = newPage.GetOrCreateRenderer();
|
|
_container.Children.Add(pageRenderer.ContainerElement);
|
|
|
|
pageRenderer.ContainerElement.Width = _container.ActualWidth;
|
|
pageRenderer.ContainerElement.Height = _container.ActualHeight;
|
|
|
|
completedCallback?.Invoke();
|
|
|
|
_currentPage = newPage;
|
|
|
|
UpdateToolbarTracker();
|
|
|
|
UpdateToolbarTitle(newPage);
|
|
|
|
await UpdateToolbarItems();
|
|
}
|
|
|
|
Task<CommandBar> IToolbarProvider.GetCommandBarAsync()
|
|
{
|
|
return GetCommandBarAsync();
|
|
}
|
|
|
|
async void OnToolbarItemsChanged(object sender, EventArgs e)
|
|
{
|
|
await UpdateToolbarItems();
|
|
}
|
|
|
|
void UpdateToolbarTracker()
|
|
{
|
|
Page last = _navModel.Roots.Last();
|
|
if (last != null)
|
|
_toolbarTracker.Target = last;
|
|
}
|
|
}
|
|
} |