* Stop crashing when I accidentally click on the Perf gallery

* [Core] Add TitleView to NavigationPage

- also add unit tests
- fixes #1716

* [Android] Add ClearRenderer static method to Platform

* [Android] Use Platform.ClearRenderer

it's a straight extract from this class

* [Android] Implement TitleView and TitleIcon on AppCompat backend

using lessons (and some duplicated code) from ListView header/footer views.

* [iOS] Implement TitleView

* [UWP] Implement TitleIcon and TitleView

* [Core] Add NavigationPage.BarHeight

* [Android] Use BarHeight

* NavigationBarGallery updates

* [Core] Add iOS HideNavigationBarSeparator Platform Specific

* [iOS] Implement HideNavigationBarSeparator Platform Specific

* sample search page

* Convert BarHeight to Android platform specific

* Reset BarHeight when leaving the gallery

* Add a sample TitleView xaml page

* VisualElement >> View

* Fix comment

* Improved SearchTitle sample page

* [Core] Set TitleView Parent on Changing instead of Changed

Changing is too late for the iOS layout

* [iOS] Fix layouts in iOS10

* [iOS] Stop content clipping

* Expanded test page

* [iOS] Fix HideNavigationBarSeparator for iOS<11

* [iOS] Layout TitleView with margins

* More tweaks to test page

* [UWP] Fix OnDetailPropertyChanged if/else

* [UWP] Comment empty setters

* Convert commented code to more useful comment.

* Adjust performance test async call
This commit is contained in:
Samantha Houts 2018-06-25 05:00:03 -07:00 коммит произвёл Rui Marinho
Родитель 40bee5c012
Коммит 4d5a1f5482
27 изменённых файлов: 1416 добавлений и 165 удалений

Просмотреть файл

@ -0,0 +1,46 @@
using System;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.ControlGallery.iOS;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportEffect(typeof(SearchbarEffect), "SearchbarEffect")]
namespace Xamarin.Forms.ControlGallery.iOS
{
public class SearchbarEffect : PlatformEffect
{
UIColor _defaultBackColor;
UIColor _defaultTintColor;
UIImage _defaultBackImage;
protected override void OnAttached()
{
if (_defaultBackColor == null)
_defaultBackColor = Control.BackgroundColor;
Control.BackgroundColor = Color.Cornsilk.ToUIColor();
if (Control is UISearchBar searchBar)
{
if (_defaultTintColor == null)
_defaultTintColor = searchBar.BarTintColor;
if (_defaultBackImage == null)
_defaultBackImage = searchBar.BackgroundImage;
searchBar.BarTintColor = Color.Goldenrod.ToUIColor();
searchBar.BackgroundImage = new UIImage();
}
}
protected override void OnDetached()
{
Control.BackgroundColor = _defaultBackColor;
if (Control is UISearchBar searchBar)
{
searchBar.BarTintColor = _defaultTintColor;
searchBar.BackgroundImage = _defaultBackImage;
}
}
}
}

Просмотреть файл

@ -104,6 +104,7 @@
<Compile Include="AttachedStateEffectRenderer.cs" />
<Compile Include="BrokenImageSourceHandler.cs" />
<Compile Include="BrokenNativeControl.cs" />
<Compile Include="SearchbarEffect.cs" />
<Compile Include="CustomRenderer40251.cs" />
<Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" />

Просмотреть файл

