Skip to content

Commit

Permalink
Page cache clearing
Browse files Browse the repository at this point in the history
  • Loading branch information
PureWeen committed Jul 29, 2024
1 parent b521bfc commit 9d05d89
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 5 deletions.
10 changes: 10 additions & 0 deletions src/Controls/src/Core/Shell/Shell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1634,8 +1634,18 @@ protected virtual void OnNavigating(ShellNavigatingEventArgs args)
static void OnCurrentItemChanged(BindableObject bindable, object oldValue, object newValue)
{
if (oldValue is ShellItem oldShellItem)
{
oldShellItem.SendDisappearing();

foreach(var section in oldShellItem.Items)
{
foreach(var content in section.Items)
{
content.EvaluateDisconnect();
}
}
}

if (newValue == null)
return;

Expand Down
85 changes: 80 additions & 5 deletions src/Controls/src/Core/Shell/ShellContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Specialized;
using System.ComponentModel;
using System.Reflection;
using System.Security.Cryptography;
using Microsoft.Maui.Controls.Internals;

namespace Microsoft.Maui.Controls
Expand Down Expand Up @@ -54,6 +55,7 @@ public DataTemplate ContentTemplate
EventHandler _isPageVisibleChanged;
event EventHandler IShellContentController.IsPageVisibleChanged { add => _isPageVisibleChanged += value; remove => _isPageVisibleChanged -= value; }

bool _createdViaService;
Page IShellContentController.GetOrCreateContent()
{
var template = ContentTemplate;
Expand All @@ -74,11 +76,19 @@ Page IShellContentController.GetOrCreateContent()
var services = Parent?.FindMauiContext()?.Services;
if (services is not null)
{
return Extensions.DependencyInjection.ActivatorUtilities.GetServiceOrCreateInstance(services, template.Type);
var result = services.GetService(template.Type);
if (result is not null)
{
_createdViaService = true;
return result;
}
}
return Activator.CreateInstance(template.Type);

_createdViaService = false;
return Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(services, template.Type);
};
}

result = ContentCache ?? (Page)template.CreateContent(content, this);
ContentCache = result;
}
Expand Down Expand Up @@ -108,7 +118,10 @@ void IShellContentController.RecyclePage(Page page)
Page _contentCache;

/// <include file="../../../docs/Microsoft.Maui.Controls/ShellContent.xml" path="//Member[@MemberName='.ctor']/Docs/*" />
public ShellContent() => ((INotifyCollectionChanged)MenuItems).CollectionChanged += MenuItemsCollectionChanged;
public ShellContent()
{
((INotifyCollectionChanged)MenuItems).CollectionChanged += MenuItemsCollectionChanged;
}

internal bool IsVisibleContent => Parent is ShellSection shellSection && shellSection.IsVisibleSection && shellSection.CurrentItem == this;

Expand Down Expand Up @@ -187,18 +200,79 @@ Page ContentCache
var oldCache = _contentCache;
_contentCache = value;
if (oldCache != null)
{
RemoveLogicalChild(oldCache);
oldCache.Unloaded -= OnPageUnloaded;
}

if (value != null && value.Parent != this)
if (value is not null && value.Parent != this)
{
AddLogicalChild(value);

if (_createdViaService)
{
value.Unloaded += OnPageUnloaded;
}
}

if (Parent != null)
if (Parent is not null)
{
((ShellSection)Parent).UpdateDisplayedPage();
}
}
}

internal void EvaluateDisconnect()
{
if(!_createdViaService)
return;

// If the user has set the IsVisible property on this shell content to false
bool disconnect = true;

if(Parent is ShellSection shellSection &&
shellSection.Parent is ShellItem shellItem &&
shellItem.Parent is Shell shell)
{
disconnect =
!this.IsVisible || // user has set the IsVisible property to false
(_contentCache is not null && !_contentCache.IsVisible) || // user has set IsVisible on the Page to false
shell.CurrentItem != shellItem || // user has navigated to a different TabBar or a different FlyoutItem
!shellSection.IsVisible || // user has set IsVisible on the ShellSection to false
this.Window is null; // user has set the main page to a different shell instance
}

if (!disconnect)
{
return;
}

if (_contentCache is not null)
{
RemoveLogicalChild(_contentCache);
_contentCache.Unloaded -= OnPageUnloaded;
}

_contentCache = null;
}

#pragma warning disable RS0016 // Add public types and members to the declared API
protected override void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
#pragma warning restore RS0016 // Add public types and members to the declared API
{
base.OnPropertyChanged(propertyName);

if (propertyName == WindowProperty.PropertyName)
{
if(_contentCache?.IsLoaded == true)
return;

EvaluateDisconnect();
}
}

void OnPageUnloaded(object sender, EventArgs e) => EvaluateDisconnect();

public static implicit operator ShellContent(TemplatedPage page)
{
if (page.Parent != null)
Expand All @@ -223,6 +297,7 @@ public static implicit operator ShellContent(TemplatedPage page)
static void OnContentChanged(BindableObject bindable, object oldValue, object newValue)
{
var shellContent = (ShellContent)bindable;
shellContent._createdViaService = false;
// This check is wrong but will work for testing
if (shellContent.ContentTemplate == null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using System.Diagnostics;
using Microsoft.Maui.Controls;

namespace Maui.Controls.Sample.Issues
{
[Issue(IssueTracker.None, 0, "Navigating Between Transient Shell States Recreates Pages")]
public class ShellTransientTests : Shell
{
List<ContentPage> _contentPages = new List<ContentPage>();
protected override void OnNavigated(ShellNavigatedEventArgs args)
{
base.OnNavigated(args);

if (_contentPages.Contains(this.CurrentPage))
{
(CurrentPage as ContentPage).Content = new VerticalStackLayout()
{
Children =
{
new Label { Text = "Test Failed I am not a new page", AutomationId = "Failure" }
}
};
}
else
{
(CurrentPage as ContentPage).Content = new VerticalStackLayout()
{
Children =
{
new Label { Text = "I am a new page", AutomationId = "Success" }
}
};
}

_contentPages.Add((ContentPage)this.CurrentPage);
}

public ShellTransientTests()
{
var shellContent1 = new ShellContent()
{
ContentTemplate = new DataTemplate(typeof(TransientPage))
};


var shellContent2 = new ShellContent()
{
ContentTemplate = new DataTemplate(typeof(TransientPage))
};

var shellContent3 = new ShellContent()
{
ContentTemplate = new DataTemplate(typeof(ContentPage))
};

Items.Add(new FlyoutItem()
{
Title = "Flyout Item 1",
Items =
{
shellContent1
}
});

Items.Add(new FlyoutItem()
{
Title = "Flyout Item 2",
Items =
{
shellContent2
}
});


Items.Add(new FlyoutItem()
{
Title = "Flyout Item 3",
Items =
{
shellContent3
}
});
}
}
}

0 comments on commit 9d05d89

Please sign in to comment.