Skip to content

Commit

Permalink
Merge pull request PrismLibrary#3119 from PrismLibrary/dev/ds/cleanup
Browse files Browse the repository at this point in the history
Adding MAUI Navigation tests
  • Loading branch information
dansiegel authored Apr 5, 2024
2 parents 52c04e2 + b102560 commit e8d6d1a
Show file tree
Hide file tree
Showing 111 changed files with 1,081 additions and 661 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ csharp_indent_labels = one_less_than_current
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_namespace_declarations = file_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
Expand Down
2 changes: 2 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute;
System.Runtime.CompilerServices.IsExternalInit;
</PolySharpIncludeGeneratedTypes>
<WarningsAsErrors>$(WarningsAsErrors);IDE0003</WarningsAsErrors>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<PropertyGroup>
Expand Down
7 changes: 5 additions & 2 deletions e2e/Maui/MauiModule/Views/ViewB.xaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="http://prismlibrary.com"
prism:DynamicTab.Title="Tab B"
prism:DynamicTab.IconImageSource="profile.png"
x:Class="MauiModule.Views.ViewB"
Title="{Binding Title}"
IconImageSource="profile.png"
IconImageSource="home.png"
BackgroundColor="White">
<Grid RowDefinitions="*,Auto"
ColumnDefinitions="*,*">
Expand Down
31 changes: 25 additions & 6 deletions e2e/Maui/PrismMauiDemo/ViewModels/RootPageViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace PrismMauiDemo.ViewModels;
using MauiModule.ViewModels;

namespace PrismMauiDemo.ViewModels;

public class RootPageViewModel
{
Expand All @@ -7,14 +9,31 @@ public class RootPageViewModel
public RootPageViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
NavigateCommand = new DelegateCommand<string>(OnNavigateCommandExecuted);
NavigateCommand = new AsyncDelegateCommand<string>(OnNavigateCommandExecuted);
}

public DelegateCommand<string> NavigateCommand { get; }
public AsyncDelegateCommand<string> NavigateCommand { get; }

private void OnNavigateCommandExecuted(string uri)
private async Task OnNavigateCommandExecuted(string uri)
{
_navigationService.NavigateAsync(uri)
.OnNavigationError(ex => Console.WriteLine(ex));
if (uri == "TabbedPage")
{
var result = await _navigationService.CreateBuilder()
.AddTabbedSegment(s => s.CreateTab<ViewAViewModel>()
.CreateTab(t => t.AddNavigationPage().AddSegment<ViewBViewModel>())
.CreateTab("ViewC")
.CreateTab("ViewD"))
.NavigateAsync();
if (result.Exception is not null)
{
Console.WriteLine(result.Exception);
System.Diagnostics.Debugger.Break();
}
}
else
{
_navigationService.NavigateAsync(uri)
.OnNavigationError(ex => Console.WriteLine(ex));
}
}
}
4 changes: 2 additions & 2 deletions e2e/Maui/PrismMauiDemo/Views/RootPage.xaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PrismMauiDemo.Views.RootPage"
Expand All @@ -13,7 +13,7 @@
CommandParameter="/MainPage/NavigationPage/ViewA/ViewB/ViewC/ViewD" />
<Button Text="Tabbed Page"
Command="{Binding NavigateCommand}"
CommandParameter="/TabbedPage?createTab=ViewA&amp;createTab=ViewB&amp;createTab=ViewC&amp;createTab=ViewD" />
CommandParameter="TabbedPage" />
<Button Text="Regions"
Command="{Binding NavigateCommand}"
CommandParameter="/RegionHome/NavigationPage/ContentRegionPage" />
Expand Down
1 change: 1 addition & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<IsPackable Condition=" '$(IsFormsProject)' ">!$(DisableFormsPublish)</IsPackable>
<IsPackable Condition=" '$(IsUnoProject)' ">!$(DisableUnoPublish)</IsPackable>
<ContinuousIntegrationBuild Condition="'$(BUILD_BUILDID)' != ''">true</ContinuousIntegrationBuild>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<Choose>
Expand Down
1 change: 0 additions & 1 deletion src/Maui/Prism.Maui.Rx/GlobalNavigationObserver.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Reactive.Subjects;
using Prism.Events;

Expand Down
3 changes: 1 addition & 2 deletions src/Maui/Prism.Maui.Rx/IGlobalNavigationObserver.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;

namespace Prism.Navigation;


public interface IGlobalNavigationObserver
{
IObservable<NavigationRequestContext> NavigationRequest { get; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using Prism.Ioc;

namespace Prism.Navigation;

public static class NavigationObserverRegistrationExtensions
Expand Down Expand Up @@ -29,4 +27,4 @@ public static PrismAppBuilder AddGlobalNavigationObserver(this PrismAppBuilder b
{
addObservable(c, c.Resolve<IGlobalNavigationObserver>().NavigationRequest);
});
}
}
71 changes: 37 additions & 34 deletions src/Maui/Prism.Maui/Behaviors/NavigationPageTabbedParentBehavior.cs
Original file line number Diff line number Diff line change
@@ -1,56 +1,59 @@
namespace Prism.Behaviors;
using System.ComponentModel;
using Prism.Xaml;

