diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index ce764ab..1b33977 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -1,13 +1,13 @@ # This workflow will build a .NET project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net -name: .NET +name: CI on: push: - branches: [ "main" ] + branches: [ "main", "develop" ] pull_request: - branches: [ "main" ] + branches: [ "main", "develop" ] jobs: build: diff --git a/README.md b/README.md index 1ca7b90..c537e80 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ We welcome feedback, suggestions, and contributions from anyone who is intereste To use AvaloniaInside.Shell in your Avalonia project, you can install the package via NuGet using the following command in the Package Manager Console: ```bash -dotnet add package AvaloniaInside.Shell --version 1.2.0 +dotnet add package AvaloniaInside.Shell --version 1.3.0 ``` Alternatively, you can also install the package through Visual Studio's NuGet Package Manager. diff --git a/src/AvaloniaInside.Shell/AppBuilderExtensions.cs b/src/AvaloniaInside.Shell/AppBuilderExtensions.cs index d7918e6..353a705 100644 --- a/src/AvaloniaInside.Shell/AppBuilderExtensions.cs +++ b/src/AvaloniaInside.Shell/AppBuilderExtensions.cs @@ -8,7 +8,7 @@ namespace AvaloniaInside.Shell; public static class AppBuilderExtensions { public static AppBuilder UseShell(this AppBuilder builder, Func? viewLocatorFactory = null) => - builder.AfterPlatformServicesSetup(_ => + builder.AfterPlatformServicesSetup(_ => Locator.RegisterResolverCallbackChanged(() => { if (Locator.CurrentMutable is null) { @@ -37,7 +37,7 @@ public static AppBuilder UseShell(this AppBuilder builder, Func viewFactory) => builder.UseShell(() => new DelegateNavigationViewLocator(viewFactory)); diff --git a/src/AvaloniaInside.Shell/AvaloniaInside.Shell.csproj b/src/AvaloniaInside.Shell/AvaloniaInside.Shell.csproj index a5135a9..ffb01a1 100644 --- a/src/AvaloniaInside.Shell/AvaloniaInside.Shell.csproj +++ b/src/AvaloniaInside.Shell/AvaloniaInside.Shell.csproj @@ -3,7 +3,7 @@ net6.0;net8.0 enable latest - 1.2.0 + 1.3.0 Shell view for Avalonia Shell reduces the complexity of mobile/desktop application development by providing the fundamental features that most applications require AvaloniaInside @@ -19,6 +19,7 @@ + @@ -26,7 +27,6 @@ - diff --git a/src/AvaloniaInside.Shell/BindingNavigate.cs b/src/AvaloniaInside.Shell/BindingNavigate.cs index 996eb74..d27afe8 100644 --- a/src/AvaloniaInside.Shell/BindingNavigate.cs +++ b/src/AvaloniaInside.Shell/BindingNavigate.cs @@ -7,65 +7,64 @@ using System.Threading.Tasks; using System.Windows.Input; -namespace AvaloniaInside.Shell +namespace AvaloniaInside.Shell; + +[TypeConverter(typeof(BindingNavigateConverter))] +public class BindingNavigate : AvaloniaObject, ICommand { - [TypeConverter(typeof(BindingNavigateConverter))] - public class BindingNavigate : AvaloniaObject, ICommand - { - private bool _singletonCanExecute = true; - private EventHandler? _singletonCanExecuteChanged; + private bool _singletonCanExecute = true; + private EventHandler? _singletonCanExecuteChanged; - public AvaloniaObject? Sender { get; internal set; } - public string Path { get; set; } - public NavigateType? Type { get; set; } - public IPageTransition? Transition { get; set; } + public AvaloniaObject? Sender { get; internal set; } + public string Path { get; set; } + public NavigateType? Type { get; set; } + public IPageTransition? Transition { get; set; } - public event EventHandler? CanExecuteChanged - { - add => _singletonCanExecuteChanged += value; - remove => _singletonCanExecuteChanged -= value; - } + public event EventHandler? CanExecuteChanged + { + add => _singletonCanExecuteChanged += value; + remove => _singletonCanExecuteChanged -= value; + } - public bool CanExecute(object? parameter) => _singletonCanExecute; - public void Execute(object? parameter) => ExecuteAsync(parameter, CancellationToken.None); + public bool CanExecute(object? parameter) => _singletonCanExecute; + public void Execute(object? parameter) => ExecuteAsync(parameter, CancellationToken.None); - public async Task ExecuteAsync(object? parameter, CancellationToken cancellationToken) - { - if (Sender is not Visual visual) return; - if (visual.FindAncestorOfType() is not { } shell) return; + public async Task ExecuteAsync(object? parameter, CancellationToken cancellationToken) + { + if (Sender is not Visual visual) return; + if (visual.FindAncestorOfType() is not { } shell) return; - _singletonCanExecute = false; - _singletonCanExecuteChanged?.Invoke(this, EventArgs.Empty); - try - { - if (parameter != null) - await shell.Navigator.NavigateAsync( - Path, - Type, - parameter, - Sender, - true, - Transition, - cancellationToken); - else - await shell.Navigator.NavigateAsync( - Path, - Type, - Sender, - true, - Transition, - cancellationToken); - } - finally - { - _singletonCanExecute = true; - _singletonCanExecuteChanged?.Invoke(this, EventArgs.Empty); - } + _singletonCanExecute = false; + _singletonCanExecuteChanged?.Invoke(this, EventArgs.Empty); + try + { + if (parameter != null) + await shell.Navigator.NavigateAsync( + Path, + Type, + parameter, + Sender, + true, + Transition, + cancellationToken); + else + await shell.Navigator.NavigateAsync( + Path, + Type, + Sender, + true, + Transition, + cancellationToken); } - - public static implicit operator BindingNavigate(string path) => new BindingNavigate + finally { - Path = path - }; + _singletonCanExecute = true; + _singletonCanExecuteChanged?.Invoke(this, EventArgs.Empty); + } } + + public static implicit operator BindingNavigate(string path) => new BindingNavigate + { + Path = path + }; } diff --git a/src/AvaloniaInside.Shell/BindingNavigateConverter.cs b/src/AvaloniaInside.Shell/BindingNavigateConverter.cs index 9a7f194..c319265 100644 --- a/src/AvaloniaInside.Shell/BindingNavigateConverter.cs +++ b/src/AvaloniaInside.Shell/BindingNavigateConverter.cs @@ -1,5 +1,4 @@ -using Avalonia.Media; -using System; +using System; using System.ComponentModel; using System.Globalization; diff --git a/src/AvaloniaInside.Shell/Default.axaml b/src/AvaloniaInside.Shell/Default.axaml index 87f9ab4..7a24fda 100644 --- a/src/AvaloniaInside.Shell/Default.axaml +++ b/src/AvaloniaInside.Shell/Default.axaml @@ -3,192 +3,11 @@ - - - - White - - - Black - Black - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/src/AvaloniaInside.Shell/DefaultNavigationUpdateStrategy.cs b/src/AvaloniaInside.Shell/DefaultNavigationUpdateStrategy.cs index aef77e2..3019542 100644 --- a/src/AvaloniaInside.Shell/DefaultNavigationUpdateStrategy.cs +++ b/src/AvaloniaInside.Shell/DefaultNavigationUpdateStrategy.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Avalonia.Controls; -using Avalonia.Controls.Primitives; namespace AvaloniaInside.Shell; @@ -21,7 +21,6 @@ public DefaultNavigationUpdateStrategy(IPresenterProvider presenterProvider) public async Task UpdateChangesAsync( ShellView shellView, NavigationStackChanges changes, - List newInstances, NavigateType navigateType, object? argument, bool hasArgument, @@ -29,7 +28,7 @@ public async Task UpdateChangesAsync( { var isSame = changes.Previous == changes.Front; - foreach (var instance in newInstances) + foreach (var instance in changes.NewNavigationChains.Select(s => s.Instance)) { if (instance is INavigationLifecycle navigationLifecycle) await navigationLifecycle.InitialiseAsync(cancellationToken); @@ -52,7 +51,7 @@ public async Task UpdateChangesAsync( await newInstanceLifecycle.ArgumentAsync(argument, cancellationToken); } - if (!isSame && changes.Front != null) + if (!isSame && changes.Front != null && navigateType != NavigateType.Pop) await _presenterProvider.For(navigateType).PresentAsync(shellView, changes.Front, navigateType, cancellationToken); } @@ -80,14 +79,15 @@ private async Task InvokeRemoveAsync(ShellView shellView, private void SubscribeForUpdateIfNeeded(object? instance) { - if (instance is not SelectingItemsControl selectingItemsControl) return; - selectingItemsControl.SelectionChanged += SelectingItemsControlOnSelectionChanged; + if (HostedItemsHelper.GetSelectableHostedItems(instance) is {} hosted) + hosted.SelectionChanged += SelectingItemsControlOnSelectionChanged; + } private void UnSubscribeForUpdateIfNeeded(object instance) { - if (instance is not SelectingItemsControl selectingItemsControl) return; - selectingItemsControl.SelectionChanged -= SelectingItemsControlOnSelectionChanged; + if (HostedItemsHelper.GetSelectableHostedItems(instance) is {} hosted) + hosted.SelectionChanged -= SelectingItemsControlOnSelectionChanged; } private void SelectingItemsControlOnSelectionChanged(object? sender, SelectionChangedEventArgs e) diff --git a/src/AvaloniaInside.Shell/HostedItemsHelper.cs b/src/AvaloniaInside.Shell/HostedItemsHelper.cs new file mode 100644 index 0000000..683f487 --- /dev/null +++ b/src/AvaloniaInside.Shell/HostedItemsHelper.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; + +namespace AvaloniaInside.Shell; + +public static class HostedItemsHelper +{ + private class ItemsControlProxy(ItemsControl itemsControl) : IHostItems + { + public IEnumerable? ItemsSource + { + get => itemsControl.ItemsSource; + set => itemsControl.ItemsSource = value; + } + + public ItemCollection Items => itemsControl.Items; + } + + private class SelectingItemsControlProxy(SelectingItemsControl itemsControl) + : ItemsControlProxy(itemsControl), ISelectableHostItems + { + public event EventHandler? SelectionChanged + { + add => itemsControl.SelectionChanged += value; + remove => itemsControl.SelectionChanged -= value; + } + + public object? SelectedItem + { + get => itemsControl.SelectedItem; + set => itemsControl.SelectedItem = value; + } + } + + public static bool CanBeHosted(Type viewType) => + viewType.IsSubclassOf(typeof(ItemsControl)) || typeof(IHostItems).IsAssignableFrom(viewType); + + public static bool CanBeHosted(object view) => + view is ItemsControl or SelectingItemsControl or IHostItems or ISelectableHostItems; + + public static IHostItems? GetHostedItems(object? control) + { + if (GetSelectableHostedItems(control) is { } casted) return casted; + + if (control is IHostItems hostedItems) + return hostedItems; + if (control is ItemsControl itemsControl) + return new ItemsControlProxy(itemsControl); + + return null; + } + + public static ISelectableHostItems? GetSelectableHostedItems(object? control) + { + if (control is ISelectableHostItems selectableHostedItem) + return selectableHostedItem; + if (control is SelectingItemsControl selectingItemsControl) + return new SelectingItemsControlProxy(selectingItemsControl); + + return null; + } +} diff --git a/src/AvaloniaInside.Shell/IHostItems.cs b/src/AvaloniaInside.Shell/IHostItems.cs new file mode 100644 index 0000000..ab239a3 --- /dev/null +++ b/src/AvaloniaInside.Shell/IHostItems.cs @@ -0,0 +1,10 @@ +using System.Collections; +using Avalonia.Controls; + +namespace AvaloniaInside.Shell; + +public interface IHostItems +{ + IEnumerable? ItemsSource { get; set; } + ItemCollection Items { get; } +} diff --git a/src/AvaloniaInside.Shell/INavigationBarProvider.cs b/src/AvaloniaInside.Shell/INavigationBarProvider.cs new file mode 100644 index 0000000..94d79e4 --- /dev/null +++ b/src/AvaloniaInside.Shell/INavigationBarProvider.cs @@ -0,0 +1,7 @@ +namespace AvaloniaInside.Shell; + +public interface INavigationBarProvider +{ + NavigationBar? NavigationBar { get; } + NavigationBar? AttachedNavigationBar { get; } +} diff --git a/src/AvaloniaInside.Shell/INavigationUpdateStrategy.cs b/src/AvaloniaInside.Shell/INavigationUpdateStrategy.cs index a9f0703..95e5fe4 100644 --- a/src/AvaloniaInside.Shell/INavigationUpdateStrategy.cs +++ b/src/AvaloniaInside.Shell/INavigationUpdateStrategy.cs @@ -11,9 +11,8 @@ public interface INavigationUpdateStrategy Task UpdateChangesAsync( ShellView shellView, NavigationStackChanges changes, - List newInstances, NavigateType navigateType, object? argument, bool hasArgument, CancellationToken cancellationToken); -} \ No newline at end of file +} diff --git a/src/AvaloniaInside.Shell/INavigator.cs b/src/AvaloniaInside.Shell/INavigator.cs index 5db2e4c..18d03a5 100644 --- a/src/AvaloniaInside.Shell/INavigator.cs +++ b/src/AvaloniaInside.Shell/INavigator.cs @@ -11,6 +11,8 @@ public interface INavigator INavigationRegistrar Registrar { get; } + NavigationChain? CurrentChain { get; } + void RegisterShell(ShellView shellView); bool HasItemInStack(); diff --git a/src/AvaloniaInside.Shell/INavigatorLifecycle.cs b/src/AvaloniaInside.Shell/INavigatorLifecycle.cs index 908d5d6..eeb9e51 100644 --- a/src/AvaloniaInside.Shell/INavigatorLifecycle.cs +++ b/src/AvaloniaInside.Shell/INavigatorLifecycle.cs @@ -1,9 +1,5 @@ -using Avalonia; -using Avalonia.Animation; +using Avalonia.Animation; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; diff --git a/src/AvaloniaInside.Shell/ISelectableHostItems.cs b/src/AvaloniaInside.Shell/ISelectableHostItems.cs new file mode 100644 index 0000000..0ff8475 --- /dev/null +++ b/src/AvaloniaInside.Shell/ISelectableHostItems.cs @@ -0,0 +1,10 @@ +using System; +using Avalonia.Controls; + +namespace AvaloniaInside.Shell; + +public interface ISelectableHostItems : IHostItems +{ + event EventHandler SelectionChanged; + object? SelectedItem { get; set; } +} diff --git a/src/AvaloniaInside.Shell/NavigationBar.cs b/src/AvaloniaInside.Shell/NavigationBar.cs index 3c6f9b7..35a201d 100644 --- a/src/AvaloniaInside.Shell/NavigationBar.cs +++ b/src/AvaloniaInside.Shell/NavigationBar.cs @@ -1,45 +1,72 @@ using System; -using System.Threading; -using System.Threading.Tasks; using System.Windows.Input; using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; +using Avalonia.Interactivity; namespace AvaloniaInside.Shell; +[TemplatePart("PART_Header", typeof(ContentControl))] +[TemplatePart("PART_ActionButton", typeof(Button))] +[TemplatePart("PART_Items", typeof(ContentControl))] public class NavigationBar : TemplatedControl { - public static readonly DirectProperty SideMenuCommandProperty = - AvaloniaProperty.RegisterDirect( - nameof(SideMenuCommand), - o => o.SideMenuCommand, - (o, v) => o.SideMenuCommand = v); - - public static readonly DirectProperty BackCommandProperty = - AvaloniaProperty.RegisterDirect( - nameof(BackCommand), - o => o.BackCommand, - (o, v) => o.BackCommand = v); - - public static readonly DirectProperty HasSideMenuOptionProperty = - AvaloniaProperty.RegisterDirect( - nameof(HasSideMenuOption), - o => o.HasSideMenuOption, - (o, v) => o.HasSideMenuOption = v); - private ContentControl? _header; private Button? _actionButton; private ContentControl? _items; private object? _pendingHeader; - private NavigateType? _pendingNavType; - public ShellView? ShellView { get; internal set; } + public NavigationBar(ShellView shellView) + { + ShellView = shellView; + } + + public NavigationBar(Page page) + { + Page = page; + ShellView = page.Shell; + } + + #region ShellView + + public static readonly StyledProperty ShellViewProperty = + AvaloniaProperty.Register(nameof(ShellView)); - private ICommand _backCommand; + public ShellView? ShellView + { + get => GetValue(ShellViewProperty); + private set => SetValue(ShellViewProperty, value); + } - public ICommand BackCommand + #endregion + + #region Page + + public static readonly StyledProperty PageProperty = + AvaloniaProperty.Register(nameof(Page)); + + public Page? Page + { + get => GetValue(PageProperty); + private set => SetValue(PageProperty, value); + } + + #endregion + + #region BackCommand + + public static readonly DirectProperty BackCommandProperty = + AvaloniaProperty.RegisterDirect( + nameof(BackCommand), + o => o.BackCommand, + (o, v) => o.BackCommand = v); + + private ICommand? _backCommand; + + public ICommand? BackCommand { get => _backCommand; set @@ -49,9 +76,19 @@ public ICommand BackCommand } } - private ICommand _sideMenuCommand; + #endregion + + #region SideMenuCommand + + public static readonly DirectProperty SideMenuCommandProperty = + AvaloniaProperty.RegisterDirect( + nameof(SideMenuCommand), + o => o.SideMenuCommand, + (o, v) => o.SideMenuCommand = v); - public ICommand SideMenuCommand + private ICommand? _sideMenuCommand; + + public ICommand? SideMenuCommand { get => _sideMenuCommand; set @@ -61,6 +98,16 @@ public ICommand SideMenuCommand } } + #endregion + + #region HasSideMenuOption + + public static readonly DirectProperty HasSideMenuOptionProperty = + AvaloniaProperty.RegisterDirect( + nameof(HasSideMenuOption), + o => o.HasSideMenuOption, + (o, v) => o.HasSideMenuOption = v); + private bool _hasSideMenuOption = true; public bool HasSideMenuOption @@ -73,28 +120,51 @@ public bool HasSideMenuOption } } - #region TopSafeSpace + #endregion + + #region CurrentView + + public static readonly DirectProperty CurrentViewProperty = + AvaloniaProperty.RegisterDirect( + nameof(CurrentView), + o => o.CurrentView, + (o, v) => o.CurrentView = v); + + private object? _currentView; + public object? CurrentView + { + get => _currentView; + set + { + if (SetAndRaise(CurrentViewProperty, ref _currentView, value)) + UpdateView(value); + } + } + + #endregion + + #region SafePadding - public static readonly StyledProperty TopSafeSpaceProperty = - AvaloniaProperty.Register(nameof(TopSafeSpace)); + public static readonly StyledProperty SafePaddingProperty = + AvaloniaProperty.Register(nameof(SafePadding)); - public double TopSafeSpace + public Thickness SafePadding { - get => GetValue(TopSafeSpaceProperty); - set => SetValue(TopSafeSpaceProperty, value); + get => GetValue(SafePaddingProperty); + set => SetValue(SafePaddingProperty, value); } #endregion - #region TopSafePadding + #region PlatformHeight - public static readonly StyledProperty TopSafePaddingProperty = - AvaloniaProperty.Register(nameof(TopSafePadding)); + public static readonly StyledProperty PlatformHeightProperty = + AvaloniaProperty.Register(nameof(PlatformHeight), defaultValue: 36); - public Thickness TopSafePadding + public double PlatformHeight { - get => GetValue(TopSafePaddingProperty); - set => SetValue(TopSafePaddingProperty, value); + get => GetValue(PlatformHeightProperty); + set => SetValue(PlatformHeightProperty, value); } #endregion @@ -112,6 +182,110 @@ public bool ApplyTopSafePadding #endregion + #region ApplyLeftSafePadding + + public static readonly StyledProperty ApplyLeftSafePaddingProperty = + AvaloniaProperty.Register(nameof(ApplyLeftSafePadding), defaultValue: true); + + public bool ApplyLeftSafePadding + { + get => GetValue(ApplyLeftSafePaddingProperty); + set => SetValue(ApplyLeftSafePaddingProperty, value); + } + + #endregion + + #region ApplyRightSafePadding + + public static readonly StyledProperty ApplyRightSafePaddingProperty = + AvaloniaProperty.Register(nameof(ApplyRightSafePadding), defaultValue: true); + + public bool ApplyRightSafePadding + { + get => GetValue(ApplyRightSafePaddingProperty); + set => SetValue(ApplyRightSafePaddingProperty, value); + } + + #endregion + + #region TopSafeSpace + + public static readonly StyledProperty TopSafeSpaceProperty = + AvaloniaProperty.Register(nameof(TopSafeSpace)); + + public double TopSafeSpace + { + get => GetValue(TopSafeSpaceProperty); + set => SetValue(TopSafeSpaceProperty, value); + } + + #endregion + + #region TopSafePadding + + public static readonly StyledProperty TopSafePaddingProperty = + AvaloniaProperty.Register(nameof(TopSafePadding)); + + public Thickness TopSafePadding + { + get => GetValue(TopSafePaddingProperty); + set => SetValue(TopSafePaddingProperty, value); + } + + #endregion + + #region LeftSafeSpace + + public static readonly StyledProperty LeftSafeSpaceProperty = + AvaloniaProperty.Register(nameof(LeftSafeSpace)); + + public double LeftSafeSpace + { + get => GetValue(LeftSafeSpaceProperty); + set => SetValue(LeftSafeSpaceProperty, value); + } + + #endregion + + #region LeftSafePadding + + public static readonly StyledProperty LeftSafePaddingProperty = + AvaloniaProperty.Register(nameof(LeftSafePadding)); + + public Thickness LeftSafePadding + { + get => GetValue(LeftSafePaddingProperty); + set => SetValue(LeftSafePaddingProperty, value); + } + + #endregion + + #region RightSafeSpace + + public static readonly StyledProperty RightSafeSpaceProperty = + AvaloniaProperty.Register(nameof(RightSafeSpace)); + + public double RightSafeSpace + { + get => GetValue(RightSafeSpaceProperty); + set => SetValue(RightSafeSpaceProperty, value); + } + + #endregion + + #region RightSafePadding + + public static readonly StyledProperty RightSafePaddingProperty = + AvaloniaProperty.Register(nameof(RightSafePadding)); + + public Thickness RightSafePadding + { + get => GetValue(RightSafePaddingProperty); + set => SetValue(RightSafePaddingProperty, value); + } + + #endregion + #region Attached properties #region Item @@ -140,6 +314,19 @@ public static void SetHeader(AvaloniaObject element, object parameter) => #endregion + #region HeaderIcon + + public static readonly AttachedProperty HeaderIconProperty = + AvaloniaProperty.RegisterAttached("HeaderIcon"); + + public static object GetHeaderIcon(AvaloniaObject element) => + element.GetValue(HeaderIconProperty); + + public static void SetHeaderIcon(AvaloniaObject element, object parameter) => + element.SetValue(HeaderIconProperty, parameter); + + #endregion + #region Visible public static readonly AttachedProperty VisibleProperty = @@ -155,10 +342,67 @@ public static void SetVisible(AvaloniaObject element, bool parameter) => #endregion + #region Setup and loading template + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ShellViewProperty) + { + ShellViewUpdated(); + } + else if (change.Property == SafePaddingProperty || + change.Property == ApplyTopSafePaddingProperty || + change.Property == PlatformHeightProperty) + { + UpdateSafePaddingSizes(); + } + } + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + + if (_pendingHeader != null) + UpdateView(_pendingHeader); + + UpdateSafePaddingSizes(); + } + + protected virtual void ShellViewUpdated() + { + if (ShellView is not { } shellView) return; + + _backCommand = shellView.BackCommand; + _sideMenuCommand = shellView.SideMenuCommand; + + this[!SafePaddingProperty] = shellView[!ShellView.SafePaddingProperty]; + + if (Page is { } page) + { + this[!ApplyTopSafePaddingProperty] = shellView[!ShellView.EnableSafeAreaForTopProperty]; + this[!ApplyLeftSafePaddingProperty] = shellView[!ShellView.EnableSafeAreaForLeftProperty]; + this[!ApplyRightSafePaddingProperty] = shellView[!ShellView.EnableSafeAreaForRightProperty]; + + UpdateView(page); + } + else + { + this[!ApplyTopSafePaddingProperty] = shellView[!ShellView.ApplyTopSafePaddingProperty]; + this[!ApplyLeftSafePaddingProperty] = shellView[!ShellView.ApplyLeftSafePaddingProperty]; + this[!ApplyRightSafePaddingProperty] = shellView[!ShellView.ApplyRightSafePaddingProperty]; + + if (shellView.ContentView?.CurrentView is { } currentView) + UpdateView(currentView); + } + } + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); - _header = e.NameScope.Find("PART_Header") ?? throw new ArgumentNullException("PART_Header"); + _header = e.NameScope.Find("PART_Header") ?? + throw new Exception("PART_Header cannot found for NavigationBar"); _actionButton = e.NameScope.Find