Skip to content

Commit

Permalink
fix: IActiveAware activates inactive views
Browse files Browse the repository at this point in the history
  • Loading branch information
dansiegel committed Mar 21, 2023
1 parent 13b1620 commit 2e4692d
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 27 deletions.
55 changes: 35 additions & 20 deletions src/Maui/Prism.Maui/Behaviors/MultiPageActiveAwareBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ namespace Prism.Behaviors;

public class MultiPageActiveAwareBehavior<T> : BehaviorBase<MultiPage<T>> where T : Page
{
protected T _lastSelectedPage;

/// <inheritDoc/>
protected override void OnAttachedTo(MultiPage<T> bindable)
{
Expand All @@ -31,16 +29,7 @@ protected override void OnDetachingFrom(MultiPage<T> bindable)
/// <param name="e">Event Args</param>
protected void CurrentPageChangedHandler(object sender, EventArgs e)
{
if (_lastSelectedPage == null)
_lastSelectedPage = AssociatedObject.CurrentPage;

//inactive
SetIsActive(_lastSelectedPage, false);

_lastSelectedPage = AssociatedObject.CurrentPage;

//active
SetIsActive(_lastSelectedPage, true);
SetActiveAware();
}

/// <summary>
Expand All @@ -50,10 +39,7 @@ protected void CurrentPageChangedHandler(object sender, EventArgs e)
/// <param name="e">Event Args</param>
protected void RootPageAppearingHandler(object sender, EventArgs e)
{
if (_lastSelectedPage == null)
_lastSelectedPage = AssociatedObject.CurrentPage;

SetIsActive(_lastSelectedPage, true);
SetActiveAware();
}

/// <summary>
Expand All @@ -63,13 +49,42 @@ protected void RootPageAppearingHandler(object sender, EventArgs e)
/// <param name="e">Event Args</param>
protected void RootPageDisappearingHandler(object sender, EventArgs e)
{
SetIsActive(_lastSelectedPage, false);
SetActiveAware();
}

private static void SetIsActive(object view, bool isActive)
private void SetActiveAware()
{
var pageToSetIsActive = view is NavigationPage page ? page.CurrentPage : view;
foreach (var page in AssociatedObject.Children)
{
SetPageIsActive(page);
}
}

MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(pageToSetIsActive, activeAware => activeAware.IsActive = isActive);
private void SetPageIsActive(Page page)
{
if(AssociatedObject.CurrentPage == page)
{
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(page, SetIsActive);
if (page is NavigationPage navPage)
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(navPage.CurrentPage, SetIsActive);
}
else
{
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(page, SetNotActive);
if (page is NavigationPage navPage)
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(navPage.CurrentPage, SetNotActive);
}
}

private void SetNotActive(IActiveAware activeAware)
{
if (activeAware.IsActive)
activeAware.IsActive = false;
}

private void SetIsActive(IActiveAware activeAware)
{
if (!activeAware.IsActive)
activeAware.IsActive = true;
}
}
33 changes: 27 additions & 6 deletions src/Maui/Prism.Maui/Behaviors/NavigationPageActiveAwareBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@ public class NavigationPageActiveAwareBehavior : BehaviorBase<NavigationPage>
{
protected override void OnAttachedTo(NavigationPage bindable)
{
bindable.PropertyChanging += NavigationPage_PropertyChanging;
bindable.PropertyChanged += NavigationPage_PropertyChanged;
if (bindable.Parent is null)
bindable.ParentChanged += OnParentChanged;
base.OnAttachedTo(bindable);
}

private void OnParentChanged(object sender, EventArgs e)
{
AssociatedObject.ParentChanged -= OnParentChanged;
SetActiveAware();
}

protected override void OnDetachingFrom(NavigationPage bindable)
{
bindable.PropertyChanging -= NavigationPage_PropertyChanging;
bindable.PropertyChanged -= NavigationPage_PropertyChanged;
base.OnDetachingFrom(bindable);
}
Expand All @@ -24,15 +30,30 @@ private void NavigationPage_PropertyChanged(object sender, PropertyChangedEventA
{
if (e.PropertyName == "CurrentPage")
{
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(AssociatedObject.CurrentPage, (obj) => obj.IsActive = true);
SetActiveAware();
}
}

private void NavigationPage_PropertyChanging(object sender, PropertyChangingEventArgs e)
private void SetActiveAware()
{
if (e.PropertyName == "CurrentPage")
foreach(var page in AssociatedObject.Navigation.NavigationStack)
{
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(AssociatedObject.CurrentPage, (obj) => obj.IsActive = false);
if (page != AssociatedObject.CurrentPage || AssociatedObject.Parent is TabbedPage tabbed && tabbed.CurrentPage != AssociatedObject)
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(page, SetNotActive);
else
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(page, SetIsActive);
}
}

private void SetNotActive(IActiveAware activeAware)
{
if (activeAware.IsActive)
activeAware.IsActive = false;
}

private void SetIsActive(IActiveAware activeAware)
{
if(!activeAware.IsActive)
activeAware.IsActive = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using Microsoft.Maui.Controls;
using Prism.Controls;
using Prism.DryIoc.Maui.Tests.Mocks.ViewModels;
using Prism.DryIoc.Maui.Tests.Mocks.Views;
using Prism.Navigation.Builder;

namespace Prism.DryIoc.Maui.Tests.Fixtures.Behaviors;

public class NavigationBehaviors : TestBase
{
public NavigationBehaviors(ITestOutputHelper testOutputHelper)
: base(testOutputHelper)
{
}

[Fact]
public void RootPageIsNotActive()
{
var rootPage = StartAndGetRootPage("NavigationPage/MockViewA/MockViewB");

Assert.IsType<PrismNavigationPage>(rootPage);
var navPage = (NavigationPage)rootPage;

var viewA = navPage.Navigation.NavigationStack[0];
var viewB = navPage.Navigation.NavigationStack[1];

Assert.IsType<MockViewA>(viewA);
Assert.IsType<MockViewB>(viewB);

AssertIsActive(viewA, false);
AssertIsActive(viewB, true);
}

[Fact]
public void TabPageSetsFirstTabIsActive()
{
var rootPage = StartAndGetRootPage(b => b.AddTabbedSegment(t => t.CreateTab("MockViewA").CreateTab("MockViewB")));

Assert.IsType<TabbedPage>(rootPage);

var tabbed = (TabbedPage)rootPage;
AssertIsActive(tabbed.Children[0], true);
AssertIsActive(tabbed.Children[1], false);
}

[Fact]
public void TabPageSetsSecondTabIsActive()
{
var rootPage = StartAndGetRootPage(b => b.AddTabbedSegment(t => t.CreateTab("MockViewA").CreateTab("MockViewB").SelectedTab("MockViewB")));

Assert.IsType<TabbedPage>(rootPage);

var tabbed = (TabbedPage)rootPage;
AssertIsActive(tabbed.Children[0], false);
AssertIsActive(tabbed.Children[1], true);
}

[Fact]
public void TabPageSetsFirstTabIsActiveWithNavigationPage()
{
var rootPage = StartAndGetRootPage(b => b.AddTabbedSegment(t => t.CreateTab(tb => tb.AddNavigationPage().AddSegment("MockViewA")).CreateTab(tb => tb.AddNavigationPage().AddSegment("MockViewB"))));

Assert.IsType<TabbedPage>(rootPage);

var tabbed = (TabbedPage)rootPage;

AssertIsActive(GetTabChild(tabbed, 0), true);
AssertIsActive(GetTabChild(tabbed, 1), false);
}

[Fact]
public void TabPageSetsSecondTabIsActiveWithNavigationPage()
{
var rootPage = StartAndGetRootPage(b => b.AddTabbedSegment(t => t.CreateTab(tb => tb.AddNavigationPage().AddSegment("MockViewA")).CreateTab(tb => tb.AddNavigationPage().AddSegment("MockViewB")).SelectedTab("MockViewB")));

Assert.IsType<TabbedPage>(rootPage);

var tabbed = (TabbedPage)rootPage;
AssertIsActive(GetTabChild(tabbed, 0), false);
AssertIsActive(GetTabChild(tabbed, 1), true);
}

private void AssertIsActive(Page page, bool expected)
{
var viewModel = (MockViewModelBase)page.BindingContext;
Assert.Equal(expected, viewModel.IsActive);
}

private Page GetTabChild(TabbedPage tabbed, int index)
{
var child = tabbed.Children[index];
Assert.IsType<PrismNavigationPage>(child);
var navPage = (NavigationPage)child;
return navPage.CurrentPage;
}

private Page StartAndGetRootPage(Action<INavigationBuilder> initialNav)
{
var mauiApp = CreateBuilder(prism => prism.OnAppStart((_, nav) =>
{
var navBuilder = nav.CreateBuilder();
initialNav(navBuilder);
return navBuilder.NavigateAsync();
}))
.Build();
var window = GetWindow(mauiApp);

var rootPage = window.Page;

Assert.NotNull(rootPage);
return rootPage;
}

private Page StartAndGetRootPage(string uri)
{
var mauiApp = CreateBuilder(prism => prism.OnAppStart(uri))
.Build();
var window = GetWindow(mauiApp);

var rootPage = window.Page;

Assert.NotNull(rootPage);
return rootPage;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

namespace Prism.DryIoc.Maui.Tests.Mocks.ViewModels;

public abstract class MockViewModelBase : IConfirmNavigation
public abstract class MockViewModelBase : IActiveAware, IConfirmNavigation
{
private readonly IPageAccessor _pageAccessor;

public event EventHandler IsActiveChanged;

protected MockViewModelBase(IPageAccessor pageAccessor, INavigationService navigationService)
{
_pageAccessor = pageAccessor;
Expand All @@ -22,6 +24,8 @@ protected MockViewModelBase(IPageAccessor pageAccessor, INavigationService navig

public bool StopNavigation { get; set; }

public bool IsActive { get; set; }

public bool CanNavigate(INavigationParameters parameters) =>
!StopNavigation;
}

0 comments on commit 2e4692d

Please sign in to comment.