[UWP] Fix TitleView Width and MDP rendering quirks (#3987)

* [UWP] Fix TitleView Width and MDP rendering quirks

* [UWP] fix load timing for pushed pages and titleview

* [UWP] move renderer check

* [UWP] remove re-measure until 4116 fixed

- fixes #3828
- fixes #3834
This commit is contained in:
Shane Neuville 2018-10-24 01:23:46 -06:00 коммит произвёл Stephane Delcroix
Родитель 2eee6d1eff
Коммит 71429f7cdd
9 изменённых файлов: 215 добавлений и 112 удалений

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

@ -9,7 +9,6 @@
<Label Text="I can be a subtitle" />
</StackLayout>
</NavigationPage.TitleView>
<ScrollView>
<StackLayout BackgroundColor="Pink">
<Button Clicked="masterDetailsPage_Clicked" Text="Master Details Page"></Button>
@ -22,6 +21,7 @@
<Button Clicked="toggleSecondaryToolBarItem_Clicked" Text="Toggle Secondary Tool Bar Item" ></Button>
<Button Clicked="toggleLargeTitles_Clicked" Text="Toggle Large Titles" ></Button>
<Button Clicked="changeTitleView_Clicked" Text="Toggle Different Title Views"></Button>
<Button Clicked="swapDetails_Page" Text="Swap Details Page"></Button>
<Button x:Name="btnToggleBackButtonTitle" Clicked="toggleBackButtonText_Clicked" Text="Toggle Back Button Title Text"></Button>
<Button Clicked="backToGallery_Clicked" Text="Back to Gallery"></Button>
</StackLayout>

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

@ -24,12 +24,13 @@ namespace Xamarin.Forms.Controls.GalleryPages
}
}
NavigationPage CreateNavigationPage()
static NavigationPage CreateNavigationPage()
{
return new NavigationPage(new TitleView(false) { Title = "Nav Title" });
var page = new TitleView(false) { Title = "Nav Title" };
return new NavigationPage(page);
}
public Page GetPage()
public static Page GetPage()
{
return new MasterDetailPage()
{
@ -38,6 +39,14 @@ namespace Xamarin.Forms.Controls.GalleryPages
};
}
void swapDetails_Page(object sender, EventArgs e)
{
if (App.Current.MainPage is MasterDetailPage mdp)
{
mdp.Detail = CreateNavigationPage();
}
}
void masterDetailsPage_Clicked(object sender, EventArgs e)
{
App.Current.MainPage =
@ -125,12 +134,12 @@ namespace Xamarin.Forms.Controls.GalleryPages
Navigation.PushAsync(page);
}
View createSearchBarView()
static View createSearchBarView()
{
return new SearchBar { BackgroundColor = Color.Cornsilk, HorizontalOptions = LayoutOptions.FillAndExpand, Margin = new Thickness(10, 0) };
}
View createGrid()
static View createGrid()
{
var grid = new Grid
{
@ -157,13 +166,18 @@ namespace Xamarin.Forms.Controls.GalleryPages
void titleIcon_Clicked(object sender, EventArgs e)
{
var titleIcon = NavigationPage.GetTitleIcon(this);
toggleTitleIcon(this);
}
static void toggleTitleIcon(Page page)
{
var titleIcon = NavigationPage.GetTitleIcon(page);
if (titleIcon == null)
NavigationPage.SetTitleIcon(this, "coffee.png");
NavigationPage.SetTitleIcon(page, "coffee.png");
else
NavigationPage.SetTitleIcon(this, null);
NavigationPage.SetTitleIcon(page, null);
}
void masterDetailsPageIcon_Clicked(object sender, EventArgs e)
@ -188,9 +202,10 @@ namespace Xamarin.Forms.Controls.GalleryPages
(App.Current as App).Reset();
}
void toggleToolBarItem_Clicked(object sender, EventArgs e)
void toggleToolBarItem_Clicked(object sender, EventArgs e) => toggleToolBarItem(Navigation.NavigationStack.Last());
static void toggleToolBarItem(Page page)
{
var page = Navigation.NavigationStack.Last();
var items = page.ToolbarItems.Where(x => x.Order == ToolbarItemOrder.Primary).ToList();
if (items.Any())
@ -215,14 +230,19 @@ namespace Xamarin.Forms.Controls.GalleryPages
void changeTitleView_Clicked(object sender, EventArgs e)
{
var currentView = NavigationPage.GetTitleView(Navigation.NavigationStack.Last());
changeTitleView(Navigation.NavigationStack.Last());
}
static void changeTitleView(Page page)
{
var currentView = NavigationPage.GetTitleView(page);
if (currentView is Grid)
NavigationPage.SetTitleView(Navigation.NavigationStack.Last(), createSearchBarView());
NavigationPage.SetTitleView(page, createSearchBarView());
else if (currentView is SearchBar)
NavigationPage.SetTitleView(Navigation.NavigationStack.Last(), null);
NavigationPage.SetTitleView(page, null);
else
NavigationPage.SetTitleView(Navigation.NavigationStack.Last(), createGrid());
NavigationPage.SetTitleView(page, createGrid());
}
}

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

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace Xamarin.Forms.Platform.UWP
{
internal interface ITitleViewRendererController
{
View TitleView { get; }
FrameworkElement TitleViewPresenter { get; }
Visibility TitleViewVisibility { get; set; }
CommandBar CommandBar { get; }
}
}

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

@ -8,7 +8,7 @@ using WImageSource = Windows.UI.Xaml.Media.ImageSource;
namespace Xamarin.Forms.Platform.UWP
{
public class MasterDetailControl : Control, IToolbarProvider
public class MasterDetailControl : Control, IToolbarProvider, ITitleViewRendererController
{
public static readonly DependencyProperty MasterProperty = DependencyProperty.Register("Master", typeof(FrameworkElement), typeof(MasterDetailControl),
new PropertyMetadata(default(FrameworkElement)));
@ -26,7 +26,7 @@ namespace Xamarin.Forms.Platform.UWP
public static readonly DependencyProperty ShouldShowNavigationBarProperty = DependencyProperty.Register(nameof(ShouldShowNavigationBar), typeof(bool), typeof(MasterDetailControl),
new PropertyMetadata(true, OnShouldShowSplitModeChanged));
public static readonly DependencyProperty CollapseStyleProperty = DependencyProperty.Register(nameof(CollapseStyle), typeof(CollapseStyle),
public static readonly DependencyProperty CollapseStyleProperty = DependencyProperty.Register(nameof(CollapseStyle), typeof(CollapseStyle),
typeof(MasterDetailControl), new PropertyMetadata(CollapseStyle.Full, CollapseStyleChanged));
public static readonly DependencyProperty CollapsedPaneWidthProperty = DependencyProperty.Register(nameof(CollapsedPaneWidth), typeof(double), typeof(MasterDetailControl),
@ -58,7 +58,7 @@ namespace Xamarin.Forms.Platform.UWP
public static readonly DependencyProperty ContentTogglePaneButtonVisibilityProperty = DependencyProperty.Register(nameof(ContentTogglePaneButtonVisibility), typeof(Visibility), typeof(MasterDetailControl),
new PropertyMetadata(default(Visibility)));
CommandBar _commandBar;
readonly ToolbarPlacementHelper _toolbarPlacementHelper = new ToolbarPlacementHelper();
bool _firstLoad;
@ -73,10 +73,11 @@ namespace Xamarin.Forms.Platform.UWP
FrameworkElement _masterPresenter;
FrameworkElement _detailPresenter;
SplitView _split;
ToolbarPlacement _toolbarPlacement;
ToolbarPlacement _toolbarPlacement;
FrameworkElement _titleViewPresenter;
TitleViewManager _titleViewManager;
public MasterDetailControl()
public MasterDetailControl()
{
DefaultStyleKey = typeof(MasterDetailControl);
@ -215,17 +216,17 @@ namespace Xamarin.Forms.Platform.UWP
set { SetValue(CollapseStyleProperty, value); }
}
public ToolbarPlacement ToolbarPlacement
{
get { return _toolbarPlacement; }
set
{
_toolbarPlacement = value;
_toolbarPlacementHelper.UpdateToolbarPlacement();
}
}
public ToolbarPlacement ToolbarPlacement
{
get { return _toolbarPlacement; }
set
{
_toolbarPlacement = value;
_toolbarPlacementHelper.UpdateToolbarPlacement();
}
}
public Visibility ContentTogglePaneButtonVisibility
public Visibility ContentTogglePaneButtonVisibility
{
get { return (Visibility)GetValue(ContentTogglePaneButtonVisibilityProperty); }
set { SetValue(ContentTogglePaneButtonVisibilityProperty, value); }
@ -291,11 +292,13 @@ namespace Xamarin.Forms.Platform.UWP
_commandBar = GetTemplateChild("CommandBar") as CommandBar;
_toolbarPlacementHelper.Initialize(_commandBar, () => ToolbarPlacement, GetTemplateChild);
UpdateMode();
UpdateMode();
if (_commandBarTcs != null)
_commandBarTcs.SetResult(_commandBar);
_titleViewManager = new TitleViewManager(this);
}
static void OnShouldShowSplitModeChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
@ -315,7 +318,7 @@ namespace Xamarin.Forms.Platform.UWP
static void OnTitleViewPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
((MasterDetailControl)dependencyObject).UpdateTitleViewPresenter();
((MasterDetailControl)dependencyObject)._titleViewManager?.OnTitleViewPropertyChanged();
}
void OnToggleClicked(object sender, RoutedEventArgs args)
@ -323,14 +326,6 @@ namespace Xamarin.Forms.Platform.UWP
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)
@ -338,8 +333,8 @@ namespace Xamarin.Forms.Platform.UWP
return;
}
_split.DisplayMode = ShouldShowSplitMode
? SplitViewDisplayMode.Inline
_split.DisplayMode = ShouldShowSplitMode
? SplitViewDisplayMode.Inline
: CollapseStyle == CollapseStyle.Full ? SplitViewDisplayMode.Overlay : SplitViewDisplayMode.CompactOverlay;
_split.CompactPaneLength = CollapsedPaneWidth;
@ -353,10 +348,10 @@ namespace Xamarin.Forms.Platform.UWP
// If we're in compact mode or the pane is always open,
// we don't need to display the content pane's toggle button
ContentTogglePaneButtonVisibility = _split.DisplayMode == SplitViewDisplayMode.Overlay
? Visibility.Visible
ContentTogglePaneButtonVisibility = _split.DisplayMode == SplitViewDisplayMode.Overlay
? Visibility.Visible
: Visibility.Collapsed;
if (ContentTogglePaneButtonVisibility == Visibility.Visible)
DetailTitleVisibility = Visibility.Visible;
@ -366,22 +361,15 @@ namespace Xamarin.Forms.Platform.UWP
_firstLoad = true;
}
void UpdateTitleViewPresenter()
View ITitleViewRendererController.TitleView => DetailTitleView;
FrameworkElement ITitleViewRendererController.TitleViewPresenter => _titleViewPresenter;
Visibility ITitleViewRendererController.TitleViewVisibility
{
if (DetailTitleView == null)
{
DetailTitleViewVisibility = Visibility.Collapsed;
if (_titleViewPresenter != null)
_titleViewPresenter.Loaded -= OnTitleViewPresenterLoaded;
}
else
{
DetailTitleViewVisibility = Visibility.Visible;
if (_titleViewPresenter != null)
_titleViewPresenter.Loaded += OnTitleViewPresenterLoaded;
}
get => DetailTitleViewVisibility;
set => DetailTitleViewVisibility = value;
}
CommandBar ITitleViewRendererController.CommandBar { get => _commandBar; }
}
}

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