namespace Prism.Behaviors;

/// <summary>
/// Adds a behavior to use the RootPage Title and IconImageSource if they are not set on the NavigaitonPage
/// when the NavigationPage has a TabbedPage parent.
/// </summary>
public sealed class NavigationPageTabbedParentBehavior : BehaviorBase<NavigationPage>
{
private static readonly BindableProperty NavigationPageRootPageMonitorTitleProperty =
BindableProperty.CreateAttached("NavigationPageRootPageMonitorTitle", typeof(bool), typeof(NavigationPageTabbedParentBehavior), false);

private static readonly BindableProperty NavigationPageRootPageMonitorIconImageSourceProperty =
BindableProperty.CreateAttached("NavigationPageRootPageMonitorIconImageSource", typeof(bool), typeof(NavigationPageTabbedParentBehavior), false);

private static bool GetNavigationPageRootPageMonitorTitle(BindableObject bindable) =>
(bool)bindable.GetValue(NavigationPageRootPageMonitorTitleProperty);

private static void SetNavigationPageRootPageMonitorTitle(BindableObject bindable, bool monitorTitle) =>
bindable.SetValue(NavigationPageRootPageMonitorTitleProperty, monitorTitle);

private static bool GetNavigationPageRootPageMonitorIconImageSource(BindableObject bindable) =>
(bool)bindable.GetValue(NavigationPageRootPageMonitorIconImageSourceProperty);

private static void SetNavigationPageRootPageMonitorIconImageSource(BindableObject bindable, bool monitorTitle) =>
bindable.SetValue(NavigationPageRootPageMonitorIconImageSourceProperty, monitorTitle);

/// <inheritdoc />
protected override void OnAttachedTo(NavigationPage bindable)
{
base.OnAttachedTo(bindable);
SetNavigationPageRootPageMonitorTitle(bindable, !bindable.IsSet(NavigationPage.TitleProperty));
SetNavigationPageRootPageMonitorIconImageSource(bindable, !bindable.IsSet(NavigationPage.IconImageSourceProperty));
bindable.ParentChanged += OnParentChanged;
if (bindable.Parent is TabbedPage)
OnParentChanged(bindable, EventArgs.Empty);
bindable.PropertyChanged += OnRootPageSet;
}

private void OnRootPagePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (sender is not Page page || page.Parent is not NavigationPage navigationPage)
{
return;
}

if (e.PropertyName == DynamicTab.TitleProperty.PropertyName)
{
navigationPage.Title = DynamicTab.GetTitle(page);
}

if (e.PropertyName == DynamicTab.IconImageSourceProperty.PropertyName)
{
navigationPage.IconImageSource = DynamicTab.GetIconImageSource(page);
}
}

/// <inheritdoc />
protected override void OnDetachingFrom(NavigationPage bindable)
{
base.OnDetachingFrom(bindable);
bindable.ParentChanged -= OnParentChanged;
// Sanity Check
bindable.PropertyChanged -= OnRootPageSet;
if (bindable.RootPage is not null)
{
bindable.RootPage.PropertyChanged -= OnRootPagePropertyChanged;
}
}

private void OnParentChanged(object sender, EventArgs e)
private void OnRootPageSet(object sender, PropertyChangedEventArgs e)
{
if (sender is not NavigationPage navigationPage || navigationPage.Parent is not TabbedPage)
return;

if (GetNavigationPageRootPageMonitorTitle(navigationPage))
navigationPage.SetBinding(NavigationPage.TitleProperty, new Binding("RootPage.Title", BindingMode.OneWay, source: navigationPage));

if (GetNavigationPageRootPageMonitorIconImageSource(navigationPage))
navigationPage.SetBinding(NavigationPage.IconImageSourceProperty, new Binding("RootPage.IconImageSource", BindingMode.OneWay, source: navigationPage));
if (sender is NavigationPage navigationPage && navigationPage.RootPage is not null)
{
navigationPage.PropertyChanged -= OnRootPageSet;
navigationPage.RootPage.PropertyChanged += OnRootPagePropertyChanged;
navigationPage.Title = DynamicTab.GetTitle(navigationPage.RootPage);
navigationPage.IconImageSource = DynamicTab.GetIconImageSource(navigationPage.RootPage);
}
}
}
1 change: 1 addition & 0 deletions src/Maui/Prism.Maui/Controls/PrismNavigationPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ private async void HandleBackButtonPressed(object sender, EventArgs args)
}

