[Android] ToolbarHandler and fixes for various page nesting scenarios (#2781)

* Nested Navigation Page setup

* - toolbar handler

* - remove control root manager for now

* - clean up events

* - cleanup code

* - remove no longer needed handlers

* - cleanup shell code

* - cleanup adding removing tabbedpage

* - cleanup adding removing pages

* - fix up fragment reuse

* - cleanup

* - create remap for shell

* - mapper extension methods

* - fix bottom tabs offset

* - rename to StackNavigationManager

* - toolbar not visible by default

* - IElement only

* - fix toolbar to be more swappable

* - remove navigationlayout

* - fix toolbar so it recreates if app is refreshed

* - if user doesn't handle back event and there are no more pages in the stack then just close the activity

* - fix context stub

* - fix test

* - merge with main

* - remove static classes

* - remove code

* - fix tests

* - fix parenting check

* - fix appearing tests to work with window

* Update src/Controls/src/Core/Handlers/Toolbar/ToolbarHandler.Android.cs

Co-authored-by: Matthew Leibowitz <mattleibow@live.com>

* - only create top level root manager

* - fix nav root creation

* WinUI Toolbar Handler (#2875)

* Wire up Window ToolbarHandler

* - fix NRE

* - fix null exception

* - fix compile error

Co-authored-by: Matthew Leibowitz <mattleibow@live.com>
This commit is contained in:
Shane Neuville 2021-10-15 11:34:06 -05:00 коммит произвёл GitHub
Родитель 9903a2c0fb
Коммит e2f3aaa222
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
86 изменённых файлов: 1581 добавлений и 1483 удалений

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

@ -2,6 +2,7 @@ using System;
using Android.Content;
using Android.Runtime;
using Android.Views;
using AndroidX.CoordinatorLayout.Widget;
using AndroidX.Fragment.App;
using Microsoft.Maui.Handlers;
@ -32,7 +33,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
Child.UpdateLayout();
if (Child.View is ContentViewGroup ||
Child.View is NavigationLayout ||
Child.View is CoordinatorLayout ||
Child.View is FragmentContainerView)
{
// This is a way to handle situations where the root page is shimmed and uses fragments to host other pages (e.g., NavigationPageRenderer)

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

@ -11,6 +11,7 @@ using Microsoft.Maui.Controls.Platform;
using AndroidAnimation = Android.Views.Animations.Animation;
using AnimationSet = Android.Views.Animations.AnimationSet;
using AView = Android.Views.View;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
{
@ -68,7 +69,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
AView _root;
ShellPageContainer _shellPageContainer;
ShellContent _shellContent;
Toolbar _toolbar;
AToolbar _toolbar;
IShellToolbarTracker _toolbarTracker;
bool _disposed;
@ -133,7 +134,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
_root = inflater.Inflate(Resource.Layout.shellcontent, null).JavaCast<CoordinatorLayout>();
_toolbar = _root.FindViewById<Toolbar>(Resource.Id.shellcontent_toolbar);
_toolbar = _root.FindViewById<AToolbar>(Resource.Id.shellcontent_toolbar);
_renderer = Platform.CreateRenderer(_page, Context);
Platform.SetRenderer(_page, _renderer);

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

@ -16,7 +16,7 @@ using AView = Android.Views.View;
using Color = Microsoft.Maui.Graphics.Color;
using LP = Android.Views.ViewGroup.LayoutParams;
using Paint = Android.Graphics.Paint;
using Toolbar = AndroidX.AppCompat.Widget.Toolbar;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
{
@ -105,7 +105,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
return CreateShellSectionRenderer(shellSection);
}
IShellToolbarTracker Microsoft.Maui.Controls.Platform.IShellContext.CreateTrackerForToolbar(Toolbar toolbar)
IShellToolbarTracker Microsoft.Maui.Controls.Platform.IShellContext.CreateTrackerForToolbar(AToolbar toolbar)
{
return CreateTrackerForToolbar(toolbar);
}
@ -185,7 +185,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
return new ShellSectionRenderer(this);
}
protected virtual IShellToolbarTracker CreateTrackerForToolbar(Toolbar toolbar)
protected virtual IShellToolbarTracker CreateTrackerForToolbar(AToolbar toolbar)
{
return new ShellToolbarTracker(this, toolbar, ((IShellContext)this).CurrentDrawerLayout);
}

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

@ -13,6 +13,7 @@ using Google.Android.Material.Tabs;
using Microsoft.Maui.Controls.Compatibility.Platform.Android.AppCompat;
using Microsoft.Maui.Controls.Platform;
using AView = Android.Views.View;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
{
@ -114,7 +115,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
bool _selecting;
TabLayout _tablayout;
IShellTabLayoutAppearanceTracker _tabLayoutAppearanceTracker;
Toolbar _toolbar;
AToolbar _toolbar;
IShellToolbarAppearanceTracker _toolbarAppearanceTracker;
IShellToolbarTracker _toolbarTracker;
FormsViewPager _viewPager;
@ -143,7 +144,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
var root = inflater.Inflate(Resource.Layout.rootlayout, null).JavaCast<CoordinatorLayout>();
_toolbar = root.FindViewById<Toolbar>(Resource.Id.maui_toolbar);
_toolbar = root.FindViewById<AToolbar>(Resource.Id.maui_toolbar);
_viewPager = root.FindViewById<FormsViewPager>(Resource.Id.main_viewpager);
_tablayout = root.FindViewById<TabLayout>(Resource.Id.main_tablayout);

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

@ -2,6 +2,7 @@ using Android.Graphics.Drawables;
using AndroidX.AppCompat.Widget;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Graphics;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
{
@ -17,7 +18,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
_shellContext = shellContext;
}
public virtual void SetAppearance(Toolbar toolbar, IShellToolbarTracker toolbarTracker, ShellAppearance appearance)
public virtual void SetAppearance(AToolbar toolbar, IShellToolbarTracker toolbarTracker, ShellAppearance appearance)
{
var foreground = appearance.ForegroundColor;
var background = appearance.BackgroundColor;
@ -26,12 +27,12 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
SetColors(toolbar, toolbarTracker, foreground, background, titleColor);
}
public virtual void ResetAppearance(Toolbar toolbar, IShellToolbarTracker toolbarTracker)
public virtual void ResetAppearance(AToolbar toolbar, IShellToolbarTracker toolbarTracker)
{
SetColors(toolbar, toolbarTracker, ShellRenderer.DefaultForegroundColor, ShellRenderer.DefaultBackgroundColor, ShellRenderer.DefaultTitleColor);
}
protected virtual void SetColors(Toolbar toolbar, IShellToolbarTracker toolbarTracker, Color foreground, Color background, Color title)
protected virtual void SetColors(AToolbar toolbar, IShellToolbarTracker toolbarTracker, Color foreground, Color background, Color title)
{
var titleArgb = title.ToAndroid(ShellRenderer.DefaultTitleColor).ToArgb();

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

@ -25,7 +25,7 @@ using Color = Microsoft.Maui.Graphics.Color;
using LP = Android.Views.ViewGroup.LayoutParams;
using Paint = Android.Graphics.Paint;
using R = Android.Resource;
using Toolbar = AndroidX.AppCompat.Widget.Toolbar;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
{
@ -57,14 +57,14 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
protected IShellContext ShellContext { get; private set; }
//assume the default
Color _tintColor = null;
Toolbar _toolbar;
AToolbar _toolbar;
AppBarLayout _appBar;
float _appBarElevation;
GenericGlobalLayoutListener _globalLayoutListener;
List<IMenuItem> _currentMenuItems = new List<IMenuItem>();
List<ToolbarItem> _currentToolbarItems = new List<ToolbarItem>();
public ShellToolbarTracker(IShellContext shellContext, Toolbar toolbar, DrawerLayout drawerLayout)
public ShellToolbarTracker(IShellContext shellContext, AToolbar toolbar, DrawerLayout drawerLayout)
{
ShellContext = shellContext ?? throw new ArgumentNullException(nameof(shellContext));
_toolbar = toolbar ?? throw new ArgumentNullException(nameof(toolbar));
@ -333,7 +333,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
return image;
}
protected virtual async void UpdateLeftBarButtonItem(Context context, Toolbar toolbar, DrawerLayout drawerLayout, Page page)
protected virtual async void UpdateLeftBarButtonItem(Context context, AToolbar toolbar, DrawerLayout drawerLayout, Page page)
{
if (_drawerToggle == null)
{
@ -435,12 +435,12 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
}
protected virtual Task UpdateDrawerArrow(Context context, Toolbar toolbar, DrawerLayout drawerLayout)
protected virtual Task UpdateDrawerArrow(Context context, AToolbar toolbar, DrawerLayout drawerLayout)
{
return Task.CompletedTask;
}
protected virtual void UpdateToolbarIconAccessibilityText(Toolbar toolbar, Shell shell)
protected virtual void UpdateToolbarIconAccessibilityText(AToolbar toolbar, Shell shell)
{
var backButtonHandler = Shell.GetBackButtonBehavior(Page);
var image = GetFlyoutIcon(backButtonHandler, Page);
@ -459,7 +459,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
}
}
protected virtual Task UpdateDrawerArrowFromBackButtonBehavior(Context context, Toolbar toolbar, DrawerLayout drawerLayout, BackButtonBehavior backButtonHandler)
protected virtual Task UpdateDrawerArrowFromBackButtonBehavior(Context context, AToolbar toolbar, DrawerLayout drawerLayout, BackButtonBehavior backButtonHandler)
{
return Task.CompletedTask;
}
@ -486,7 +486,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
});
}
protected virtual void UpdateNavBarVisible(Toolbar toolbar, Page page)
protected virtual void UpdateNavBarVisible(AToolbar toolbar, Page page)
{
var navBarVisible = Shell.GetNavBarIsVisible(page);
toolbar.Visibility = navBarVisible ? ViewStates.Visible : ViewStates.Gone;
@ -510,12 +510,12 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
}
}
protected virtual void UpdatePageTitle(Toolbar toolbar, Page page)
protected virtual void UpdatePageTitle(AToolbar toolbar, Page page)
{
_toolbar.Title = page.Title;
}
protected virtual void UpdateTitleView(Context context, Toolbar toolbar, View titleView)
protected virtual void UpdateTitleView(Context context, AToolbar toolbar, View titleView)
{
if (titleView == null)
{
@ -530,7 +530,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
{
_titleViewContainer = new ContainerView(context, titleView);
_titleViewContainer.MatchHeight = _titleViewContainer.MatchWidth = true;
_titleViewContainer.LayoutParameters = new Toolbar.LayoutParams(LP.MatchParent, LP.MatchParent)
_titleViewContainer.LayoutParameters = new AToolbar.LayoutParams(LP.MatchParent, LP.MatchParent)
{
LeftMargin = (int)context.ToPixels(titleView.Margin.Left),
TopMargin = (int)context.ToPixels(titleView.Margin.Top),
@ -546,7 +546,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
}
}
protected virtual void UpdateToolbarItems(Toolbar toolbar, Page page)
protected virtual void UpdateToolbarItems(AToolbar toolbar, Page page)
{
var menu = toolbar.Menu;
var sortedItems = page.ToolbarItems.OrderBy(x => x.Order);

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

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using Android.Content;
using Android.Views;
using AndroidX.CoordinatorLayout.Widget;
using AndroidX.Fragment.App;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Controls.Platform;
@ -97,7 +98,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
formsViewGroup.MeasureAndLayout(MeasureSpecFactory.MakeMeasureSpec(width, MeasureSpecMode.Exactly), MeasureSpecFactory.MakeMeasureSpec(height, MeasureSpecMode.Exactly), x, y, x + width, y + height);
Performance.Stop(reference, "MeasureAndLayout");
}
else if ((aview is LayoutViewGroup || aview is ContentViewGroup || aview is NavigationLayout || aview is FragmentContainerView) && width == 0 && height == 0)
else if ((aview is LayoutViewGroup || aview is ContentViewGroup || aview is CoordinatorLayout || aview is FragmentContainerView) && width == 0 && height == 0)
{
// Nothing to do here; just chill.
}

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

@ -163,6 +163,7 @@ namespace Microsoft.Maui.Controls.Hosting
VisualElement.RemapForControls();
Label.RemapForControls();
Button.RemapForControls();
Window.RemapForControls();
});
builder.AddMauiCompat();

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

@ -1,42 +0,0 @@
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP
{
internal class MenuItemCommand : ICommand
{
readonly MenuItem _menuItem;
public MenuItemCommand(MenuItem item)
{
_menuItem = item;
_menuItem.PropertyChanged += OnElementPropertyChanged;
}
public virtual bool CanExecute(object parameter)
{
return _menuItem.IsEnabled;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
((IMenuItemController)_menuItem).Activate();
}
void OnCanExecuteChanged()
{
EventHandler changed = CanExecuteChanged;
if (changed != null)
changed(this, EventArgs.Empty);
}
void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
OnCanExecuteChanged();
}
}
}

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

@ -143,5 +143,10 @@
<Link>Resources\layout\rootlayout.axml</Link>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="..\..\..\Core\src\Platform\Android\Resources\Layout\fragment_backstack.axml">
<Link>Resources\layout\fragment_backstack.axml</Link>
</AndroidResource>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project>

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

@ -6,8 +6,9 @@
xmlns:views="clr-namespace:Maui.Controls.Sample.Pages.Base"
Title="Tabbed Page">
<ContentPage Title="Tab 1">
<StackLayout>
<Label Text="Welcome to Tab 1"></Label>
</StackLayout>
<VerticalStackLayout>
<Button Text="Set Tabbed Page as Root" Clicked="OnTabbedPageAsRoot"></Button>
<Button Text="Toggle Bottom Tabs (Android)" Clicked="OnSetToBottomTabs"></Button>
</VerticalStackLayout>
</ContentPage>
</TabbedPage>

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

@ -1,6 +1,7 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui.Controls;
using AndroidSpecific = Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;
namespace Maui.Controls.Sample.Pages
{
public partial class TabbedPageGallery
@ -10,5 +11,40 @@ namespace Maui.Controls.Sample.Pages
InitializeComponent();
this.Children.Add(new NavigationGallery());
}
void OnTabbedPageAsRoot(object sender, EventArgs e)
{
var topTabs =
new TabbedPage()
{
Children =
{
Handler.MauiContext.Services.GetRequiredService<Page>(),
new NavigationPage(new Pages.NavigationGallery()) { Title = "Navigation Gallery" }
}
};
this.Handler?.DisconnectHandler();
Application.Current.MainPage?.Handler?.DisconnectHandler();
Application.Current.MainPage = topTabs;
}
void OnSetToBottomTabs(object sender, EventArgs e)
{
var bottomTabs = new TabbedPage()
{
Children =
{
Handler.MauiContext.Services.GetRequiredService<Page>(),
new NavigationPage(new Pages.NavigationGallery()) { Title = "Navigation Gallery" }
}
};
this.Handler?.DisconnectHandler();
Application.Current.MainPage?.Handler?.DisconnectHandler();
AndroidSpecific.TabbedPage.SetToolbarPlacement(bottomTabs, AndroidSpecific.ToolbarPlacement.Bottom);
Application.Current.MainPage = bottomTabs;
}
}
}

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

@ -51,4 +51,4 @@
</VerticalStackLayout>
</ScrollView>
</views:BasePage.Content>
</views:BasePage>
</views:BasePage>

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

@ -3,6 +3,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Pages.CustomNavigationPage"
BarBackgroundColor="{AppThemeBinding Light={StaticResource LightAccentColor}, Dark={StaticResource DarkAccentColor}}"
BarTextColor="{StaticResource WhiteColor}"
BarTextColor="{StaticResource WhiteColor}"
Title="Custom Nav Page">
</NavigationPage>

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

@ -12,4 +12,4 @@ namespace Maui.Controls.Sample.Pages
InitializeComponent();
}
}
}
}

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

@ -5,6 +5,7 @@
xmlns:views="clr-namespace:Maui.Controls.Sample.Pages.Base"
x:Name="HomePage"
BackgroundColor="{DynamicResource BackgroundColor}"
NavigationPage.IconColor="Pink"
Title="Gallery">
<views:BasePage.Resources>
<ResourceDictionary>

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

@ -28,4 +28,4 @@ namespace Maui.Controls.Sample
public IServiceProvider Services { get; }
}
}
}

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

@ -12,6 +12,11 @@ namespace Microsoft.Maui.Controls
{
public partial class NavigationPage : INavigationView
{
partial void Init()
{
this.Appearing += OnAppearing;
}
Thickness IView.Margin => Thickness.Zero;
protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
@ -86,34 +91,24 @@ namespace Microsoft.Maui.Controls
IReadOnlyList<IView> NavigationStack => this.Navigation.NavigationStack;
static void CurrentPagePropertyChanged(BindableObject bindable, object oldValue, object newValue)
static void OnCurrentPageChanged(BindableObject bindable, object oldValue, object newValue)
{
var np = (NavigationPage)bindable;
if (oldValue is Page oldPage)
oldPage.SendDisappearing();
if (oldValue is INotifyPropertyChanged ncpOld)
{
ncpOld.PropertyChanged -= np.CurrentPagePropertyChanged;
}
if (newValue is INotifyPropertyChanged ncpNew)
{
ncpNew.PropertyChanged += np.CurrentPagePropertyChanged;
}
if (newValue is Page newPage && ((NavigationPage)bindable).HasAppeared)
newPage.SendAppearing();
}
void CurrentPagePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.IsOneOf(NavigationPage.HasNavigationBarProperty,
NavigationPage.HasBackButtonProperty,
NavigationPage.TitleIconImageSourceProperty,
NavigationPage.TitleViewProperty,
NavigationPage.IconColorProperty) ||
e.IsOneOf(Page.TitleProperty, PlatformConfiguration.AndroidSpecific.AppCompat.NavigationPage.BarHeightProperty))
{
Handler?.UpdateValue(e.PropertyName);
}
}
void OnAppearing(object sender, EventArgs e)
{
// Update the Window level Toolbar with my Toolbar information
var window = this.FindParentOfType<Window>();
if (window?.Toolbar != null)
window.Toolbar.ApplyNavigationPage(this);
}
Task WaitForCurrentNavigationTask() =>
CurrentNavigationTask ?? Task.CompletedTask;

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

@ -2,6 +2,7 @@
using System;
using Android.App;
using AndroidX.AppCompat.App;
using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Controls
{

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

@ -24,8 +24,11 @@ namespace Microsoft.Maui.Controls
ReadOnlyCollection<Element>? _logicalChildren;
List<IVisualTreeElement> _visualChildren;
internal Toolbar Toolbar { get; }
public Window()
{
Toolbar = new Toolbar();
_visualChildren = new List<IVisualTreeElement>();
AlertManager = new AlertManager(this);
ModalNavigationManager = new ModalNavigationManager(this);
@ -259,7 +262,7 @@ namespace Microsoft.Maui.Controls
}
}
bool IWindow.BackButtonPressed()
bool IWindow.BackButtonClicked()
{
return this.Page?.SendBackButtonPressed() ?? false;
}

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