@ -6,6 +6,7 @@ using System.Reflection;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
using System.IO;
using System.Threading.Tasks;
#if UITEST
using Xamarin.UITest;
@ -43,7 +44,7 @@ namespace Xamarin.Forms.Controls.Issues
PerformanceViewModel ViewModel => BindingContext as PerformanceViewModel;
protected override async void Init()
protected override void Init()
{
_BuildInfo = GetBuildNumber();
@ -82,7 +83,7 @@ namespace Xamarin.Forms.Controls.Issues
Content = new StackLayout { Children = { testRunRef, nextButton, _PerformanceTracker } };
ViewModel.BenchmarkResults = await PerformanceDataManager.GetScenarioResults(_DeviceIdentifier);
ViewModel.BenchmarkResults = Task.Run(() => PerformanceDataManager.GetScenarioResults(_DeviceIdentifier)).GetAwaiter().GetResult();
nextButton.IsEnabled = true;
nextButton.Text = Next;

Просмотреть файл

@ -386,7 +386,8 @@ namespace Xamarin.Forms.Controls
// avoid NRE for root pages without NavigationBar
if (navigationBehavior == NavigationBehavior.PushAsync && rootPage.GetType () == typeof (CoreNavigationPage))
{
_pages.Add(new GalleryPageFactory(() => new NavigationBarGallery((NavigationPage)rootPage), "NavigationBar Gallery - Legacy"));
_pages.Insert (0, new GalleryPageFactory(() => new NavigationBarGallery((NavigationPage)rootPage), "NavigationBar Gallery - Legacy"));
_pages.Insert(1, new GalleryPageFactory(() => new TitleView(), "TitleView"));
}
var template = new DataTemplate(typeof(TextCell));

Просмотреть файл

@ -1,17 +1,67 @@
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat;
using static Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat.NavigationPage;
namespace Xamarin.Forms.Controls
{
public class NavigationBarGallery : ContentPage
{
NavigationPage _rootNavPage;
public NavigationBarGallery(NavigationPage rootNavPage)
{
_rootNavPage = rootNavPage;
int toggleBarTextColor = 0;
int toggleBarBackgroundColor = 0;
Content = new StackLayout {
ToolbarItems.Add(new ToolbarItem { Text = "Save" });
NavigationPage.SetTitleIcon(this, "coffee.png");
SearchBar searchBar = new SearchBar { HeightRequest = 44, WidthRequest = 100 };
// Note: Large and complex controls, such as ListView and TableView, are not recommended.
var controls = new List<View>
{
searchBar,
new ActivityIndicator{ IsRunning = true },
new BoxView{ BackgroundColor = Color.Red },
new Button{ Text = "Button!"},
new DatePicker{},
new Editor{ Text = "Editor"},
new Entry{ Placeholder = "Entry"},
new Image{ Source = "crimson.jpg", HeightRequest = 44 },
new Label{ Text = "Title View Label!" },
new Picker{ ItemsSource = Enumerable.Range(0,10).Select(i => $"Item {i}").ToList(), Title = "Picker" },
new ProgressBar{ Progress = 50 },
new Slider{},
new Stepper{},
new Switch{},
new TimePicker{}
};
int idx = 0;
NavigationPage.SetTitleView(this, CreateTitleView(controls[idx]));
rootNavPage.On<Android>().SetBarHeight(450);
rootNavPage.On<iOS>().SetPrefersLargeTitles(false);
Content = new ScrollView
{
Content =
new StackLayout
{
Children = {
new Button {
Text = "Go to SearchBarTitlePage",
Command = new Command (() => {
rootNavPage.PushAsync(new SearchBarTitlePage(rootNavPage));
})
},
new Button {
Text = "Change BarTextColor",
Command = new Command (() => {
@ -56,9 +106,221 @@ namespace Xamarin.Forms.Controls
rootNavPage.BarTextColor = Color.White;
rootNavPage.BarBackgroundColor = Color.Black;
})
},
new Button {
Text = "Toggle TitleIcon",
Command = new Command (() => {
var titleIcon = NavigationPage.GetTitleIcon(this);
if (titleIcon == null)
titleIcon = "coffee.png";
else
titleIcon = null;
NavigationPage.SetTitleIcon(this, titleIcon);
})
},
new Button {
Text = "Toggle TitleView",
Command = new Command (() => {
var titleView = NavigationPage.GetTitleView(this);
if (titleView == null)
titleView = CreateTitleView(controls[idx]);
else
titleView = null;
NavigationPage.SetTitleView(this, titleView);
})
},
new Button {
Text = "Next TitleView",
Command = new Command (() => {
idx++;
if(idx >=controls.Count)
idx = 0;
var titleView = CreateTitleView(controls[idx]);
NavigationPage.SetTitleView(this, titleView);
})
},
new Button {
Text = "Toggle Back Title",
Command = new Command (() => {
var backTitle = NavigationPage.GetBackButtonTitle(rootNavPage);
if (backTitle == null)
backTitle= "Go back home";
else
backTitle = null;
NavigationPage.SetBackButtonTitle(rootNavPage, backTitle);
})
},
new Button {
Text = "Toggle Toolbar Item",
Command = new Command (() => {
if (ToolbarItems.Count > 0)
ToolbarItems.Clear();
else
ToolbarItems.Add(new ToolbarItem { Text = "Save" });
})
},
new Button {
Text = "Toggle Title",
Command = new Command (() => {
if (Title == null)
Title = "NavigationBar Gallery - Legacy";
else
Title = null;
})
},
new Button {
Text = "Toggle BarHeight",
Command = new Command (() => {
if (rootNavPage.On<Android>().GetBarHeight() == -1)
rootNavPage.On<Android>().SetBarHeight(450);
else
rootNavPage.ClearValue(BarHeightProperty);
})
}
}
}
};
}
protected override void OnDisappearing()
{
base.OnDisappearing();
_rootNavPage.ClearValue(BarHeightProperty);
}
static View CreateTitleView(View control)
{
control.HorizontalOptions = LayoutOptions.Fill;
control.VerticalOptions = LayoutOptions.CenterAndExpand;
var titleView = new StackLayout
{
Children = { control },
BackgroundColor = Color.FromHex("#ccc"),
Margin = new Thickness(15, 0),
};
return titleView;
}
class SearchBarTitlePage : ContentPage
{
bool _extended = false;
List<string> items = new List<string> { "The Ocean at the End of the Lane", "So Long, and Thanks for All the Fish", "Twenty Thousand Leagues Under the Sea", "Rosencrantz and Guildenstern Are Dead" };
ObservableCollection<string> filtereditems;
SearchBar search;
Button button;
ListView list;
public SearchBarTitlePage(NavigationPage parent)
{
filtereditems = new ObservableCollection<string>(items);
search = new SearchBar { BackgroundColor = Color.Cornsilk, HorizontalOptions = LayoutOptions.FillAndExpand, Margin = new Thickness(10, 0) };
search.Effects.Add(Effect.Resolve($"{Issues.Effects.ResolutionGroupName}.SearchbarEffect"));
search.TextChanged += Search_TextChanged;
list = new ListView
{
ItemsSource = filtereditems
};
parent.BarBackgroundColor = Color.Cornsilk;
parent.BarTextColor = Color.Orange;
NavigationPage.SetBackButtonTitle(parent, "");
switch (Device.RuntimePlatform)
{
case Device.iOS:
button = new Button();
button.Clicked += (s, e) =>
{
ToggleContent(parent);
};
ToggleContent(parent);
break;
default:
NavigationPage.SetTitleView(this, search);
Content = list;
break;
}
}
void ToggleContent(NavigationPage parent)
{
StackLayout topStack = new StackLayout { Children = { button }, BackgroundColor = Color.Cornsilk };
StackLayout layout = new StackLayout { Children = { topStack, list } };
if (_extended)
{
parent.On<iOS>().SetPrefersLargeTitles(false)
.SetHideNavigationBarSeparator(false);
NavigationPage.SetTitleView(this, new StackLayout { Children = { search }, HorizontalOptions = LayoutOptions.Fill });
NavigationPage.SetHasBackButton(this, false);
button.Text = "Expand";
Title = "Small Titles";
}
else
{
topStack.Children.Insert(0, search);
parent.On<iOS>().SetPrefersLargeTitles(true)
.SetHideNavigationBarSeparator(true);
ClearValue(NavigationPage.TitleViewProperty);
NavigationPage.SetHasBackButton(this, true);
button.Text = "Collapse";
Title = "Large Titles";
}
_extended = !_extended;
Content = layout;
}
void Search_TextChanged(object sender, TextChangedEventArgs e)
{
for (int i = 0; i < filtereditems.Count; i++)
{
filtereditems.RemoveAt(0);
}
if (search.Text?.Length >= 3)
{
foreach (var item in items.Where(i => i.ToLower().Contains(search.Text.ToLower())))
{
if (!filtereditems.Contains(item))
filtereditems.Add(item);
}
}
else
{
foreach (var item in items)
{
if (!filtereditems.Contains(item))
filtereditems.Add(item);
}
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.Forms.Controls.GalleryPages.TitleView">
<NavigationPage.TitleView>
<StackLayout>
<Label Text="This is my TitleView" />
<Label Text="I can be a subtitle" />
</StackLayout>
</NavigationPage.TitleView>
<StackLayout>
<Label Text="Welcome to Xamarin.Forms!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>

Просмотреть файл

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Xamarin.Forms.Controls.GalleryPages
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TitleView : ContentPage
{
public TitleView ()
{
InitializeComponent ();
}
}
}

Просмотреть файл

@ -43,6 +43,9 @@
<Compile Update="GalleryPages\VisualStateManagerGalleries\OnPlatformExample.xaml.cs">
<DependentUpon>OnPlatformExample.xaml</DependentUpon>
</Compile>
<EmbeddedResource Update="GalleryPages\TitleView.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="GalleryPages\VisualStateManagerGalleries\ButtonDisabledStatesGallery.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>

Просмотреть файл

@ -338,6 +338,49 @@ namespace Xamarin.Forms.Core.UnitTests
}
[Test]
public void TitleViewSetProperty()
{
var root = new ContentPage();
var nav = new NavigationPage(root);
View target = new View();
NavigationPage.SetTitleView(root, target);
var result = NavigationPage.GetTitleView(root);
Assert.AreSame(result, target);
}
[Test]
public void TitleViewSetsParentWhenAdded()
{
var root = new ContentPage();
var nav = new NavigationPage(root);
View target = new View();
NavigationPage.SetTitleView(root, target);
Assert.AreSame(root, target.Parent);
}
[Test]
public void TitleViewClearsParentWhenRemoved()
{
var root = new ContentPage();
var nav = new NavigationPage(root);
View target = new View();
NavigationPage.SetTitleView(root, target);
NavigationPage.SetTitleView(root, null);
Assert.IsNull(target.Parent);
}
[Test]
public async Task NavigationChangedEventArgs ()
{

Просмотреть файл

@ -27,6 +27,8 @@ namespace Xamarin.Forms
public static readonly BindableProperty TitleIconProperty = BindableProperty.CreateAttached("TitleIcon", typeof(FileImageSource), typeof(NavigationPage), default(FileImageSource));
public static readonly BindableProperty TitleViewProperty = BindableProperty.CreateAttached("TitleView", typeof(View), typeof(NavigationPage), null, propertyChanging: TitleViewPropertyChanging);
static readonly BindablePropertyKey CurrentPagePropertyKey = BindableProperty.CreateReadOnly("CurrentPage", typeof(Page), typeof(NavigationPage), null);
public static readonly BindableProperty CurrentPageProperty = CurrentPagePropertyKey.BindableProperty;
@ -103,6 +105,24 @@ namespace Xamarin.Forms
private set { SetValue(RootPagePropertyKey, value); }
}
static void TitleViewPropertyChanging(BindableObject bindable, object oldValue, object newValue)
{
if (oldValue == newValue)
return;
if (oldValue != null)
{
var oldElem = (View)oldValue;
oldElem.Parent = null;
}
if (newValue != null && bindable != null)
{
var newElem = (View)newValue;
newElem.Parent = (Page)bindable;
}
}
public static string GetBackButtonTitle(BindableObject page)
{
return (string)page.GetValue(BackButtonTitleProperty);
@ -125,6 +145,11 @@ namespace Xamarin.Forms
return (FileImageSource)bindable.GetValue(TitleIconProperty);
}
public static View GetTitleView(BindableObject bindable)
{
return (View)bindable.GetValue(TitleViewProperty);
}
public Task<Page> PopAsync()
{
return PopAsync(true);
@ -222,6 +247,11 @@ namespace Xamarin.Forms
bindable.SetValue(TitleIconProperty, value);
}
public static void SetTitleView(BindableObject bindable, View value)
{
bindable.SetValue(TitleViewProperty, value);
}
protected override bool OnBackButtonPressed()
{
if (CurrentPage.SendBackButtonPressed())

Просмотреть файл

@ -0,0 +1,30 @@
namespace Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat
{
using FormsElement = Forms.NavigationPage;
public static class NavigationPage
{
public static readonly BindableProperty BarHeightProperty = BindableProperty.Create("BarHeight", typeof(int), typeof(NavigationPage), default(int));
public static int GetBarHeight(BindableObject element)
{
return (int)element.GetValue(BarHeightProperty);
}
public static void SetBarHeight(BindableObject element, int value)
{
element.SetValue(BarHeightProperty, value);
}
public static int GetBarHeight(this IPlatformElementConfiguration<Android, FormsElement> config)
{
return GetBarHeight(config.Element);
}
public static IPlatformElementConfiguration<Android, FormsElement> SetBarHeight(this IPlatformElementConfiguration<Android, FormsElement> config, int value)
{
SetBarHeight(config.Element, value);
return config;
}
}
}

Просмотреть файл

@ -72,6 +72,7 @@ namespace Xamarin.Forms.PlatformConfiguration.iOSSpecific
}
#endregion
#region PrefersLargeTitles
public static readonly BindableProperty PrefersLargeTitlesProperty = BindableProperty.Create(nameof(PrefersLargeTitles), typeof(bool), typeof(Page), false);
public static bool GetPrefersLargeTitles(BindableObject element)
@ -94,5 +95,31 @@ namespace Xamarin.Forms.PlatformConfiguration.iOSSpecific
{
return GetPrefersLargeTitles(config.Element);
}
#endregion
#region HideNavigationBarSeparator
public static readonly BindableProperty HideNavigationBarSeparatorProperty = BindableProperty.Create(nameof(HideNavigationBarSeparator), typeof(bool), typeof(Page), false);
public static bool GetHideNavigationBarSeparator(BindableObject element)
{
return (bool)element.GetValue(HideNavigationBarSeparatorProperty);
}
public static void SetHideNavigationBarSeparator(BindableObject element, bool value)
{
element.SetValue(HideNavigationBarSeparatorProperty, value);
}
public static IPlatformElementConfiguration<iOS, FormsElement> SetHideNavigationBarSeparator(this IPlatformElementConfiguration<iOS, FormsElement> config, bool value)
{
SetHideNavigationBarSeparator(config.Element, value);
return config;
}
public static bool HideNavigationBarSeparator(this IPlatformElementConfiguration<iOS, FormsElement> config)
{
return GetHideNavigationBarSeparator(config.Element);
}
#endregion
}
}

Просмотреть файл

@ -23,7 +23,10 @@ using Fragment = Android.Support.V4.App.Fragment;
using FragmentManager = Android.Support.V4.App.FragmentManager;
using FragmentTransaction = Android.Support.V4.App.FragmentTransaction;
using Object = Java.Lang.Object;
using static Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat.NavigationPage;
using static Android.Views.View;
using System.IO;
using Android.Widget;
namespace Xamarin.Forms.Platform.Android.AppCompat
{
@ -45,6 +48,10 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
DrawerLayout _drawerLayout;
MasterDetailPage _masterDetailPage;
bool _toolbarVisible;
IVisualElementRenderer _titleViewRenderer;
Container _titleView;
ImageView _titleIconView;
ImageSource _imageSource;
bool _isAttachedToWindow;
bool _didInitialPushPages;
@ -135,6 +142,22 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
{
_disposed = true;
if (_titleViewRenderer != null)
{
Android.Platform.ClearRenderer(_titleViewRenderer.View);
_titleViewRenderer.Dispose();
_titleViewRenderer = null;
}
_toolbar.RemoveView(_titleView);
_titleView?.Dispose();
_titleView = null;
_toolbar.RemoveView(_titleIconView);
_titleIconView?.Dispose();
_titleIconView = null;
_imageSource = null;
if (_toolbarTracker != null)
{
@ -312,6 +335,10 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
UpdateToolbar();
else if (e.PropertyName == NavigationPage.BarTextColorProperty.PropertyName)
UpdateToolbar();
else if (e.PropertyName == NavigationPage.BackButtonTitleProperty.PropertyName)
UpdateToolbar();
else if (e.PropertyName == BarHeightProperty.PropertyName)
UpdateToolbar();
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
@ -324,6 +351,9 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
int barHeight = ActionBarHeight();
if (Element.IsSet(BarHeightProperty))
barHeight = Element.OnThisPlatform().GetBarHeight();
if (barHeight != _lastActionBarHeight && _lastActionBarHeight > 0)
{
ResetToolbar();
@ -476,6 +506,9 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
UpdateToolbar();
else if (e.PropertyName == NavigationPage.HasBackButtonProperty.PropertyName)
UpdateToolbar();
else if (e.PropertyName == NavigationPage.TitleIconProperty.PropertyName ||
e.PropertyName == NavigationPage.TitleViewProperty.PropertyName)
UpdateToolbar();
}
#pragma warning disable 1998 // considered for removal
@ -646,6 +679,20 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
{
AToolbar oldToolbar = _toolbar;
if (_titleViewRenderer != null)
{
Android.Platform.ClearRenderer(_titleViewRenderer.View);
_titleViewRenderer = null;
}
_toolbar.RemoveView(_titleView);
_titleView = null;
_toolbar.RemoveView(_titleIconView);
_titleIconView = null;
_imageSource = null;
_toolbar.RemoveFromParent();
_toolbar.SetNavigationOnClickListener(null);
_toolbar = null;
@ -852,6 +899,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
bool isNavigated = ((INavigationPageController)Element).StackDepth > 1;
bar.NavigationIcon = null;
Page currentPage = Element.CurrentPage;
if (isNavigated)
{
@ -861,7 +909,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
toggle.SyncState();
}
if (NavigationPage.GetHasBackButton(Element.CurrentPage))
if (NavigationPage.GetHasBackButton(currentPage))
{
var icon = new DrawerArrowDrawable(activity.SupportActionBar.ThemedContext);
icon.Progress = 1;
@ -905,7 +953,112 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
if (!textColor.IsDefault)
bar.SetTitleTextColor(textColor.ToAndroid().ToArgb());
bar.Title = Element.CurrentPage.Title ?? "";
bar.Title = currentPage.Title ?? "";
UpdateTitleIcon();
UpdateTitleView();
}
void UpdateTitleIcon()
{
Page currentPage = Element.CurrentPage;
var source = NavigationPage.GetTitleIcon(currentPage);
if (source == null)
{
_toolbar.RemoveView(_titleIconView);
_titleIconView?.Dispose();
_titleIconView = null;
_imageSource = null;
return;
}
if (_titleIconView == null)
{
_titleIconView = new ImageView(Context);
_toolbar.AddView(_titleIconView, 0);
}
UpdateBitmap(source, _imageSource);
_imageSource = source;
}
async void UpdateBitmap(ImageSource source, ImageSource previousSource = null)
{
if (Equals(source, previousSource))
return;
_titleIconView.SetImageResource(global::Android.Resource.Color.Transparent);
Bitmap bitmap = null;
IImageSourceHandler handler;
if (source != null && (handler = Registrar.Registered.GetHandlerForObject<IImageSourceHandler>(source)) != null)
{
try
{
bitmap = await handler.LoadImageAsync(source, Context);
}
catch (TaskCanceledException)
{
}
catch (IOException ex)
{
Internals.Log.Warning("Xamarin.Forms.Platform.Android.AppCompat.NavigationPageRenderer", "Error updating bitmap: {0}", ex);
}
}
if (bitmap == null && source is FileImageSource)
_titleIconView.SetImageResource(ResourceManager.GetDrawableByName(((FileImageSource)source).File));
else
_titleIconView.SetImageBitmap(bitmap);
bitmap?.Dispose();
}
void UpdateTitleView()
{
AToolbar bar = _toolbar;
if (bar == null)
return;
Page currentPage = Element.CurrentPage;
VisualElement titleView = NavigationPage.GetTitleView(currentPage);
if (_titleViewRenderer != null)
{
var reflectableType = _titleViewRenderer as System.Reflection.IReflectableType;
var rendererType = reflectableType != null ? reflectableType.GetTypeInfo().AsType() : _titleViewRenderer.GetType();
if (titleView == null || Registrar.Registered.GetHandlerTypeForObject(titleView) != rendererType)
{
if (_titleView != null)
_titleView.Child = null;
Android.Platform.ClearRenderer(_titleViewRenderer.View);
_titleViewRenderer.Dispose();
_titleViewRenderer = null;
}
}
if (titleView == null)
return;
if (_titleViewRenderer != null)
_titleViewRenderer.SetElement(titleView);
else
{
_titleViewRenderer = Android.Platform.CreateRenderer(titleView, Context);
if (_titleView == null)
{
_titleView = new Container(Context);
bar.AddView(_titleView);
}
_titleView.Child = _titleViewRenderer;
}
Android.Platform.SetRenderer(titleView, _titleViewRenderer);
}
void AddTransitionTimer(TaskCompletionSource<bool> tcs, Fragment fragment, FragmentManager fragmentManager, IReadOnlyCollection<Fragment> fragmentsToRemove, int duration, bool shouldUpdateToolbar)
@ -969,6 +1122,66 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
}
}
internal class Container : ViewGroup
{
IVisualElementRenderer _child;
public Container(IntPtr p, global::Android.Runtime.JniHandleOwnership o) : base(p, o)
{
// Added default constructor to prevent crash in Dispose
}
public Container(Context context) : base(context)
{
}
public IVisualElementRenderer Child
{
set
{
if (_child != null)
RemoveView(_child.View);
_child = value;
if (value != null)
AddView(value.View);
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
if (_child == null)
return;
_child.UpdateLayout();
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (_child == null)
{
SetMeasuredDimension(0, 0);
return;
}
VisualElement element = _child.Element;
Context ctx = Context;
var width = (int)ctx.FromPixels(MeasureSpecFactory.GetSize(widthMeasureSpec));
SizeRequest request = _child.Element.Measure(width, double.PositiveInfinity, MeasureFlags.IncludeMargins);
Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion(_child.Element, new Rectangle(0, 0, width, request.Request.Height));
int widthSpec = MeasureSpecFactory.MakeMeasureSpec((int)ctx.ToPixels(width), MeasureSpecMode.Exactly);
int heightSpec = MeasureSpecFactory.MakeMeasureSpec((int)ctx.ToPixels(request.Request.Height), MeasureSpecMode.Exactly);
_child.View.Measure(widthMeasureSpec, heightMeasureSpec);
SetMeasuredDimension(widthSpec, heightSpec);
}
}
class DrawerMultiplexedListener : Object, DrawerLayout.IDrawerListener
{
public List<DrawerLayout.IDrawerListener> Listeners { get; } = new List<DrawerLayout.IDrawerListener>(2);

Просмотреть файл

@ -17,6 +17,7 @@ using Android.Widget;
using Xamarin.Forms.Platform.Android.AppCompat;
using FragmentManager = Android.Support.V4.App.FragmentManager;
using Xamarin.Forms.Internals;
using AView = Android.Views.View;
namespace Xamarin.Forms.Platform.Android
{
@ -298,6 +299,23 @@ namespace Xamarin.Forms.Platform.Android
throw new InvalidOperationException("RemovePage is not supported globally on Android, please use a NavigationPage.");
}
public static void ClearRenderer(AView renderedView)
{
var element = (renderedView as IVisualElementRenderer)?.Element;
var view = element as View;
if (view != null)
{
var renderer = GetRenderer(view);
if (renderer == renderedView)
element.ClearValue(RendererProperty);
renderer?.Dispose();
renderer = null;
}
var layout = view as IVisualElementRenderer;
layout?.Dispose();
layout = null;
}
[Obsolete("CreateRenderer(VisualElement) is obsolete as of version 2.5. Please use CreateRendererWithContext(VisualElement, Context) instead.")]
public static IVisualElementRenderer CreateRenderer(VisualElement element)
{

Просмотреть файл

@ -57,7 +57,7 @@ namespace Xamarin.Forms.Platform.Android
{
if (_headerRenderer != null)
{
ClearRenderer(_headerRenderer.View);
Platform.ClearRenderer(_headerRenderer.View);
_headerRenderer.Dispose();
_headerRenderer = null;
}
@ -67,7 +67,7 @@ namespace Xamarin.Forms.Platform.Android
if (_footerRenderer != null)
{
ClearRenderer(_footerRenderer.View);
Platform.ClearRenderer(_footerRenderer.View);
_footerRenderer.Dispose();
_footerRenderer = null;
}
@ -286,23 +286,6 @@ namespace Xamarin.Forms.Platform.Android
Control.SetSelectionFromTop(realPositionWithHeader, y);
}
void ClearRenderer(AView renderedView)
{
var element = (renderedView as IVisualElementRenderer)?.Element;
var view = element as View;
if (view != null)
{
var renderer = Platform.GetRenderer(view);
if (renderer == renderedView)
element.ClearValue(Platform.RendererProperty);
renderer?.Dispose();
renderer = null;
}
var layout = view as IVisualElementRenderer;
layout?.Dispose();
layout = null;
}
void UpdateFooter()
{
var footer = (VisualElement)Controller.FooterElement;
@ -314,7 +297,7 @@ namespace Xamarin.Forms.Platform.Android
{
if (_footerView != null)
_footerView.Child = null;
ClearRenderer(_footerRenderer.View);
Platform.ClearRenderer(_footerRenderer.View);
_footerRenderer.Dispose();
_footerRenderer = null;
}
@ -346,7 +329,7 @@ namespace Xamarin.Forms.Platform.Android
{
if (_headerView != null)
_headerView.Child = null;
ClearRenderer(_headerRenderer.View);
Platform.ClearRenderer(_headerRenderer.View);
_headerRenderer.Dispose();
_headerRenderer = null;
}

Просмотреть файл

@ -1,8 +1,11 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
using Xamarin.Forms.Internals;
using WImageSource = Windows.UI.Xaml.Media.ImageSource;
namespace Xamarin.Forms.Platform.UWP
{
@ -28,6 +31,27 @@ namespace Xamarin.Forms.Platform.UWP
self.SetBinding(property, new Windows.UI.Xaml.Data.Binding { Path = new PropertyPath(path), Converter = converter });
}
public static async Task<WImageSource> ToWindowsImageSource(this ImageSource source)
{
IImageSourceHandler handler;
if (source != null && (handler = Registrar.Registered.GetHandlerForObject<IImageSourceHandler>(source)) != null)
{
try
{
return await handler.LoadImageAsync(source);
}
catch (OperationCanceledException)
{
return null;
}
}
else
{
return null;
}
}
internal static InputScopeNameValue GetKeyboardButtonType(this ReturnType returnType)
{
switch (returnType)

Просмотреть файл

@ -0,0 +1,7 @@
namespace Xamarin.Forms.Platform.UWP
{
internal interface ITitleIconProvider
{
Windows.UI.Xaml.Media.ImageSource TitleIcon { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,7 @@
namespace Xamarin.Forms.Platform.UWP
{
internal interface ITitleViewProvider
{
View TitleView { get; set; }
}
}

Просмотреть файл

@ -4,6 +4,7 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
using WImageSource = Windows.UI.Xaml.Media.ImageSource;
namespace Xamarin.Forms.Platform.UWP
{
@ -33,6 +34,10 @@ namespace Xamarin.Forms.Platform.UWP
public static readonly DependencyProperty DetailTitleProperty = DependencyProperty.Register("DetailTitle", typeof(string), typeof(MasterDetailControl), new PropertyMetadata(default(string)));
public static readonly DependencyProperty DetailTitleIconProperty = DependencyProperty.Register(nameof(DetailTitleIcon), typeof(WImageSource), typeof(MasterDetailControl), new PropertyMetadata(default(WImageSource)));
public static readonly DependencyProperty DetailTitleViewProperty = DependencyProperty.Register(nameof(DetailTitleView), typeof(View), typeof(MasterDetailControl), new PropertyMetadata(default(View), OnTitleViewPropertyChanged));
public static readonly DependencyProperty ToolbarForegroundProperty = DependencyProperty.Register("ToolbarForeground", typeof(Brush), typeof(MasterDetailControl),
new PropertyMetadata(default(Brush)));
@ -45,6 +50,9 @@ namespace Xamarin.Forms.Platform.UWP
public static readonly DependencyProperty DetailTitleVisibilityProperty = DependencyProperty.Register("DetailTitleVisibility", typeof(Visibility), typeof(MasterDetailControl),
new PropertyMetadata(default(Visibility)));
public static readonly DependencyProperty DetailTitleViewVisibilityProperty = DependencyProperty.Register(nameof(DetailTitleViewVisibility), typeof(Visibility), typeof(MasterDetailControl),
new PropertyMetadata(default(Visibility)));
public static readonly DependencyProperty MasterToolbarVisibilityProperty = DependencyProperty.Register("MasterToolbarVisibility", typeof(Visibility), typeof(MasterDetailControl),
new PropertyMetadata(default(Visibility)));
@ -66,6 +74,7 @@ namespace Xamarin.Forms.Platform.UWP
FrameworkElement _detailPresenter;
SplitView _split;
ToolbarPlacement _toolbarPlacement;
FrameworkElement _titleViewPresenter;
public MasterDetailControl()
{
@ -110,12 +119,30 @@ namespace Xamarin.Forms.Platform.UWP
set { SetValue(DetailTitleProperty, value); }
}
public WImageSource DetailTitleIcon
{
get { return (WImageSource)GetValue(DetailTitleIconProperty); }
set { SetValue(DetailTitleIconProperty, value); }
}
public View DetailTitleView
{
get { return (View)GetValue(DetailTitleViewProperty); }
set { SetValue(DetailTitleViewProperty, value); }
}
public Visibility DetailTitleVisibility
{
get { return (Visibility)GetValue(DetailTitleVisibilityProperty); }
set { SetValue(DetailTitleVisibilityProperty, value); }
}
public Visibility DetailTitleViewVisibility
{
get { return (Visibility)GetValue(DetailTitleViewVisibilityProperty); }
set { SetValue(DetailTitleViewVisibilityProperty, value); }
}
public bool IsPaneOpen
{
get { return (bool)GetValue(IsPaneOpenProperty); }
@ -260,6 +287,7 @@ namespace Xamarin.Forms.Platform.UWP
_masterPresenter = GetTemplateChild("MasterPresenter") as FrameworkElement;
_detailPresenter = GetTemplateChild("DetailPresenter") as FrameworkElement;
_titleViewPresenter = GetTemplateChild("TitleViewPresenter") as FrameworkElement;
_commandBar = GetTemplateChild("CommandBar") as CommandBar;
_toolbarPlacementHelper.Initialize(_commandBar, () => ToolbarPlacement, GetTemplateChild);
@ -285,11 +313,24 @@ namespace Xamarin.Forms.Platform.UWP
((MasterDetailControl)dependencyObject).UpdateMode();
}
static void OnTitleViewPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
((MasterDetailControl)dependencyObject).UpdateTitleViewPresenter();
}
void OnToggleClicked(object sender, RoutedEventArgs args)
{
IsPaneOpen = !IsPaneOpen;
}
void OnTitleViewPresenterLoaded(object sender, RoutedEventArgs e)
{
if (DetailTitleView == null || _titleViewPresenter == null || _commandBar == null)
return;
_titleViewPresenter.Width = _commandBar.ActualWidth;
}
void UpdateMode()
{
if (_split == null)
@ -324,5 +365,23 @@ namespace Xamarin.Forms.Platform.UWP
_firstLoad = true;
}
void UpdateTitleViewPresenter()
{
if (DetailTitleView == null)
{
DetailTitleViewVisibility = Visibility.Collapsed;
if (_titleViewPresenter != null)
_titleViewPresenter.Loaded -= OnTitleViewPresenterLoaded;
}
else
{
DetailTitleViewVisibility = Visibility.Visible;
if (_titleViewPresenter != null)
_titleViewPresenter.Loaded += OnTitleViewPresenterLoaded;
}
}
}
}

Просмотреть файл

@ -30,19 +30,30 @@
</Grid.RowDefinitions>
<Border x:Name="TopCommandBarArea" HorizontalAlignment="Stretch" Background="{TemplateBinding ToolbarBackground}">
<uwp:FormsCommandBar x:Name="CommandBar" Background="{TemplateBinding ToolbarBackground}" MinHeight="{ThemeResource TitleBarHeight}">
<uwp:FormsCommandBar x:Name="CommandBar" Background="{TemplateBinding ToolbarBackground}" MinHeight="{ThemeResource TitleBarHeight}" HorizontalAlignment="Stretch">
<uwp:FormsCommandBar.Content>
<Border x:Name="TitleArea" Height="{ThemeResource TitleBarHeight}" Visibility="{TemplateBinding DetailTitleVisibility}">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Background="{TemplateBinding ToolbarBackground}" >
<Border x:Name="TitleArea" Height="{ThemeResource TitleBarHeight}" Visibility="{TemplateBinding DetailTitleVisibility}" HorizontalAlignment="Stretch">
<Grid x:Name="TitleViewPresenter" VerticalAlignment="Center" Background="{TemplateBinding ToolbarBackground}" HorizontalAlignment="Stretch">
<Button Name="ContentTogglePane" Style="{ThemeResource PaneButton}" Foreground="{TemplateBinding ToolbarForeground}"
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Name="ContentTogglePane" Style="{ThemeResource PaneButton}" Foreground="{TemplateBinding ToolbarForeground}"
Visibility="{TemplateBinding ContentTogglePaneButtonVisibility}" />
<Border Height="{ThemeResource TitleBarHeight}" Visibility="{TemplateBinding DetailTitleVisibility}">
<Image Grid.Column="1" Source="{TemplateBinding DetailTitleIcon}" />
<Border Grid.Column="2" Height="{ThemeResource TitleBarHeight}" Visibility="{TemplateBinding DetailTitleVisibility}">
<TextBlock Text="{TemplateBinding DetailTitle}" VerticalAlignment="Center" Margin="10,0,0,0" Foreground="{TemplateBinding ToolbarForeground}" Style="{ThemeResource TitleTextBlockStyle}" />
</Border>
</StackPanel>
<ContentPresenter Grid.Column="3" Content="{Binding DetailTitleView, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource ViewToRenderer}}" Visibility="{TemplateBinding DetailTitleViewVisibility}" HorizontalAlignment="Stretch" />
</Grid>
</Border>
</uwp:FormsCommandBar.Content>
</uwp:FormsCommandBar>

Просмотреть файл

@ -7,10 +7,11 @@ using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
using Specifics = Xamarin.Forms.PlatformConfiguration.WindowsSpecific.MasterDetailPage;
using WImageSource = Windows.UI.Xaml.Media.ImageSource;
namespace Xamarin.Forms.Platform.UWP
{
public class MasterDetailPageRenderer : IVisualElementRenderer, IToolbarProvider, ITitleProvider, IToolBarForegroundBinder
public class MasterDetailPageRenderer : IVisualElementRenderer, IToolbarProvider, ITitleProvider, ITitleIconProvider, ITitleViewProvider, IToolBarForegroundBinder
{
Page _master;
Page _detail;
@ -97,6 +98,27 @@ namespace Xamarin.Forms.Platform.UWP
get { return Element; }
}
WImageSource ITitleIconProvider.TitleIcon
{
get { return Control?.DetailTitleIcon; }
set
{
if (Control != null)
Control.DetailTitleIcon = value;
}
}
View ITitleViewProvider.TitleView
{
get => Control?.DetailTitleView;
set
{
if (Control != null)
Control.DetailTitleView = value;
}
}
#pragma warning disable 0067 // Revisit: Can't remove; required by interface
public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
#pragma warning restore
@ -176,6 +198,10 @@ namespace Xamarin.Forms.Platform.UWP
{
((ITitleProvider)this).ShowTitle = false;
var titleView = ((ITitleViewProvider)this).TitleView;
titleView?.ClearValue(Platform.RendererProperty);
titleView = null;
if (_detail == null)
return;
@ -219,8 +245,18 @@ namespace Xamarin.Forms.Platform.UWP
void OnDetailPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == Page.TitleProperty.PropertyName || e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName)
if (e.PropertyName == Page.TitleProperty.PropertyName)
UpdateDetailTitle();
else if (e.PropertyName == NavigationPage.TitleIconProperty.PropertyName)
UpdateDetailTitleIcon();
else if (e.PropertyName == NavigationPage.TitleViewProperty.PropertyName)
UpdateDetailTitleView();
else if (e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName)
{
UpdateDetailTitle();
UpdateDetailTitleIcon();
UpdateDetailTitleView();
}
}
void OnIsPaneOpenChanged(DependencyObject sender, DependencyProperty dp)
@ -268,6 +304,8 @@ namespace Xamarin.Forms.Platform.UWP
Control.Detail = element;
UpdateDetailTitle();
UpdateDetailTitleIcon();
UpdateDetailTitleView();
}
void UpdateDetailTitle()
@ -279,6 +317,24 @@ namespace Xamarin.Forms.Platform.UWP
(this as ITitleProvider).ShowTitle = !string.IsNullOrEmpty(Control.DetailTitle);
}
async void UpdateDetailTitleIcon()
{
if (_detail == null)
return;
Control.DetailTitleIcon = await NavigationPage.GetTitleIcon(_detail).ToWindowsImageSource();
Control.InvalidateMeasure();
}
void UpdateDetailTitleView()
{
if (_detail == null)
return;
Control.DetailTitleView = NavigationPage.GetTitleView(_detail) as View;
Control.InvalidateMeasure();
}
void UpdateFlowDirection()
{
Control.UpdateFlowDirection(Element);
@ -317,6 +373,8 @@ namespace Xamarin.Forms.Platform.UWP
void UpdateMode()
{
UpdateDetailTitle();
UpdateDetailTitleIcon();
UpdateDetailTitleView();
Control.CollapseStyle = Element.OnThisPlatform().GetCollapseStyle();
Control.CollapsedPaneWidth = Element.OnThisPlatform().CollapsedPaneWidth();
Control.ShouldShowSplitMode = Element.ShouldShowSplitMode;

Просмотреть файл

@ -11,12 +11,14 @@ using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using Xamarin.Forms.Internals;
using static Xamarin.Forms.PlatformConfiguration.WindowsSpecific.Page;
using WImageSource = Windows.UI.Xaml.Media.ImageSource;
using Windows.UI.Core;
namespace Xamarin.Forms.Platform.UWP
{
public partial class NavigationPageRenderer : IVisualElementRenderer, ITitleProvider, IToolbarProvider
public partial class NavigationPageRenderer : IVisualElementRenderer, ITitleProvider, ITitleIconProvider, ITitleViewProvider, IToolbarProvider
{
PageControl _container;
Page _currentPage;
@ -27,6 +29,7 @@ namespace Xamarin.Forms.Platform.UWP
MasterDetailPage _parentMasterDetailPage;
TabbedPage _parentTabbedPage;
bool _showTitle = true;
WImageSource _titleIcon;
VisualElementTracker<Page, PageControl> _tracker;
EntranceThemeTransition _transition;
@ -88,7 +91,25 @@ namespace Xamarin.Forms.Platform.UWP
{
get { return _currentPage?.Title; }
set { }
set { /*Not implemented but required by interface*/ }
}
public WImageSource TitleIcon
{
get => _titleIcon;
set => _titleIcon = value;
}
public View TitleView
{
get
{
if (_currentPage == null)
return null;
return NavigationPage.GetTitleView(_currentPage) as View;
}
set { /*Not implemented but required by interface*/ }
}
Task<CommandBar> IToolbarProvider.GetCommandBarAsync()
@ -175,6 +196,8 @@ namespace Xamarin.Forms.Platform.UWP
UpdateTitleColor();
UpdateNavigationBarBackground();
UpdateToolbarPlacement();
UpdateTitleIcon();
UpdateTitleView();
// Enforce consistency rules on toolbar (show toolbar if top-level page is Navigation Page)
_container.ShouldShowToolbar = _parentMasterDetailPage == null && _parentTabbedPage == null;
@ -283,12 +306,20 @@ namespace Xamarin.Forms.Platform.UWP
UpdateShowTitle();
UpdateTitleOnParents();
UpdateTitleIcon();
UpdateTitleView();
}
void MultiPagePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "CurrentPage" || e.PropertyName == "Detail")
{
UpdateTitleOnParents();
UpdateTitleIcon();
UpdateTitleView();
}
}
async void OnBackClicked(object sender, RoutedEventArgs e)
@ -311,6 +342,10 @@ namespace Xamarin.Forms.Platform.UWP
UpdateTitleVisible();
else if (e.PropertyName == Page.TitleProperty.PropertyName)
UpdateTitleOnParents();
else if (e.PropertyName == NavigationPage.TitleIconProperty.PropertyName)
UpdateTitleIcon();
else if (e.PropertyName == NavigationPage.TitleViewProperty.PropertyName)
UpdateTitleView();
}
void OnElementAppearing(object sender, EventArgs e)
@ -327,9 +362,12 @@ namespace Xamarin.Forms.Platform.UWP
UpdateNavigationBarBackground();
else if (e.PropertyName == Page.PaddingProperty.PropertyName)
UpdatePadding();
else if (e.PropertyName == PlatformConfiguration.WindowsSpecific.Page.ToolbarPlacementProperty.PropertyName)
else if (e.PropertyName == ToolbarPlacementProperty.PropertyName)
UpdateToolbarPlacement();
else if (e.PropertyName == NavigationPage.TitleIconProperty.PropertyName)
UpdateTitleIcon();
else if (e.PropertyName == NavigationPage.TitleViewProperty.PropertyName)
UpdateTitleView();
}
void OnLoaded(object sender, RoutedEventArgs args)
@ -424,6 +462,8 @@ namespace Xamarin.Forms.Platform.UWP
UpdateTitleVisible();
UpdateTitleOnParents();
UpdateTitleIcon();
UpdateTitleView();
if (isAnimated && _transition == null)
{
@ -484,5 +524,34 @@ namespace Xamarin.Forms.Platform.UWP
{
(this as ITitleProvider).BarForegroundBrush = GetBarForegroundBrush();
}
async void UpdateTitleIcon()
{
if (_currentPage == null)
return;
ImageSource source = NavigationPage.GetTitleIcon(_currentPage);
_titleIcon = await source.ToWindowsImageSource();
_container.TitleIcon = _titleIcon;
if (_parentMasterDetailPage != null && Platform.GetRenderer(_parentMasterDetailPage) is ITitleIconProvider parent)
parent.TitleIcon = _titleIcon;
_container.UpdateLayout();
UpdateContainerArea();
}
void UpdateTitleView()
{
if (_currentPage == null)
return;
_container.TitleView = TitleView;
if (_parentMasterDetailPage != null && Platform.GetRenderer(_parentMasterDetailPage) is ITitleViewProvider parent)
parent.TitleView = TitleView;
}
}
}

Просмотреть файл

@ -1,8 +1,9 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
using WImageSource = Windows.UI.Xaml.Media.ImageSource;
namespace Xamarin.Forms.Platform.UWP
{
@ -24,12 +25,19 @@ namespace Xamarin.Forms.Platform.UWP
public static readonly DependencyProperty ContentMarginProperty = DependencyProperty.Register("ContentMargin", typeof(Windows.UI.Xaml.Thickness), typeof(PageControl),
new PropertyMetadata(default(Windows.UI.Xaml.Thickness)));
public static readonly DependencyProperty TitleIconProperty = DependencyProperty.Register(nameof(TitleIcon), typeof(WImageSource), typeof(PageControl), new PropertyMetadata(default(WImageSource)));
public static readonly DependencyProperty TitleViewProperty = DependencyProperty.Register(nameof(TitleView), typeof(View), typeof(PageControl), new PropertyMetadata(default(View), OnTitleViewPropertyChanged));
public static readonly DependencyProperty TitleViewVisibilityProperty = DependencyProperty.Register(nameof(TitleViewVisibility), typeof(Visibility), typeof(PageControl), new PropertyMetadata(Visibility.Collapsed));
public static readonly DependencyProperty TitleInsetProperty = DependencyProperty.Register("TitleInset", typeof(double), typeof(PageControl), new PropertyMetadata(default(double)));
public static readonly DependencyProperty TitleBrushProperty = DependencyProperty.Register("TitleBrush", typeof(Brush), typeof(PageControl), new PropertyMetadata(null));
AppBarButton _backButton;
CommandBar _commandBar;
FrameworkElement _titleViewPresenter;
ToolbarPlacement _toolbarPlacement;
readonly ToolbarPlacementHelper _toolbarPlacementHelper = new ToolbarPlacementHelper();
@ -40,6 +48,18 @@ namespace Xamarin.Forms.Platform.UWP
set { _toolbarPlacementHelper.ShouldShowToolBar = value; }
}
public WImageSource TitleIcon
{
get { return (WImageSource)GetValue(TitleIconProperty); }
set { SetValue(TitleIconProperty, value); }
}
public View TitleView
{
get { return (View)GetValue(TitleViewProperty); }
set { SetValue(TitleViewProperty, value); }
}
TaskCompletionSource<CommandBar> _commandBarTcs;
Windows.UI.Xaml.Controls.ContentPresenter _presenter;
@ -105,6 +125,12 @@ namespace Xamarin.Forms.Platform.UWP
set { SetValue(TitleVisibilityProperty, value); }
}
public Visibility TitleViewVisibility
{
get { return (Visibility)GetValue(TitleViewVisibilityProperty); }
set { SetValue(TitleViewVisibilityProperty, value); }
}
public Brush TitleBrush
{
get { return (Brush)GetValue(TitleBrushProperty); }
@ -139,6 +165,8 @@ namespace Xamarin.Forms.Platform.UWP
_presenter = GetTemplateChild("presenter") as Windows.UI.Xaml.Controls.ContentPresenter;
_titleViewPresenter = GetTemplateChild("TitleViewPresenter") as FrameworkElement;
_commandBar = GetTemplateChild("CommandBar") as CommandBar;
@ -165,6 +193,19 @@ namespace Xamarin.Forms.Platform.UWP
((PageControl)dependencyObject).UpdateBackButton();
}
static void OnTitleViewPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
((PageControl)dependencyObject).UpdateTitleViewPresenter();
}
void OnTitleViewPresenterLoaded(object sender, RoutedEventArgs e)
{
if (TitleView == null || _titleViewPresenter == null || _commandBar == null)
return;
_titleViewPresenter.Width = _commandBar.ActualWidth;
}
void UpdateBackButton()
{
if (_backButton == null)
@ -177,5 +218,23 @@ namespace Xamarin.Forms.Platform.UWP
_backButton.Opacity = ShowBackButton ? 1 : 0;
}
void UpdateTitleViewPresenter()
{
if (TitleView == null)
{
TitleViewVisibility = Visibility.Collapsed;
if (_titleViewPresenter != null)
_titleViewPresenter.Loaded -= OnTitleViewPresenterLoaded;
}
else
{
TitleViewVisibility = Visibility.Visible;
if (_titleViewPresenter != null)
_titleViewPresenter.Loaded += OnTitleViewPresenterLoaded;
}
}
}
}

Просмотреть файл

@ -17,11 +17,27 @@
</Grid.RowDefinitions>
<Border x:Name="TopCommandBarArea" HorizontalAlignment="Stretch" Background="{TemplateBinding ToolbarBackground}">
<uwp:FormsCommandBar x:Name="CommandBar" Background="{TemplateBinding ToolbarBackground}" MinHeight="{ThemeResource TitleBarHeight}">
<uwp:FormsCommandBar x:Name="CommandBar" Background="{TemplateBinding ToolbarBackground}" MinHeight="{ThemeResource TitleBarHeight}" HorizontalAlignment="Stretch">
<uwp:FormsCommandBar.Content>
<Border x:Name="TitleArea" Visibility="{TemplateBinding TitleVisibility}" Height="{ThemeResource TitleBarHeight}">
<Border x:Name="TitleArea" Height="{ThemeResource TitleBarHeight}" Visibility="{TemplateBinding TitleVisibility}" HorizontalAlignment="Stretch">
<Grid x:Name="TitleViewPresenter" VerticalAlignment="Center" Background="{TemplateBinding ToolbarBackground}" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{TemplateBinding TitleIcon}" />
<Border Grid.Column="1" Height="{ThemeResource TitleBarHeight}" Visibility="{TemplateBinding TitleVisibility}">
<TextBlock Text="{Binding Title}" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="10,0,0,0" Foreground="{TemplateBinding TitleBrush}" Style="{ThemeResource TitleTextBlockStyle}" />
</Border>
<ContentPresenter Grid.Column="2" Content="{Binding TitleView, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource ViewToRenderer}}" Visibility="{TemplateBinding TitleViewVisibility}" HorizontalAlignment="Stretch" />
</Grid>
</Border>
</uwp:FormsCommandBar.Content>
</uwp:FormsCommandBar>
</Border>

Просмотреть файл

@ -68,9 +68,9 @@ namespace Xamarin.Forms.Platform.UWP
{
_view.IsInNativeLayout = true;
Layout.LayoutChildIntoBoundingRegion(_view, new Rectangle(0, 0, finalSize.Width, finalSize.Height));
FrameworkElement?.Arrange(new Rect(_view.X, _view.Y, _view.Width, _view.Height));
_view.IsInNativeLayout = false;
FrameworkElement?.Arrange(new Rect(_view.X, _view.Y, _view.Width, _view.Height));
return finalSize;
}
@ -93,7 +93,7 @@ namespace Xamarin.Forms.Platform.UWP
result = new Windows.Foundation.Size(request.Width, request.Height);
}
_view.Layout(new Rectangle(0, 0, result.Width, result.Height));
Layout.LayoutChildIntoBoundingRegion(_view, new Rectangle(0, 0, result.Width, result.Height));
FrameworkElement?.Measure(availableSize);

Просмотреть файл

@ -56,7 +56,9 @@
<Compile Include="FormsComboBox.cs" />
<Compile Include="InterceptVisualStateManager.cs" />
<Compile Include="HorizontalTextAlignmentConverter.cs" />
<Compile Include="ITitleIconProvider.cs" />
<Compile Include="ITitleProvider.cs" />
<Compile Include="ITitleViewProvider.cs" />
<Compile Include="IToolbarProvider.cs" />
<Compile Include="NativeBindingExtensions.cs" />
<Compile Include="NativeEventWrapper.cs" />

Просмотреть файл

@ -13,6 +13,7 @@ using static Xamarin.Forms.PlatformConfiguration.iOSSpecific.NavigationPage;
using PageUIStatusBarAnimation = Xamarin.Forms.PlatformConfiguration.iOSSpecific.UIStatusBarAnimation;
using PointF = CoreGraphics.CGPoint;
using RectangleF = CoreGraphics.CGRect;
using SizeF = CoreGraphics.CGSize;
namespace Xamarin.Forms.Platform.iOS
{
@ -29,6 +30,8 @@ namespace Xamarin.Forms.Platform.iOS
VisualElementTracker _tracker;
nfloat _navigationBottom = 0;
bool _hasNavigationBar;
UIImage _defaultNavBarShadowImage;
UIImage _defaultNavBarBackImage;
public NavigationRenderer()
{
@ -45,6 +48,8 @@ namespace Xamarin.Forms.Platform.iOS
IPageController PageController => Element as IPageController;
NavigationPage NavPage => Element as NavigationPage;
public VisualElement Element { get; private set; }
public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
@ -62,7 +67,7 @@ namespace Xamarin.Forms.Platform.iOS
public void SetElement(VisualElement element)
{
var oldElement = Element;
Element = (NavigationPage)element;
Element = element;
OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
if (element != null)
@ -84,6 +89,7 @@ namespace Xamarin.Forms.Platform.iOS
get { return this; }
}
//TODO: this was deprecated in iOS8.0 and is not called in 9.0+
public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation)
{
base.DidRotate(fromInterfaceOrientation);
@ -205,7 +211,7 @@ namespace Xamarin.Forms.Platform.iOS
FindParentMasterDetail();
var navPage = (NavigationPage)Element;
var navPage = NavPage;
if (navPage.CurrentPage == null)
{
@ -223,6 +229,7 @@ namespace Xamarin.Forms.Platform.iOS
UpdateBarBackgroundColor();
UpdateBarTextColor();
UpdateUseLargeTitles();
UpdateHideNavigationBarSeparator();
// If there is already stuff on the stack we need to push it
navPage.Pages.ForEach(async p => await PushPageAsync(p, false));
@ -255,7 +262,7 @@ namespace Xamarin.Forms.Platform.iOS
_parentMasterDetailPage = null;
Current = null; // unhooks events
var navPage = (NavigationPage)Element;
var navPage = NavPage;
navPage.PropertyChanged -= HandlePropertyChanged;
navPage.PushRequested -= OnPushRequested;
@ -348,32 +355,8 @@ namespace Xamarin.Forms.Platform.iOS
// must pack into container so padding can work
// otherwise the view controller is forced to 0,0
var pack = new ParentingViewController(this) { Child = page };
if (!string.IsNullOrWhiteSpace(page.Title))
pack.NavigationItem.Title = page.Title;
// First page and we have a master detail to contend with
UpdateLeftBarButtonItem(pack);
//var pack = Platform.GetRenderer (view).ViewController;
var titleIcon = NavigationPage.GetTitleIcon(page);
if (!string.IsNullOrEmpty(titleIcon?.File))
{
try
{
SetTitleImage(pack, titleIcon);
}
catch
{
}
}
var titleText = NavigationPage.GetBackButtonTitle(page);
if (titleText != null)
{
// adding a custom event handler to UIBarButtonItem for navigating back seems to be ignored.
pack.NavigationItem.BackBarButtonItem = new UIBarButtonItem { Title = titleText, Style = UIBarButtonItemStyle.Plain };
}
UpdateTitleArea(pack, page);
var pageRenderer = Platform.GetRenderer(page);
pack.View.AddSubview(pageRenderer.ViewController.View);
@ -383,17 +366,11 @@ namespace Xamarin.Forms.Platform.iOS
return pack;
}
async void SetTitleImage(ParentingViewController pack, FileImageSource titleIcon)
{
var source = Internals.Registrar.Registered.GetHandlerForObject<IImageSourceHandler>(titleIcon);
var image = await source.LoadImageAsync(titleIcon);
//UIImage ctor throws on file not found if MonoTouch.ObjCRuntime.Class.ThrowOnInitFailure is true;
pack.NavigationItem.TitleView = new UIImageView(image);
}
void FindParentMasterDetail()
{
var parentPages = ((Page)Element).GetParentPages();
Page page = Element as Page;
var parentPages = page.GetParentPages();
var masterDetail = parentPages.OfType<MasterDetailPage>().FirstOrDefault();
if (masterDetail != null && parentPages.Append((Page)Element).Contains(masterDetail.Detail))
@ -436,29 +413,50 @@ namespace Xamarin.Forms.Platform.iOS
#pragma warning disable 0618 //retaining legacy call to obsolete code
if (e.PropertyName == NavigationPage.TintProperty.PropertyName)
#pragma warning restore 0618
{
UpdateTint();
if (e.PropertyName == NavigationPage.BarBackgroundColorProperty.PropertyName)
}
else if (e.PropertyName == NavigationPage.BarBackgroundColorProperty.PropertyName)
{
UpdateBarBackgroundColor();
else if (e.PropertyName == NavigationPage.BarTextColorProperty.PropertyName || e.PropertyName == PlatformConfiguration.iOSSpecific.NavigationPage.StatusBarTextColorModeProperty.PropertyName)
}
else if (e.PropertyName == NavigationPage.BarTextColorProperty.PropertyName
|| e.PropertyName == StatusBarTextColorModeProperty.PropertyName)
{
UpdateBarTextColor();
SetStatusBarStyle();
}
else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
{
UpdateBackgroundColor();
}
else if (e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName)
{
Current = ((NavigationPage)Element).CurrentPage;
Current = NavPage?.CurrentPage;
ValidateNavbarExists(Current);
}
else if (e.PropertyName == PlatformConfiguration.iOSSpecific.NavigationPage.IsNavigationBarTranslucentProperty.PropertyName)
else if (e.PropertyName == IsNavigationBarTranslucentProperty.PropertyName)
{
UpdateTranslucent();
}
else if (e.PropertyName == PreferredStatusBarUpdateAnimationProperty.PropertyName)
{
UpdateCurrentPagePreferredStatusBarUpdateAnimation();
}
else if (e.PropertyName == PrefersLargeTitlesProperty.PropertyName)
{
UpdateUseLargeTitles();
}
else if (e.PropertyName == NavigationPage.BackButtonTitleProperty.PropertyName)
{
var pack = (ParentingViewController)TopViewController;
UpdateTitleArea(pack, pack.Child);
}
else if (e.PropertyName == HideNavigationBarSeparatorProperty.PropertyName)
{
UpdateHideNavigationBarSeparator();
}
}
void ValidateNavbarExists(Page newCurrentPage)
{
@ -468,6 +466,33 @@ namespace Xamarin.Forms.Platform.iOS
ViewDidLayoutSubviews();
}
void UpdateHideNavigationBarSeparator()
{
bool shouldHide = NavPage.OnThisPlatform().HideNavigationBarSeparator();
// Just setting the ShadowImage is good for iOS11
if (_defaultNavBarShadowImage == null)
_defaultNavBarShadowImage = NavigationBar.ShadowImage;
if (shouldHide)
NavigationBar.ShadowImage = new UIImage();
else
NavigationBar.ShadowImage = _defaultNavBarShadowImage;
if (!Forms.IsiOS11OrNewer)
{
// For iOS 10 and lower, you need to set the background image.
// If you set this for iOS11, you'll remove the background color.
if (_defaultNavBarBackImage == null)
_defaultNavBarBackImage = NavigationBar.GetBackgroundImage(UIBarMetrics.Default);
if (shouldHide)
NavigationBar.SetBackgroundImage(new UIImage(), UIBarMetrics.Default);
else
NavigationBar.SetBackgroundImage(_defaultNavBarBackImage, UIBarMetrics.Default);
}
}
void UpdateCurrentPagePreferredStatusBarUpdateAnimation()
{
// Not using the extension method syntax here because for some reason it confuses the mono compiler
@ -478,14 +503,125 @@ namespace Xamarin.Forms.Platform.iOS
void UpdateUseLargeTitles()
{
var navPage = (Element as NavigationPage);
if (Forms.IsiOS11OrNewer && navPage != null)
NavigationBar.PrefersLargeTitles = navPage.OnThisPlatform().PrefersLargeTitles();
if (Forms.IsiOS11OrNewer && NavPage != null)
NavigationBar.PrefersLargeTitles = NavPage.OnThisPlatform().PrefersLargeTitles();
}
void UpdateTitleArea(ParentingViewController pack, Page page)
{
if (pack == null || page == null)
return;
if (!string.IsNullOrWhiteSpace(page.Title))
pack.NavigationItem.Title = page.Title;
// First page and we have a master detail to contend with
UpdateLeftBarButtonItem(pack);
var titleText = NavigationPage.GetBackButtonTitle(page);
if (titleText != null)
{
// adding a custom event handler to UIBarButtonItem for navigating back seems to be ignored.
pack.NavigationItem.BackBarButtonItem = new UIBarButtonItem { Title = titleText, Style = UIBarButtonItemStyle.Plain };
}
FileImageSource titleIcon = NavigationPage.GetTitleIcon(page);
VisualElement titleView = NavigationPage.GetTitleView(page);
ClearTitleViewContainer(pack);
bool needContainer = titleView != null || titleIcon != null;
if (needContainer)
{
Container titleViewContainer = new Container();
pack.NavigationItem.TitleView = titleViewContainer;
UpdateTitleImage(titleViewContainer, titleIcon);
UpdateTitleView(titleViewContainer, titleView);
// Need to call this for iOS10 to properly frame the renderer
TopViewController?.NavigationItem?.TitleView?.SizeToFit();
TopViewController?.NavigationItem?.TitleView?.LayoutSubviews();
}
}
async void UpdateTitleImage(Container titleViewContainer, FileImageSource titleIcon)
{
if (titleViewContainer == null)
return;
if (string.IsNullOrWhiteSpace(titleIcon))
{
titleViewContainer.Icon = null;
}
else
{
var source = Internals.Registrar.Registered.GetHandlerForObject<IImageSourceHandler>(titleIcon);
var image = await source.LoadImageAsync(titleIcon);
try
{
titleViewContainer.Icon = new UIImageView(image) { };
}
catch
{
//UIImage ctor throws on file not found if MonoTouch.ObjCRuntime.Class.ThrowOnInitFailure is true;
}
}
}
void ClearTitleViewContainer(UIViewController pack)
{
if (pack == null)
return;
if (pack.NavigationItem.TitleView != null && pack.NavigationItem.TitleView is Container titleViewContainer)
{
titleViewContainer.Dispose();
titleViewContainer = null;
pack.NavigationItem.TitleView = null;
}
}
void UpdateTitleView(Container titleViewContainer, VisualElement titleView)
{
if (titleViewContainer == null)
return;
var titleViewRenderer = titleViewContainer.Child;
if (titleView != null)
{
if (titleViewRenderer != null)
{
var rendererType = titleViewRenderer is System.Reflection.IReflectableType reflectableType
? reflectableType.GetTypeInfo().AsType()
: titleViewRenderer.GetType();
if (titleView != null && rendererType == Internals.Registrar.Registered.GetHandlerTypeForObject(titleView))
{
if (titleViewContainer != null)
titleViewContainer.Child = null;
titleViewRenderer.SetElement(titleView);
return;
}
titleViewContainer?.DisposeChild();
}
titleViewRenderer = Platform.CreateRenderer(titleView);
titleViewContainer.Child = titleViewRenderer;
}
else if (titleViewRenderer != null)
{
titleViewContainer?.DisposeChild();
}
}
void UpdateTranslucent()
{
NavigationBar.Translucent = ((NavigationPage)Element).OnThisPlatform().IsNavigationBarTranslucent();
NavigationBar.Translucent = NavPage.OnThisPlatform().IsNavigationBarTranslucent();
}
void InsertPageBefore(Page page, Page before)
@ -591,7 +727,7 @@ namespace Xamarin.Forms.Platform.iOS
void UpdateBarBackgroundColor()
{
var barBackgroundColor = ((NavigationPage)Element).BarBackgroundColor;
var barBackgroundColor = NavPage.BarBackgroundColor;
// Set navigation bar background color
NavigationBar.BarTintColor = barBackgroundColor == Color.Default
? UINavigationBar.Appearance.BarTintColor
@ -600,7 +736,7 @@ namespace Xamarin.Forms.Platform.iOS
void UpdateBarTextColor()
{
var barTextColor = ((NavigationPage)Element).BarTextColor;
var barTextColor = NavPage.BarTextColor;
var globalAttributes = UINavigationBar.Appearance.GetTitleTextAttributes();
@ -633,7 +769,7 @@ namespace Xamarin.Forms.Platform.iOS
NavigationBar.LargeTitleTextAttributes = NavigationBar.TitleTextAttributes;
}
var statusBarColorMode = (Element as NavigationPage).OnThisPlatform().GetStatusBarTextColorMode();
var statusBarColorMode = NavPage.OnThisPlatform().GetStatusBarTextColorMode();
// set Tint color (i. e. Back Button arrow and Text)
NavigationBar.TintColor = barTextColor == Color.Default || statusBarColorMode == StatusBarTextColorMode.DoNotAdjust
@ -643,8 +779,8 @@ namespace Xamarin.Forms.Platform.iOS
void SetStatusBarStyle()
{
var barTextColor = ((NavigationPage)Element).BarTextColor;
var statusBarColorMode = (Element as NavigationPage).OnThisPlatform().GetStatusBarTextColorMode();
var barTextColor = NavPage.BarTextColor;
var statusBarColorMode = NavPage.OnThisPlatform().GetStatusBarTextColorMode();
if (statusBarColorMode == StatusBarTextColorMode.DoNotAdjust || barTextColor.Luminosity <= 0.5)
{
@ -663,7 +799,7 @@ namespace Xamarin.Forms.Platform.iOS
if (containerController == null)
return;
var currentChild = containerController.Child;
var firstPage = ((NavigationPage)Element).Pages.FirstOrDefault();
var firstPage = NavPage.Pages.FirstOrDefault();
if ((firstPage != pageBeingRemoved && currentChild != firstPage && NavigationPage.GetHasBackButton(currentChild)) || _parentMasterDetailPage == null)
return;
@ -673,7 +809,7 @@ namespace Xamarin.Forms.Platform.iOS
void UpdateTint()
{
#pragma warning disable 0618 //retaining legacy call to obsolete code
var tintColor = ((NavigationPage)Element).Tint;
var tintColor = NavPage.Tint;
#pragma warning restore 0618
NavigationBar.BarTintColor = tintColor == Color.Default
? UINavigationBar.Appearance.BarTintColor
@ -698,16 +834,18 @@ namespace Xamarin.Forms.Platform.iOS
_secondaryToolbar.Hidden = true;
//secondaryToolbar.Items = null;
}
TopViewController?.NavigationItem?.TitleView?.SizeToFit();
TopViewController?.NavigationItem?.TitleView?.LayoutSubviews();
}
internal async Task UpdateFormsInnerNavigation(Page pageBeingRemoved)
{
var navPage = Element as NavigationPage;
if (navPage == null)
if (NavPage == null)
return;
_ignorePopCall = true;
if (Element.Navigation.NavigationStack.Contains(pageBeingRemoved))
await (navPage as INavigationPageController)?.RemoveAsyncInner(pageBeingRemoved, false, true);
await (NavPage as INavigationPageController)?.RemoveAsyncInner(pageBeingRemoved, false, true);
_ignorePopCall = false;
}
@ -968,6 +1106,13 @@ namespace Xamarin.Forms.Platform.iOS
UpdatePrefersStatusBarHidden();
else if (e.PropertyName == LargeTitleDisplayProperty.PropertyName)
UpdateLargeTitles();
else if (e.PropertyName == NavigationPage.TitleIconProperty.PropertyName ||
e.PropertyName == NavigationPage.TitleViewProperty.PropertyName)
{
NavigationRenderer n;
if (_navigation.TryGetTarget(out n))
n.UpdateTitleArea(this, Child);
}
}
void UpdatePrefersStatusBarHidden()
@ -1116,5 +1261,102 @@ namespace Xamarin.Forms.Platform.iOS
{
VisualElementRenderer<VisualElement>.RegisterEffect(effect, View);
}
class Container : UIView
{
IVisualElementRenderer _child;
UIImageView _icon;
nfloat IconHeight => _icon?.Frame.Height ?? 0;
nfloat IconWidth => _icon?.Frame.Width ?? 0;
protected override void Dispose(bool disposing)
{
if (disposing)
{
DisposeChild();
_icon?.Dispose();
_icon = null;
}
base.Dispose(disposing);
}
public UIImageView Icon
{
set
{
if (_icon != null)
_icon.RemoveFromSuperview();
_icon = value;
if (_icon != null)
AddSubview(_icon);
}
}
public IVisualElementRenderer Child
{
get { return _child; }
set
{
if (_child != null)
DisposeChild();
_child = value;
if (value != null)
AddSubview(value.NativeView);
}
}
public override SizeF SizeThatFits(SizeF size)
{
IVisualElementRenderer renderer = _child;
if (renderer == null || renderer.Element == null || renderer.Element.Parent == null)
return new SizeF(IconWidth, IconHeight);
var result = renderer.Element.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins);
return new SizeF(result.Request.Width + IconWidth, Math.Max(IconHeight, result.Request.Height));
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
// Navigation bar will not stretch past these values. Prevent content clipping.
// iOS11 does this for us automatically, but apparently iOS10 doesn't.
int toolbarHeight = 44;
if (Device.Idiom == TargetIdiom.Phone && Device.Info.CurrentOrientation.IsLandscape())
toolbarHeight = 32;
double height = Math.Min(toolbarHeight, Bounds.Height);
Frame = new RectangleF(Frame.X, Frame.Y, Bounds.Width, height);
if (_icon != null)
_icon.Frame = new RectangleF(0, 0, IconWidth, Math.Min(toolbarHeight, IconHeight));
if (_child.Element != null)
Layout.LayoutChildIntoBoundingRegion(_child.Element, new Rectangle(IconWidth, 0, Bounds.Width - IconWidth, height));
}
public void DisposeChild()
{
if (_child == null)
return;
if (_child.Element.Platform is Platform platform)
platform.DisposeModelAndChildrenRenderers(_child.Element);
_child.NativeView.RemoveFromSuperview();
_child.Dispose();
_child = null;
}
}
}
}