@ -18,7 +18,7 @@ namespace Xamarin.Forms.Platform.UWP
bool _showTitle;
VisualElementTracker<Page, FrameworkElement> _tracker;
public MasterDetailControl Control { get; private set; }
public MasterDetailPage Element { get; private set; }
@ -185,10 +185,10 @@ namespace Xamarin.Forms.Platform.UWP
else if (e.PropertyName == "Detail")
UpdateDetail();
else if (e.PropertyName == nameof(MasterDetailControl.ShouldShowSplitMode)
|| e.PropertyName == Specifics.CollapseStyleProperty.PropertyName
|| e.PropertyName == Specifics.CollapsedPaneWidthProperty.PropertyName)
|| e.PropertyName == Specifics.CollapseStyleProperty.PropertyName
|| e.PropertyName == Specifics.CollapsedPaneWidthProperty.PropertyName)
UpdateMode();
else if(e.PropertyName == PlatformConfiguration.WindowsSpecific.Page.ToolbarPlacementProperty.PropertyName)
else if (e.PropertyName == PlatformConfiguration.WindowsSpecific.Page.ToolbarPlacementProperty.PropertyName)
UpdateToolbarPlacement();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
@ -255,7 +255,6 @@ namespace Xamarin.Forms.Platform.UWP
{
UpdateDetailTitle();
UpdateDetailTitleIcon();
UpdateDetailTitleView();
}
}
@ -313,7 +312,7 @@ namespace Xamarin.Forms.Platform.UWP
if (_detail == null)
return;
Control.DetailTitle = (_detail as NavigationPage)?.CurrentPage?.Title ?? _detail.Title ?? Element?.Title;
Control.DetailTitle = GetCurrentPage().Title ?? Element?.Title;
(this as ITitleProvider).ShowTitle = !string.IsNullOrEmpty(Control.DetailTitle);
}
@ -322,7 +321,7 @@ namespace Xamarin.Forms.Platform.UWP
if (_detail == null)
return;
Control.DetailTitleIcon = await NavigationPage.GetTitleIcon(_detail).ToWindowsImageSource();
Control.DetailTitleIcon = await NavigationPage.GetTitleIcon(GetCurrentPage()).ToWindowsImageSource();
Control.InvalidateMeasure();
}
@ -331,7 +330,7 @@ namespace Xamarin.Forms.Platform.UWP
if (_detail == null)
return;
Control.DetailTitleView = NavigationPage.GetTitleView(_detail) as View;
Control.DetailTitleView = NavigationPage.GetTitleView(GetCurrentPage()) as View;
Control.InvalidateMeasure();
}
@ -389,9 +388,9 @@ namespace Xamarin.Forms.Platform.UWP
{
// Enforce consistency rules on toolbar
Control.ShouldShowToolbar = _detail is NavigationPage || _master is NavigationPage;
if(_detail is NavigationPage _detailNav)
if (_detail is NavigationPage _detailNav)
Control.ShouldShowNavigationBar = NavigationPage.GetHasNavigationBar(_detailNav.CurrentPage);
}
public void BindForegroundColor(AppBar appBar)
@ -409,5 +408,13 @@ namespace Xamarin.Forms.Platform.UWP
element.SetBinding(Windows.UI.Xaml.Controls.Control.ForegroundProperty,
new Windows.UI.Xaml.Data.Binding { Path = new PropertyPath("Control.ToolbarForeground"), Source = this, RelativeSource = new RelativeSource { Mode = RelativeSourceMode.TemplatedParent } });
}
Page GetCurrentPage()
{
if (_detail is NavigationPage page)
return page.CurrentPage;
return _detail;
}
}
}

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