@ -0,0 +1,56 @@
using System;
using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Controls
{
public partial class Window
{
public static IPropertyMapper<IWindow, WindowHandler> ControlsLabelMapper = new PropertyMapper<IWindow, WindowHandler>(WindowHandler.WindowMapper)
{
#if __ANDROID__ || WINDOWS
[nameof(IWindow.Content)] = MapContent,
[nameof(Toolbar)] = MapToolbar,
#endif
};
public static void RemapForControls()
{
WindowHandler.WindowMapper = ControlsLabelMapper;
}
#if ANDROID || WINDOWS
public static void MapToolbar(WindowHandler handler, IWindow view)
{
_ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(handler.MauiContext)} null");
if (view is Window window && window.Toolbar != null)
{
_ = window.Toolbar.ToNative(handler.MauiContext);
}
}
public static void MapContent(WindowHandler handler, IWindow view)
{
if (view.Content is not Shell)
{
WindowHandler.MapContent(handler, view);
return;
}
#if ANDROID
var nativeContent = view.Content.ToContainerView(handler.MauiContext!);
handler.NativeView.SetContentView(nativeContent);
#else
if (handler.NativeView.Content is UI.Xaml.Controls.Panel panel)
{
var nativeContent = view.Content.ToNative(handler.MauiContext!);
panel.Children.Clear();
panel.Children.Add(nativeContent);
}
#endif
}
#endif
}
}

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

@ -1,38 +0,0 @@
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Controls.Handlers
{
// Currently only inheriting because we can't tap into CreateNativeView
// Once we can wire up into CreateNativeView then all of this code can move into the
// Remap structures
internal partial class NavigationPageHandler : NavigationViewHandler
{
public static PropertyMapper<NavigationPage, NavigationPageHandler> NavigationPageMapper =
new PropertyMapper<NavigationPage, NavigationPageHandler>(NavigationViewHandler.NavigationViewMapper)
{
[NavigationPage.HasNavigationBarProperty.PropertyName] = UpdateToolBar,
[NavigationPage.HasBackButtonProperty.PropertyName] = UpdateToolBar,
[NavigationPage.TitleIconImageSourceProperty.PropertyName] = UpdateToolBar,
[NavigationPage.TitleViewProperty.PropertyName] = UpdateToolBar,
[NavigationPage.IconColorProperty.PropertyName] = UpdateToolBar,
[Page.TitleProperty.PropertyName] = UpdateToolBar,
[NavigationPage.CurrentPageProperty.PropertyName] = UpdateToolBar,
[PlatformConfiguration.AndroidSpecific.AppCompat.NavigationPage.BarHeightProperty.PropertyName] = UpdateToolBar,
};
ControlsNavigationManager _controlsNavigationManager;
public NavigationPageHandler() : base(NavigationPageMapper)
{
}
protected override NavigationManager CreateNavigationManager()
=> _controlsNavigationManager ??= new ControlsNavigationManager(MauiContext);
private static void UpdateToolBar(NavigationPageHandler arg1, NavigationPage arg2)
{
arg1._controlsNavigationManager.ToolbarPropertyChanged();
}
}
}

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

@ -1,93 +0,0 @@
using System;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Controls.Handlers
{
// Currently only inheriting because we can't tap into CreateNativeView
// Once we can wire up into CreateNativeView then all of this code can move into the
// Remap structures
internal partial class NavigationPageHandler : Microsoft.Maui.Handlers.NavigationViewHandler
{
public static PropertyMapper<NavigationPage, NavigationPageHandler> NavigationPageMapper =
new PropertyMapper<NavigationPage, NavigationPageHandler>(NavigationViewHandler.NavigationViewMapper)
{
[NavigationPage.HasNavigationBarProperty.PropertyName] = MapHasNavigationBar,
[NavigationPage.HasBackButtonProperty.PropertyName] = MapHasBackButton,
[NavigationPage.TitleIconImageSourceProperty.PropertyName] = MapTitleIconImageSource,
[NavigationPage.TitleViewProperty.PropertyName] = MapTitleView,
[NavigationPage.IconColorProperty.PropertyName] = MapIconColor,
[Page.TitleProperty.PropertyName] = MapTitle,
[NavigationPage.CurrentPageProperty.PropertyName] = MapCurrentPage,
[NavigationPage.BarBackgroundColorProperty.PropertyName] = MapBarBackground,
[NavigationPage.BarBackgroundProperty.PropertyName] = MapBarBackground,
[PlatformConfiguration.WindowsSpecific.Page.ToolbarPlacementProperty.PropertyName] = MapToolbarPlacement,
[PlatformConfiguration.WindowsSpecific.Page.ToolbarDynamicOverflowEnabledProperty.PropertyName] = MapToolbarDynamicOverflowEnabled,
};
// TODO MAUI: break these out into extension methods
public static void MapBarBackground(NavigationPageHandler handler, NavigationPage view)
{
handler._controlsNavigationManager.ToolbarPropertyChanged();
}
public static void MapToolbarDynamicOverflowEnabled(NavigationPageHandler handler, NavigationPage view)
{
handler._controlsNavigationManager.ToolbarPropertyChanged();
}
public static void MapToolbarPlacement(NavigationPageHandler handler, NavigationPage view)
{
handler._controlsNavigationManager.ToolbarPropertyChanged();
}
public static void MapCurrentPage(NavigationPageHandler handler, NavigationPage view)
{
handler._controlsNavigationManager.ToolbarPropertyChanged();
}
public static void MapTitle(NavigationPageHandler handler, NavigationPage view)
{
handler._controlsNavigationManager.ToolbarPropertyChanged();
}
public static void MapIconColor(NavigationPageHandler handler, NavigationPage view)
{
handler._controlsNavigationManager.ToolbarPropertyChanged();
}
public static void MapTitleView(NavigationPageHandler handler, NavigationPage view)
{
handler._controlsNavigationManager.ToolbarPropertyChanged();
}
public static void MapTitleIconImageSource(NavigationPageHandler handler, NavigationPage view)
{
handler._controlsNavigationManager.ToolbarPropertyChanged();
}
public static void MapHasBackButton(NavigationPageHandler handler, NavigationPage view)
{
handler._controlsNavigationManager.ToolbarPropertyChanged();
}
public static void MapHasNavigationBar(NavigationPageHandler handler, NavigationPage view)
{
handler._controlsNavigationManager.ToolbarPropertyChanged();
}
ControlsNavigationManager _controlsNavigationManager;
public NavigationPageHandler() : base(NavigationPageMapper)
{
}
protected override NavigationManager CreateNavigationManager()
=> _controlsNavigationManager ??= new ControlsNavigationManager(MauiContext!);
public static void UpdateToolBar(NavigationPageHandler handler, NavigationPage view)
{
handler._controlsNavigationManager.ToolbarPropertyChanged();
}
}
}

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

@ -11,16 +11,11 @@ namespace Microsoft.Maui.Controls.Handlers
{
public sealed class TabbedPageHandler : ViewHandler<TabbedPage, ViewPager2>
{
public TabLayout TabLayout =>
_tabbedPageManager.IsBottomTabPlacement ? null :
_tabbedPageManager.TabLayout;
public BottomNavigationView BottomNavigationView =>
_tabbedPageManager.IsBottomTabPlacement ? _tabbedPageManager.BottomNavigationView :
null;
public static PropertyMapper<TabbedPage, TabbedPageHandler> Mapper =
new PropertyMapper<TabbedPage, TabbedPageHandler>(ViewMapper);
TabbedPageManager _tabbedPageManager;
public TabbedPageHandler() : base(ViewHandler.ViewMapper, null)
public TabbedPageHandler() : base(Mapper, null)
{
}
@ -35,5 +30,11 @@ namespace Microsoft.Maui.Controls.Handlers
base.SetVirtualView(view);
_tabbedPageManager.SetElement((TabbedPage)view);
}
protected override void DisconnectHandler(ViewPager2 nativeView)
{
base.DisconnectHandler(nativeView);
_tabbedPageManager.SetElement(null);
}
}
}

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

@ -0,0 +1,206 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Android.Content;
using Android.Content.Res;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Views;
using AndroidX.AppCompat.App;
using AndroidX.AppCompat.Graphics.Drawable;
using AndroidX.DrawerLayout.Widget;
using Google.Android.Material.AppBar;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Controls.Handlers
{
public partial class ToolbarHandler : ElementHandler<Toolbar, MaterialToolbar>
{
IViewHandler? _titleViewHandler;
Container? _titleView;
List<IMenuItem> _currentMenuItems = new List<IMenuItem>();
List<ToolbarItem> _currentToolbarItems = new List<ToolbarItem>();
NavigationRootManager? NavigationRootManager =>
MauiContext?.GetNavigationRootManager();
protected override MaterialToolbar CreateNativeElement()
{
return NavigationRootManager!
.NavigationLayout
.FindViewById<MaterialToolbar>(Resource.Id.navigationlayout_toolbar)!;
}
protected virtual void OnToolbarItemPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
var toolbarItems = VirtualView.ToolbarItems;
List<ToolbarItem> newToolBarItems = new List<ToolbarItem>();
if (toolbarItems != null)
newToolBarItems.AddRange(toolbarItems);
if (sender is ToolbarItem ti)
NativeView.OnToolbarItemPropertyChanged(e, ti, newToolBarItems, MauiContext!, null, OnToolbarItemPropertyChanged, _currentMenuItems, _currentToolbarItems, UpdateMenuItemIcon);
}
protected virtual void UpdateMenuItemIcon(Context context, IMenuItem menuItem, ToolbarItem toolBarItem)
{
_ = MauiContext ?? throw new ArgumentNullException(nameof(MauiContext));
ToolbarExtensions.UpdateMenuItemIcon(MauiContext, menuItem, toolBarItem, null);
}
void UpdateMenu()
{
_ = MauiContext ?? throw new ArgumentNullException(nameof(MauiContext));
if (_currentMenuItems == null)
return;
NativeView.UpdateMenuItems(VirtualView.ToolbarItems, MauiContext, null, OnToolbarItemPropertyChanged, _currentMenuItems, _currentToolbarItems, UpdateMenuItemIcon);
}
void UpdateTitleView()
{
_ = MauiContext ?? throw new ArgumentNullException(nameof(MauiContext));
_ = MauiContext.Context ?? throw new ArgumentNullException(nameof(MauiContext.Context));
VisualElement titleView = VirtualView.TitleView;
if (_titleViewHandler != null)
{
var reflectableType = _titleViewHandler as System.Reflection.IReflectableType;
var rendererType = reflectableType != null ? reflectableType.GetTypeInfo().AsType() : _titleViewHandler.GetType();
if (titleView == null || Internals.Registrar.Registered.GetHandlerTypeForObject(titleView) != rendererType)
{
if (_titleView != null)
_titleView.Child = null;
if (_titleViewHandler?.VirtualView != null)
_titleViewHandler.VirtualView.Handler = null;
_titleViewHandler = null;
}
}
if (titleView == null)
return;
if (_titleViewHandler != null)
_titleViewHandler.SetVirtualView(titleView);
else
{
titleView.ToNative(MauiContext);
_titleViewHandler = titleView.Handler;
if (_titleView == null)
{
_titleView = new Container(MauiContext.Context);
NativeView.AddView(_titleView);
}
_titleView.Child = (INativeViewHandler)_titleViewHandler;
}
}
public static void MapBarTextColor(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateBarTextColor(arg2);
}
public static void MapBarBackground(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateBarBackground(arg2);
}
public static void MapBarBackgroundColor(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateBarBackgroundColor(arg2);
}
public static void MapBackButtonTitle(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateBackButton(arg2);
}
public static void MapToolbarItems(ToolbarHandler arg1, Toolbar arg2)
{
arg1.UpdateMenu();
}
public static void MapTitle(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateTitle(arg2);
}
public static void MapIconColor(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateIconColor(arg2);
}
public static void MapTitleView(ToolbarHandler arg1, Toolbar arg2)
{
arg1.UpdateTitleView();
}
public static void MapTitleIcon(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateTitleIcon(arg2);
}
public static void MapBackButtonVisible(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateBackButton(arg2);
}
public static void MapIsVisible(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateIsVisible(arg2);
}
internal class Container : ViewGroup
{
INativeViewHandler? _child;
public Container(Context context) : base(context)
{
}
public INativeViewHandler? Child
{
set
{
if (_child != null)
RemoveView(_child.NativeView);
_child = value;
if (value != null)
AddView(value.NativeView);
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
if (_child?.NativeView == null)
return;
_child.NativeView.Layout(l, t, r, b);
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (_child?.NativeView == null)
{
SetMeasuredDimension(0, 0);
return;
}
_child.NativeView.Measure(widthMeasureSpec, heightMeasureSpec);
SetMeasuredDimension(_child.NativeView.MeasuredWidth, _child.NativeView.MeasuredHeight);
}
}
}
}

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

@ -0,0 +1,162 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using Microsoft.UI.Xaml.Controls;
using WImage = Microsoft.UI.Xaml.Controls.Image;
using NativeAutomationProperties = Microsoft.UI.Xaml.Automation.AutomationProperties;
namespace Microsoft.Maui.Controls.Handlers
{
public partial class ToolbarHandler : ElementHandler<Toolbar, WindowHeader>
{
readonly ImageConverter _imageConverter = new ImageConverter();
readonly ImageSourceIconElementConverter _imageSourceIconElementConverter = new ImageSourceIconElementConverter();
NavigationRootManager? NavigationRootManager =>
MauiContext?.GetNavigationRootManager();
protected override WindowHeader CreateNativeElement()
{
if(NavigationRootManager?.RootView is NavigationView nv &&
nv.Header is WindowHeader windowHeader)
{
windowHeader.NavigationView = nv as MauiNavigationView;
return windowHeader;
}
return new WindowHeader()
{
NavigationView = NavigationRootManager?.RootView as MauiNavigationView
};
}
internal void UpdateMenu()
{
if (NavigationRootManager == null)
return;
if (NavigationRootManager.RootView is not MauiNavigationView)
return;
var commandBar = NavigationRootManager.GetCommandBar();
if (commandBar == null)
{
return;
}
commandBar.PrimaryCommands.Clear();
commandBar.SecondaryCommands.Clear();
List<ToolbarItem> toolbarItems = new List<ToolbarItem>(VirtualView.ToolbarItems ?? new ToolbarItem[0]);
foreach (ToolbarItem item in toolbarItems)
{
var button = new AppBarButton();
button.SetBinding(AppBarButton.LabelProperty, "Text");
if (commandBar.IsDynamicOverflowEnabled && item.Order == ToolbarItemOrder.Secondary)
{
button.SetBinding(AppBarButton.IconProperty, "IconImageSource", _imageSourceIconElementConverter);
}
else
{
var img = new WImage();
img.SetBinding(WImage.SourceProperty, "Value");
img.SetBinding(WImage.DataContextProperty, "IconImageSource", _imageConverter);
button.Content = img;
}
button.Command = new MenuItemCommand(item);
button.DataContext = item;
button.SetValue(NativeAutomationProperties.AutomationIdProperty, item.AutomationId);
button.SetAutomationPropertiesName(item);
button.SetAutomationPropertiesAccessibilityView(item);
button.SetAutomationPropertiesHelpText(item);
button.SetAutomationPropertiesLabeledBy(item, null);
ToolbarItemOrder order = item.Order == ToolbarItemOrder.Default ? ToolbarItemOrder.Primary : item.Order;
if (order == ToolbarItemOrder.Primary)
{
commandBar.PrimaryCommands.Add(button);
}
else
{
commandBar.SecondaryCommands.Add(button);
}
}
}
public static void MapToolbarPlacement(ToolbarHandler arg1, Toolbar arg2)
{
}
public static void MapToolbarDynamicOverflowEnabled(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateToolbarDynamicOverflowEnabled(arg2);
}
public static void MapBarTextColor(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateBarTextColor(arg2);
}
public static void MapBarBackground(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateBarBackground(arg2);
}
public static void MapBarBackgroundColor(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateBarBackgroundColor(arg2);
}
public static void MapBackButtonTitle(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateBackButton(arg2);
}
public static void MapToolbarItems(ToolbarHandler arg1, Toolbar arg2)
{
arg1.UpdateMenu();
}
public static void MapTitle(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateTitle(arg2);
}
public static void MapIconColor(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateIconColor(arg2);
}
public static void MapTitleView(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateTitleView(arg2);
}
public static void MapTitleIcon(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateTitleIcon(arg2);
}
public static void MapBackButtonVisible(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateBackButton(arg2);
}
public static void MapIsVisible(ToolbarHandler arg1, Toolbar arg2)
{
arg1.NativeView.UpdateIsVisible(arg2);
}
}
}

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

@ -0,0 +1,45 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Controls.Handlers
{
#if ANDROID || WINDOWS
public partial class ToolbarHandler
{
public static IPropertyMapper<Toolbar, ToolbarHandler> Mapper =
new PropertyMapper<Toolbar, ToolbarHandler>(ElementMapper)
{
[nameof(Toolbar.IsVisible)] = MapIsVisible,
[nameof(Toolbar.BackButtonVisible)] = MapBackButtonVisible,
[nameof(Toolbar.TitleIcon)] = MapTitleIcon,
[nameof(Toolbar.TitleView)] = MapTitleView,
[nameof(Toolbar.IconColor)] = MapIconColor,
[nameof(Toolbar.Title)] = MapTitle,
[nameof(Toolbar.ToolbarItems)] = MapToolbarItems,
[nameof(Toolbar.BackButtonTitle)] = MapBackButtonTitle,
[nameof(Toolbar.BarBackgroundColor)] = MapBarBackgroundColor,
[nameof(Toolbar.BarBackground)] = MapBarBackground,
[nameof(Toolbar.BarTextColor)] = MapBarTextColor,
[nameof(Toolbar.IconColor)] = MapIconColor,
#if WINDOWS
[PlatformConfiguration.WindowsSpecific.Page.ToolbarPlacementProperty.PropertyName] = MapToolbarPlacement,
[PlatformConfiguration.WindowsSpecific.Page.ToolbarDynamicOverflowEnabledProperty.PropertyName] = MapToolbarDynamicOverflowEnabled,
#endif
};
public static CommandMapper<Toolbar, ToolbarHandler> CommandMapper = new()
{
};
public ToolbarHandler() : base(Mapper, CommandMapper)
{
}
}
#endif
}

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

@ -51,11 +51,12 @@ namespace Microsoft.Maui.Controls.Hosting
{ typeof(RefreshView), typeof(RefreshViewHandler) },
#endif
#if WINDOWS
{ typeof(NavigationPage), typeof(NavigationPageHandler) },
#if WINDOWS || ANDROID
{ typeof(NavigationPage), typeof(NavigationViewHandler) },
{ typeof(Toolbar), typeof(Controls.Handlers.ToolbarHandler) },
#endif
#if __ANDROID__
//{ typeof(TabbedPage), typeof(Controls.Handlers.TabbedPageHandler) },
{ typeof(TabbedPage), typeof(Controls.Handlers.TabbedPageHandler) },
#endif
};

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

