Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When swapping out ShellItems clear services #23863

Merged
merged 5 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ protected virtual void LoadView(IShellContext shellContext)

void InitialLoad(GenericGlobalLayoutListener listener, AView view)
{
// This means the handler was disconnected before the flyout was loaded
if (_shellContext?.Shell is null)
{
listener.Invalidate();
sfl.LayoutChanging -= OnFlyoutViewLayoutChanging;
return;
}

OnFlyoutViewLayoutChanging();

if (_flyoutContentView == null || ggll == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ static Microsoft.Maui.Controls.Binding.Create<TSource, TProperty>(System.Func<TS
*REMOVED*~static Microsoft.Maui.Controls.WebView.ControlsWebViewMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.IWebView, Microsoft.Maui.Handlers.WebViewHandler>
*REMOVED*static Microsoft.Maui.Controls.FlyoutPage.ControlsFlyoutPageMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.IFlyoutView!, Microsoft.Maui.Handlers.FlyoutViewHandler!>!
*REMOVED*static Microsoft.Maui.Controls.Toolbar.ControlsToolbarMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.Controls.Toolbar!, Microsoft.Maui.Handlers.ToolbarHandler!>!
~override Microsoft.Maui.Controls.ShellContent.OnPropertyChanged(string propertyName = null) -> void
Microsoft.Maui.Controls.Embedding.EmbeddingExtensions
static Microsoft.Maui.Controls.Embedding.EmbeddingExtensions.CreateEmbeddedWindowContext(this Microsoft.Maui.Hosting.MauiApp! mauiApp, Android.App.Activity! platformWindow) -> Microsoft.Maui.IMauiContext!
static Microsoft.Maui.Controls.Embedding.EmbeddingExtensions.ToPlatformEmbedded(this Microsoft.Maui.IElement! element, Microsoft.Maui.IMauiContext! context) -> Android.Views.View!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ override Microsoft.Maui.Controls.SearchBar.IsEnabledCore.get -> bool
~Microsoft.Maui.Controls.WebView.UserAgent.set -> void
~override Microsoft.Maui.Controls.Region.Equals(object obj) -> bool
~override Microsoft.Maui.Controls.Shapes.Matrix.Equals(object obj) -> bool
~override Microsoft.Maui.Controls.ShellContent.OnPropertyChanged(string propertyName = null) -> void
~static Microsoft.Maui.Controls.Region.FromRectangles(System.Collections.Generic.IEnumerable<Microsoft.Maui.Graphics.Rect> rectangles) -> Microsoft.Maui.Controls.Region
~static readonly Microsoft.Maui.Controls.InputView.CursorPositionProperty -> Microsoft.Maui.Controls.BindableProperty
~static readonly Microsoft.Maui.Controls.InputView.FontAttributesProperty -> Microsoft.Maui.Controls.BindableProperty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ static Microsoft.Maui.Controls.Binding.Create<TSource, TProperty>(System.Func<TS
*REMOVED*~static Microsoft.Maui.Controls.WebView.ControlsWebViewMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.IWebView, Microsoft.Maui.Handlers.WebViewHandler>
*REMOVED*static Microsoft.Maui.Controls.FlyoutPage.ControlsFlyoutPageMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.IFlyoutView!, Microsoft.Maui.Handlers.FlyoutViewHandler!>!
*REMOVED*static Microsoft.Maui.Controls.Toolbar.ControlsToolbarMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.Controls.Toolbar!, Microsoft.Maui.Handlers.ToolbarHandler!>!
~override Microsoft.Maui.Controls.ShellContent.OnPropertyChanged(string propertyName = null) -> void
Microsoft.Maui.Controls.Embedding.EmbeddingExtensions
static Microsoft.Maui.Controls.Embedding.EmbeddingExtensions.CreateEmbeddedWindowContext(this Microsoft.Maui.Hosting.MauiApp! mauiApp, UIKit.UIWindow! platformWindow) -> Microsoft.Maui.IMauiContext!
static Microsoft.Maui.Controls.Embedding.EmbeddingExtensions.ToPlatformEmbedded(this Microsoft.Maui.IElement! element, Microsoft.Maui.IMauiContext! context) -> UIKit.UIView!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,4 @@ Microsoft.Maui.Controls.WebViewProcessTerminatedEventArgs
Microsoft.Maui.Controls.WebViewProcessTerminatedEventArgs.WebViewProcessTerminatedEventArgs() -> void
Microsoft.Maui.Controls.PlatformWebViewProcessTerminatedEventArgs
Microsoft.Maui.Controls.PlatformWebViewProcessTerminatedEventArgs.PlatformWebViewProcessTerminatedEventArgs() -> void
~override Microsoft.Maui.Controls.ShellContent.OnPropertyChanged(string propertyName = null) -> void
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ static Microsoft.Maui.Controls.Binding.Create<TSource, TProperty>(System.Func<TS
*REMOVED*~static Microsoft.Maui.Controls.WebView.ControlsWebViewMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.IWebView, Microsoft.Maui.Handlers.WebViewHandler>
*REMOVED*static Microsoft.Maui.Controls.FlyoutPage.ControlsFlyoutPageMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.IFlyoutView!, Microsoft.Maui.Handlers.FlyoutViewHandler!>!
*REMOVED*static Microsoft.Maui.Controls.Toolbar.ControlsToolbarMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.Controls.Toolbar!, Microsoft.Maui.Handlers.ToolbarHandler!>!
~override Microsoft.Maui.Controls.ShellContent.OnPropertyChanged(string propertyName = null) -> void
Microsoft.Maui.Controls.Embedding.EmbeddingExtensions
static Microsoft.Maui.Controls.Embedding.EmbeddingExtensions.CreateEmbeddedWindowContext(this Microsoft.Maui.Hosting.MauiApp! mauiApp, Microsoft.UI.Xaml.Window! platformWindow) -> Microsoft.Maui.IMauiContext!
static Microsoft.Maui.Controls.Embedding.EmbeddingExtensions.ToPlatformEmbedded(this Microsoft.Maui.IElement! element, Microsoft.Maui.IMauiContext! context) -> Microsoft.UI.Xaml.FrameworkElement!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,4 @@ static Microsoft.Maui.Controls.Binding.Create<TSource, TProperty>(System.Func<TS
*REMOVED*~static Microsoft.Maui.Controls.WebView.ControlsWebViewMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.IWebView, Microsoft.Maui.Handlers.WebViewHandler>
*REMOVED*static Microsoft.Maui.Controls.FlyoutPage.ControlsFlyoutPageMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.IFlyoutView!, Microsoft.Maui.Handlers.FlyoutViewHandler!>!
*REMOVED*static Microsoft.Maui.Controls.Toolbar.ControlsToolbarMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.Controls.Toolbar!, Microsoft.Maui.Handlers.ToolbarHandler!>!
~override Microsoft.Maui.Controls.ShellContent.OnPropertyChanged(string propertyName = null) -> void
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,4 @@ static Microsoft.Maui.Controls.Binding.Create<TSource, TProperty>(System.Func<TS
*REMOVED*~static Microsoft.Maui.Controls.WebView.ControlsWebViewMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.IWebView, Microsoft.Maui.Handlers.WebViewHandler>
*REMOVED*static Microsoft.Maui.Controls.FlyoutPage.ControlsFlyoutPageMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.IFlyoutView!, Microsoft.Maui.Handlers.FlyoutViewHandler!>!
*REMOVED*static Microsoft.Maui.Controls.Toolbar.ControlsToolbarMapper -> Microsoft.Maui.IPropertyMapper<Microsoft.Maui.Controls.Toolbar!, Microsoft.Maui.Handlers.ToolbarHandler!>!
~override Microsoft.Maui.Controls.ShellContent.OnPropertyChanged(string propertyName = null) -> void
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
82 changes: 77 additions & 5 deletions src/Controls/src/Core/Shell/ShellContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,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 +75,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 +117,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 +199,77 @@ 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)
{
_contentCache.Unloaded -= OnPageUnloaded;
RemoveLogicalChild(_contentCache);
}

_contentCache = null;
}

protected override void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
{
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 +294,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,139 @@
using System;
using System.Diagnostics;
using Microsoft.Maui.Controls;

namespace Maui.Controls.Sample.Issues
{
[Issue(IssueTracker.None, 0, "Validate Basic Service Lifetime Behavior On Shell")]
public class ShellTransientTests : Shell
{
static List<ContentPage> _contentPages = new List<ContentPage>();
protected override void OnNavigated(ShellNavigatedEventArgs args)
{
base.OnNavigated(args);
LoadCurrentPage();
}

void LoadCurrentPage()
{
var navigatetoTransientPage = new Button
{
Text = "Navigate to transient page",
AutomationId = "NavigateToTransientPage",
Command = new Command(() =>
{
((ContentPage)this.CurrentPage).Content = new Label(){ Text = "Navigating. If you tried to navigate to the same page type, you'll be stuck here."};
this.CurrentItem = Items[0];
})
};

var navigateToNotRegisteredPage = new Button
{
Text = "Navigate to Unregistered page",
AutomationId = "NavigateToUnregisteredPage",
Command = new Command(() =>
{
((ContentPage)this.CurrentPage).Content = new Label(){ Text = "Navigating. If you tried to navigate to the same page type, you'll be stuck here."};
this.CurrentItem = Items[1];
})
};

var navigateToScopedPage = new Button
{
Text = "Navigate to scoped page",
AutomationId = "NavigateToScopedPage",
Command = new Command(() =>
{
((ContentPage)this.CurrentPage).Content = new Label(){ Text = "Navigating. If you tried to navigate to the same page type, you'll be stuck here."};
this.CurrentItem = Items[2];
})
};

var navigateToNewShell = new Button
{
Text = "Navigate to New Shell",
AutomationId = "NavigateToNewShell",
Command = new Command(() =>
{
this.Window.Page = new ShellTransientTests();
})
};

if (_contentPages.Contains(this.CurrentPage))
{
(CurrentPage as ContentPage).Content = new VerticalStackLayout()
{
Children =
{
navigatetoTransientPage,
navigateToNotRegisteredPage,
navigateToScopedPage,
navigateToNewShell,
new Label { Text = "I am not a new page", AutomationId = "OldPage" }
}
};
}
else
{
(CurrentPage as ContentPage).Content = new VerticalStackLayout()
{
Children =
{
navigatetoTransientPage,
navigateToNotRegisteredPage,
navigateToScopedPage,
navigateToNewShell,
new Label { Text = "I am a new page", AutomationId = "NewPage" }
}
};
}

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

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

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

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

Items.Add(new FlyoutItem()
{
Title = "Transient Page",
Items =
{
shellContent1
}
});

Items.Add(new FlyoutItem()
{
Title = "Not Registered Page",
Items =
{
shellContent2
}
});

Items.Add(new FlyoutItem()
{
Title = "Scoped Page",
Items =
{
shellContent3
}
});
}
}
}
2 changes: 2 additions & 0 deletions src/Controls/tests/TestCases.HostApp/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public static MauiApp CreateMauiApp()
})
.Issue21109AddMappers();

appBuilder.Services.AddTransient<TransientPage>();
appBuilder.Services.AddScoped<ScopedPage>();
return appBuilder.Build();
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/Controls/tests/TestCases.HostApp/ScopedPage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

namespace Maui.Controls.Sample;

public class ScopedPage : ContentPage
{
static int i = 0;
public ScopedPage()
{
Index = i;
Content = new Label { Text = $"I'm a scoped page: {Index}" };
i++;
}

public int Index {get; private set;}
}
15 changes: 15 additions & 0 deletions src/Controls/tests/TestCases.HostApp/TransientPage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

namespace Maui.Controls.Sample;

public class TransientPage : ContentPage
{
static int i = 0;
public TransientPage()
{
Index = i;
Content = new Label { Text = $"I'm a transient page: {Index}" };
i++;
}

public int Index {get; private set;}
}
Loading
Loading