#if IOS
/// <inheritdoc/>
protected override async void OnDisappearing()
{
var presentationStyle = Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.Page.GetModalPresentationStyle(this);
Expand Down
66 changes: 66 additions & 0 deletions src/Maui/Prism.Maui/Dialogs/DialogContainerPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,46 @@

namespace Prism.Dialogs;

/// <summary>
/// Represents a page that serves as a container for dialogs in Prism.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public class DialogContainerPage : ContentPage, IDialogContainer
{
/// <summary>
/// The name of the automation ID for the dialog container page.
/// </summary>
public const string AutomationIdName = "PrismDialogModal";

/// <summary>
/// Initializes a new instance of the <see cref="DialogContainerPage"/> class.
/// </summary>
public DialogContainerPage()
{
AutomationId = AutomationIdName;
BackgroundColor = Colors.Transparent;
On<iOS>().SetModalPresentationStyle(UIModalPresentationStyle.OverFullScreen);
}

/// <summary>
/// Gets the dialog view displayed in the container page.
/// </summary>
public View DialogView { get; private set; }

/// <summary>
/// Gets the command used to dismiss the dialog.
/// </summary>
public ICommand Dismiss { get; private set; }

/// <summary>
/// Configures the layout of the dialog container page.
/// </summary>
/// <param name="currentPage">The current page.</param>
/// <param name="dialogView">The dialog view to be displayed.</param>
/// <param name="hideOnBackgroundTapped">A flag indicating whether the dialog should be hidden when the background is tapped.</param>
/// <param name="dismissCommand">The command to be executed when the dialog is dismissed.</param>
/// <param name="parameters">The parameters passed to the dialog.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task ConfigureLayout(Page currentPage, View dialogView, bool hideOnBackgroundTapped, ICommand dismissCommand, IDialogParameters parameters)
{
Dismiss = dismissCommand;
Expand All @@ -34,16 +58,35 @@ public async Task ConfigureLayout(Page currentPage, View dialogView, bool hideOn
await DoPush(currentPage);
}

/// <summary>
/// Performs the push operation to display the dialog container page.
/// </summary>
/// <param name="currentPage">The current page.</param>
/// <returns>A task representing the asynchronous operation.</returns>
protected virtual async Task DoPush(Page currentPage)
{
await currentPage.Navigation.PushModalAsync(this, false);
}

/// <summary>
/// Performs the pop operation to dismiss the dialog container page.
/// </summary>
/// <param name="currentPage">The current page.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public virtual async Task DoPop(Page currentPage)
{
await currentPage.Navigation.PopModalAsync(false);
}

/// <summary>
/// Gets the content layout for the dialog container page.
/// </summary>
/// <param name="currentPage">The current page.</param>
/// <param name="dialogView">The dialog view to be displayed.</param>
/// <param name="hideOnBackgroundTapped">A flag indicating whether the dialog should be hidden when the background is tapped.</param>
/// <param name="dismissCommand">The command to be executed when the dialog is dismissed.</param>
/// <param name="parameters">The parameters passed to the dialog.</param>
/// <returns>The content layout for the dialog container page.</returns>
protected virtual View GetContentLayout(Page currentPage, View dialogView, bool hideOnBackgroundTapped, ICommand dismissCommand, IDialogParameters parameters)
{
var overlay = new AbsoluteLayout();
Expand Down Expand Up @@ -92,6 +135,14 @@ protected virtual View GetContentLayout(Page currentPage, View dialogView, bool
return overlay;
}

/// <summary>
/// Gets the mask view for the dialog container page.
/// </summary>
/// <param name="currentPage">The current page.</param>
/// <param name="dialogView">The dialog view to be displayed.</param>
/// <param name="hideOnBackgroundTapped">A flag indicating whether the dialog should be hidden when the background is tapped.</param>
/// <param name="dismissCommand">The command to be executed when the dialog is dismissed.</param>
/// <returns>The mask view for the dialog container page.</returns>
private View GetMask(Page currentPage, View dialogView, bool hideOnBackgroundTapped, ICommand dismissCommand)
{
View mask = DialogLayout.GetMask(dialogView);
Expand Down Expand Up @@ -130,6 +181,12 @@ private View GetMask(Page currentPage, View dialogView, bool hideOnBackgroundTap
return mask;
}

/// <summary>
/// Gets the overlay style for the dialog container page.
/// </summary>
/// <param name="popupView">The popup view.</param>
/// <param name="currentPage">The current page.</param>
/// <returns>The overlay style for the dialog container page.</returns>
private Style GetOverlayStyle(View popupView, Page currentPage)
{
var style = DialogLayout.GetMaskStyle(popupView);
Expand All @@ -141,6 +198,11 @@ private Style GetOverlayStyle(View popupView, Page currentPage)
return GetStyle(currentPage);
}

/// <summary>
/// Gets the style for the specified element.
/// </summary>
/// <param name="element">The element.</param>
/// <returns>The style for the specified element.</returns>
private static Style GetStyle(Element element)
{
if (element is Page page && page.Resources.ContainsKey(DialogLayout.PopupOverlayStyle) && page.Resources[DialogLayout.PopupOverlayStyle] is Style pageStyle)
Expand All @@ -165,6 +227,10 @@ private static Style GetStyle(Element element)
return GetStyle(element.Parent);
}

/// <summary>
/// Gets the default overlay style for the dialog container page.
/// </summary>
/// <returns>The default overlay style for the dialog container page.</returns>
private static Style DefaultStyle()
{
var overlayStyle = new Style(typeof(BoxView));
Expand Down
Loading

0 comments on commit e8d6d1a

Please sign in to comment.