From 173bdc6fdc8d6f91e6e791beabbcd9eabbdca6f8 Mon Sep 17 00:00:00 2001
From: Brian Lagunas <835562+brianlagunas@users.noreply.github.com>
Date: Fri, 27 Mar 2026 22:04:22 -0600
Subject: [PATCH 1/4] check to see if dialog page is attached to window
---
src/Maui/Prism.Maui/Dialogs/DialogService.cs | 23 ++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/src/Maui/Prism.Maui/Dialogs/DialogService.cs b/src/Maui/Prism.Maui/Dialogs/DialogService.cs
index d36a6d0f4..6d97bd48b 100644
--- a/src/Maui/Prism.Maui/Dialogs/DialogService.cs
+++ b/src/Maui/Prism.Maui/Dialogs/DialogService.cs
@@ -1,4 +1,5 @@
using Prism.Common;
+using Prism.Navigation;
#nullable enable
namespace Prism.Dialogs;
@@ -9,18 +10,36 @@ namespace Prism.Dialogs;
public sealed class DialogService : DialogServiceBase
{
private readonly IPageAccessor _pageAccessor;
+ private readonly IWindowManager _windowManager;
///
/// Creates a new instance of the for Maui Applications
///
/// The used to determine where in the Navigation Stack we need to process the Dialog.
+ /// The used to resolve the current page when the scoped page is no longer attached.
/// Throws when any constructor arguments are null.
- public DialogService(IPageAccessor pageAccessor)
+ public DialogService(IPageAccessor pageAccessor, IWindowManager windowManager)
{
ArgumentNullException.ThrowIfNull(pageAccessor);
+ ArgumentNullException.ThrowIfNull(windowManager);
_pageAccessor = pageAccessor;
+ _windowManager = windowManager;
}
///
- protected override Page? GetCurrentPage() => _pageAccessor.Page;
+ protected override Page? GetCurrentPage()
+ {
+ var page = _pageAccessor.Page;
+
+ // If the scoped page is still connected to a window, use it
+ if (page?.GetParentWindow() is not null)
+ return page;
+
+ // Fallback: the scoped page was detached (e.g. after absolute navigation).
+ // Resolve the current page from the active window instead.
+ if (_windowManager.Current is PrismWindow prismWindow)
+ return prismWindow.CurrentPage;
+
+ return page;
+ }
}
From 5fc17f4230b62ddcb79b54cd5d4c980e56e9bbb4 Mon Sep 17 00:00:00 2001
From: Brian Lagunas <835562+brianlagunas@users.noreply.github.com>
Date: Fri, 27 Mar 2026 22:18:13 -0600
Subject: [PATCH 2/4] fix race condition in dialog service navigation state
updates
Ensures that PageNavigationSource and IsActive state updates are deferred until the dialog has been pushed by using ContinueWith on the asynchronous ConfigureLayout call. This prevents state updates from executing before the modal is actually displayed.
---
.../Prism.Maui/Dialogs/DialogServiceBase.cs | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/src/Maui/Prism.Maui/Dialogs/DialogServiceBase.cs b/src/Maui/Prism.Maui/Dialogs/DialogServiceBase.cs
index 940520836..dbe81e8a8 100644
--- a/src/Maui/Prism.Maui/Dialogs/DialogServiceBase.cs
+++ b/src/Maui/Prism.Maui/Dialogs/DialogServiceBase.cs
@@ -97,12 +97,23 @@ async Task DialogAware_RequestClose(IDialogResult outResult)
var dismissCommand = new DelegateCommand(() => dialogAware.RequestClose.Invoke(), dialogAware.CanCloseDialog);
+ // ConfigureLayout is async (it awaits DoPush internally). Since ShowDialog is void,
+ // we use ContinueWith to defer post-push work until the modal is actually displayed.
+ // Without this, NavigationSource resets and IsActive updates race ahead of the push.
PageNavigationService.NavigationSource = PageNavigationSource.DialogService;
- dialogModal.ConfigureLayout(currentPage, view, closeOnBackgroundTapped, dismissCommand, parameters);
- PageNavigationService.NavigationSource = PageNavigationSource.Device;
+ dialogModal.ConfigureLayout(currentPage, view, closeOnBackgroundTapped, dismissCommand, parameters)
+ .ContinueWith(t =>
+ {
+ if (t.IsFaulted)
+ {
+ callback.Invoke(t.Exception?.InnerException ?? t.Exception);
+ return;
+ }
- MvvmHelpers.InvokeViewAndViewModelAction(currentPage, aa => aa.IsActive = false);
- MvvmHelpers.InvokeViewAndViewModelAction(view, aa => aa.IsActive = true);
+ PageNavigationService.NavigationSource = PageNavigationSource.Device;
+ MvvmHelpers.InvokeViewAndViewModelAction(currentPage, aa => aa.IsActive = false);
+ MvvmHelpers.InvokeViewAndViewModelAction(view, aa => aa.IsActive = true);
+ }, TaskScheduler.FromCurrentSynchronizationContext());
}
catch (Exception ex)
{
From 3273515dd18704df5cc4b64a816a6ec8f4c65daa Mon Sep 17 00:00:00 2001
From: Brian Lagunas <835562+brianlagunas@users.noreply.github.com>
Date: Fri, 27 Mar 2026 22:19:26 -0600
Subject: [PATCH 3/4] fixed window deactivation state
---
src/Maui/Prism.Maui/Navigation/PrismWindow.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Maui/Prism.Maui/Navigation/PrismWindow.cs b/src/Maui/Prism.Maui/Navigation/PrismWindow.cs
index bd0debf92..2ad4dcf2b 100644
--- a/src/Maui/Prism.Maui/Navigation/PrismWindow.cs
+++ b/src/Maui/Prism.Maui/Navigation/PrismWindow.cs
@@ -105,7 +105,7 @@ protected override void OnActivated()
protected override void OnDeactivated()
{
IsActive = false;
- MvvmHelpers.InvokeViewAndViewModelAction(CurrentPage, x => x.IsActive = true);
+ MvvmHelpers.InvokeViewAndViewModelAction(CurrentPage, x => x.IsActive = false);
}
///
From 2775770051944e1d85ab7519fd906f9d7a69bdd0 Mon Sep 17 00:00:00 2001
From: Brian Lagunas <835562+brianlagunas@users.noreply.github.com>
Date: Fri, 27 Mar 2026 22:27:52 -0600
Subject: [PATCH 4/4] fix dialog mask layout and tap gesture reliability
Replaces the root AbsoluteLayout in DialogContainerPage with a Grid. This ensures the background mask fills the screen naturally without needing proportional sizing or explicit WidthRequest/HeightRequest bindings, which were causing tap gestures to fail on some MAUI platforms.
---
.../Prism.Maui/Dialogs/DialogContainerPage.cs | 39 ++++++++-----------
1 file changed, 17 insertions(+), 22 deletions(-)
diff --git a/src/Maui/Prism.Maui/Dialogs/DialogContainerPage.cs b/src/Maui/Prism.Maui/Dialogs/DialogContainerPage.cs
index 42f40ab98..ad3cd0de2 100644
--- a/src/Maui/Prism.Maui/Dialogs/DialogContainerPage.cs
+++ b/src/Maui/Prism.Maui/Dialogs/DialogContainerPage.cs
@@ -89,7 +89,12 @@ public virtual async Task DoPop(Page currentPage)
/// The content layout for the dialog container page.
protected virtual View GetContentLayout(Page currentPage, View dialogView, bool hideOnBackgroundTapped, ICommand dismissCommand, IDialogParameters parameters)
{
- var overlay = new AbsoluteLayout();
+ // Use a Grid so the mask fills the page naturally without needing
+ // AbsoluteLayout proportional sizing, which conflicted with the
+ // WidthRequest/HeightRequest bindings and caused tap gestures on
+ // the mask to not fire reliably across MAUI platforms.
+ var overlay = new Grid();
+
var popupContainer = new DialogContainerView
{
IsPopupContent = true,
@@ -118,20 +123,21 @@ protected virtual View GetContentLayout(Page currentPage, View dialogView, bool
source: this));
}
- AbsoluteLayout.SetLayoutFlags(popupContainer, AbsoluteLayoutFlags.PositionProportional);
- var popupBounds = DialogLayout.GetLayoutBounds(dialogView);
- AbsoluteLayout.SetLayoutBounds(popupContainer, popupBounds);
-
- var useMask = DialogLayout.GetUseMask(popupContainer.Content) ?? true;
+ var useMask = DialogLayout.GetUseMask(dialogView) ?? true;
if (useMask)
{
var mask = GetMask(currentPage, dialogView, hideOnBackgroundTapped, dismissCommand);
- AbsoluteLayout.SetLayoutFlags(mask, AbsoluteLayoutFlags.All);
- AbsoluteLayout.SetLayoutBounds(mask, new Rect(0, 0, 1, 1));
overlay.Children.Add(mask);
}
- overlay.Children.Add(popupContainer);
+ // Keep an AbsoluteLayout for the popup so LayoutBounds positioning still works
+ var popupArea = new AbsoluteLayout();
+ AbsoluteLayout.SetLayoutFlags(popupContainer, AbsoluteLayoutFlags.PositionProportional);
+ var popupBounds = DialogLayout.GetLayoutBounds(dialogView);
+ AbsoluteLayout.SetLayoutBounds(popupContainer, popupBounds);
+ popupArea.Children.Add(popupContainer);
+
+ overlay.Children.Add(popupArea);
return overlay;
}
@@ -146,7 +152,6 @@ protected virtual View GetContentLayout(Page currentPage, View dialogView, bool
private View GetMask(Page currentPage, View dialogView, bool hideOnBackgroundTapped, ICommand dismissCommand)
{
View mask = DialogLayout.GetMask(dialogView);
- var reference = currentPage.GetParentWindow().Page;
if (mask is null)
{
Style overlayStyle = GetOverlayStyle(dialogView, currentPage);
@@ -154,21 +159,11 @@ private View GetMask(Page currentPage, View dialogView, bool hideOnBackgroundTap
mask = new BoxView
{
Style = overlayStyle,
- //HeightRequest = reference.Height,
- //WidthRequest = reference.Width
};
}
- mask.SetBinding(WidthRequestProperty, new Binding
- {
- Path = nameof(Width),
- Source = reference
- });
- mask.SetBinding(HeightRequestProperty, new Binding
- {
- Path = nameof(Height),
- Source = reference
- });
+ // No explicit WidthRequest/HeightRequest needed — the Grid parent
+ // sizes the mask to fill the page automatically.
if (hideOnBackgroundTapped)
{