diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 08371f46f87f..0dab7bf7fd78 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -21,7 +21,7 @@
]
},
"microsoft.dotnet.xharness.cli": {
- "version": "9.0.0-prerelease.24311.2",
+ "version": "9.0.0-prerelease.24312.3",
"commands": [
"xharness"
]
diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
index ba8454f62a00..da107f057221 100644
--- a/.github/ISSUE_TEMPLATE/bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -49,6 +49,7 @@ body:
- 9.0.0-preview.3.10457
- 9.0.0-preview.2.10293
- 9.0.0-preview.1.9973
+ - 8.0.60 SR6
- 8.0.40 SR5
- 8.0.21 SR4.1
- 8.0.20 SR4
@@ -111,6 +112,7 @@ body:
- 8.0.20 SR4
- 8.0.21 SR4.1
- 8.0.40 SR5
+ - 8.0.60 SR6
- 9.0.0-preview.1.9973
- 9.0.0-preview.2.10293
- 9.0.0-preview.3.10457
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 4930f5b8a0c4..45d0d17ffd51 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,7 +1,7 @@
{
"recommendations": [
- "ms-dotnettools.csharp",
- "ms-vscode.mono-debug",
- "visualstudioexptteam.vscodeintellicode",
+ "ms-dotnettools.vscodeintellicode-csharp",
+ "ms-dotnettools.dotnet-maui",
+ "github.copilot-chat"
]
}
\ No newline at end of file
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 95c10af5868c..1534a65f5854 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -135,17 +135,17 @@
https://github.com/dotnet/runtime
4a0a04cf3e8b8c2a3613270df7f838246e4597e8
-
+
https://github.com/dotnet/xharness
- 975b330d51119efc4884f7a323784662cbf74391
+ 6ce15319de72ab6d4c3b0f4c40f59300cffc5450
-
+
https://github.com/dotnet/xharness
- 975b330d51119efc4884f7a323784662cbf74391
+ 6ce15319de72ab6d4c3b0f4c40f59300cffc5450
-
+
https://github.com/dotnet/xharness
- 975b330d51119efc4884f7a323784662cbf74391
+ 6ce15319de72ab6d4c3b0f4c40f59300cffc5450
diff --git a/eng/Versions.props b/eng/Versions.props
index beb4d12083dd..78fb859fb8c8 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -62,7 +62,7 @@
9.0.0-preview.5.24273.1
$(MicrosoftNETWorkloadEmscriptenCurrentManifest90100TransportVersion)
- 1.5.240311000
+ 1.5.240607001
10.0.22621.756
1.2.0
@@ -108,9 +108,9 @@
<_HarfBuzzSharpVersion>7.3.0.2
<_SkiaSharpNativeAssetsVersion>0.0.0-commit.7af1d0840a381c0ce7ef2877454a88dbb2949686.1086
7.0.114
- 9.0.0-prerelease.24311.2
- 9.0.0-prerelease.24311.2
- 9.0.0-prerelease.24311.2
+ 9.0.0-prerelease.24312.3
+ 9.0.0-prerelease.24312.3
+ 9.0.0-prerelease.24312.3
0.9.2
1.0.0.16
1.3.0
diff --git a/src/Controls/src/Core/Cells/Cell.cs b/src/Controls/src/Core/Cells/Cell.cs
index 2da47d8120ec..eafc18ba5658 100644
--- a/src/Controls/src/Core/Cells/Cell.cs
+++ b/src/Controls/src/Core/Cells/Cell.cs
@@ -273,7 +273,7 @@ async void OnForceUpdateSizeRequested()
// don't run more than once per 16 milliseconds
await Task.Delay(TimeSpan.FromMilliseconds(16));
ForceUpdateSizeRequested?.Invoke(this, null);
- Handler.Invoke("ForceUpdateSizeRequested", null);
+ Handler?.Invoke("ForceUpdateSizeRequested", null);
_nextCallToForceUpdateSizeQueued = false;
}
@@ -307,8 +307,14 @@ void OnParentPropertyChanging(object sender, PropertyChangingEventArgs e)
internal Android.Views.View ConvertView { get; set; }
#elif IOS
internal UIKit.UITableViewCell ReusableCell { get; set; }
- internal UIKit.UITableView TableView { get; set; }
+ WeakReference _tableView;
+
+ internal UIKit.UITableView TableView
+ {
+ get => _tableView?.GetTargetOrDefault();
+ set => _tableView = value is null ? null : new(value);
+ }
#endif
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Android/FrameRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Android/FrameRenderer.cs
index 6cab4f0a2e94..bb64e53baf5b 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Android/FrameRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Android/FrameRenderer.cs
@@ -176,7 +176,7 @@ protected override void OnLayout(bool changed, int l, int t, int r, int b)
if (Element.Handler is IPlatformViewHandler pvh &&
- Element is IContentView cv)
+ Element is ICrossPlatformLayout cv)
{
pvh.LayoutVirtualView(l, t, r, b, cv.CrossPlatformArrange);
}
diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/Windows/ListViewRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/Windows/ListViewRenderer.cs
index 942adb97ce67..1b5c24b9fd15 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/ListView/Windows/ListViewRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/Windows/ListViewRenderer.cs
@@ -260,8 +260,6 @@ void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
ClearSizeEstimate();
ReloadData();
}
-
- Element.Dispatcher.DispatchIfRequired(() => List?.UpdateLayout());
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellRenderer.cs
index eab9e8276267..23d034109c1b 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellRenderer.cs
@@ -18,7 +18,7 @@ public class CellRenderer : ElementHandler, IRegisterable
public static CommandMapper CommandMapper =
new CommandMapper(ElementHandler.ElementCommandMapper);
- UITableView? _tableView;
+ WeakReference? _tableView;
private protected event PropertyChangedEventHandler? CellPropertyChanged;
@@ -37,7 +37,7 @@ protected override UITableViewCell CreatePlatformElement()
public virtual UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
- _tableView = tv;
+ _tableView = new(tv);
Performance.Start(out string reference);
var tvc = reusableCell as CellTableViewCell ?? new CellTableViewCell(UITableViewCellStyle.Default, item.GetType().FullName);
@@ -173,15 +173,17 @@ public override void Invoke(string command, object? args)
if (command == "ForceUpdateSizeRequested" &&
VirtualView is BindableObject bindableObject &&
- GetRealCell(bindableObject) is UITableViewCell ctv)
+ GetRealCell(bindableObject) is UITableViewCell ctv &&
+ _tableView is not null &&
+ _tableView.TryGetTarget(out var tableView))
{
- var index = _tableView?.IndexPathForCell(ctv);
+ var index = tableView.IndexPathForCell(ctv);
if (index == null && VirtualView is Cell c)
{
index = Controls.Compatibility.Platform.iOS.CellExtensions.GetIndexPath(c);
}
if (index != null)
- _tableView?.ReloadRows(new[] { index }, UITableViewRowAnimation.None);
+ tableView.ReloadRows(new[] { index }, UITableViewRowAnimation.None);
}
}
}
diff --git a/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs
index 7c6a684c1492..1d69ffefe8d3 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs
@@ -48,7 +48,8 @@ public class NavigationRenderer : UINavigationController, INavigationViewHandler
public static CommandMapper CommandMapper = new CommandMapper(ViewHandler.ViewCommandMapper);
ViewHandlerDelegator _viewHandlerWrapper;
bool _navigating = false;
- VisualElement _element;
+ WeakReference _element;
+ WeakReference _current;
bool _uiRequestedPop; // User tapped the back button or swiped to navigate back
MauiNavigationDelegate NavigationDelegate => Delegate as MauiNavigationDelegate;
@@ -60,14 +61,18 @@ public NavigationRenderer() : base(typeof(MauiControlsNavigationBar), null)
Delegate = new MauiNavigationDelegate(this);
}
- Page Current { get; set; }
+ Page Current
+ {
+ get => _current?.GetTargetOrDefault();
+ set => _current = value is null ? null : new(value);
+ }
IPageController PageController => Element as IPageController;
NavigationPage NavPage => Element as NavigationPage;
INavigationPageController NavPageController => NavPage;
- public VisualElement Element { get => _viewHandlerWrapper.Element ?? _element; }
+ public VisualElement Element { get => _viewHandlerWrapper.Element ?? _element?.GetTargetOrDefault(); }
public event EventHandler ElementChanged;
@@ -85,7 +90,7 @@ public UIView NativeView
public void SetElement(VisualElement element)
{
(this as IElementHandler).SetVirtualView(element);
- _element = element;
+ _element = element is null ? null : new(element);
}
public UIViewController ViewController
@@ -171,7 +176,8 @@ public override void ViewDidDisappear(bool animated)
public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
- if (Current == null)
+
+ if (Current is not Page current)
return;
UpdateToolBarVisible();
@@ -180,7 +186,7 @@ public override void ViewWillLayoutSubviews()
var toolbar = _secondaryToolbar;
//save the state of the Current page we are calculating, this will fire before Current is updated
- _hasNavigationBar = NavigationPage.GetHasNavigationBar(Current);
+ _hasNavigationBar = NavigationPage.GetHasNavigationBar(current);
// Use 0 if the NavBar is hidden or will be hidden
var toolbarY = NavigationBarHidden || NavigationBar.Translucent || !_hasNavigationBar ? 0 : navBarFrameBottom;
@@ -475,8 +481,8 @@ void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
}
else if (e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName)
{
- Current = NavPage?.CurrentPage;
- ValidateNavbarExists(Current);
+ var current = Current = NavPage?.CurrentPage;
+ ValidateNavbarExists(current);
}
else if (e.PropertyName == IsNavigationBarTranslucentProperty.PropertyName)
{
@@ -806,7 +812,7 @@ void UpdateBarTextColor()
}
// set Tint color (i. e. Back Button arrow and Text)
- var iconColor = Current != null ? NavigationPage.GetIconColor(Current) : null;
+ var iconColor = Current is Page current ? NavigationPage.GetIconColor(current) : null;
if (iconColor == null)
iconColor = barTextColor;
@@ -860,8 +866,8 @@ void UpdateToolBarVisible()
if (currentHidden != _secondaryToolbar.Hidden)
{
- if (Current?.Handler != null)
- Current.ToPlatform().InvalidateMeasure(Current);
+ if (Current is Page current && current.Handler is not null)
+ current.ToPlatform().InvalidateMeasure(current);
if (VisibleViewController is ParentingViewController pvc)
pvc.UpdateFrames();
@@ -1716,7 +1722,7 @@ void IElementHandler.SetMauiContext(IMauiContext mauiContext)
void IElementHandler.SetVirtualView(Maui.IElement view)
{
_viewHandlerWrapper.SetVirtualView(view, ElementChanged, false);
- _element = view as VisualElement;
+ _element = view is VisualElement v ? new(v) : null;
void ElementChanged(ElementChangedEventArgs e)
{
diff --git a/src/Controls/src/Core/Compatibility/Handlers/TabbedPage/iOS/TabbedRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/TabbedPage/iOS/TabbedRenderer.cs
index fe066c9e7116..54613aab56b1 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/TabbedPage/iOS/TabbedRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/TabbedPage/iOS/TabbedRenderer.cs
@@ -27,7 +27,7 @@ public class TabbedRenderer : UITabBarController, IPlatformViewHandler
bool? _defaultBarTranslucent;
IMauiContext _mauiContext;
UITabBarAppearance _tabBarAppearance;
- VisualElement _element;
+ WeakReference _element;
IMauiContext MauiContext => _mauiContext;
public static IPropertyMapper Mapper = new PropertyMapper(TabbedViewHandler.ViewMapper);
@@ -57,7 +57,7 @@ protected TabbedPage Tabbed
get { return (TabbedPage)Element; }
}
- public VisualElement Element => _viewHandlerWrapper.Element ?? _element;
+ public VisualElement Element => _viewHandlerWrapper.Element ?? _element?.GetTargetOrDefault();
public event EventHandler ElementChanged;
@@ -72,11 +72,14 @@ public UIView NativeView
public void SetElement(VisualElement element)
{
_viewHandlerWrapper.SetVirtualView(element, OnElementChanged, false);
- _element = element;
+ _element = element is null ? null : new(element);
FinishedCustomizingViewControllers += HandleFinishedCustomizingViewControllers;
- Tabbed.PropertyChanged += OnPropertyChanged;
- Tabbed.PagesChanged += OnPagesChanged;
+ if (element is TabbedPage tabbed)
+ {
+ tabbed.PropertyChanged += OnPropertyChanged;
+ tabbed.PagesChanged += OnPagesChanged;
+ }
OnPagesChanged(null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
@@ -134,8 +137,11 @@ protected override void Dispose(bool disposing)
Page?.SendDisappearing();
- Tabbed.PropertyChanged -= OnPropertyChanged;
- Tabbed.PagesChanged -= OnPagesChanged;
+ if (Tabbed is TabbedPage tabbed)
+ {
+ tabbed.PropertyChanged -= OnPropertyChanged;
+ tabbed.PagesChanged -= OnPagesChanged;
+ }
FinishedCustomizingViewControllers -= HandleFinishedCustomizingViewControllers;
}
@@ -188,8 +194,8 @@ void OnPagesChanged(object sender, NotifyCollectionChangedEventArgs e)
SetControllers();
UIViewController controller = null;
- if (Tabbed.CurrentPage != null)
- controller = GetViewController(Tabbed.CurrentPage);
+ if (Tabbed?.CurrentPage is Page currentPage)
+ controller = GetViewController(currentPage);
if (controller != null && controller != base.SelectedViewController)
base.SelectedViewController = controller;
@@ -202,7 +208,7 @@ void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(TabbedPage.CurrentPage))
{
- var current = Tabbed.CurrentPage;
+ var current = Tabbed?.CurrentPage;
if (current == null)
return;
@@ -244,15 +250,20 @@ public override UIViewController ChildViewControllerForStatusBarHidden()
void UpdateCurrentPagePreferredStatusBarUpdateAnimation()
{
- PageUIStatusBarAnimation animation = ((Page)Element).OnThisPlatform().PreferredStatusBarUpdateAnimation();
- Tabbed.CurrentPage.OnThisPlatform().SetPreferredStatusBarUpdateAnimation(animation);
+ if (Page is Page page)
+ {
+ PageUIStatusBarAnimation animation = page.OnThisPlatform().PreferredStatusBarUpdateAnimation();
+ Tabbed?.CurrentPage?.OnThisPlatform().SetPreferredStatusBarUpdateAnimation(animation);
+ }
}
void UpdatePrefersStatusBarHiddenOnPages()
{
+ if (Tabbed is not TabbedPage tabbed)
+ return;
for (var i = 0; i < ViewControllers.Length; i++)
{
- Tabbed.GetPageByIndex(i).OnThisPlatform().SetPrefersStatusBarHidden(Tabbed.OnThisPlatform().PrefersStatusBarHidden());
+ tabbed.GetPageByIndex(i).OnThisPlatform().SetPrefersStatusBarHidden(tabbed.OnThisPlatform().PrefersStatusBarHidden());
}
}
@@ -260,7 +271,7 @@ public override UIViewController ChildViewControllerForHomeIndicatorAutoHidden
{
get
{
- var current = Tabbed.CurrentPage;
+ var current = Tabbed?.CurrentPage;
if (current == null)
return null;
@@ -276,15 +287,19 @@ void UpdatePageSpecifics()
void Reset()
{
+ if (Tabbed is not TabbedPage tabbed)
+ return;
var i = 0;
- foreach (var page in Tabbed.Children)
+ foreach (var page in tabbed.Children)
SetupPage(page, i++);
}
void SetControllers()
{
+ if (Tabbed is not TabbedPage tabbed)
+ return;
var list = new List();
- var pages = Tabbed.InternalChildren;
+ var pages = tabbed.InternalChildren;
for (var i = 0; i < pages.Count; i++)
{
var child = pages[i];
@@ -315,10 +330,10 @@ void TeardownPage(Page page, int index)
void UpdateBarBackgroundColor()
{
- if (Tabbed == null || TabBar == null)
+ if (Tabbed is not TabbedPage tabbed || TabBar == null)
return;
- var barBackgroundColor = Tabbed.BarBackgroundColor;
+ var barBackgroundColor = tabbed.BarBackgroundColor;
var isDefaultColor = barBackgroundColor == null;
if (isDefaultColor && !_barBackgroundColorWasSet)
@@ -342,20 +357,20 @@ void UpdateBarBackgroundColor()
void UpdateBarBackground()
{
- if (Tabbed == null || TabBar == null)
+ if (Tabbed is not TabbedPage tabbed || TabBar == null)
return;
- var barBackground = Tabbed.BarBackground;
+ var barBackground = tabbed.BarBackground;
TabBar.UpdateBackground(barBackground);
}
void UpdateBarTextColor()
{
- if (Tabbed == null || TabBar == null || TabBar.Items == null)
+ if (Tabbed is not TabbedPage tabbed || TabBar == null || TabBar.Items == null)
return;
- var barTextColor = Tabbed.BarTextColor;
+ var barTextColor = tabbed.BarTextColor;
var isDefaultColor = barTextColor == null;
if (isDefaultColor && !_barTextColorWasSet)
@@ -395,11 +410,11 @@ void UpdateBarTextColor()
void UpdateBarTranslucent()
{
- if (Tabbed == null || TabBar == null || Element == null)
+ if (Tabbed == null || TabBar == null || Element is not VisualElement element)
return;
_defaultBarTranslucent = _defaultBarTranslucent ?? TabBar.Translucent;
- switch (TabbedPageConfiguration.GetTranslucencyMode(Element))
+ switch (TabbedPageConfiguration.GetTranslucencyMode(element))
{
case TranslucencyMode.Translucent:
TabBar.Translucent = true;
@@ -415,12 +430,14 @@ void UpdateBarTranslucent()
void UpdateChildrenOrderIndex(UIViewController[] viewControllers)
{
+ if (Tabbed is not TabbedPage tabbed)
+ return;
for (var i = 0; i < viewControllers.Length; i++)
{
var originalIndex = -1;
if (int.TryParse(viewControllers[i].TabBarItem.Tag.ToString(), out originalIndex))
{
- var page = (Page)Tabbed.InternalChildren[originalIndex];
+ var page = (Page)tabbed.InternalChildren[originalIndex];
TabbedPage.SetIndex(page, i);
}
}
@@ -428,9 +445,12 @@ void UpdateChildrenOrderIndex(UIViewController[] viewControllers)
void UpdateCurrentPage()
{
- var count = Tabbed.InternalChildren.Count;
- var index = (int)SelectedIndex;
- ((TabbedPage)Element).CurrentPage = index >= 0 && index < count ? Tabbed.GetPageByIndex(index) : null;
+ if (Tabbed is TabbedPage tabbed)
+ {
+ var count = tabbed.InternalChildren.Count;
+ var index = (int)SelectedIndex;
+ tabbed.CurrentPage = index >= 0 && index < count ? tabbed.GetPageByIndex(index) : null;
+ }
}
async void SetTabBarItem(IPlatformViewHandler renderer)
@@ -442,7 +462,7 @@ async void SetTabBarItem(IPlatformViewHandler renderer)
var icons = await GetIcon(page);
renderer.ViewController.TabBarItem = new UITabBarItem(page.Title, icons?.Item1, icons?.Item2)
{
- Tag = Tabbed.Children.IndexOf(page),
+ Tag = Tabbed?.Children.IndexOf(page) ?? -1,
AccessibilityIdentifier = page.AutomationId
};
icons?.Item1?.Dispose();
@@ -451,12 +471,12 @@ async void SetTabBarItem(IPlatformViewHandler renderer)
void UpdateSelectedTabColors()
{
- if (Tabbed == null || TabBar == null || TabBar.Items == null)
+ if (Tabbed is not TabbedPage tabbed || TabBar == null || TabBar.Items == null)
return;
- if (Tabbed.IsSet(TabbedPage.SelectedTabColorProperty) && Tabbed.SelectedTabColor != null)
+ if (tabbed.IsSet(TabbedPage.SelectedTabColorProperty) && tabbed.SelectedTabColor != null)
{
- TabBar.TintColor = Tabbed.SelectedTabColor.ToPlatform();
+ TabBar.TintColor = tabbed.SelectedTabColor.ToPlatform();
}
else
{
@@ -467,8 +487,8 @@ void UpdateSelectedTabColors()
UpdateiOS15TabBarAppearance();
else
{
- if (Tabbed.IsSet(TabbedPage.UnselectedTabColorProperty) && Tabbed.UnselectedTabColor != null)
- TabBar.UnselectedItemTintColor = Tabbed.UnselectedTabColor.ToPlatform();
+ if (tabbed.IsSet(TabbedPage.UnselectedTabColorProperty) && tabbed.UnselectedTabColor != null)
+ TabBar.UnselectedItemTintColor = tabbed.UnselectedTabColor.ToPlatform();
else
TabBar.UnselectedItemTintColor = UITabBar.Appearance.TintColor;
}
@@ -501,14 +521,16 @@ protected virtual Task> GetIcon(Page page)
[System.Runtime.Versioning.SupportedOSPlatform("tvos15.0")]
void UpdateiOS15TabBarAppearance()
{
+ if (Tabbed is not TabbedPage tabbed)
+ return;
TabBar.UpdateiOS15TabBarAppearance(
ref _tabBarAppearance,
_defaultBarColor,
_defaultBarTextColor,
- Tabbed.IsSet(TabbedPage.SelectedTabColorProperty) ? Tabbed.SelectedTabColor : null,
- Tabbed.IsSet(TabbedPage.UnselectedTabColorProperty) ? Tabbed.UnselectedTabColor : null,
- Tabbed.IsSet(TabbedPage.BarBackgroundColorProperty) ? Tabbed.BarBackgroundColor : null,
- Tabbed.IsSet(TabbedPage.BarTextColorProperty) ? Tabbed.BarTextColor : null,
+ tabbed.IsSet(TabbedPage.SelectedTabColorProperty) ? tabbed.SelectedTabColor : null,
+ tabbed.IsSet(TabbedPage.UnselectedTabColorProperty) ? tabbed.UnselectedTabColor : null,
+ tabbed.IsSet(TabbedPage.BarBackgroundColorProperty) ? tabbed.BarBackgroundColor : null,
+ tabbed.IsSet(TabbedPage.BarTextColorProperty) ? tabbed.BarTextColor : null,
null);
}
diff --git a/src/Controls/src/Core/Compatibility/Handlers/ViewHandlerDelegator.cs b/src/Controls/src/Core/Compatibility/Handlers/ViewHandlerDelegator.cs
index 7ed4921d5a51..ca93a919967a 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/ViewHandlerDelegator.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/ViewHandlerDelegator.cs
@@ -20,8 +20,14 @@ class ViewHandlerDelegator
internal IPropertyMapper _mapper;
internal readonly CommandMapper _commandMapper;
IPlatformViewHandler _viewHandler;
+#if IOS || MACCATALYST
+ TElement? _tempElement;
+ WeakReference? _element;
+ public TElement? Element => _tempElement ?? _element?.GetTargetOrDefault();
+#else
TElement? _element;
public TElement? Element => _element;
+#endif
bool _disposed;
public ViewHandlerDelegator(
@@ -48,11 +54,11 @@ public void Invoke(string command, object? args)
public void DisconnectHandler()
{
- if (_element == null)
+ if (Element is not TElement element)
return;
- if (_element.Handler == _viewHandler)
- _element.Handler = null;
+ if (element.Handler == _viewHandler)
+ element.Handler = null;
_element = null;
@@ -80,6 +86,12 @@ public void SetVirtualView(
{
#if WINDOWS
VisualElementRenderer.SetVirtualView(view, _viewHandler, onElementChanged, ref _element, ref _mapper, _defaultMapper, autoPackage);
+#elif IOS || MACCATALYST
+ // _tempElement is used here, because the Element property is accessed before SetVirtualView() returns
+ VisualElementRenderer.SetVirtualView(view, _viewHandler, onElementChanged, ref _tempElement, ref _mapper, _defaultMapper, autoPackage);
+ // We use _element as a WeakReference, and clear _tempElement
+ _element = _tempElement is null ? null : new(_tempElement);
+ _tempElement = null;
#else
VisualElementRenderer.SetVirtualView(view, _viewHandler, onElementChanged, ref _element, ref _mapper, _defaultMapper, autoPackage);
#endif
diff --git a/src/Controls/src/Core/Page/Page.cs b/src/Controls/src/Core/Page/Page.cs
index bc49235eccba..007b91ce6c84 100644
--- a/src/Controls/src/Core/Page/Page.cs
+++ b/src/Controls/src/Core/Page/Page.cs
@@ -282,7 +282,7 @@ public Task DisplayActionSheet(string title, string cancel, string destr
/// Displays a platform action sheet, allowing the application user to choose from several buttons.
///
/// Title of the displayed action sheet. Can be to hide the title.
- /// Text to be displayed in the 'Cancel' button. Can be null to hide the action.
+ /// Text to be displayed in the 'Cancel' button. Can be null to hide the cancel action.
/// Text to be displayed in the 'Destruct' button. Can be to hide the destructive option.
/// The flow direction to be used by the action sheet.
/// Text labels for additional buttons.
diff --git a/src/Controls/src/Core/Platform/AlertManager/AlertManager.iOS.cs b/src/Controls/src/Core/Platform/AlertManager/AlertManager.iOS.cs
index e4323b096bbf..14a8c19e4988 100644
--- a/src/Controls/src/Core/Platform/AlertManager/AlertManager.iOS.cs
+++ b/src/Controls/src/Core/Platform/AlertManager/AlertManager.iOS.cs
@@ -187,12 +187,15 @@ static void PresentPopUp(Page sender, Window virtualView, UIWindow platformView,
presentingWindow = senderPageWindow;
}
- if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad && arguments != null)
+ if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad &&
+ arguments is not null &&
+ alert.PopoverPresentationController is not null &&
+ platformView.RootViewController?.View is not null)
{
var topViewController = GetTopUIViewController(presentingWindow);
UIDevice.CurrentDevice.BeginGeneratingDeviceOrientationNotifications();
var observer = NSNotificationCenter.DefaultCenter.AddObserver(UIDevice.OrientationDidChangeNotification,
- n => { alert.PopoverPresentationController.SourceRect = topViewController.View.Bounds; });
+ n => alert.PopoverPresentationController.SourceRect = topViewController.View.Bounds);
arguments.Result.Task.ContinueWith(t =>
{
@@ -216,7 +219,7 @@ static void PresentPopUp(Page sender, Window virtualView, UIWindow platformView,
static UIViewController GetTopUIViewController(UIWindow platformWindow)
{
var topUIViewController = platformWindow.RootViewController;
- while (topUIViewController.PresentedViewController is not null)
+ while (topUIViewController?.PresentedViewController is not null)
{
topUIViewController = topUIViewController.PresentedViewController;
}
diff --git a/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Windows.cs b/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Windows.cs
index 75a511c14e25..ba6d8a98f69c 100644
--- a/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Windows.cs
+++ b/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Windows.cs
@@ -608,6 +608,11 @@ void OnTap(object sender, RoutedEventArgs e)
if (view == null)
return;
+ if (!view.IsEnabled)
+ {
+ return;
+ }
+
var tapPosition = e.GetPositionRelativeToPlatformElement(Control);
if (tapPosition == null)
diff --git a/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.iOS.cs b/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.iOS.cs
index 758342768947..128878f5e566 100644
--- a/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.iOS.cs
+++ b/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.iOS.cs
@@ -757,6 +757,11 @@ public bool ShouldReceiveTouch(UIGestureRecognizer recognizer, UITouch touch)
return false;
}
+ if (!virtualView.IsEnabled)
+ {
+ return false;
+ }
+
if (touch.View == platformView)
{
return true;
diff --git a/src/Controls/src/Core/SwipeView/SwipeView.Mapper.cs b/src/Controls/src/Core/SwipeView/SwipeView.Mapper.cs
new file mode 100644
index 000000000000..1b1f918451d6
--- /dev/null
+++ b/src/Controls/src/Core/SwipeView/SwipeView.Mapper.cs
@@ -0,0 +1,39 @@
+using System;
+using Microsoft.Maui.Controls.Compatibility;
+
+namespace Microsoft.Maui.Controls
+{
+ public partial class SwipeView
+ {
+ [Obsolete("Use SwipeViewHandler.Mapper instead.")]
+ internal static IPropertyMapper ControlsSwipeMapper =
+ new ControlsMapper(SwipeViewHandler.Mapper);
+
+ internal static new void RemapForControls()
+ {
+ // Adjusted the mapping to preserve SwipeView.Entry legacy behavior
+ SwipeViewHandler.Mapper.AppendToMapping(nameof(Background), MapBackground);
+ }
+
+ static void MapBackground(ISwipeViewHandler handler, SwipeView swipeView)
+ {
+ if (swipeView.Content is not null)
+ {
+ var contentBackgroundIsNull = Brush.IsNullOrEmpty(swipeView.Content.Background);
+ var contentBackgroundColorIsNull = swipeView.Content.BackgroundColor == null;
+
+ if (contentBackgroundIsNull && contentBackgroundColorIsNull)
+ {
+ if (!Brush.IsNullOrEmpty(swipeView.Background))
+ {
+ swipeView.Content.Background = swipeView.Background;
+ }
+ else if (swipeView.BackgroundColor != null)
+ {
+ swipeView.Content.BackgroundColor = swipeView.BackgroundColor;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Controls/src/Core/View/View.cs b/src/Controls/src/Core/View/View.cs
index 3d8d52d22de4..48a88634bf16 100644
--- a/src/Controls/src/Core/View/View.cs
+++ b/src/Controls/src/Core/View/View.cs
@@ -1,10 +1,10 @@
#nullable disable
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
-using Microsoft.Maui;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Graphics;
@@ -96,19 +96,20 @@ protected internal View()
_gestureManager = new GestureManager(this);
_gestureRecognizers.CollectionChanged += (sender, args) =>
{
- void AddItems(IEnumerable elements)
+ void AddItems(IList newItems)
{
- foreach (IElementDefinition item in elements)
+ foreach (IElementDefinition item in newItems)
{
- ValidateGesture(item as IGestureRecognizer);
+ var gestureRecognizer = item as IGestureRecognizer;
+ ValidateGesture(gestureRecognizer);
item.Parent = this;
- GestureController.CompositeGestureRecognizers.Add(item as IGestureRecognizer);
+ GestureController.CompositeGestureRecognizers.Add(gestureRecognizer);
}
}
- void RemoveItems(IEnumerable elements)
+ void RemoveItems(IList oldItems)
{
- foreach (IElementDefinition item in elements)
+ foreach (IElementDefinition item in oldItems)
{
item.Parent = null;
GestureController.CompositeGestureRecognizers.Remove(item as IGestureRecognizer);
@@ -118,41 +119,46 @@ void RemoveItems(IEnumerable elements)
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
- AddItems(args.NewItems.OfType());
+ AddItems(args.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
- RemoveItems(args.OldItems.OfType());
+ RemoveItems(args.OldItems);
break;
case NotifyCollectionChangedAction.Replace:
- AddItems(args.NewItems.OfType());
- RemoveItems(args.OldItems.OfType());
+ AddItems(args.NewItems);
+ RemoveItems(args.OldItems);
break;
case NotifyCollectionChangedAction.Reset:
- List remove = new List();
- List add = new List();
-
- foreach (IElementDefinition item in _gestureRecognizers.OfType())
+ foreach (IGestureRecognizer gestureRecognizer in _gestureRecognizers)
{
- if (!_gestureRecognizers.Contains((IGestureRecognizer)item))
- add.Add(item);
- item.Parent = this;
+ if (gestureRecognizer is IElementDefinition item)
+ {
+ item.Parent = this;
+ }
}
- foreach (IElementDefinition item in GestureController.CompositeGestureRecognizers.OfType())
- {
- if (item == _recognizerForPointerOverState)
- continue;
+ HashSet compositeGestureRecognizers = new(GestureController.CompositeGestureRecognizers);
- if (_gestureRecognizers.Contains((IGestureRecognizer)item))
- item.Parent = this;
- else
- remove.Add(item);
+ foreach (IGestureRecognizer gestureRecognizer in compositeGestureRecognizers)
+ {
+ if (gestureRecognizer is IElementDefinition item)
+ {
+ if (item == _recognizerForPointerOverState)
+ continue;
+
+ if (_gestureRecognizers.Contains(gestureRecognizer))
+ {
+ item.Parent = this;
+ }
+ else
+ {
+ item.Parent = null;
+ GestureController.CompositeGestureRecognizers.Remove(gestureRecognizer);
+ }
+ }
}
- AddItems(add);
- RemoveItems(remove);
-
break;
}
};
diff --git a/src/Controls/src/Core/VisualElement/VisualElement.Standard.cs b/src/Controls/src/Core/VisualElement/VisualElement.Standard.cs
index 06d69a098931..7cc73f2bcf5a 100644
--- a/src/Controls/src/Core/VisualElement/VisualElement.Standard.cs
+++ b/src/Controls/src/Core/VisualElement/VisualElement.Standard.cs
@@ -8,13 +8,13 @@ public partial class VisualElement
{
partial void HandlePlatformUnloadedLoaded()
{
- if (Window != null)
+ if (this.IsLoaded)
{
- SendLoaded();
+ SendLoaded(false);
}
else
{
- SendUnloaded();
+ SendUnloaded(false);
}
}
}
diff --git a/src/Controls/src/Core/VisualElement/VisualElement.cs b/src/Controls/src/Core/VisualElement/VisualElement.cs
index 950db64beaba..38f9eabf7fec 100644
--- a/src/Controls/src/Core/VisualElement/VisualElement.cs
+++ b/src/Controls/src/Core/VisualElement/VisualElement.cs
@@ -2146,19 +2146,19 @@ public event EventHandler? Loaded
{
add
{
- bool watchingLoaded = _watchingPlatformLoaded;
+ bool loadedAlreadyFired = _isLoadedFired;
+
_loaded += value;
UpdatePlatformUnloadedLoadedWiring(Window);
- // The point of this code, is to fire loaded if the element is already loaded.
- //
- // If this is the first time the user is subscribing to Loaded,
- // UpdatePlatformUnloadedLoadedWiring will take care of firing Loaded.
- // If we are already wired up to watch loaded, then we'll fire it off if we know this
- // view is in a state where it's been determined that it's accurate to fire
- // _isLoadedFired.
- if (_isLoadedFired && watchingLoaded)
+ // The first time UpdatePlatformUnloadedLoadedWiring is called it will handle
+ // invoking _loaded
+ // This is only to make sure that new subscribers get invoked when the element is already loaded
+ // and a previous subscriber has already invoked the UpdatePlatformUnloadedLoadedWiring path
+ if (loadedAlreadyFired && _isLoadedFired)
+ {
value?.Invoke(this, EventArgs.Empty);
+ }
}
remove
@@ -2212,7 +2212,9 @@ void SendLoaded(bool updateWiring)
// unloaded is still correctly being watched for.
if (updateWiring)
+ {
UpdatePlatformUnloadedLoadedWiring(Window);
+ }
}
void SendUnloaded() => SendUnloaded(true);
diff --git a/src/Controls/src/Xaml/Hosting/AppHostBuilderExtensions.cs b/src/Controls/src/Xaml/Hosting/AppHostBuilderExtensions.cs
index 5b68f826f76f..d7c8df8dbee6 100644
--- a/src/Controls/src/Xaml/Hosting/AppHostBuilderExtensions.cs
+++ b/src/Controls/src/Xaml/Hosting/AppHostBuilderExtensions.cs
@@ -241,6 +241,7 @@ internal static MauiAppBuilder RemapForControls(this MauiAppBuilder builder)
Window.RemapForControls();
Editor.RemapForControls();
Entry.RemapForControls();
+ SwipeView.RemapForControls();
Picker.RemapForControls();
SearchBar.RemapForControls();
TabbedPage.RemapForControls();
diff --git a/src/Controls/src/Xaml/SimplifyTypeExtensionVisitor.cs b/src/Controls/src/Xaml/SimplifyTypeExtensionVisitor.cs
index ba4f4ec2d476..22c1117bd8a9 100644
--- a/src/Controls/src/Xaml/SimplifyTypeExtensionVisitor.cs
+++ b/src/Controls/src/Xaml/SimplifyTypeExtensionVisitor.cs
@@ -55,16 +55,22 @@ static bool IsTargetTypePropertyOfMauiType(INode parentNode, XmlName propertyNam
static bool IsTypeExtension(ElementNode node, out ValueNode typeNameValueNode)
{
- XmlName typeNameXmlName = new("", "TypeName");
-
- if (node.XmlType.Name == nameof(TypeExtension)
- && node.XmlType.NamespaceUri == XamlParser.X2009Uri
- && node.Properties.ContainsKey(typeNameXmlName)
- && node.Properties[typeNameXmlName] is ValueNode valueNode
- && valueNode.Value is string)
+ if (node.XmlType.Name == nameof(TypeExtension) && node.XmlType.NamespaceUri == XamlParser.X2009Uri)
{
- typeNameValueNode = valueNode;
- return true;
+ XmlName typeNameXmlName = new("", "TypeName");
+ if (node.Properties.ContainsKey(typeNameXmlName)
+ && node.Properties[typeNameXmlName] is ValueNode { Value: string } propertyValueNode)
+ {
+ typeNameValueNode = propertyValueNode;
+ return true;
+ }
+
+ if (node.CollectionItems.Count == 1
+ && node.CollectionItems[0] is ValueNode { Value: string } collectionValueNode)
+ {
+ typeNameValueNode = collectionValueNode;
+ return true;
+ }
}
typeNameValueNode = null;
diff --git a/src/Controls/tests/Core.UnitTests/ListViewTests.cs b/src/Controls/tests/Core.UnitTests/ListViewTests.cs
index e1bd22ce486f..78fe5abcbb10 100644
--- a/src/Controls/tests/Core.UnitTests/ListViewTests.cs
+++ b/src/Controls/tests/Core.UnitTests/ListViewTests.cs
@@ -1649,5 +1649,25 @@ public void DoesNotRetainInRecycleMode()
Assert.False(ReferenceEquals(item1, item2));
}
+
+ [Fact]
+ public void ForceUpdateSizeCalledOnViewCellDoesntCrash()
+ {
+ var list = new ListView(){
+ HasUnevenRows = true
+ };
+
+ list.ItemTemplate = new DataTemplate(() =>
+ {
+ return new ViewCell { View = new Label() };
+ }
+ );
+
+ list.ItemsSource = new[] { "Hi" };
+
+ var element = (ViewCell)list.TemplatedItems[0];
+
+ element.ForceUpdateSize();
+ }
}
}
diff --git a/src/Controls/tests/Core.UnitTests/NavigationPageLifecycleTests.cs b/src/Controls/tests/Core.UnitTests/NavigationPageLifecycleTests.cs
index 299e15b8e9b1..c2065b8aad00 100644
--- a/src/Controls/tests/Core.UnitTests/NavigationPageLifecycleTests.cs
+++ b/src/Controls/tests/Core.UnitTests/NavigationPageLifecycleTests.cs
@@ -60,6 +60,7 @@ public async Task PushLifeCycle(bool useMaui)
[InlineData(true)]
public async Task PopLifeCycle(bool useMaui)
{
+ bool appearingShouldFireOnInitialPage = false;
ContentPage initialPage = new ContentPage();
ContentPage pushedPage = new ContentPage();
@@ -82,14 +83,18 @@ void OnInitialPageAppearing(object sender, EventArgs e)
_ = new TestWindow(nav);
await waitForFirstAppearing.Task;
- initialPage.Appearing += (sender, _)
- => rootPageFiresAppearingAfterPop = (ContentPage)sender;
+ initialPage.Appearing += (sender, _) =>
+ {
+ Assert.True(appearingShouldFireOnInitialPage);
+ rootPageFiresAppearingAfterPop = (ContentPage)sender;
+ };
pushedPage.Disappearing += (sender, _)
=> pageDisappeared = (ContentPage)sender;
await nav.PushAsync(pushedPage);
Assert.Null(rootPageFiresAppearingAfterPop);
+ appearingShouldFireOnInitialPage = true;
Assert.Null(pageDisappeared);
await nav.PopAsync();
diff --git a/src/Controls/tests/Core.UnitTests/PageLifeCycleTests.cs b/src/Controls/tests/Core.UnitTests/PageLifeCycleTests.cs
index 7211de857b1b..f0d728848605 100644
--- a/src/Controls/tests/Core.UnitTests/PageLifeCycleTests.cs
+++ b/src/Controls/tests/Core.UnitTests/PageLifeCycleTests.cs
@@ -287,6 +287,31 @@ public async Task LoadedUnLoadedEvents()
Assert.Equal(1, unLoadedCnt);
}
+ [Fact]
+ public async Task LoadedFiresOnSecondSubscription()
+ {
+ var previousPage = new LCPage();
+ var lcPage = new LCPage();
+ var navigationPage =
+ new TestNavigationPage(true, previousPage)
+ .AddToTestWindow();
+
+ await navigationPage.PushAsync(lcPage);
+
+ int loadedCnt = 0;
+ lcPage.Loaded += OnLoaded;
+ Assert.Equal(1, loadedCnt);
+
+ lcPage.Loaded -= OnLoaded;
+ lcPage.Loaded += OnLoaded;
+ Assert.Equal(2, loadedCnt);
+
+ void OnLoaded(object sender, System.EventArgs e)
+ {
+ loadedCnt++;
+ }
+ }
+
[Fact]
public async Task LoadedFiresOnInitialSubscription()
{
@@ -299,6 +324,7 @@ public async Task LoadedFiresOnInitialSubscription()
await navigationPage.PushAsync(lcPage);
int loadedCnt = 0;
+ int secondLoadedSubscriberCnt = 0;
int unLoadedCnt = 0;
Assert.True(lcPage.IsLoaded);
@@ -309,21 +335,25 @@ public async Task LoadedFiresOnInitialSubscription()
loadedCnt++;
};
- // Subscribing to loaded should fire the loaded
- // event if the page is already loaded
+ Assert.Equal(1, loadedCnt);
+
+ // Subscribing to loaded a second time
+ // Should fire the event on the new subsciber;
lcPage.Loaded += (_, _) =>
{
- loadedCnt++;
+ secondLoadedSubscriberCnt++;
};
lcPage.Unloaded += (_, _) => unLoadedCnt++;
- Assert.Equal(2, loadedCnt);
+ Assert.Equal(1, loadedCnt);
+ Assert.Equal(1, secondLoadedSubscriberCnt);
Assert.Equal(0, unLoadedCnt);
await navigationPage.PopAsync();
- Assert.Equal(2, loadedCnt);
+ Assert.Equal(1, loadedCnt);
+ Assert.Equal(1, secondLoadedSubscriberCnt);
Assert.Equal(1, unLoadedCnt);
}
diff --git a/src/Controls/tests/CustomAttributes/Test.cs b/src/Controls/tests/CustomAttributes/Test.cs
index 21bb6c59f57d..1eed63091e90 100644
--- a/src/Controls/tests/CustomAttributes/Test.cs
+++ b/src/Controls/tests/CustomAttributes/Test.cs
@@ -393,6 +393,7 @@ public enum Image
{
Source,
Source_FontImageSource,
+ IsAnimationPlaying,
Aspect,
IsOpaque,
IsLoading,
@@ -743,6 +744,16 @@ public enum InputTransparency
CascadeTransLayoutOverlayWithButton,
}
+ public enum Alerts
+ {
+ AlertCancel,
+ AlertAcceptCancelClickAccept,
+ AlertAcceptCancelClickCancel,
+ ActionSheetClickItem,
+ ActionSheetClickCancel,
+ ActionSheetClickDestroy,
+ }
+
public static class InputTransparencyMatrix
{
// this is both for color diff and cols
diff --git a/src/Controls/tests/DeviceTests/Elements/ListView/ListViewTests.Windows.cs b/src/Controls/tests/DeviceTests/Elements/ListView/ListViewTests.Windows.cs
index 44ad92d6f3e0..79e673dd5da6 100644
--- a/src/Controls/tests/DeviceTests/Elements/ListView/ListViewTests.Windows.cs
+++ b/src/Controls/tests/DeviceTests/Elements/ListView/ListViewTests.Windows.cs
@@ -1,9 +1,16 @@
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Maui.Controls;
+using Microsoft.Maui.Controls.Handlers.Compatibility;
+using Microsoft.Maui.Controls.Platform;
+using Microsoft.Maui.Handlers;
+using Microsoft.Maui.Platform;
+using Microsoft.UI.Xaml;
+using Xunit;
namespace Microsoft.Maui.DeviceTests
{
@@ -14,5 +21,38 @@ void ValidatePlatformCells(ListView listView)
{
}
+
+ [Fact]
+ public async Task InsertItemWorks()
+ {
+ SetupBuilder();
+
+ var data = new ObservableCollection();
+ var listView = new ListView()
+ {
+ ItemsSource = data
+ };
+
+ var layout = new VerticalStackLayout()
+ {
+ listView
+ };
+
+ await CreateHandlerAndAddToWindow(layout, async (handler) =>
+ {
+ await Task.Delay(100);
+
+ data.Add("Item 1");
+ data.Add("Item 3");
+ data.Insert(1, "Item 2");
+
+ await Task.Delay(200);
+
+ var actualListView = listView.ToPlatform() as ListViewRenderer;
+ var textChildren = actualListView.GetChildren();
+
+ Assert.Contains(textChildren, (x) => x.Text == "Item 3");
+ });
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.cs b/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.cs
index ec1bfac0cd6e..43bccb1d2ad7 100644
--- a/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.cs
+++ b/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.cs
@@ -311,8 +311,7 @@ await CreateHandlerAndAddToWindow(new Window(navPage), async
public async Task DoesNotLeak()
{
SetupBuilder();
- WeakReference pageReference = null;
- WeakReference handlerReference = null;
+ var references = new List();
{
var navPage = new NavigationPage(new ContentPage());
@@ -320,15 +319,17 @@ public async Task DoesNotLeak()
await CreateHandlerAndAddToWindow(window, (handler) =>
{
- pageReference = new WeakReference(navPage);
- handlerReference = new WeakReference(handler);
+ references.Add(new(navPage));
+ references.Add(new(navPage.Handler));
+ references.Add(new(navPage.Handler.PlatformView));
+ references.Add(new(handler));
// Just replace the page with a new one
window.Page = new ContentPage();
});
}
- await AssertionExtensions.WaitForGC(pageReference, handlerReference);
+ await AssertionExtensions.WaitForGC(references.ToArray());
}
[Fact(DisplayName = "Child Pages Do Not Leak")]
diff --git a/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs b/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs
index ee9dedbe9541..a5120677c4f3 100644
--- a/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs
+++ b/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs
@@ -8,6 +8,7 @@
using Microsoft.Maui.Controls.Handlers.Items;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Hosting;
+using Microsoft.Maui.Platform;
using Xunit;
namespace Microsoft.Maui.DeviceTests
@@ -22,167 +23,29 @@ void SetupBuilder()
builder.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler();
+ handlers.AddHandler | | |