@ -36,6 +36,7 @@ namespace Xamarin.Forms.Platform.UWP
VisualElementTracker<Page, PageControl> _tracker;
EntranceThemeTransition _transition;
Platform _platform;
bool _parentsLookedUp = false;
Platform Platform => _platform ?? (_platform = Platform.Current);
@ -308,12 +309,8 @@ namespace Xamarin.Forms.Platform.UWP
_parentMasterDetailPage.PropertyChanged += MultiPagePropertyChanged;
UpdateShowTitle();
UpdateTitleOnParents();
UpdateTitleIcon();
UpdateTitleView();
_parentsLookedUp = true;
}
void MultiPagePropertyChanged(object sender, PropertyChangedEventArgs e)
@ -383,6 +380,12 @@ namespace Xamarin.Forms.Platform.UWP
Element.SendAppearing();
UpdateBackButton();
UpdateTitleOnParents();
if (_parentMasterDetailPage != null)
{
UpdateTitleView();
UpdateTitleIcon();
}
}
void OnNativeSizeChanged(object sender, SizeChangedEventArgs e)
@ -466,7 +469,6 @@ namespace Xamarin.Forms.Platform.UWP
UpdateTitleVisible();
UpdateTitleOnParents();
UpdateTitleIcon();
UpdateTitleView();
if (isAnimated && _transition == null)
@ -549,13 +551,21 @@ namespace Xamarin.Forms.Platform.UWP
void UpdateTitleView()
{
if (_currentPage == null)
// if the life cycle hasn't reached the point where _parentMasterDetailPage gets wired up then
// don't update the title view
if (_currentPage == null || !_parentsLookedUp)
return;
_container.TitleView = TitleView;
// If the container TitleView gets initialized before the MDP TitleView it causes the
// MDP TitleView to not render correctly
if (_parentMasterDetailPage != null)
{
if (Platform.GetRenderer(_parentMasterDetailPage) is ITitleViewProvider parent)
parent.TitleView = TitleView;
}
else if (_parentMasterDetailPage == null)
_container.TitleView = TitleView;
if (_parentMasterDetailPage != null && Platform.GetRenderer(_parentMasterDetailPage) is ITitleViewProvider parent)
parent.TitleView = TitleView;
}
SystemNavigationManager _navManager;

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