@ -79,13 +79,19 @@ namespace Microsoft.Maui.Controls
var previousPage = _current;
OnPropertyChanging();
// TODO MAUI refine this to fire earlier
// TODO: MAUI refine this to fire earlier
_current?.SendNavigatingFrom(new NavigatingFromEventArgs());
_current = value;
previousPage?.SendDisappearing();
OnPropertyChanged();
OnCurrentPageChanged();
if (HasAppeared)
_current?.SendAppearing();
previousPage?.SendNavigatedFrom(new NavigatedFromEventArgs(_current));
_current?.SendNavigatedTo(new NavigatedToEventArgs(previousPage));
}

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

@ -29,7 +29,7 @@ namespace Microsoft.Maui.Controls
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, propertyChanged: CurrentPagePropertyChanged);
static readonly BindablePropertyKey CurrentPagePropertyKey = BindableProperty.CreateReadOnly("CurrentPage", typeof(Page), typeof(NavigationPage), null, propertyChanged: OnCurrentPageChanged);
public static readonly BindableProperty CurrentPageProperty = CurrentPagePropertyKey.BindableProperty;
@ -44,7 +44,7 @@ namespace Microsoft.Maui.Controls
{
_platformConfigurationRegistry = new Lazy<PlatformConfigurationRegistry<NavigationPage>>(() => new PlatformConfigurationRegistry<NavigationPage>(this));
#if WINDOWS
#if WINDOWS || __ANDROID__
Navigation = new MauiNavigationImpl(this);
#else
Navigation = new NavigationImpl(this);

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

@ -124,7 +124,7 @@ namespace Microsoft.Maui.Controls.Internals
public Task PushModalAsync(Page modal, bool animated)
{
if (modal.RealParent != null)
if (modal.RealParent != null && modal.RealParent is not IWindow)
throw new InvalidOperationException("Page must not already have a parent.");
return OnPushModal(modal, animated);
}

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

@ -412,6 +412,14 @@ namespace Microsoft.Maui.Controls
[EditorBrowsable(EditorBrowsableState.Never)]
public void SendAppearing()
{
// Only fire appearing if the page has been added to the windows
// Visual Hierarchy
var window = this.FindParentOfType<Window>();
if (window == null)
{
return;
}
if (_hasAppeared)
return;

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

@ -1,634 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Android.Animation;
using Android.Content;
using Android.Content.Res;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using AndroidX.AppCompat.Graphics.Drawable;
using AndroidX.CoordinatorLayout.Widget;
using AndroidX.DrawerLayout.Widget;
using AndroidX.Fragment.App;
using AndroidX.Navigation;
using Google.Android.Material.AppBar;
using Google.Android.Material.Tabs;
using Microsoft.Maui.Controls.Handlers;
using static Android.Views.View;
using static Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific.AppCompat.NavigationPage;
using ActionBarDrawerToggle = AndroidX.AppCompat.App.ActionBarDrawerToggle;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
using AView = Android.Views.View;
using Color = Microsoft.Maui.Graphics.Color;
using FragmentManager = AndroidX.Fragment.App.FragmentManager;
using Object = Java.Lang.Object;
namespace Microsoft.Maui.Controls.Platform
{
// This class is still a bit too over complicated
// As we move things into Core we'll better split it apart
public class ControlsNavigationManager : NavigationManager, IManageFragments
{
AppBarLayout _appBar;
Fragment _tabLayoutFragment;
internal AppBarLayout AppBar =>
_appBar ??= NavigationLayout.FindViewById<AppBarLayout>(Resource.Id.appbar)
?? throw new InvalidOperationException($"AppBar cannot be null");
ActionBarDrawerToggle _drawerToggle;
FragmentManager _fragmentManager;
ToolbarTracker ToolbarTracker;
DrawerMultiplexedListener _drawerListener;
DrawerLayout _drawerLayout;
FlyoutPage _flyoutPage;
IViewHandler _titleViewHandler;
Container _titleView;
Android.Widget.ImageView _titleIconView;
ImageSource _imageSource;
List<IMenuItem> _currentMenuItems = new List<IMenuItem>();
List<ToolbarItem> _currentToolbarItems = new List<ToolbarItem>();
new NavigationPage NavigationView => (NavigationPage)base.VirtualView;
new Page CurrentPage => (Page)base.CurrentPage;
public ControlsNavigationManager(IMauiContext mauiContext) : base(mauiContext)
{
}
INavigationPageController NavigationPageController => NavigationView as INavigationPageController;
IPageController PageController => NavigationView;
void IManageFragments.SetFragmentManager(FragmentManager childFragmentManager)
{
if (_fragmentManager == null)
_fragmentManager = childFragmentManager;
}
public override void Connect(IView navigationView, NavigationLayout nativeView)
{
base.Connect(navigationView, nativeView);
if (ToolbarTracker == null)
{
ToolbarTracker = new ToolbarTracker();
ToolbarTracker.CollectionChanged += ToolbarTrackerOnCollectionChanged;
}
ToolbarTracker.AdditionalTargets = NavigationView.GetParentPages();
}
// These are only relevant when nested inside a drawer layout
void AnimateArrowIn()
{
var icon = Toolbar.NavigationIcon as DrawerArrowDrawable;
if (icon == null)
return;
ValueAnimator valueAnim = ValueAnimator.OfFloat(0, 1);
valueAnim.SetDuration(200);
valueAnim.Update += (s, a) => icon.Progress = (float)a.Animation.AnimatedValue;
valueAnim.Start();
}
void AnimateArrowOut()
{
var icon = Toolbar.NavigationIcon as DrawerArrowDrawable;
if (icon == null)
return;
ValueAnimator valueAnim = ValueAnimator.OfFloat(1, 0);
valueAnim.SetDuration(200);
valueAnim.Update += (s, a) => icon.Progress = (float)a.Animation.AnimatedValue;
valueAnim.Start();
}
public void OnClick(AView v)
{
NavigationView?.PopAsync();
}
public override void RequestNavigation(NavigationRequest e)
{
NavAnimationInProgress = true;
base.RequestNavigation(e);
NavAnimationInProgress = false;
var currentStack = NavigationStack;
var newStackSize = e.NavigationStack.Count;
bool animated = e.Animated;
bool removed = currentStack.Count > newStackSize;
if (animated)
{
var page = (Page)e.NavigationStack[newStackSize - 1];
if (!removed)
{
UpdateToolbar();
if (_drawerToggle != null && NavigationPageController.StackDepth == 2 &&
NavigationPage.GetHasBackButton(page))
AnimateArrowIn();
}
else if (_drawerToggle != null && NavigationPageController.StackDepth == 2 &&
NavigationPage.GetHasBackButton(page))
{
AnimateArrowOut();
}
}
}
internal void SetTabLayout(TabbedPageHandler tabbedPageHandler)
{
if (tabbedPageHandler == null)
{
if (_tabLayoutFragment != null)
{
MauiContext
.GetFragmentManager()
.BeginTransaction()
.Remove(_tabLayoutFragment)
.SetReorderingAllowed(true)
.Commit();
_tabLayoutFragment = null;
}
return;
}
int id;
if (tabbedPageHandler.BottomNavigationView != null)
{
id = Resource.Id.bottomtab_containerview;
_tabLayoutFragment = new ViewFragment(tabbedPageHandler.BottomNavigationView);
}
else
{
_tabLayoutFragment = new ViewFragment(tabbedPageHandler.TabLayout);
id = Resource.Id.toptabs_containerview;
}
MauiContext
.GetFragmentManager()
.BeginTransaction()
.Replace(id, _tabLayoutFragment)
.SetReorderingAllowed(true)
.Commit();
}
protected override void OnNavigationViewFragmentResumed(FragmentManager fm, NavigationViewFragment navHostPageFragment)
{
base.OnNavigationViewFragmentResumed(fm, navHostPageFragment);
// This appears to be the best place to update the toolbar so that the tinting works
// Any early and the tinting will be replaced by the native tinting
UpdateToolbar();
}
protected override void OnDestinationChanged(NavController navController, NavDestination navDestination, Bundle bundle)
{
base.OnDestinationChanged(navController, navDestination, bundle);
if (ToolbarTracker != null)
{
ToolbarTracker.Target = CurrentPage;
}
// Check if the current Visible View has a Set of Tabs it would like to display
if (CurrentPage.Handler == null)
{
CurrentPage.HandlerChanged += OnHandlerChanged;
}
else
{
UpdateTablayout();
}
void OnHandlerChanged(object sender, EventArgs __)
{
UpdateTablayout();
((Element)sender).HandlerChanged -= OnHandlerChanged;
}
void UpdateTablayout()
{
if (CurrentPage.Handler is TabbedPageHandler tph)
{
SetTabLayout(tph);
}
else
{
SetTabLayout(null);
}
}
}
void RegisterToolbar()
{
Context context = NavigationLayout.Context;
AToolbar bar = Toolbar;
Element page = NavigationView.RealParent;
_flyoutPage = null;
while (page != null)
{
if (page is FlyoutPage)
{
_flyoutPage = page as FlyoutPage;
break;
}
page = page.RealParent;
}
if (_flyoutPage == null)
{
if (PageController.InternalChildren.Count > 0)
_flyoutPage = PageController.InternalChildren[0] as FlyoutPage;
if (_flyoutPage == null)
return;
}
if (((IFlyoutPageController)_flyoutPage).ShouldShowSplitMode)
return;
var renderer = _flyoutPage.ToNative(NavigationView.Handler.MauiContext) as DrawerLayout;
if (renderer == null)
return;
_drawerLayout = renderer;
AutomationPropertiesProvider.GetDrawerAccessibilityResources(context, _flyoutPage, out int resourceIdOpen, out int resourceIdClose);
if (_drawerToggle != null)
{
_drawerToggle.ToolbarNavigationClickListener = null;
_drawerToggle.Dispose();
}
_drawerToggle = new ActionBarDrawerToggle(context.GetActivity(), _drawerLayout, bar,
resourceIdOpen == 0 ? global::Android.Resource.String.Ok : resourceIdOpen,
resourceIdClose == 0 ? global::Android.Resource.String.Ok : resourceIdClose)
{
ToolbarNavigationClickListener = new ClickListener(NavigationView)
};
if (_drawerListener != null)
{
_drawerLayout.RemoveDrawerListener(_drawerListener);
_drawerListener.Dispose();
}
_drawerListener = new DrawerMultiplexedListener { Listeners = { _drawerToggle, (DrawerLayout.IDrawerListener)_drawerLayout } };
_drawerLayout.AddDrawerListener(_drawerListener);
}
// AFAICT this is specific to ListView and Context Items
bool _navAnimationInProgress;
internal const string CloseContextActionsSignalName = "Xamarin.CloseContextActions";
internal bool NavAnimationInProgress
{
get { return _navAnimationInProgress; }
set
{
if (_navAnimationInProgress == value)
return;
_navAnimationInProgress = value;
if (value)
MessagingCenter.Send(this, CloseContextActionsSignalName);
}
}
void ToolbarTrackerOnCollectionChanged(object sender, EventArgs eventArgs)
{
UpdateMenu();
}
void UpdateMenu()
{
if (_currentMenuItems == null)
return;
Toolbar.UpdateMenuItems(ToolbarTracker?.ToolbarItems, NavigationView.FindMauiContext(), null, OnToolbarItemPropertyChanged, _currentMenuItems, _currentToolbarItems, UpdateMenuItemIcon);
}
protected virtual void OnToolbarItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var toolbarItems = ToolbarTracker?.ToolbarItems;
List<ToolbarItem> newToolBarItems = new List<ToolbarItem>();
if (toolbarItems != null)
newToolBarItems.AddRange(toolbarItems);
Toolbar.OnToolbarItemPropertyChanged(e, (ToolbarItem)sender, newToolBarItems, NavigationView.FindMauiContext(), null, OnToolbarItemPropertyChanged, _currentMenuItems, _currentToolbarItems, UpdateMenuItemIcon);
}
protected virtual void UpdateMenuItemIcon(Context context, IMenuItem menuItem, ToolbarItem toolBarItem)
{
ToolbarExtensions.UpdateMenuItemIcon(NavigationView.FindMauiContext(), menuItem, toolBarItem, null);
}
void UpdateToolbarVisibility()
{
bool showNavBar = NavigationPage.GetHasNavigationBar(CurrentPage);
var lp = Toolbar.LayoutParameters;
if (lp == null)
return;
if (!showNavBar)
{
lp.Height = 0;
}
else
{
if (NavigationView.IsSet(BarHeightProperty))
lp.Height = NavigationView.OnThisPlatform().GetBarHeight();
else
lp.Height = ActionBarHeight();
}
Toolbar.LayoutParameters = lp;
int ActionBarHeight()
{
int actionBarHeight = (int)NavigationLayout.Context.GetThemeAttributePixels(Resource.Attribute.actionBarSize);
return actionBarHeight;
}
}
Drawable _defaultNavigationIcon;
internal void ToolbarPropertyChanged() => UpdateToolbar();
protected virtual void UpdateToolbar()
{
ActionBarDrawerToggle toggle = _drawerToggle;
if (Toolbar == null || NavigationStack.Count == 0 || CurrentPage == null || VirtualView == null)
return;
bool isNavigated = NavigationStack.Count > 1;
Page currentPage = CurrentPage;
_defaultNavigationIcon ??= Toolbar.NavigationIcon;
if (isNavigated)
{
if (NavigationPage.GetHasBackButton(currentPage))
{
Toolbar.NavigationIcon ??= _defaultNavigationIcon;
if (toggle != null)
{
toggle.DrawerIndicatorEnabled = false;
toggle.SyncState();
}
var prevPage = (Page)NavigationStack[NavigationStack.Count - 2];
var backButtonTitle = NavigationPage.GetBackButtonTitle(prevPage);
ImageSource image = NavigationPage.GetTitleIconImageSource(currentPage);
if (!string.IsNullOrEmpty(backButtonTitle))
{
Toolbar.NavigationContentDescription = backButtonTitle;
}
else if (image == null ||
Toolbar.SetNavigationContentDescription(image) == null)
{
Toolbar.SetNavigationContentDescription(Resource.String.nav_app_bar_navigate_up_description);
}
}
else if (toggle != null && _flyoutPage != null)
{
toggle.DrawerIndicatorEnabled = _flyoutPage.ShouldShowToolbarButton();
toggle.SyncState();
}
else
{
Toolbar.NavigationIcon = null;
}
}
else
{
if (toggle != null && _flyoutPage != null)
{
toggle.DrawerIndicatorEnabled = _flyoutPage.ShouldShowToolbarButton();
toggle.SyncState();
Toolbar.SetNavigationContentDescription(Resource.String.nav_app_bar_open_drawer_description);
}
}
Color tintColor = NavigationView.BarBackgroundColor;
if (tintColor == null)
Toolbar.BackgroundTintMode = null;
else
{
Toolbar.BackgroundTintMode = PorterDuff.Mode.Src;
Toolbar.BackgroundTintList = ColorStateList.ValueOf(tintColor.ToNative());
}
Brush barBackground = NavigationView.BarBackground;
Toolbar.UpdateBackground(barBackground);
Color textColor = NavigationView.BarTextColor;
if (textColor != null)
Toolbar.SetTitleTextColor(textColor.ToNative().ToArgb());
Color navIconColor = NavigationPage.GetIconColor(CurrentPage);
if (navIconColor != null && Toolbar.NavigationIcon != null)
DrawableExtensions.SetColorFilter(Toolbar.NavigationIcon, navIconColor, FilterMode.SrcAtop);
Toolbar.Title = currentPage?.Title ?? string.Empty;
if (Toolbar.NavigationIcon != null && textColor != null)
{
var icon = this.Toolbar.NavigationIcon as DrawerArrowDrawable;
if (icon != null)
icon.Color = textColor.ToNative().ToArgb();
}
UpdateTitleIcon();
UpdateTitleView();
UpdateToolbarVisibility();
}
void UpdateTitleIcon()
{
Page currentPage = CurrentPage;
if (currentPage == null)
return;
ImageSource source = NavigationPage.GetTitleIconImageSource(currentPage);
if (source == null || source.IsEmpty)
{
Toolbar.RemoveView(_titleIconView);
_titleIconView?.Dispose();
_titleIconView = null;
_imageSource = null;
return;
}
if (_titleIconView == null)
{
_titleIconView = new Android.Widget.ImageView(NavigationLayout.Context);
Toolbar.AddView(_titleIconView, 0);
}
if (_imageSource != source)
{
_imageSource = source;
_titleIconView.SetImageResource(global::Android.Resource.Color.Transparent);
source.LoadImage(MauiContext, (result) =>
{
_titleIconView.SetImageDrawable(result?.Value);
AutomationPropertiesProvider.AccessibilitySettingsChanged(_titleIconView, source);
});
}
}
void UpdateTitleView()
{
AToolbar bar = Toolbar;
if (bar == null)
return;
Page currentPage = CurrentPage;
if (currentPage == null)
return;
VisualElement titleView = NavigationPage.GetTitleView(currentPage);
if (_titleViewHandler != null)
{
var reflectableType = _titleViewHandler as System.Reflection.IReflectableType;
var rendererType = reflectableType != null ? reflectableType.GetTypeInfo().AsType() : _titleViewHandler.GetType();
if (titleView == null || Internals.Registrar.Registered.GetHandlerTypeForObject(titleView) != rendererType)
{
if (_titleView != null)
_titleView.Child = null;
_titleViewHandler.VirtualView.Handler = null;
_titleViewHandler = null;
}
}
if (titleView == null)
return;
if (_titleViewHandler != null)
_titleViewHandler.SetVirtualView(titleView);
else
{
titleView.ToNative(MauiContext);
_titleViewHandler = titleView.Handler;
if (_titleView == null)
{
_titleView = new Container(NavigationLayout.Context);
bar.AddView(_titleView);
}
_titleView.Child = (INativeViewHandler)_titleViewHandler;
}
}
class ClickListener : Object, IOnClickListener
{
readonly NavigationPage _element;
public ClickListener(NavigationPage element)
{
_element = element;
}
public void OnClick(AView v)
{
_element?.PopAsync();
}
}
internal class Container : ViewGroup
{
INativeViewHandler _child;
public Container(Context context) : base(context)
{
}
public INativeViewHandler Child
{
set
{
if (_child != null)
RemoveView(_child.NativeView);
_child = value;
if (value != null)
AddView(value.NativeView);
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
if (_child == null)
return;
_child.NativeView.Layout(l, t, r, b);
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (_child == null)
{
SetMeasuredDimension(0, 0);
return;
}
_child.NativeView.Measure(widthMeasureSpec, heightMeasureSpec);
SetMeasuredDimension(_child.NativeView.MeasuredWidth, _child.NativeView.MeasuredHeight);
}
}
class DrawerMultiplexedListener : Object, DrawerLayout.IDrawerListener
{
public List<DrawerLayout.IDrawerListener> Listeners { get; } = new List<DrawerLayout.IDrawerListener>(2);
public void OnDrawerClosed(AView drawerView)
{
foreach (DrawerLayout.IDrawerListener listener in Listeners)
listener.OnDrawerClosed(drawerView);
}
public void OnDrawerOpened(AView drawerView)
{
foreach (DrawerLayout.IDrawerListener listener in Listeners)
listener.OnDrawerOpened(drawerView);
}
public void OnDrawerSlide(AView drawerView, float slideOffset)
{
foreach (DrawerLayout.IDrawerListener listener in Listeners)
listener.OnDrawerSlide(drawerView, slideOffset);
}
public void OnDrawerStateChanged(int newState)
{
foreach (DrawerLayout.IDrawerListener listener in Listeners)
listener.OnDrawerStateChanged(newState);
}
}
}
}

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

@ -3,10 +3,15 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using Android.Content;
using Android.Content.Res;
using Android.Graphics;
using Android.Graphics.Drawables;
#nullable enable
using Android.Text;
using Android.Text.Style;
using Android.Views;
using AndroidX.AppCompat.Graphics.Drawable;
using AndroidX.AppCompat.Widget;
using Microsoft.Maui.Graphics;
using ATextView = global::Android.Widget.TextView;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
@ -17,6 +22,151 @@ namespace Microsoft.Maui.Controls.Platform
{
internal static class ToolbarExtensions
{
static Drawable? _defaultNavigationIcon;
public static void UpdateIsVisible(this AToolbar nativeToolbar, Toolbar toolbar)
{
_ = nativeToolbar.Context ?? throw new ArgumentNullException(nameof(nativeToolbar.Context));
bool showNavBar = toolbar.IsVisible;
var lp = nativeToolbar.LayoutParameters;
if (lp == null)
return;
if (!showNavBar)
{
lp.Height = 0;
}
else
{
if (toolbar.BarHeight != null)
lp.Height = (int)nativeToolbar.Context.ToPixels(toolbar.BarHeight.Value);
else
lp.Height = ActionBarHeight();
}
nativeToolbar.LayoutParameters = lp;
int ActionBarHeight()
{
int actionBarHeight = (int)nativeToolbar.Context.GetThemeAttributePixels(Resource.Attribute.actionBarSize);
return actionBarHeight;
}
}
public static void UpdateTitleIcon(this AToolbar nativeToolbar, Toolbar toolbar)
{
_ = nativeToolbar.Context ?? throw new ArgumentNullException(nameof(nativeToolbar.Context));
_ = toolbar?.Handler?.MauiContext ?? throw new ArgumentNullException(nameof(toolbar.Handler.MauiContext));
ImageSource source = toolbar.TitleIcon;
if (source == null || source.IsEmpty)
{
if (nativeToolbar.GetChildAt(0) is ToolbarTitleIconImageView existingImageView)
nativeToolbar.RemoveView(existingImageView);
return;
}
var iconView = new ToolbarTitleIconImageView(nativeToolbar.Context);
nativeToolbar.AddView(iconView, 0);
iconView.SetImageResource(global::Android.Resource.Color.Transparent);
source.LoadImage(toolbar.Handler.MauiContext, (result) =>
{
iconView.SetImageDrawable(result?.Value);
AutomationPropertiesProvider.AccessibilitySettingsChanged(iconView, source);
});
}
public static void UpdateBackButton(this AToolbar nativeToolbar, Toolbar toolbar)
{
bool isNavigated = toolbar.HasBackStack;
_defaultNavigationIcon ??= nativeToolbar.NavigationIcon;
if (isNavigated)
{
if (toolbar.BackButtonVisible)
{
nativeToolbar.NavigationIcon ??= _defaultNavigationIcon;
var backButtonTitle = toolbar.BackButtonTitle;
ImageSource image = toolbar.TitleIcon;
if (!string.IsNullOrEmpty(backButtonTitle))
{
nativeToolbar.NavigationContentDescription = backButtonTitle;
}
else if (image == null ||
nativeToolbar.SetNavigationContentDescription(image) == null)
{
nativeToolbar.SetNavigationContentDescription(Resource.String.nav_app_bar_navigate_up_description);
}
}
else
{
nativeToolbar.NavigationIcon = null;
}
}
nativeToolbar.UpdateIconColor(toolbar);
nativeToolbar.UpdateBarTextColor(toolbar);
}
public static void UpdateBarBackgroundColor(this AToolbar nativeToolbar, Toolbar toolbar)
{
var tintColor = toolbar.BarBackgroundColor;
if (tintColor == null)
nativeToolbar.BackgroundTintMode = null;
else
{
nativeToolbar.BackgroundTintMode = PorterDuff.Mode.Src;
nativeToolbar.BackgroundTintList = ColorStateList.ValueOf(tintColor.ToNative());
}
}
public static void UpdateBarBackground(this AToolbar nativeToolbar, Toolbar toolbar)
{
Brush barBackground = toolbar.BarBackground;
nativeToolbar.UpdateBackground(barBackground);
}
public static void UpdateIconColor(this AToolbar nativeToolbar, Toolbar toolbar)
{
var navIconColor = toolbar.IconColor;
if (navIconColor != null && nativeToolbar.NavigationIcon != null)
DrawableExtensions.SetColorFilter(nativeToolbar.NavigationIcon, navIconColor, FilterMode.SrcAtop);
}
public static void UpdateTitle(this AToolbar nativeToolbar, Toolbar toolbar)
{
nativeToolbar.Title = toolbar?.Title ?? string.Empty;
}
public static void UpdateBarTextColor(this AToolbar nativeToolbar, Toolbar toolbar)
{
var textColor = toolbar.BarTextColor;
if (textColor != null)
nativeToolbar.SetTitleTextColor(textColor.ToNative().ToArgb());
if (nativeToolbar.NavigationIcon != null && textColor != null)
{
var icon = nativeToolbar.NavigationIcon as DrawerArrowDrawable;
if (icon != null)
icon.Color = textColor.ToNative().ToArgb();
}
}
class ToolbarTitleIconImageView : AppCompatImageView
{
public ToolbarTitleIconImageView(Context context) : base(context)
{
}
}
const int DefaultDisabledToolbarAlpha = 127;
public static void DisposeMenuItems(this AToolbar toolbar, IEnumerable<ToolbarItem> toolbarItems, PropertyChangedEventHandler toolbarItemChanged)
{
@ -74,8 +224,11 @@ namespace Microsoft.Maui.Controls.Platform
List<ToolbarItem> previousToolBarItems,
Action<Context, IMenuItem, ToolbarItem>? updateMenuItemIcon = null)
{
var context = mauiContext.Context!;
var context = mauiContext.Context ??
throw new ArgumentNullException($"{nameof(mauiContext.Context)}");
IMenu menu = toolbar.Menu;
item.PropertyChanged -= toolbarItemChanged;
item.PropertyChanged += toolbarItemChanged;
@ -104,7 +257,8 @@ namespace Microsoft.Maui.Controls.Platform
if (menuItemIndex == null || menuItemIndex >= previousMenuItems?.Count)
{
menuitem = menu.Add(0, AView.GenerateViewId(), 0, newTitle)!;
menuitem = menu.Add(0, AView.GenerateViewId(), 0, newTitle) ??
throw new InvalidOperationException($"Failed to create menuitem: {newTitle}");
previousMenuItems?.Add(menuitem);
}
else
@ -158,8 +312,8 @@ namespace Microsoft.Maui.Controls.Platform
if (baseDrawable != null)
{
using (var constant = baseDrawable.GetConstantState()!)
using (var newDrawable = constant.NewDrawable())
using (var constant = baseDrawable.GetConstantState())
using (var newDrawable = constant!.NewDrawable())
using (var iconDrawable = newDrawable.Mutate())
{
if (tintColor != null)

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

@ -14,10 +14,9 @@ namespace Microsoft.Maui.Controls.Platform
{
readonly WeakReference _pageRenderer;
readonly IMauiContext _mauiContext;
Action<PageContainer> _onCreateCallback;
PageContainer _pageContainer;
Action<AView> _onCreateCallback;
AView _pageContainer;
INativeViewHandler _viewhandler;
//bool _isVisible = false;
AView NativeView => _viewhandler?.NativeView as AView;
public FragmentContainer(IMauiContext mauiContext)
@ -33,31 +32,38 @@ namespace Microsoft.Maui.Controls.Platform
public virtual Page Page => (Page)_pageRenderer?.Target;
IPageController PageController => Page as IPageController;
public static Fragment CreateInstance(Page page, IMauiContext mauiContext)
public static FragmentContainer CreateInstance(Page page, IMauiContext mauiContext)
{
return new FragmentContainer(page, mauiContext) { Arguments = new Bundle() };
}
public void SetOnCreateCallback(Action<PageContainer> callback)
public void SetOnCreateCallback(Action<AView> callback)
{
_onCreateCallback = callback;
}
protected virtual PageContainer CreatePageContainer(Context context, INativeViewHandler child, bool inFragment)
{
return new PageContainer(context, child, inFragment);
}
ViewGroup _parent;
public override AView OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
_parent = container ?? _parent;
if (Page != null)
{
Page.ToNative(_mauiContext);
_viewhandler = (INativeViewHandler)Page.Handler;
_pageContainer = Page?.Handler?.NativeView as AView;
_pageContainer = CreatePageContainer(inflater.Context, _viewhandler, true);
if (_pageContainer == null)
{
var scopedContext =
_mauiContext.MakeScoped(inflater, ChildFragmentManager);
_pageContainer = Page.ToNative(scopedContext);
_viewhandler = (INativeViewHandler)Page.Handler;
}
else
{
_parent = _parent ?? (_pageContainer.Parent as ViewGroup);
}
_onCreateCallback?.Invoke(_pageContainer);
@ -67,81 +73,22 @@ namespace Microsoft.Maui.Controls.Platform
return null;
}
protected virtual void RecyclePage()
public override void OnResume()
{
// Page.Handler = null;
}
if (_pageContainer == null)
return;
public override void OnDestroyView()
{
if (Page != null)
_parent = (_pageContainer.Parent as ViewGroup) ?? _parent;
if (_pageContainer.Parent == null && _parent != null)
{
if (_viewhandler != null)
{
if (NativeView.IsAlive())
{
NativeView.RemoveFromParent();
}
RecyclePage();
}
// Re-add the view to the container if Android removed it
// Because we are re-using views inside OnCreateView Android
// will remove the "previous" view from the parent but since our
// "previous" view and "current" view are the same we have to re-add it
_parent.AddView(_pageContainer);
}
_onCreateCallback = null;
_viewhandler = null;
base.OnDestroyView();
base.OnResume();
}
//public override void OnHiddenChanged(bool hidden)
//{
// base.OnHiddenChanged(hidden);
// if (Page == null)
// return;
// if (hidden)
// PageController?.SendDisappearing();
// else
// PageController?.SendAppearing();
//}
// TODO MAUI
//public override void OnPause()
//{
// _isVisible = false;
// bool shouldSendEvent = Application.Current.OnThisPlatform().GetSendDisappearingEventOnPause();
// if (shouldSendEvent)
// SendLifecycleEvent(false);
// base.OnPause();
//}
//public override void OnResume()
//{
// _isVisible = true;
// bool shouldSendEvent = Application.Current.OnThisPlatform().GetSendAppearingEventOnResume();
// if (shouldSendEvent)
// SendLifecycleEvent(true);
// base.OnResume();
//}
//void SendLifecycleEvent(bool isAppearing)
//{
// var flyoutPage = Application.Current.MainPage as FlyoutPage;
// var pageContainer = (flyoutPage != null ? flyoutPage.Detail : Application.Current.MainPage) as IPageContainer<Page>;
// Page currentPage = pageContainer?.CurrentPage;
// if (!(currentPage == null || currentPage == PageController))
// return;
// if (isAppearing && _isVisible)
// PageController?.SendAppearing();
// else if (!isAppearing)
// PageController?.SendDisappearing();
//}
}
}

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

@ -1,26 +1,20 @@
using System.Collections.Generic;
using AndroidX.AppCompat.App;
using AndroidX.Fragment.App;
using AndroidX.ViewPager2.Adapter;
using FragmentTransit = Android.App.FragmentTransit;
namespace Microsoft.Maui.Controls.Platform
{
internal class MultiPageFragmentStateAdapter<T> : FragmentStateAdapter where T : Page
{
MultiPage<T> _page;
FragmentManager _fragmentManager;
readonly IMauiContext _context;
List<Fragment> _fragments;
public MultiPageFragmentStateAdapter(
MultiPage<T> page, FragmentManager fragmentManager, IMauiContext context)
: base(fragmentManager, (context.GetActivity() as AppCompatActivity).Lifecycle)
: base(fragmentManager, context.GetActivity().Lifecycle)
{
_page = page;
_fragmentManager = fragmentManager;
_context = context;
_fragments = new List<Fragment>();
}
public override int ItemCount => CountOverride;
@ -30,9 +24,6 @@ namespace Microsoft.Maui.Controls.Platform
public override Fragment CreateFragment(int position)
{
var fragment = FragmentContainer.CreateInstance(_page.Children[position], _context);
_fragments.Add(fragment);
return fragment;
}

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

@ -1,45 +0,0 @@
using System;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Microsoft.Maui.Graphics;
using AView = Android.Views.View;
namespace Microsoft.Maui.Controls.Platform
{
// TODO Probably just get rid of this?
internal class PageContainer : ViewGroup
{
public PageContainer(Context context, IViewHandler child, bool inFragment = false) : base(context)
{
Id = AView.GenerateViewId();
Child = child;
IsInFragment = inFragment;
AddView((AView)child.NativeView);
}
public IViewHandler Child { get; set; }
public bool IsInFragment { get; set; }
protected PageContainer(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
if (changed && Child.NativeView is AView aView)
aView.Layout(l, t, r, b);
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (Child.NativeView is AView aView)
{
aView.Measure(widthMeasureSpec, heightMeasureSpec);
SetMeasuredDimension(aView.MeasuredWidth, aView.MeasuredHeight);
}
}
}
}

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

@ -76,7 +76,10 @@ namespace Microsoft.Maui.Controls
public static int bottomtab_tabbar = 2131230818;
// aapt resource value: 0x7F080063
public static int bottomtab_containerview = 2131230819;
public static int navigationlayout_bottomtabs = 2131230819;
// aapt resource value: 0x7F08013E
public static int navigationlayout_content = 2131231038;
// aapt resource value: 0x7F0800C7
public static int flyoutcontent_appbar = 2131230919;
@ -97,7 +100,10 @@ namespace Microsoft.Maui.Controls
public static int shellcontent_toolbar = 2131231101;
// aapt resource value: 0x7F0801CD
public static int toptabs_containerview = 2131231181;
public static int navigationlayout_toptabs = 2131231181;
// aapt resource value: 0x7F08013F
public static int navigationlayout_toolbar = 2131231039;
static Id()
{
@ -110,6 +116,9 @@ namespace Microsoft.Maui.Controls
// aapt resource value: 0x7F0B001C
public static int bottomtablayout = 2131427356;
// aapt resource value: 0x7F0B002E
public static int fragment_backstack = 2131427374;
// aapt resource value: 0x7F0B002F
public static int flyoutcontent = 2131427375;
@ -125,6 +134,17 @@ namespace Microsoft.Maui.Controls
}
}
public partial class Dimension
{
// aapt resource value: 0x7F060067
public static int design_bottom_navigation_height = 2131099751;
static Dimension()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
}
public partial class String
{
// aapt resource value: 0x7F0D0065

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

@ -1,6 +1,7 @@
using Android.Content;
using AndroidX.AppCompat.Widget;
using AndroidX.DrawerLayout.Widget;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
namespace Microsoft.Maui.Controls.Platform
{
@ -18,7 +19,7 @@ namespace Microsoft.Maui.Controls.Platform
IShellSectionView CreateShellSectionView(ShellSection shellSection);
IShellToolbarTracker CreateTrackerForToolbar(Toolbar toolbar);
IShellToolbarTracker CreateTrackerForToolbar(AToolbar toolbar);
IShellToolbarAppearanceTracker CreateToolbarAppearanceTracker();

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

@ -1,11 +1,12 @@
using System;
using AndroidX.AppCompat.Widget;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
namespace Microsoft.Maui.Controls.Platform
{
public interface IShellToolbarAppearanceTracker : IDisposable
{
void SetAppearance(Toolbar toolbar, IShellToolbarTracker toolbarTracker, ShellAppearance appearance);
void ResetAppearance(Toolbar toolbar, IShellToolbarTracker toolbarTracker);
void SetAppearance(AToolbar toolbar, IShellToolbarTracker toolbarTracker, ShellAppearance appearance);
void ResetAppearance(AToolbar toolbar, IShellToolbarTracker toolbarTracker);
}
}

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

@ -10,6 +10,7 @@ using Google.Android.Material.AppBar;
using AndroidAnimation = Android.Views.Animations.Animation;
using AnimationSet = Android.Views.Animations.AnimationSet;
using AView = Android.Views.View;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
namespace Microsoft.Maui.Controls.Platform
{
@ -66,7 +67,7 @@ namespace Microsoft.Maui.Controls.Platform
AView _root;
ShellPageContainer _shellPageContainer;
ShellContent _shellContent;
Toolbar _toolbar;
AToolbar _toolbar;
IShellToolbarTracker _toolbarTracker;
bool _disposed;
IMauiContext MauiContext => _shellContext.Shell.Handler.MauiContext;
@ -132,7 +133,7 @@ namespace Microsoft.Maui.Controls.Platform
_root = inflater.Inflate(Resource.Layout.shellcontent, null).JavaCast<CoordinatorLayout>();
_toolbar = _root.FindViewById<Toolbar>(Resource.Id.shellcontent_toolbar);
_toolbar = _root.FindViewById<AToolbar>(Resource.Id.shellcontent_toolbar);
_page.ToNative(MauiContext);
_viewhandler = (INativeViewHandler)_page.Handler;

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

@ -3,36 +3,31 @@ using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using AndroidX.Fragment.App;
using AView = Android.Views.View;
using LP = Android.Views.ViewGroup.LayoutParams;
namespace Microsoft.Maui.Controls.Platform
{
internal class ShellFragmentContainer : FragmentContainer
internal class ShellFragmentContainer : Fragment
{
Page _page;
readonly IMauiContext _mauiContext;
public ShellContent ShellContentTab { get; private set; }
public ShellFragmentContainer(ShellContent shellContent, IMauiContext mauiContext) : base(mauiContext)
public ShellFragmentContainer(ShellContent shellContent, IMauiContext mauiContext)
{
ShellContentTab = shellContent;
}
public override Page Page => _page;
protected override PageContainer CreatePageContainer(Context context, INativeViewHandler child, bool inFragment)
{
return new ShellPageContainer(context, child, inFragment)
{
LayoutParameters = new LP(LP.MatchParent, LP.MatchParent)
};
_mauiContext = mauiContext;
}
public override AView OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
_page = ((IShellContentController)ShellContentTab).GetOrCreateContent();
return base.OnCreateView(inflater, container, savedInstanceState);
var view = _page.ToNative(_mauiContext);
view.LayoutParameters = new LP(LP.MatchParent, LP.MatchParent);
return view;
}
public override void OnDestroyView()
@ -42,11 +37,6 @@ namespace Microsoft.Maui.Controls.Platform
_page = null;
}
protected override void RecyclePage()
{
// Don't remove the handler inside shell we just keep it around
}
public override void OnDestroy()
{
Device.BeginInvokeOnMainThread(Dispose);

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

@ -1,4 +1,5 @@
using Android.Content;
using Android.Views;
using AndroidX.Core.Content;
using Microsoft.Maui.Graphics;
using AColor = Android.Graphics.Color;
@ -7,13 +8,20 @@ using AView = Android.Views.View;
namespace Microsoft.Maui.Controls.Platform
{
internal class ShellPageContainer : PageContainer
internal class ShellPageContainer : ViewGroup
{
static int? DarkBackground;
static int? LightBackground;
public ShellPageContainer(Context context, INativeViewHandler child, bool inFragment = false) : base(context, child, inFragment)
public IViewHandler Child { get; set; }
public bool IsInFragment { get; set; }
public ShellPageContainer(Context context, INativeViewHandler child, bool inFragment = false) : base(context)
{
Child = child;
IsInFragment = inFragment;
if (child.VirtualView.Background == null)
{
int color;
@ -34,5 +42,14 @@ namespace Microsoft.Maui.Controls.Platform
if (Child.NativeView is AView aView)
aView.Layout(0, 0, width, height);
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (Child.NativeView is AView aView)
{
aView.Measure(widthMeasureSpec, heightMeasureSpec);
SetMeasuredDimension(aView.MeasuredWidth, aView.MeasuredHeight);
}
}
}
}

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

@ -13,6 +13,7 @@ using AndroidX.ViewPager.Widget;
using AndroidX.ViewPager2.Widget;
using Google.Android.Material.Tabs;
using AView = Android.Views.View;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
namespace Microsoft.Maui.Controls.Platform
{
@ -104,7 +105,7 @@ namespace Microsoft.Maui.Controls.Platform
bool _selecting;
TabLayout _tablayout;
IShellTabLayoutAppearanceTracker _tabLayoutAppearanceTracker;
Toolbar _toolbar;
AToolbar _toolbar;
IShellToolbarAppearanceTracker _toolbarAppearanceTracker;
IShellToolbarTracker _toolbarTracker;
ViewPager2 _viewPager;
@ -134,7 +135,7 @@ namespace Microsoft.Maui.Controls.Platform
var root = inflater.Inflate(Resource.Layout.shellrootlayout, null).JavaCast<CoordinatorLayout>();
_toolbar = root.FindViewById<Toolbar>(Resource.Id.maui_toolbar);
_toolbar = root.FindViewById<AToolbar>(Resource.Id.maui_toolbar);
if (Context.GetActivity() is AppCompatActivity aca)
aca.SetSupportActionBar(_toolbar);

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

@ -2,6 +2,7 @@ using Android.Graphics.Drawables;
using AndroidX.AppCompat.Widget;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Graphics;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
namespace Microsoft.Maui.Controls.Platform
{
@ -17,7 +18,7 @@ namespace Microsoft.Maui.Controls.Platform
_shellContext = shellContext;
}
public virtual void SetAppearance(Toolbar toolbar, IShellToolbarTracker toolbarTracker, ShellAppearance appearance)
public virtual void SetAppearance(AToolbar toolbar, IShellToolbarTracker toolbarTracker, ShellAppearance appearance)
{
var foreground = appearance.ForegroundColor;
var background = appearance.BackgroundColor;
@ -26,12 +27,12 @@ namespace Microsoft.Maui.Controls.Platform
SetColors(toolbar, toolbarTracker, foreground, background, titleColor);
}
public virtual void ResetAppearance(Toolbar toolbar, IShellToolbarTracker toolbarTracker)
public virtual void ResetAppearance(AToolbar toolbar, IShellToolbarTracker toolbarTracker)
{
SetColors(toolbar, toolbarTracker, ShellView.DefaultForegroundColor, ShellView.DefaultBackgroundColor, ShellView.DefaultTitleColor);
}
protected virtual void SetColors(Toolbar toolbar, IShellToolbarTracker toolbarTracker, Color foreground, Color background, Color title)
protected virtual void SetColors(AToolbar toolbar, IShellToolbarTracker toolbarTracker, Color foreground, Color background, Color title)
{
var titleArgb = title.ToNative(ShellView.DefaultTitleColor).ToArgb();

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

@ -27,7 +27,7 @@ using Color = Microsoft.Maui.Graphics.Color;
using LP = Android.Views.ViewGroup.LayoutParams;
using Paint = Android.Graphics.Paint;
using R = Android.Resource;
using Toolbar = AndroidX.AppCompat.Widget.Toolbar;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
namespace Microsoft.Maui.Controls.Platform
{
@ -59,7 +59,7 @@ namespace Microsoft.Maui.Controls.Platform
protected IShellContext ShellContext { get; private set; }
//assume the default
Color _tintColor = null;
Toolbar _toolbar;
AToolbar _toolbar;
AppBarLayout _appBar;
float _appBarElevation;
GenericGlobalLayoutListener _globalLayoutListener;
@ -67,7 +67,7 @@ namespace Microsoft.Maui.Controls.Platform
List<ToolbarItem> _currentToolbarItems = new List<ToolbarItem>();
protected IMauiContext MauiContext => ShellContext.Shell.Handler.MauiContext;
public ShellToolbarTracker(IShellContext shellContext, Toolbar toolbar, DrawerLayout drawerLayout)
public ShellToolbarTracker(IShellContext shellContext, AToolbar toolbar, DrawerLayout drawerLayout)
{
ShellContext = shellContext ?? throw new ArgumentNullException(nameof(shellContext));
_toolbar = toolbar ?? throw new ArgumentNullException(nameof(toolbar));
@ -336,7 +336,7 @@ namespace Microsoft.Maui.Controls.Platform
return image;
}
protected virtual async void UpdateLeftBarButtonItem(Context context, Toolbar toolbar, DrawerLayout drawerLayout, Page page)
protected virtual async void UpdateLeftBarButtonItem(Context context, AToolbar toolbar, DrawerLayout drawerLayout, Page page)
{
if (_drawerToggle == null)
{
@ -438,12 +438,12 @@ namespace Microsoft.Maui.Controls.Platform
}
protected virtual Task UpdateDrawerArrow(Context context, Toolbar toolbar, DrawerLayout drawerLayout)
protected virtual Task UpdateDrawerArrow(Context context, AToolbar toolbar, DrawerLayout drawerLayout)
{
return Task.CompletedTask;
}
protected virtual void UpdateToolbarIconAccessibilityText(Toolbar toolbar, Shell shell)
protected virtual void UpdateToolbarIconAccessibilityText(AToolbar toolbar, Shell shell)
{
var backButtonHandler = Shell.GetBackButtonBehavior(Page);
var image = GetFlyoutIcon(backButtonHandler, Page);
@ -465,7 +465,7 @@ namespace Microsoft.Maui.Controls.Platform
}
}
protected virtual Task UpdateDrawerArrowFromBackButtonBehavior(Context context, Toolbar toolbar, DrawerLayout drawerLayout, BackButtonBehavior backButtonHandler)
protected virtual Task UpdateDrawerArrowFromBackButtonBehavior(Context context, AToolbar toolbar, DrawerLayout drawerLayout, BackButtonBehavior backButtonHandler)
{
return Task.CompletedTask;
}
@ -493,7 +493,7 @@ namespace Microsoft.Maui.Controls.Platform
});
}
protected virtual void UpdateNavBarVisible(Toolbar toolbar, Page page)
protected virtual void UpdateNavBarVisible(AToolbar toolbar, Page page)
{
var navBarVisible = Shell.GetNavBarIsVisible(page);
toolbar.Visibility = navBarVisible ? ViewStates.Visible : ViewStates.Gone;
@ -517,12 +517,12 @@ namespace Microsoft.Maui.Controls.Platform
}
}
protected virtual void UpdatePageTitle(Toolbar toolbar, Page page)
protected virtual void UpdatePageTitle(AToolbar toolbar, Page page)
{
_toolbar.Title = page.Title;
}
protected virtual void UpdateTitleView(Context context, Toolbar toolbar, View titleView)
protected virtual void UpdateTitleView(Context context, AToolbar toolbar, View titleView)
{
if (titleView == null)
{
@ -537,7 +537,7 @@ namespace Microsoft.Maui.Controls.Platform
{
_titleViewContainer = new ShellContainerView(context, titleView, MauiContext);
_titleViewContainer.MatchHeight = _titleViewContainer.MatchWidth = true;
_titleViewContainer.LayoutParameters = new Toolbar.LayoutParams(LP.MatchParent, LP.MatchParent)
_titleViewContainer.LayoutParameters = new AToolbar.LayoutParams(LP.MatchParent, LP.MatchParent)
{
LeftMargin = (int)context.ToPixels(titleView.Margin.Left),
TopMargin = (int)context.ToPixels(titleView.Margin.Top),
@ -553,7 +553,7 @@ namespace Microsoft.Maui.Controls.Platform
}
}
protected virtual void UpdateToolbarItems(Toolbar toolbar, Page page)
protected virtual void UpdateToolbarItems(AToolbar toolbar, Page page)
{
var menu = toolbar.Menu;
var sortedItems = page.ToolbarItems.OrderBy(x => x.Order);

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

@ -16,7 +16,7 @@ using AView = Android.Views.View;
using Color = Microsoft.Maui.Graphics.Color;
using LP = Android.Views.ViewGroup.LayoutParams;
using Paint = Android.Graphics.Paint;
using Toolbar = AndroidX.AppCompat.Widget.Toolbar;
using AToolbar = AndroidX.AppCompat.Widget.Toolbar;
namespace Microsoft.Maui.Controls.Platform
{
@ -54,7 +54,7 @@ namespace Microsoft.Maui.Controls.Platform
return CreateShellSectionView(shellSection);
}
IShellToolbarTracker IShellContext.CreateTrackerForToolbar(Toolbar toolbar)
IShellToolbarTracker IShellContext.CreateTrackerForToolbar(AToolbar toolbar)
{
return CreateTrackerForToolbar(toolbar);
}
@ -150,7 +150,7 @@ namespace Microsoft.Maui.Controls.Platform
return new ShellSectionView(this);
}
protected virtual IShellToolbarTracker CreateTrackerForToolbar(Toolbar toolbar)
protected virtual IShellToolbarTracker CreateTrackerForToolbar(AToolbar toolbar)
{
return new ShellToolbarTracker(this, toolbar, ((IShellContext)this).CurrentDrawerLayout);
}

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

@ -29,12 +29,11 @@ namespace Microsoft.Maui.Controls.Handlers
{
public class TabbedPageManager
{
Fragment _tabLayoutFragment;
ColorStateList _originalTabTextColors;
ColorStateList _orignalTabIconColors;
ColorStateList _newTabTextColors;
ColorStateList _newTabIconColors;
FragmentManager _fragmentManager;
TabLayout _tabLayout;
BottomNavigationView _bottomNavigationView;
@ -46,16 +45,17 @@ namespace Microsoft.Maui.Controls.Handlers
int _defaultARGBColor = Colors.Transparent.ToNative().ToArgb();
AColor _defaultAndroidColor = Colors.Transparent.ToNative();
readonly IMauiContext _context;
private readonly Listeners _listeners;
readonly Listeners _listeners;
TabbedPage Element { get; set; }
internal TabLayout TabLayout => _tabLayout;
internal BottomNavigationView BottomNavigationView => _bottomNavigationView;
internal ViewPager2 ViewPager => _viewPager;
NavigationRootManager NavigationRootManager { get; }
public TabbedPageManager(IMauiContext context)
{
_context = context;
NavigationRootManager = _context.GetNavigationRootManager();
_listeners = new Listeners(this);
_viewPager = new ViewPager2(context.Context)
{
@ -104,11 +104,16 @@ namespace Microsoft.Maui.Controls.Handlers
{
Element.PropertyChanged -= OnElementPropertyChanged;
((IPageController)Element).InternalChildren.CollectionChanged -= OnChildrenCollectionChanged;
Element.Appearing -= OnTabbedPageAppearing;
Element.Disappearing -= OnTabbedPageDisappearing;
RemoveTabs();
}
Element = tabbedPage;
if (Element != null)
{
Element.Appearing += OnTabbedPageAppearing;
Element.Disappearing += OnTabbedPageDisappearing;
_viewPager.Adapter = new MultiPageFragmentStateAdapter<Page>(tabbedPage, FragmentManager, _context) { CountOverride = tabbedPage.Children.Count };
Element.PropertyChanged += OnElementPropertyChanged;
if (IsBottomTabPlacement)
@ -126,7 +131,6 @@ namespace Microsoft.Maui.Controls.Handlers
if (_tabLayout == null)
{
var layoutInflater = Element.Handler.MauiContext.GetLayoutInflater();
var navManager = Element.Handler.MauiContext.GetNavigationManager();
_tabLayout = new TabLayout(_context.Context)
{
TabMode = TabLayout.ModeFixed,
@ -150,9 +154,83 @@ namespace Microsoft.Maui.Controls.Handlers
UpdateItemIconColor();
UpdateSwipePaging();
UpdateOffscreenPageLimit();
SetTabLayout();
}
}
void RemoveTabs()
{
if (_tabLayoutFragment != null)
{
var fragment = _tabLayoutFragment;
_tabLayoutFragment = null;
_ = _context
.GetNavigationRootManager()
.FragmentManager
.BeginTransaction()
.Remove(fragment)
.SetReorderingAllowed(true)
.Commit();
_tabplacementId = 0;
}
}
void OnTabbedPageDisappearing(object sender, EventArgs e)
{
RemoveTabs();
}
void OnTabbedPageAppearing(object sender, EventArgs e)
{
SetTabLayout();
}
int _tabplacementId;
internal void SetTabLayout()
{
int id;
var rootManager =
_context.GetNavigationRootManager();
if (IsBottomTabPlacement)
{
id = Resource.Id.navigationlayout_bottomtabs;
if (_tabplacementId == id)
return;
_tabLayoutFragment = new ViewFragment(BottomNavigationView);
var layoutContent = rootManager.RootView.FindViewById(Resource.Id.navigationlayout_content);
if (layoutContent.LayoutParameters is ViewGroup.MarginLayoutParams cl)
{
cl.BottomMargin = _context.Context.Resources.GetDimensionPixelSize(Resource.Dimension.design_bottom_navigation_height);
}
}
else
{
id = Resource.Id.navigationlayout_toptabs;
if (_tabplacementId == id)
return;
_tabLayoutFragment = new ViewFragment(TabLayout);
var layoutContent = rootManager.RootView.FindViewById(Resource.Id.navigationlayout_content);
if (layoutContent.LayoutParameters is ViewGroup.MarginLayoutParams cl)
{
cl.BottomMargin = 0;
}
}
_tabplacementId = id;
_ = rootManager
.FragmentManager
.BeginTransaction()
.Replace(id, _tabLayoutFragment)
.SetReorderingAllowed(true)
.Commit();
}
void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(TabbedPage.CurrentPage))
@ -181,14 +259,14 @@ namespace Microsoft.Maui.Controls.Handlers
{
e.Apply((o, i, c) => SetupPage((Page)o), (o, i) => TeardownPage((Page)o), Reset);
ViewPager2 pager = _viewPager;
var adapter = (MultiPageFragmentStateAdapter<Page>)pager.Adapter;
adapter.CountOverride = Element.Children.Count;
if (IsBottomTabPlacement)
{
ViewPager2 pager = _viewPager;
BottomNavigationView bottomNavigationView = _bottomNavigationView;
((MultiPageFragmentStateAdapter<Page>)pager.Adapter).CountOverride = Element.Children.Count;
pager.Adapter.NotifyDataSetChanged();
adapter.NotifyDataSetChanged();
if (Element.Children.Count == 0)
{
@ -204,11 +282,9 @@ namespace Microsoft.Maui.Controls.Handlers
}
else
{
ViewPager2 pager = _viewPager;
TabLayout tabs = _tabLayout;
((MultiPageFragmentStateAdapter<Page>)pager.Adapter).CountOverride = Element.Children.Count;
pager.Adapter.NotifyDataSetChanged();
adapter.NotifyDataSetChanged();
if (Element.Children.Count == 0)
{
tabs.RemoveAllTabs();
@ -375,7 +451,7 @@ namespace Microsoft.Maui.Controls.Handlers
_context,
result =>
{
SetTabIconImageSource(tab, result.Value);
SetTabIconImageSource(tab, result?.Value);
});
}

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

@ -1,154 +0,0 @@
#nullable enable
using System.Collections.Generic;
using Microsoft.Maui.Controls.PlatformConfiguration.WindowsSpecific;
using Microsoft.UI.Xaml.Controls;
using WImage = Microsoft.UI.Xaml.Controls.Image;
using NativeAutomationProperties = Microsoft.UI.Xaml.Automation.AutomationProperties;
namespace Microsoft.Maui.Controls.Platform
{
public class ControlsNavigationManager : NavigationManager
{
readonly ImageConverter _imageConverter = new ImageConverter();
readonly ImageSourceIconElementConverter _imageSourceIconElementConverter = new ImageSourceIconElementConverter();
public new Page CurrentPage => (Page)base.CurrentPage;
public new NavigationPage? NavigationView => (NavigationPage?)base.NavigationView;
public ControlsNavigationManager(IMauiContext mauiContext) : base(mauiContext)
{
}
internal void ToolbarPropertyChanged() => UpdateToolbar();
public override void NavigateTo(NavigationRequest arg3)
{
base.NavigateTo(arg3);
UpdateToolbar();
}
// TODO MAUI: This will need to be updated to handle multiple nested navigation pages
protected virtual void UpdateToolbar()
{
if (WindowManager.RootView is not MauiNavigationView nativeNavigationView || NavigationStack.Count == 0)
return;
var commandBar = WindowManager.GetCommandBar();
var header = nativeNavigationView.Header as WindowHeader;
// TODO MAUI: these only apply for the top level NavigationManager
if (commandBar != null)
commandBar.IsDynamicOverflowEnabled = NavigationView.OnThisPlatform().GetToolbarDynamicOverflowEnabled();
bool hasNavigationBar = NavigationPage.GetHasNavigationBar(CurrentPage);
bool hasBackButton =
NavigationPage.GetHasBackButton(CurrentPage) &&
hasNavigationBar &&
NavigationStack.Count > 1;
var title = CurrentPage.Title;
var titleIcon = NavigationPage.GetTitleIconImageSource(CurrentPage);
var titleView = NavigationPage.GetTitleView(CurrentPage);
var barBackground = NavigationView?.BarBackground;
var barBackgroundColor = NavigationView?.BarBackgroundColor;
var barTextColor = NavigationView?.BarTextColor;
// TODO MAUI: it seems like this isn't wired up on WinUI
//var iconColor = NavigationPage.GetIconColor(CurrentPage);
// TODO MAUI: Should be able to just modify the GRID inside NavigationLayout to move header to footer
// Or we add a control in the footer
var toolbarPlacement = NavigationView?.OnThisPlatform().GetToolbarPlacement();
if (header != null)
{
header.Visibility = (hasNavigationBar) ? UI.Xaml.Visibility.Visible : UI.Xaml.Visibility.Collapsed;
// HeaderContent is set to a MinHeight of 48 so we have to collapse it if we
// we want to hide the navbar completely
if (nativeNavigationView.HeaderContent != null)
nativeNavigationView.HeaderContent.Visibility = header.Visibility;
header.Title = title;
titleIcon.LoadImage(MauiContext, (result) =>
{
header.TitleIcon = result?.Value;
});
if (barTextColor != null)
header.TitleColor = barTextColor.ToNative();
header.TitleView = titleView?.ToNative(MauiContext);
}
nativeNavigationView.IsBackButtonVisible = (hasBackButton) ? NavigationViewBackButtonVisible.Visible : NavigationViewBackButtonVisible.Collapsed;
nativeNavigationView.IsBackEnabled = true;
nativeNavigationView.UpdateBarBackgroundBrush(
barBackground?.ToBrush() ?? barBackgroundColor?.ToNative());
UpdateToolbarItems();
nativeNavigationView.PaneDisplayMode = NavigationViewPaneDisplayMode.LeftMinimal;
nativeNavigationView.IsPaneOpen = false;
}
//TODO MAUI: This will need to be updated to handle nested pages propagating up
internal void UpdateToolbarItems()
{
if (WindowManager.RootView is not MauiNavigationView navigationView || NavigationStack.Count == 0)
return;
var commandBar = WindowManager.GetCommandBar();
if (commandBar == null)
{
return;
}
commandBar.PrimaryCommands.Clear();
commandBar.SecondaryCommands.Clear();
List<ToolbarItem> toolbarItems = new List<ToolbarItem>();
if (NavigationView != null)
toolbarItems.AddRange(NavigationView.ToolbarItems);
toolbarItems.AddRange(CurrentPage.ToolbarItems);
foreach (ToolbarItem item in toolbarItems)
{
var button = new AppBarButton();
button.SetBinding(AppBarButton.LabelProperty, "Text");
if (commandBar.IsDynamicOverflowEnabled && item.Order == ToolbarItemOrder.Secondary)
{
button.SetBinding(AppBarButton.IconProperty, "IconImageSource", _imageSourceIconElementConverter);
}
else
{
var img = new WImage();
img.SetBinding(WImage.SourceProperty, "Value");
img.SetBinding(WImage.DataContextProperty, "IconImageSource", _imageConverter);
button.Content = img;
}
// TODO MAUI: MenuItemCommand is no longer available in WinUI
//button.Command = new MenuItemCommand(item);
button.DataContext = item;
button.SetValue(NativeAutomationProperties.AutomationIdProperty, item.AutomationId);
button.SetAutomationPropertiesName(item);
button.SetAutomationPropertiesAccessibilityView(item);
button.SetAutomationPropertiesHelpText(item);
button.SetAutomationPropertiesLabeledBy(item, null);
ToolbarItemOrder order = item.Order == ToolbarItemOrder.Default ? ToolbarItemOrder.Primary : item.Order;
if (order == ToolbarItemOrder.Primary)
{
commandBar.PrimaryCommands.Add(button);
}
else
{
commandBar.SecondaryCommands.Add(button);
}
}
}
}
}

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

@ -0,0 +1,98 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Maui.Graphics;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Color = Microsoft.Maui.Graphics.Color;
namespace Microsoft.Maui.Controls.Platform
{
internal static class ToolbarExtensions
{
public static void UpdateIsVisible(this WindowHeader nativeToolbar, Toolbar toolbar)
{
nativeToolbar.Visibility = (toolbar.IsVisible) ? UI.Xaml.Visibility.Visible : UI.Xaml.Visibility.Collapsed;
if (nativeToolbar.NavigationView?.HeaderContent != null)
nativeToolbar.NavigationView.HeaderContent.Visibility = nativeToolbar.Visibility;
}
public static void UpdateTitleIcon(this WindowHeader nativeToolbar, Toolbar toolbar)
{
_ = toolbar?.Handler?.MauiContext ?? throw new ArgumentNullException(nameof(toolbar.Handler.MauiContext));
toolbar.TitleIcon.LoadImage(toolbar.Handler.MauiContext, (result) =>
{
if (result != null)
{
nativeToolbar.TitleIconImageSource = result.Value;
toolbar.Handler.UpdateValue(nameof(Toolbar.IconColor));
}
else
nativeToolbar.TitleIconImageSource = null;
});
}
public static void UpdateBackButton(this WindowHeader nativeToolbar, Toolbar toolbar)
{
if (nativeToolbar.NavigationView == null)
return;
nativeToolbar
.NavigationView
.IsBackButtonVisible = (toolbar.BackButtonVisible) ? NavigationViewBackButtonVisible.Visible : NavigationViewBackButtonVisible.Collapsed;
nativeToolbar.NavigationView.IsBackEnabled = toolbar.BackButtonVisible;
toolbar.Handler.UpdateValue(nameof(Toolbar.BarBackground));
}
public static void UpdateBarBackgroundColor(this WindowHeader nativeToolbar, Toolbar toolbar)
{
UpdateBarBackground(nativeToolbar, toolbar);
}
public static void UpdateBarBackground(this WindowHeader nativeToolbar, Toolbar toolbar)
{
if (nativeToolbar.NavigationView == null)
return;
var barBackground = toolbar.BarBackground;
var barBackgroundColor = toolbar.BarBackgroundColor;
nativeToolbar.NavigationView.UpdateBarBackgroundBrush(
barBackground?.ToBrush() ?? barBackgroundColor?.ToNative());
}
public static void UpdateTitleView(this WindowHeader nativeToolbar, Toolbar toolbar)
{
_ = toolbar.Handler?.MauiContext ?? throw new ArgumentNullException(nameof(toolbar.Handler.MauiContext));
nativeToolbar.TitleView = toolbar.TitleView?.ToNative(toolbar.Handler.MauiContext);
}
public static void UpdateIconColor(this WindowHeader nativeToolbar, Toolbar toolbar)
{
// This property wasn't wired up in Controls
}
public static void UpdateTitle(this WindowHeader nativeToolbar, Toolbar toolbar)
{
nativeToolbar.Title = toolbar.Title;
}
public static void UpdateBarTextColor(this WindowHeader nativeToolbar, Toolbar toolbar)
{
if (toolbar.BarTextColor != null)
nativeToolbar.TitleColor = toolbar.BarTextColor.ToNative();
}
public static void UpdateToolbarDynamicOverflowEnabled(this WindowHeader nativeToolbar, Toolbar toolbar)
{
if (nativeToolbar.CommandBar == null)
return;
nativeToolbar.CommandBar.IsDynamicOverflowEnabled = toolbar.DynamicOverflowEnabled;
}
}
}

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

@ -1363,6 +1363,7 @@ namespace Microsoft.Maui.Controls
if (ModalStack.Count == 0 && !_shell.CurrentItem.CurrentItem.IsPoppingModalStack)
_shell.CurrentItem.SendAppearing();
modalPopped.Parent = null;
return modalPopped;
}
@ -1371,6 +1372,8 @@ namespace Microsoft.Maui.Controls
if (ModalStack.Count == 0)
_shell.CurrentItem.SendDisappearing();
modal.Parent = (Element)_shell.FindParentOfType<IWindow>();
if (!_shell.CurrentItem.CurrentItem.IsPushingModalStack)
modal.SendAppearing();

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

@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.Maui.Graphics;
namespace Microsoft.Maui.Controls
{
public class Toolbar : Microsoft.Maui.IElement
{
NavigationPage _currentNavigationPage;
Page _currentPage;
VisualElement _titleView;
string _title;
Color _iconColor;
Color _barTextColor;
Brush _barBackground;
Color _barBackgroundColor;
ImageSource _titleIcon;
string _backButtonTitle;
double? _barHeight;
bool _backButtonVisible;
bool _hasBackStack;
bool _isVisible = false;
IEnumerable<ToolbarItem> _toolbarItems;
ToolbarTracker _toolbarTracker = new ToolbarTracker();
bool _dynamicOverflowEnabled;
public Toolbar()
{
_toolbarTracker.CollectionChanged += (_, __) => ToolbarItems = _toolbarTracker.ToolbarItems;
}
public IEnumerable<ToolbarItem> ToolbarItems { get => _toolbarItems; set => SetProperty(ref _toolbarItems, value); }
public bool IsVisible { get => _isVisible; set => SetProperty(ref _isVisible, value); }
public bool HasBackStack { get => _hasBackStack; set => SetProperty(ref _hasBackStack, value); }
public bool BackButtonVisible { get => _backButtonVisible; set => SetProperty(ref _backButtonVisible, value); }
public double? BarHeight { get => _barHeight; set => SetProperty(ref _barHeight, value); }
public string BackButtonTitle { get => _backButtonTitle; set => SetProperty(ref _backButtonTitle, value); }
public ImageSource TitleIcon { get => _titleIcon; set => SetProperty(ref _titleIcon, value); }
public Color BarBackgroundColor { get => _barBackgroundColor; set => SetProperty(ref _barBackgroundColor, value); }
public Brush BarBackground { get => _barBackground; set => SetProperty(ref _barBackground, value); }
public Color BarTextColor { get => _barTextColor; set => SetProperty(ref _barTextColor, value); }
public Color IconColor { get => _iconColor; set => SetProperty(ref _iconColor, value); }
public string Title { get => _title; set => SetProperty(ref _title, value); }
public VisualElement TitleView { get => _titleView; set => SetProperty(ref _titleView, value); }
public bool DynamicOverflowEnabled { get => _dynamicOverflowEnabled; set => SetProperty(ref _dynamicOverflowEnabled, value); }
public IElementHandler Handler { get; set; }
Maui.IElement _parent;
Maui.IElement Maui.IElement.Parent => _parent;
void SetProperty<T>(ref T backingStore, T value,
[CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return;
backingStore = value;
Handler?.UpdateValue(propertyName);
}
internal void ApplyNavigationPage(NavigationPage navigationPage)
{
_parent = _parent ?? navigationPage.FindParentOfType<Window>();
if (_currentNavigationPage == navigationPage)
return;
if (_currentNavigationPage != null)
_currentNavigationPage.PropertyChanged -= OnPropertyChanged;
_currentNavigationPage = navigationPage;
_currentNavigationPage.PropertyChanged += OnPropertyChanged;
UpdateCurrentPage();
ApplyChanges(_currentNavigationPage);
}
void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (sender == _currentNavigationPage && e.Is(NavigationPage.CurrentPageProperty))
{
UpdateCurrentPage();
ApplyChanges(_currentNavigationPage);
}
else if (e.IsOneOf(NavigationPage.HasNavigationBarProperty,
NavigationPage.HasBackButtonProperty,
NavigationPage.TitleIconImageSourceProperty,
NavigationPage.TitleViewProperty,
NavigationPage.IconColorProperty) ||
e.IsOneOf(Page.TitleProperty,
PlatformConfiguration.AndroidSpecific.AppCompat.NavigationPage.BarHeightProperty,
NavigationPage.BarBackgroundColorProperty,
NavigationPage.BarBackgroundProperty,
NavigationPage.BarTextColorProperty) ||
e.IsOneOf(
PlatformConfiguration.WindowsSpecific.Page.ToolbarDynamicOverflowEnabledProperty,
PlatformConfiguration.WindowsSpecific.Page.ToolbarPlacementProperty))
{
ApplyChanges(_currentNavigationPage);
}
}
void UpdateCurrentPage()
{
if (_currentNavigationPage == null)
return;
var stack = _currentNavigationPage.Navigation.NavigationStack;
if (stack.Count == 0)
return;
if (_currentPage == _currentNavigationPage.CurrentPage)
return;
if (_currentPage != null)
_currentPage.PropertyChanged -= OnPropertyChanged;
_currentPage = _currentNavigationPage.CurrentPage;
_currentNavigationPage.CurrentPage.PropertyChanged += OnPropertyChanged;
HasBackStack = _currentNavigationPage.Navigation.NavigationStack.Count > 1;
}
void ApplyChanges(NavigationPage navigationPage)
{
var stack = navigationPage.Navigation.NavigationStack;
if (stack.Count == 0)
return;
UpdateCurrentPage();
var currentPage = _currentPage;
Page previousPage = null;
if (stack.Count > 1)
previousPage = stack[stack.Count - 1];
_toolbarTracker.Target = navigationPage.CurrentPage;
_toolbarTracker.AdditionalTargets = navigationPage.GetParentPages();
ToolbarItems = _toolbarTracker.ToolbarItems;
IsVisible = NavigationPage.GetHasNavigationBar(currentPage);
BackButtonVisible = NavigationPage.GetHasBackButton(currentPage) && stack.Count > 1;
if (navigationPage.IsSet(PlatformConfiguration.AndroidSpecific.AppCompat.NavigationPage.BarHeightProperty))
BarHeight = PlatformConfiguration.AndroidSpecific.AppCompat.NavigationPage.GetBarHeight(navigationPage);
else
BarHeight = null;
if (previousPage != null)
BackButtonTitle = NavigationPage.GetBackButtonTitle(previousPage);
else
BackButtonTitle = null;
TitleIcon = NavigationPage.GetTitleIconImageSource(currentPage);
BarBackgroundColor = navigationPage.BarBackgroundColor;
BarBackground = navigationPage.BarBackground;
BarTextColor = navigationPage.BarTextColor;
IconColor = NavigationPage.GetIconColor(currentPage);
Title = currentPage.Title;
TitleView = NavigationPage.GetTitleView(navigationPage);
DynamicOverflowEnabled = PlatformConfiguration.WindowsSpecific.Page.GetToolbarDynamicOverflowEnabled(_currentPage);
}
}
}

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

@ -4,7 +4,6 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using Microsoft.Maui.Controls.Internals;
namespace Microsoft.Maui.Controls
{

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

@ -19,8 +19,9 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
=> resultPage = (ContentPage)sender;
NavigationPage nav = new NavigationPage(contentPage);
Assert.IsNull(resultPage);
nav.SendAppearing();
_ = new Window(nav);
Assert.AreEqual(resultPage, contentPage);
}
@ -40,6 +41,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
=> pageAppearing = (ContentPage)sender;
NavigationPage nav = new NavigationPage(initialPage);
_ = new Window(nav);
nav.SendAppearing();
await nav.PushAsync(pushedPage);
@ -58,6 +60,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
ContentPage pageDisappeared = null;
NavigationPage nav = new NavigationPage(initialPage);
_ = new Window(nav);
nav.SendAppearing();
initialPage.Appearing += (sender, _)
@ -86,6 +89,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
ContentPage pageDisappeared = null;
NavigationPage nav = new NavigationPage(initialPage);
_ = new Window(nav);
nav.SendAppearing();
initialPage.Appearing += (sender, _)
@ -115,6 +119,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
ContentPage pageDisappeared = null;
NavigationPage nav = new NavigationPage(initialPage);
_ = new Window(nav);
nav.SendAppearing();
var pageToRemove = new ContentPage();

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

@ -329,7 +329,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
Assert.That(sent, Is.False, "Busy message sent while not visible");
((IPageController)page).SendAppearing();
_ = new Window(page);
Assert.That(sent, Is.True, "Busy message not sent when visible");
}
@ -338,6 +338,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
public void BusySentWhenBusyPageDisappears()
{
var page = new ContentPage { IsBusy = true };
_ = new Window(page);
((IPageController)page).SendAppearing();
var sent = false;
@ -359,6 +360,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
MessagingCenter.Subscribe<Page, bool>(this, Page.BusySetSignalName, (p, b) => sent = true);
var page = new ContentPage();
_ = new Window(page);
((IPageController)page).SendAppearing();
Assert.That(sent, Is.False, "Busy message sent appearing while not busy");
@ -457,7 +459,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
bool sent = false;
page.Appearing += (sender, args) => sent = true;
((IPageController)page).SendAppearing();
_ = new Window(page);
Assert.True(sent);
}
@ -466,6 +468,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
public void SendDisappearing()
{
var page = new ContentPage();
_ = new Window(page);
((IPageController)page).SendAppearing();
@ -485,7 +488,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
int countAppearing = 0;
page.Appearing += (sender, args) => countAppearing++;
((IPageController)page).SendAppearing();
_ = new Window(page);
((IPageController)page).SendAppearing();
Assert.That(countAppearing, Is.EqualTo(1));
@ -515,7 +518,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
};
navPage.Appearing += (sender, e) => sentNav = true;
((IPageController)navPage).SendAppearing();
_ = new Window(navPage);
Assert.True(sentNav);
Assert.True(sent);
@ -528,6 +531,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
var page = new ContentPage();
var navPage = new NavigationPage(page);
_ = new Window(navPage);
((IPageController)navPage).SendAppearing();
bool sentNav = false;

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

@ -17,7 +17,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public void AppearingOnCreate()
{
Shell shell = new Shell();
Shell shell = new TestShell();
FlyoutItem flyoutItem = new FlyoutItem();
Tab tab = new Tab();
@ -43,7 +43,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public void MisfiringOfAppearingWithMultipleTabs()
{
Shell shell = new Shell();
Shell shell = new TestShell();
var item0 = CreateShellItem(shellContentRoute: "Outbox", templated: true);
var item1 = CreateShellItem(shellSectionRoute: "RequestType1", shellContentRoute: "RequestType1Details", templated: true);
@ -72,7 +72,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public void AppearingOnCreateFromTemplate()
{
Shell shell = new Shell();
Shell shell = new TestShell();
FlyoutItem flyoutItem = new FlyoutItem();
Tab tab = new Tab();
@ -112,7 +112,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[TestCase(false)]
public void EnsureOnAppearingFiresAfterParentIsSet(bool templated)
{
Shell shell = new Shell();
Shell shell = new TestShell();
ContentPage page = new ContentPage();
@ -137,7 +137,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task EnsureOnAppearingFiresForNavigatedToPage()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem());
await shell.GoToAsync("LifeCyclePage");
@ -150,7 +150,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task EnsureOnAppearingFiresForLastPageOnly()
{
Shell shell = new Shell();
Shell shell = new TestShell();
LifeCyclePage shellContentPage = new LifeCyclePage();
shell.Items.Add(CreateShellItem(page: shellContentPage));
await shell.GoToAsync("LifeCyclePage/LifeCyclePage");
@ -165,7 +165,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task EnsureOnAppearingFiresForLastPageOnlyAbsoluteRoute()
{
Shell shell = new Shell();
Shell shell = new TestShell();
LifeCyclePage shellContentPage = new LifeCyclePage();
shell.Items.Add(CreateShellItem());
shell.Items.Add(CreateShellItem(page: shellContentPage, shellItemRoute: "ShellItemRoute"));
@ -183,7 +183,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task EnsureOnAppearingFiresForPushedPage()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem());
shell.Navigation.PushAsync(new LifeCyclePage());
var page = (LifeCyclePage)shell.GetVisiblePage();
@ -194,7 +194,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task NavigatedFiresAfterContentIsCreatedWhenUsingTemplate()
{
Shell shell = new Shell();
Shell shell = new TestShell();
FlyoutItem flyoutItem = new FlyoutItem();
Tab tab = new Tab();
@ -239,7 +239,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public void AppearingOnShellContentChanged()
{
Shell shell = new Shell();
Shell shell = new TestShell();
var item = CreateShellItem(shellContentRoute: ContentRoute, shellSectionRoute: SectionRoute, shellItemRoute: ItemRoute);
var section = item.SearchForRoute<ShellSection>(SectionRoute);
@ -266,7 +266,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public void AppearingOnShellSectionChanged()
{
Shell shell = new Shell();
Shell shell = new TestShell();
var item = CreateShellItem(shellContentRoute: ContentRoute, shellSectionRoute: SectionRoute, shellItemRoute: ItemRoute);
var section = item.SearchForRoute<ShellSection>(SectionRoute);
var newSection = CreateShellSection();
@ -294,7 +294,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public void AppearingOnShellItemChanged()
{
Shell shell = new Shell();
Shell shell = new TestShell();
var item = CreateShellItem(shellContentRoute: ContentRoute, shellSectionRoute: SectionRoute, shellItemRoute: ItemRoute);
var item2 = CreateShellItem();
shell.Items.Add(item2);
@ -322,7 +322,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task ShellPartWithModalPush()
{
Shell shell = new Shell();
Shell shell = new TestShell();
var item = CreateShellItem(shellContentRoute: ContentRoute, shellSectionRoute: SectionRoute, shellItemRoute: ItemRoute);
ShellLifeCycleState lifeCycleState = new ShellLifeCycleState(item);
shell.Items.Add(item);
@ -340,7 +340,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task ShellPartWithPagePush()
{
Shell shell = new Shell();
Shell shell = new TestShell();
var item = CreateShellItem(shellContentRoute: ContentRoute, shellSectionRoute: SectionRoute, shellItemRoute: ItemRoute);
ShellLifeCycleState lifeCycleState = new ShellLifeCycleState(item);
shell.Items.Add(item);
@ -373,7 +373,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task ShellPartWithPopToRoot()
{
Shell shell = new Shell();
Shell shell = new TestShell();
var item = CreateShellItem(shellContentRoute: ContentRoute, shellSectionRoute: SectionRoute, shellItemRoute: ItemRoute);
ShellLifeCycleState lifeCycleState = new ShellLifeCycleState(item);
shell.Items.Add(item);
@ -397,7 +397,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task PagePushModal()
{
Shell shell = new Shell();
Shell shell = new TestShell();
var item = CreateShellItem(shellContentRoute: ContentRoute, shellSectionRoute: SectionRoute, shellItemRoute: ItemRoute);
ShellLifeCycleState lifeCycleState = new ShellLifeCycleState(item);
shell.Items.Add(item);
@ -421,7 +421,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task PagePush()
{
Shell shell = new Shell();
Shell shell = new TestShell();
var item = CreateShellItem(shellContentRoute: ContentRoute, shellSectionRoute: SectionRoute, shellItemRoute: ItemRoute);
shell.Items.Add(item);
@ -442,7 +442,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
public void OnNavigatedOnlyFiresOnce()
{
int navigated = 0;
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Navigated += (_, __) =>
{
navigated++;
@ -457,7 +457,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public void AppearingOnlyForVisiblePage()
{
Shell shell = new Shell();
Shell shell = new TestShell();
var pageAppearing = new ContentPage();
var pageNotAppearing = new ContentPage();

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

@ -14,7 +14,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task BasicModalBehaviorTest()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem());
await shell.GoToAsync("ModalTestPage");
@ -29,7 +29,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task ModalPopsWhenSwitchingShellItem()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem());
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute"));
@ -46,7 +46,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task ModalPopsWhenSwitchingShellSection()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem());
shell.Items[0].Items.Add(CreateShellSection(shellSectionRoute: "NewRoute"));
@ -62,7 +62,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task AbsoluteRoutingToRootPopsModalPages()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellContentRoute: "MainContent"));
await shell.GoToAsync($"ModalTestPage/ModalTestPage");
@ -77,7 +77,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task PoppingEntireModalStackDoesntFireAppearingOnMiddlePages()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellContentRoute: "MainContent"));
await shell.GoToAsync($"ModalTestPage2/ModalTestPage");
@ -90,7 +90,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task PoppingModalStackFiresAppearingOnRevealedModalPage()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellContentRoute: "MainContent"));
await shell.GoToAsync($"ModalTestPage2/ModalTestPage");
@ -105,7 +105,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task ModalPopsWhenSwitchingShellContent()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem());
shell.Items[0].Items[0].Items.Add(CreateShellContent(shellContentRoute: "NewRoute"));
@ -122,7 +122,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task ModalPopsWhenNavigatingWithoutModalRoute()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute"));
// pushes modal onto visible shell section
@ -139,7 +139,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task ModalPopsWhenNavigatingToNewModalRoute()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute"));
// pushes modal onto visible shell section
@ -157,7 +157,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
public async Task PagesPushToModalStack()
{
Routing.RegisterRoute("ContentPage", typeof(ContentPage));
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute", shellSectionRoute: "Section", shellContentRoute: "Content"));
await shell.GoToAsync("ModalNavigationTestPage/ContentPage");
@ -173,7 +173,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
public async Task MultipleModalStacks()
{
Routing.RegisterRoute("ContentPage", typeof(ContentPage));
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute", shellSectionRoute: "Section", shellContentRoute: "Content"));
await shell.GoToAsync("ModalTestPage/ModalNavigationTestPage/ContentPage");
@ -190,7 +190,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
public async Task MultipleModalStacksWithContentPageAlreadyPushed()
{
Routing.RegisterRoute("ContentPage", typeof(ContentPage));
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute", shellSectionRoute: "Section", shellContentRoute: "Content"));
await shell.GoToAsync("ContentPage/ModalNavigationTestPage/ContentPage/ModalNavigationTestPage/ContentPage");
@ -201,7 +201,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task SwitchingModalStackAbsoluteNavigation()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute", shellSectionRoute: "Section", shellContentRoute: "Content"));
await shell.GoToAsync("ModalNavigationTestPage/ContentPage/ModalNavigationTestPage/ContentPage");
@ -213,7 +213,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task SwitchingShellSectionsAndPushingModal()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute", shellSectionRoute: "Section", shellContentRoute: "Content1"));
shell.Items[0].Items[0].Items.Add(CreateShellContent(shellContentRoute: "Content2"));
await shell.GoToAsync("//Content2/ModalNavigationTestPage");
@ -224,7 +224,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task PushingNonNavigationPage()
{
Shell shell = new Shell();
Shell shell = new TestShell();
Routing.RegisterRoute("ContentPage", typeof(ContentPage));
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute", shellSectionRoute: "Section", shellContentRoute: "Content"));
@ -237,7 +237,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task PushingMultipleVersionsOfTheModalRoute()
{
Shell shell = new Shell();
Shell shell = new TestShell();
Routing.RegisterRoute("ContentPage", typeof(ContentPage));
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute", shellSectionRoute: "Section", shellContentRoute: "Content"));
@ -251,7 +251,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task PushingContentPageToNonNavigationPageThrowsException()
{
Shell shell = new Shell();
Shell shell = new TestShell();
Routing.RegisterRoute("ContentPage", typeof(ContentPage));
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute", shellSectionRoute: "Section", shellContentRoute: "Content"));
@ -275,7 +275,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task AppearingAndDisappearingFiresOnShellWithModal()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.NavigationProxy.Inner = new NavigationProxy();
var lifeCyclePage = new ShellLifeCycleTests.LifeCyclePage();
shell.Items.Add(CreateShellItem(lifeCyclePage, shellItemRoute: "item", shellSectionRoute: "section", shellContentRoute: "content"));
@ -292,7 +292,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task IsAppearingFiredOnLastModalPageOnly()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute", shellSectionRoute: "Section", shellContentRoute: "Content"));
await shell.GoToAsync("ModalTestPage/ModalTestPage2");
@ -307,7 +307,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task BasicQueryStringTest()
{
var shell = new Shell();
var shell = new TestShell();
var item = CreateShellItem(shellSectionRoute: "section2");
shell.Items.Add(item);
@ -337,7 +337,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task NavigatingAndNavigatedFiresForShellModal()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute", shellSectionRoute: "Section", shellContentRoute: "Content"));
ShellNavigatingEventArgs shellNavigatingEventArgs = null;
@ -366,7 +366,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task GetCurrentPageInModalNavigation()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem(shellItemRoute: "NewRoute", shellSectionRoute: "Section", shellContentRoute: "Content"));
Page page = null;
@ -384,7 +384,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
[Test]
public async Task PopModalWithDots()
{
Shell shell = new Shell();
Shell shell = new TestShell();
shell.Items.Add(CreateShellItem());
await shell.CurrentPage.Navigation.PushModalAsync(new ContentPage());

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

@ -322,6 +322,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
public TestShell()
{
_ = new Window() { Page = this };
Routing.RegisterRoute(nameof(TestPage1), typeof(TestPage1));
Routing.RegisterRoute(nameof(TestPage2), typeof(TestPage2));
Routing.RegisterRoute(nameof(TestPage3), typeof(TestPage3));
@ -332,6 +333,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
public TestShell(params ShellItem[] shellItems) : this()
{
_ = new Window() { Page = this };
shellItems.ForEach(x => Items.Add(x));
}

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

@ -1178,7 +1178,7 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
public async Task GetCurrentPageInShellNavigation()
{
Shell shell = new Shell();
Shell shell = new TestShell();
var item1 = CreateShellItem(asImplicit: true, shellContentRoute: "rootlevelcontent1");
shell.Items.Add(item1);

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

@ -9,7 +9,7 @@ namespace Microsoft.Maui.DeviceTests.Stubs
IServiceProvider _services;
IAnimationManager _manager;
#if WINDOWS
WindowManager _windowManager;
NavigationRootManager _windowManager;
#endif
public ContextStub(IServiceProvider services)
@ -30,8 +30,8 @@ namespace Microsoft.Maui.DeviceTests.Stubs
if (serviceType == typeof(UIKit.UIWindow))
return UIKit.UIApplication.SharedApplication.KeyWindow;
#elif WINDOWS
if (serviceType == typeof(WindowManager))
return _windowManager ??= new WindowManager(this);
if (serviceType == typeof(NavigationRootManager))
return _windowManager ??= new NavigationRootManager(this);
#endif
return _services.GetService(serviceType);

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

@ -22,6 +22,6 @@ namespace Microsoft.Maui
void Destroying();
bool BackButtonPressed();
bool BackButtonClicked();
}
}

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

@ -32,7 +32,7 @@ namespace Microsoft.Maui.Handlers
public IElement? VirtualView { get; private protected set; }
public void SetMauiContext(IMauiContext mauiContext) =>
public virtual void SetMauiContext(IMauiContext mauiContext) =>
MauiContext = mauiContext;
public virtual void SetVirtualView(IElement view)

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

@ -1,43 +1,62 @@
#nullable enable
using System;
using System;
using Android.Runtime;
using Android.Views;
using AndroidX.Fragment.App;
namespace Microsoft.Maui.Handlers
{
public partial class NavigationViewHandler :
ViewHandler<INavigationView, NavigationLayout>
ViewHandler<INavigationView, View>
{
NavigationManager? _navigationManager;
StackNavigationManager? _navigationManager;
protected override NavigationLayout CreateNativeView()
protected override View CreateNativeView()
{
LayoutInflater? li = MauiContext?.GetLayoutInflater();
_ = li ?? throw new InvalidOperationException($"LayoutInflater cannot be null");
var view = li.Inflate(Resource.Layout.navigationlayout, null).JavaCast<NavigationLayout>();
var view = li.Inflate(Resource.Layout.fragment_backstack, null).JavaCast<FragmentContainerView>();
_ = view ?? throw new InvalidOperationException($"Resource.Layout.navigationlayout view not found");
return view;
}
public override void SetVirtualView(IView view)
public override void SetMauiContext(IMauiContext mauiContext)
{
_navigationManager ??= CreateNavigationManager();
base.SetVirtualView(view);
var currentInflater = mauiContext.GetLayoutInflater();
var inflater =
new StackNavigationManager.StackLayoutInflater(
currentInflater,
currentInflater.Context,
CreateNavigationManager());
mauiContext =
mauiContext.MakeScoped(inflater, context: inflater.Context);
base.SetMauiContext(mauiContext);
}
protected virtual NavigationManager CreateNavigationManager() =>
new NavigationManager(MauiContext!);
StackNavigationManager CreateNavigationManager() =>
_navigationManager ??= new StackNavigationManager();
protected override void ConnectHandler(NavigationLayout nativeView)
protected override void ConnectHandler(View nativeView)
{
_navigationManager ??= CreateNavigationManager();
nativeView.NavigationManager = _navigationManager;
var rootContainer = MauiContext!.GetNavigationRootManager();
var navigationLayout = rootContainer.NavigationLayout;
base.ConnectHandler(nativeView);
_navigationManager.Connect(VirtualView, nativeView);
_navigationManager?.Connect(VirtualView, navigationLayout);
}
private protected override void OnDisconnectHandler(View nativeView)
{
_navigationManager?.Disconnect();
base.OnDisconnectHandler(nativeView);
}
protected override void DisconnectHandler(View nativeView)
{
base.DisconnectHandler(nativeView);
}
public static void RequestNavigation(NavigationViewHandler arg1, INavigationView arg2, object? arg3)

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

@ -1,5 +1,6 @@
using System;
using Android.App;
using Google.Android.Material.AppBar;
namespace Microsoft.Maui.Handlers
{
@ -12,8 +13,9 @@ namespace Microsoft.Maui.Handlers
_ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class.");
var nativeContent = window.Content.ToContainerView(handler.MauiContext);
handler.NativeView.SetContentView(nativeContent);
var rootManager = handler.MauiContext.GetNavigationRootManager();
rootManager.SetContentView(nativeContent);
handler.NativeView.SetContentView(rootManager.RootView);
}
}
}

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

@ -26,7 +26,7 @@ namespace Microsoft.Maui.Handlers
protected override void DisconnectHandler(UI.Xaml.Window nativeView)
{
var windowManager = MauiContext?.GetWindowManager();
var windowManager = MauiContext?.GetNavigationRootManager();
windowManager?.Connect(VirtualView);
_rootPanel?.Children?.Clear();
@ -42,7 +42,7 @@ namespace Microsoft.Maui.Handlers
{
_ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class.");
var windowManager = handler.MauiContext.GetWindowManager();
var windowManager = handler.MauiContext.GetNavigationRootManager();
windowManager.Connect(handler.VirtualView);
handler?._rootPanel?.Children?.Clear();
handler?._rootPanel?.Children?.Add(windowManager.RootView);

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

@ -36,10 +36,13 @@ namespace Microsoft.Maui
if (handler.VirtualView != view)
handler.SetVirtualView(view);
if (((INativeViewHandler)handler).NativeView is not AView result)
throw new InvalidOperationException($"Unable to convert {view} to {typeof(AView)}");
if (handler is INativeViewHandler nvh && nvh.NativeView is AView)
return nvh.NativeView;
return result;
if (handler.NativeView is AView result)
return result;
throw new InvalidOperationException($"Unable to convert {view} to {typeof(AView)}");
}
public static void SetApplicationHandler(this Application nativeApplication, IApplication application, IMauiContext context) =>

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

@ -17,8 +17,8 @@ namespace Microsoft.Maui
return config.LayoutDirection.ToFlowDirection();
}
public static NavigationManager GetNavigationManager(this IMauiContext mauiContext) =>
mauiContext.Services.GetRequiredService<NavigationManager>();
public static NavigationRootManager GetNavigationRootManager(this IMauiContext mauiContext) =>
mauiContext.Services.GetRequiredService<NavigationRootManager>();
public static LayoutInflater GetLayoutInflater(this IMauiContext mauiContext)
{
@ -48,7 +48,10 @@ namespace Microsoft.Maui
(mauiContext.Context?.GetActivity() as AppCompatActivity)
?? throw new InvalidOperationException("AppCompatActivity Not Found");
public static IMauiContext MakeScoped(this IMauiContext mauiContext, LayoutInflater? layoutInflater = null, FragmentManager? fragmentManager = null)
public static IMauiContext MakeScoped(this IMauiContext mauiContext,
LayoutInflater? layoutInflater = null,
FragmentManager? fragmentManager = null,
Android.Content.Context? context = null)
{
var scopedContext = new MauiContext(mauiContext);
@ -58,14 +61,8 @@ namespace Microsoft.Maui
if (fragmentManager != null)
scopedContext.AddWeakSpecific(fragmentManager);
return scopedContext;
}
public static IMauiContext MakeScoped(this IMauiContext mauiContext, NavigationManager navigationManager)
{
var scopedContext = new MauiContext(mauiContext);
scopedContext.AddSpecific(navigationManager);
if (context != null)
scopedContext.AddWeakSpecific(context);
return scopedContext;
}
@ -74,7 +71,7 @@ namespace Microsoft.Maui
{
var scopedContext = new MauiContext(mauiContext.Services, nativeWindow, mauiContext);
scopedContext.AddSpecific(nativeWindow);
scopedContext.AddWeakSpecific(nativeWindow);
return scopedContext;
}

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

@ -1,44 +0,0 @@
using System;
using Android.Content;
using Android.Runtime;
using Android.Util;
using AndroidX.AppCompat.Widget;
using AndroidX.CoordinatorLayout.Widget;
using Google.Android.Material.AppBar;
namespace Microsoft.Maui
{
public class NavigationLayout : CoordinatorLayout
{
// I'd prefer to not need this here but I'm not sure how else
// to get this class to the NavigationViewFragment
NavigationManager? _navigationManager;
public NavigationManager NavigationManager
{
get => _navigationManager ?? throw new InvalidOperationException($"NavigationManager cannot be null");
internal set => _navigationManager = value;
}
#pragma warning disable CS0618 //FIXME: [Preserve] is obsolete
[Preserve(Conditional = true)]
public NavigationLayout(Context context) : base(context)
{
}
[Preserve(Conditional = true)]
public NavigationLayout(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
[Preserve(Conditional = true)]
public NavigationLayout(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
}
[Preserve(Conditional = true)]
protected NavigationLayout(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
#pragma warning restore CS0618 //FIXME: [Preserve] is obsolete
}
}

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

@ -0,0 +1,48 @@
using System;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Views.Animations;
using AndroidX.AppCompat.Widget;
using AndroidX.CoordinatorLayout.Widget;
using AndroidX.Fragment.App;
using Google.Android.Material.AppBar;
using Google.Android.Material.Tabs;
using AView = Android.Views.View;
namespace Microsoft.Maui
{
public class NavigationRootManager
{
CoordinatorLayout? _navigationLayout;
IMauiContext _mauiContext;
internal CoordinatorLayout NavigationLayout => _navigationLayout ??=
LayoutInflater
.Inflate(Resource.Layout.navigationlayout, null)
.JavaCast<CoordinatorLayout>()
?? throw new InvalidOperationException($"Resource.Layout.navigationlayout missing");
LayoutInflater LayoutInflater => _mauiContext?.GetLayoutInflater()
?? throw new InvalidOperationException($"LayoutInflater missing");
internal FragmentManager FragmentManager => _mauiContext?.GetFragmentManager()
?? throw new InvalidOperationException($"FragmentManager missing");
public AView RootView => NavigationLayout;
public NavigationRootManager(IMauiContext mauiContext)
{
_mauiContext = mauiContext;
}
// TODO MAUI: this will eventually get replaced by Navigation
internal virtual void SetContentView(AView view)
{
FragmentManager.BeginTransaction()
.Replace(Resource.Id.navigationlayout_content, new ViewFragment(view))
.SetReorderingAllowed(true)
.Commit();
}
}
}

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

@ -4,6 +4,7 @@ using Android.Runtime;
using Android.Views;
using Android.Views.Animations;
using AndroidX.Fragment.App;
using AndroidX.Navigation;
using AView = Android.Views.View;
namespace Microsoft.Maui
@ -11,36 +12,14 @@ namespace Microsoft.Maui
public class NavigationViewFragment : Fragment
{
AView? _currentView;
NavigationLayout? _navigationLayout;
FragmentContainerView? _fragmentContainerView;
// TODO MAUI: This currently feels like a very unreliable way to retrieve the NavigationLayout
// If this is called before the Fragment View is parented then this call fails.
// This should be converted to use Android ViewModels instead of just walking up the visual tree
NavigationLayout NavigationLayout
{
get
{
if (_navigationLayout != null)
return _navigationLayout;
var view = FragmentContainerView.Parent;
while (view is not Maui.NavigationLayout && view != null)
view = view?.Parent;
_navigationLayout = view as NavigationLayout;
return _navigationLayout ?? throw new InvalidOperationException($"NavigationLayout cannot be null here");
}
}
FragmentContainerView FragmentContainerView =>
_fragmentContainerView ??= NavigationLayout.FindViewById<FragmentContainerView>(Resource.Id.nav_host)
?? throw new InvalidOperationException($"FragmentContainerView cannot be null here");
_fragmentContainerView ?? throw new InvalidOperationException($"FragmentContainerView cannot be null here");
// TODO Research ViewModels
NavigationManager NavigationManager => NavigationLayout.NavigationManager
StackNavigationManager? _navigationManager;
StackNavigationManager NavigationManager => _navigationManager
?? throw new InvalidOperationException($"Graph cannot be null here");
protected NavigationViewFragment(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
@ -53,6 +32,12 @@ namespace Microsoft.Maui
public override AView OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var context =
(container.Context as StackNavigationManager.StackContext) ??
(container.Parent as AView)?.Context as StackNavigationManager.StackContext
?? throw new InvalidOperationException($"StackNavigationManager.StackContext not found");
_navigationManager = context.StackNavigationManager;
_fragmentContainerView ??= (FragmentContainerView)container;
// When shuffling around the back stack sometimes we'll need a page to detach and then reattach.
@ -70,8 +55,16 @@ namespace Microsoft.Maui
// Then we can try some other approachs like just modifying the navbar ourselves to include a back button
// Even if there's only one page on the stack
var scopedContext = NavigationManager.MauiContext.MakeScoped(inflater, ChildFragmentManager);
_currentView = NavigationManager.CurrentPage.ToNative(scopedContext);
_currentView =
NavigationManager.CurrentPage.Handler?.NativeView as AView;
if (_currentView == null)
{
var scopedContext = NavigationManager.MauiContext.MakeScoped(inflater, ChildFragmentManager);
_currentView = NavigationManager.CurrentPage.ToNative(scopedContext);
}
_currentView.RemoveFromParent();
return _currentView;
@ -90,13 +83,6 @@ namespace Microsoft.Maui
}
base.OnResume();
}
public override void OnDestroyView()
{
_navigationLayout = null;
base.OnDestroyView();
}
public override Animation OnCreateAnimation(int transit, bool enter, int nextAnim)

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

@ -7,9 +7,9 @@ namespace Microsoft.Maui
{
class ProcessBackClick : AndroidX.Activity.OnBackPressedCallback, AView.IOnClickListener
{
NavigationManager _navigationManager;
StackNavigationManager _navigationManager;
public ProcessBackClick(NavigationManager navHostPageFragment)
public ProcessBackClick(StackNavigationManager navHostPageFragment)
: base(true)
{
_navigationManager = navHostPageFragment;
@ -17,12 +17,12 @@ namespace Microsoft.Maui
public override void HandleOnBackPressed()
{
_navigationManager.BackButtonPressed();
_navigationManager.HardwareBackButtonClicked();
}
public void OnClick(AView? v)
{
HandleOnBackPressed();
_navigationManager.ToolbarBackButtonClicked();
}
}
}

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

@ -2,7 +2,10 @@
using System.Collections.Generic;
using Android.Content;
using Android.OS;
using Android.Views;
using AndroidX.AppCompat.View;
using AndroidX.AppCompat.Widget;
using AndroidX.CoordinatorLayout.Widget;
using AndroidX.Navigation;
using AndroidX.Navigation.Fragment;
using AndroidX.Navigation.UI;
@ -11,14 +14,14 @@ using AView = Android.Views.View;
namespace Microsoft.Maui
{
public class NavigationManager
public class StackNavigationManager
{
Toolbar? _toolbar;
NavHostFragment? _navHost;
FragmentNavigator? _fragmentNavigator;
NavGraph? _navGraph;
IView? _currentPage;
NavigationLayout? _navigationLayout;
CoordinatorLayout? _navigationLayout;
ProcessBackClick BackClick { get; }
internal IView? VirtualView { get; private set; }
internal INavigationView? NavigationView { get; private set; }
@ -38,23 +41,24 @@ namespace Microsoft.Maui
internal NavGraph NavGraph => _navGraph ??
throw new InvalidOperationException($"NavGraph cannot be null");
internal NavigationLayout NavigationLayout => _navigationLayout ??
internal CoordinatorLayout NavigationLayout => _navigationLayout ??
throw new InvalidOperationException($"NavigationLayout cannot be null");
public IView CurrentPage
=> _currentPage ?? throw new InvalidOperationException("CurrentPage cannot be null");
public IMauiContext MauiContext { get; }
public IMauiContext MauiContext =>
VirtualView?.Handler?.MauiContext
?? throw new InvalidOperationException("MauiContext cannot be null");
public NavigationManager(IMauiContext mauiContext)
public StackNavigationManager()
{
MauiContext = mauiContext.MakeScoped(this);
BackClick = new ProcessBackClick(this);
}
internal Toolbar Toolbar =>
_toolbar ??= NavigationLayout.FindViewById<MaterialToolbar>(Resource.Id.maui_toolbar)
?? throw new InvalidOperationException($"ToolBar cannot be null");
internal Toolbar? Toolbar =>
_toolbar ??=
NavigationLayout.FindViewById<MaterialToolbar>(Resource.Id.navigationlayout_toolbar);
/*
* The important thing to know going into reading this method is that it's not possible to
@ -208,6 +212,7 @@ namespace Microsoft.Maui
public virtual FragmentNavigator.Destination AddFragmentDestination()
{
var destination = new FragmentNavigator.Destination(FragmentNavigator);
destination.SetClassName(Java.Lang.Class.FromType(typeof(NavigationViewFragment)).CanonicalName);
destination.Id = AView.GenerateViewId();
NavGraph.AddDestination(destination);
@ -265,7 +270,11 @@ namespace Microsoft.Maui
_currentPage = NavigationStack[NavigationStack.Count - 1];
}
public virtual void Connect(IView navigationView, NavigationLayout nativeView)
public virtual void Disconnect()
{
}
public virtual void Connect(IView navigationView, CoordinatorLayout nativeView)
{
VirtualView = navigationView;
NavigationView = (INavigationView)navigationView;
@ -306,7 +315,7 @@ namespace Microsoft.Maui
ApplyNavigationRequest(e);
}
protected virtual void OnBackButtonPressed()
protected virtual void OnToolbarBackButtonClicked()
{
_ = NavigationView ?? throw new InvalidOperationException($"NavigationView cannot be null");
var stack = new List<IView>(NavigationStack);
@ -314,7 +323,18 @@ namespace Microsoft.Maui
ApplyNavigationRequest(new NavigationRequest(stack, true));
}
internal void BackButtonPressed() => OnBackButtonPressed();
internal void ToolbarBackButtonClicked() => OnToolbarBackButtonClicked();
protected virtual void OnHardwareBackButtonClicked()
{
if (MauiContext.GetActivity().GetWindow()?.BackButtonClicked() == false &&
NavigationStack.Count == 1)
{
MauiContext.GetActivity().FinishAffinity();
}
}
internal void HardwareBackButtonClicked() => OnHardwareBackButtonClicked();
// Fragments are always destroyed if they aren't visible
// The Handler/NativeView associated with the visible IView remain intact
@ -346,19 +366,49 @@ namespace Microsoft.Maui
protected virtual void OnDestinationChanged(NavController navController, NavDestination navDestination, Bundle bundle)
{
if (CurrentPage is ITitledElement titledElement)
}
internal class StackLayoutInflater : LayoutInflater
{
readonly LayoutInflater _original;
public StackLayoutInflater(
LayoutInflater original,
Context? context,
StackNavigationManager stackNavigationManager) :
base(original, new StackContext(context, stackNavigationManager))
{
Toolbar.Title = titledElement.Title;
_original = original;
StackNavigationManager = stackNavigationManager;
}
public StackNavigationManager StackNavigationManager { get; }
public override LayoutInflater? CloneInContext(Context? newContext)
{
return new StackLayoutInflater(_original, newContext, StackNavigationManager);
}
}
internal class StackContext : AndroidX.AppCompat.View.ContextThemeWrapper
{
public StackContext(
Context? context,
StackNavigationManager stackNavigationManager) : base(context, context?.Theme)
{
StackNavigationManager = stackNavigationManager;
}
public StackNavigationManager StackNavigationManager { get; }
}
class Callbacks :
AndroidX.Fragment.App.FragmentManager.FragmentLifecycleCallbacks,
NavController.IOnDestinationChangedListener
{
NavigationManager _navigationManager;
StackNavigationManager _navigationManager;
public Callbacks(NavigationManager navigationLayout)
public Callbacks(StackNavigationManager navigationLayout)
{
_navigationManager = navigationLayout;
}
@ -382,6 +432,22 @@ namespace Microsoft.Maui
f.RequireActivity()
.OnBackPressedDispatcher
.AddCallback(f, _navigationManager.BackClick);
// Wire up the toolbar to the currently made visible Fragment
var controller = NavHostFragment.FindNavController(f);
var appbarConfig =
new AppBarConfiguration
.Builder(controller.Graph)
.Build();
if (_navigationManager.Toolbar != null)
{
NavigationUI
.SetupWithNavController(_navigationManager.Toolbar, controller, appbarConfig);
_navigationManager.Toolbar.SetNavigationOnClickListener(_navigationManager.BackClick);
}
}
public override void OnFragmentAttached(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f, Context context)
@ -399,24 +465,6 @@ namespace Microsoft.Maui
base.OnFragmentViewDestroyed(fm, f);
}
public override void OnFragmentViewCreated(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f, AView v, Bundle savedInstanceState)
{
base.OnFragmentViewCreated(fm, f, v, savedInstanceState);
var controller = NavHostFragment.FindNavController(f);
var appbarConfig =
new AppBarConfiguration
.Builder(controller.Graph)
.Build();
// These have to get called/wired up every time a new fragment view is created
// I'm not clear why this can't just be done once
NavigationUI
.SetupWithNavController(_navigationManager.Toolbar, controller, appbarConfig);
_navigationManager.Toolbar.SetNavigationOnClickListener(_navigationManager.BackClick);
}
public override void OnFragmentCreated(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f, Bundle savedInstanceState)
{
base.OnFragmentCreated(fm, f, savedInstanceState);

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

@ -14,12 +14,10 @@ namespace Microsoft.Maui
public ViewFragment(View aView)
{
_aView = aView;
_aView.RemoveFromParent();
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
_aView.RemoveFromParent();
return _aView;
}
}

36
src/Core/src/Platform/Android/Resource.designer.cs сгенерированный
Просмотреть файл

@ -69,14 +69,26 @@ namespace Microsoft.Maui
// aapt resource value: 0x7F080059
public static int automation_tag_id = 2131230809;
// aapt resource value: 0x7F080061
public static int bottomtab_containerview = 2131230817;
// aapt resource value: 0x7F08013C
public static int navigationlayout_appbar = 2131231036;
// aapt resource value: 0x7F080107
public static int maui_toolbar = 2131230983;
// aapt resource value: 0x7F08013D
public static int navigationlayout_bottomtabs = 2131231037;
// aapt resource value: 0x7F08012D
public static int nav_host = 2131231021;
// aapt resource value: 0x7F08013E
public static int navigationlayout_content = 2131231038;
// aapt resource value: 0x7F08013F
public static int navigationlayout_toolbar = 2131231039;
// aapt resource value: 0x7F080140
public static int navigationlayout_toptabs = 2131231040;
// aapt resource value: 0x7F080135
public static int nav_host = 2131231029;
// aapt resource value: 0x7F080136
public static int nav_host_fragment_container = 2131231030;
// aapt resource value: 0x7F08016A
public static int search_button = 2131231082;
@ -84,9 +96,6 @@ namespace Microsoft.Maui
// aapt resource value: 0x7F08016B
public static int search_close_btn = 2131231083;
// aapt resource value: 0x7F0801C6
public static int toptabs_containerview = 2131231174;
static Id()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
@ -95,8 +104,13 @@ namespace Microsoft.Maui
public partial class Layout
{
// aapt resource value: 0x7F0B0059
public static int navigationlayout = 2131427417;
// aapt resource value: 0x7F0B005A
public static int navigationlayout = 2131427418;
// aapt resource value: 0x7F0B002E
public static int fragment_backstack = 2131427374;
static Layout()
{

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

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
</androidx.fragment.app.FragmentContainerView>

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

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<microsoft.maui.NavigationLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
@ -7,20 +7,22 @@
>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:id="@+id/navigationlayout_appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
>
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/maui.toolbar"
android:id="@+id/navigationlayout_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/toptabs_containerview"
android:id="@+id/navigationlayout_toptabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
@ -28,17 +30,15 @@
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:id="@+id/navigationlayout_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/bottomtab_containerview"
android:id="@+id/navigationlayout_bottomtabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
/>
</microsoft.maui.NavigationLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

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

@ -12,4 +12,4 @@ namespace Microsoft.Maui
Android.Content.Context? Context { get; }
#endif
}
}
}

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

@ -21,6 +21,9 @@ namespace Microsoft.Maui
: this(services, parent)
{
AddWeakSpecific(context);
if (parent?.Services.GetService<NavigationRootManager>() == null && context is not Android.App.Application)
AddSpecific(new NavigationRootManager(this));
}
#elif __IOS__
public MauiContext(IServiceProvider services, UIKit.UIApplicationDelegate application, IMauiContext? parent = null)

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

@ -29,12 +29,13 @@ namespace Microsoft.Maui
if (handler.VirtualView != view)
handler.SetVirtualView(view);
if (((INativeViewHandler)handler).NativeView is not FrameworkElement result)
{
throw new InvalidOperationException($"Unable to convert {view} to {typeof(FrameworkElement)}");
}
if (handler is INativeViewHandler nvh && nvh.NativeView is FrameworkElement)
return nvh.NativeView;
return result;
if (handler.NativeView is FrameworkElement result)
return result;
throw new InvalidOperationException($"Unable to convert {view} to {typeof(FrameworkElement)}");
}
public static void SetApplicationHandler(this UI.Xaml.Application nativeApplication, IApplication application, IMauiContext context) =>

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

@ -16,8 +16,8 @@ namespace Microsoft.Maui
return FlowDirection.MatchParent;
}
public static WindowManager GetWindowManager(this IMauiContext mauiContext) =>
mauiContext.Services.GetRequiredService<WindowManager>();
public static NavigationRootManager GetNavigationRootManager(this IMauiContext mauiContext) =>
mauiContext.Services.GetRequiredService<NavigationRootManager>();
public static UI.Xaml.Window GetNativeWindow(this IMauiContext mauiContext) =>
mauiContext.Services.GetRequiredService<UI.Xaml.Window>();
@ -29,7 +29,7 @@ namespace Microsoft.Maui
{
var scopedContext = new MauiContext(mauiContext);
scopedContext.AddSpecific(nativeWindow);
scopedContext.AddSpecific(new WindowManager(scopedContext));
scopedContext.AddSpecific(new NavigationRootManager(scopedContext));
return scopedContext;
}
}

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

@ -14,7 +14,7 @@ namespace Microsoft.Maui
IView? _currentPage;
IMauiContext _mauiContext;
Frame? _navigationFrame;
protected WindowManager WindowManager => _mauiContext.GetWindowManager();
protected NavigationRootManager WindowManager => _mauiContext.GetNavigationRootManager();
private protected INavigationView? NavigationView { get; private set; }
public IReadOnlyList<IView> NavigationStack { get; set; } = new List<IView>();
public IMauiContext MauiContext => _mauiContext;

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

@ -7,14 +7,14 @@ using Microsoft.UI.Xaml.Controls;
namespace Microsoft.Maui
{
public class WindowManager
public class NavigationRootManager
{
IMauiContext _mauiContext;
MauiNavigationView _navigationView;
IView? _content;
IWindow? _window;
public WindowManager(IMauiContext mauiContext)
public NavigationRootManager(IMauiContext mauiContext)
{
_mauiContext = mauiContext;
_navigationView = new MauiNavigationView();
@ -23,7 +23,7 @@ namespace Microsoft.Maui
void OnBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args)
{
_window?.BackButtonPressed();
_window?.BackButtonClicked();
}
public FrameworkElement RootView => _navigationView;

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

@ -1,3 +1,4 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
@ -11,10 +12,11 @@ using WImageSource = Microsoft.UI.Xaml.Media.ImageSource;
using WVisibility = Microsoft.UI.Xaml.Visibility;
using WGrid = Microsoft.UI.Xaml.Controls.Grid;
using Microsoft.UI.Xaml.Media;
using WImage = Microsoft.UI.Xaml.Controls.Image;
namespace Microsoft.Maui
{
partial class WindowHeader
public partial class WindowHeader
{
internal TranslateTransform? ClipGeometryTransform { get; private set; }
internal RectangleGeometry? LayoutRootClip { get; private set; }
@ -25,6 +27,8 @@ namespace Microsoft.Maui
InitializeComponent();
}
internal MauiNavigationView? NavigationView { get; set; }
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
@ -39,7 +43,12 @@ namespace Microsoft.Maui
set => title.Text = value;
}
internal WImageSource? TitleIcon
internal WImage? TitleIconImage
{
get => titleIcon;
}
internal WImageSource? TitleIconImageSource
{
get => titleIcon.Source;
set => titleIcon.Source = value;

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

@ -50,6 +50,6 @@ namespace Microsoft.Maui.Handlers.Benchmarks
IsResumed = false;
}
public bool BackButtonPressed() => true;
public bool BackButtonClicked() => true;
}
}

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

@ -9,7 +9,7 @@ namespace Microsoft.Maui.DeviceTests.Stubs
IServiceProvider _services;
IAnimationManager _manager;
#if WINDOWS
WindowManager _windowManager;
NavigationRootManager _windowManager;
#endif
public ContextStub(IServiceProvider services)
@ -30,8 +30,8 @@ namespace Microsoft.Maui.DeviceTests.Stubs
if (serviceType == typeof(UIKit.UIWindow))
return UIKit.UIApplication.SharedApplication.GetKeyWindow();
#elif WINDOWS
if (serviceType == typeof(WindowManager))
return _windowManager ??= new WindowManager(this);
if (serviceType == typeof(NavigationRootManager))
return _windowManager ??= new NavigationRootManager(this);
#endif
return _services.GetService(serviceType);

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

@ -51,6 +51,6 @@
IsResumed = false;
}
public bool BackButtonPressed() => true;
public bool BackButtonClicked() => true;
}
}