@ -7,7 +7,7 @@ using WImageSource = Windows.UI.Xaml.Media.ImageSource;
namespace Xamarin.Forms.Platform.UWP
{
public sealed class PageControl : ContentControl, IToolbarProvider
public sealed class PageControl : ContentControl, IToolbarProvider, ITitleViewRendererController
{
public static readonly DependencyProperty TitleVisibilityProperty = DependencyProperty.Register(nameof(TitleVisibility), typeof(Visibility), typeof(PageControl), new PropertyMetadata(Visibility.Visible));
@ -55,7 +55,8 @@ namespace Xamarin.Forms.Platform.UWP
TaskCompletionSource<CommandBar> _commandBarTcs;
Windows.UI.Xaml.Controls.ContentPresenter _presenter;
TitleViewManager _titleViewManager;
public PageControl()
{
Style = Windows.UI.Xaml.Application.Current.Resources["DefaultPageControlStyle"] as Windows.UI.Xaml.Style;
@ -123,6 +124,9 @@ namespace Xamarin.Forms.Platform.UWP
set { SetValue(TitleInsetProperty, value); }
}
FrameworkElement ITitleViewRendererController.TitleViewPresenter => _titleViewPresenter;
CommandBar ITitleViewRendererController.CommandBar => _commandBar;
Task<CommandBar> IToolbarProvider.GetCommandBarAsync()
{
if (_commandBar != null)
@ -143,6 +147,7 @@ namespace Xamarin.Forms.Platform.UWP
_commandBar = GetTemplateChild("CommandBar") as CommandBar;
_titleViewManager = new TitleViewManager(this);
_toolbarPlacementHelper.Initialize(_commandBar, () => ToolbarPlacement, GetTemplateChild);
@ -152,33 +157,7 @@ namespace Xamarin.Forms.Platform.UWP
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;
((PageControl)dependencyObject)._titleViewManager?.OnTitleViewPropertyChanged();
}
void UpdateTitleViewPresenter()
{
if (TitleView == null)
{
TitleViewVisibility = Visibility.Collapsed;
if (_titleViewPresenter != null)
_titleViewPresenter.Loaded -= OnTitleViewPresenterLoaded;
}
else
{
TitleViewVisibility = Visibility.Visible;
if (_titleViewPresenter != null)
_titleViewPresenter.Loaded += OnTitleViewPresenterLoaded;
}
}
}
}
}

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

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace Xamarin.Forms.Platform.UWP
{
internal class TitleViewManager
{
readonly ITitleViewRendererController _titleViewRendererController;
View TitleView => _titleViewRendererController.TitleView;
CommandBar CommandBar => _titleViewRendererController.CommandBar;
FrameworkElement TitleViewPresenter => _titleViewRendererController.TitleViewPresenter;
public TitleViewManager(ITitleViewRendererController titleViewRendererController)
{
_titleViewRendererController = titleViewRendererController;
_titleViewRendererController.TitleViewPresenter.Loaded += OnTitleViewPresenterLoaded;
// Uncomment once https://github.com/xamarin/Xamarin.Forms/issues/4116 is fixed
// CommandBar.LayoutUpdated += commandLayoutUpdated;
// CommandBar.Unloaded += commandBarUnloaded;
}
internal void OnTitleViewPropertyChanged()
{
UpdateTitleViewWidth();
}
void OnTitleViewPresenterLoaded(object sender, RoutedEventArgs e)
{
UpdateTitleViewWidth();
TitleViewPresenter.Loaded -= OnTitleViewPresenterLoaded;
}
void commandBarUnloaded(object sender, RoutedEventArgs e)
{
CommandBar.LayoutUpdated -= commandLayoutUpdated;
CommandBar.Unloaded -= commandBarUnloaded;
}
void commandLayoutUpdated(object sender, object e)
{
UpdateTitleViewWidth();
}
void UpdateTitleViewWidth()
{
if (TitleView == null || TitleViewPresenter == null || CommandBar == null)
return;
if (CommandBar.ActualWidth <= 0) return;
double buttonWidth = 0;
foreach (var item in CommandBar.GetDescendantsByName<Windows.UI.Xaml.Controls.Button>("MoreButton"))
if (item.Visibility == Visibility.Visible)
buttonWidth += item.ActualWidth;
if (!CommandBar.IsDynamicOverflowEnabled)
foreach (var item in CommandBar.GetDescendantsByName<ItemsControl>("PrimaryItemsControl"))
buttonWidth += item.ActualWidth;
TitleViewPresenter.Width = CommandBar.ActualWidth - buttonWidth;
UpdateVisibility();
}
void UpdateVisibility()
{
if (TitleView == null)
_titleViewRendererController.TitleViewVisibility = Visibility.Collapsed;
else
_titleViewRendererController.TitleViewVisibility = Visibility.Visible;
}
}
}

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

@ -60,6 +60,7 @@
<Compile Include="ITitleIconProvider.cs" />
<Compile Include="ITitleProvider.cs" />
<Compile Include="ITitleViewProvider.cs" />
<Compile Include="ITitleViewRendererController.cs" />
<Compile Include="IToolbarProvider.cs" />
<Compile Include="NativeBindingExtensions.cs" />
<Compile Include="NativeEventWrapper.cs" />
@ -70,6 +71,7 @@
<Compile Include="PlatformConfigurationExtensions.cs" />
<Compile Include="PlatformEffect.cs" />
<Compile Include="TextBlockExtensions.cs" />
<Compile Include="TitleViewManager.cs" />
<Compile Include="UriImageSourceHandler.cs" />
<Compile Include="ViewExtensions.cs" />
<Compile Include="LayoutExtensions.cs" />