Skip to content

Commit

Permalink
fix(droid): workaround for ItemsControl's ObjectDisposedException
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiaoy312 committed May 9, 2024
1 parent 4b12c49 commit ae0ee5c
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Page x:Class="Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml.Controls.When_XBind_TargetDisposed"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<ItemsControl ItemsSource="{x:Bind ViewModel.Items}" />
</Page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Microsoft.UI.Xaml.Controls;

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml.Controls;

public sealed partial class When_XBind_TargetDisposed : Page
{
public When_XBind_TargetDisposed_VM ViewModel { get; }

public When_XBind_TargetDisposed()
{
ViewModel = new();
this.InitializeComponent();
}
//~When_XBind_TargetDisposed()
//{

Check warning on line 18 in src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_XBind_TargetDisposed.xaml.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_XBind_TargetDisposed.xaml.cs#L18

Remove this commented out code.
//}

//protected override void Dispose(bool disposing)
//{
// base.Dispose(disposing);
//}
}

public class When_XBind_TargetDisposed_VM
{
public ObservableCollection<string> Items { get; }

public When_XBind_TargetDisposed_VM()
{
Items = new(Enumerable.Range(0, 10).Select(x => $"Item {x}"));
}
}
61 changes: 59 additions & 2 deletions src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Given_xBind.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml.Controls;
using Private.Infrastructure;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.UI.Xaml.Controls;
using Private.Infrastructure;
using Uno.UI.RuntimeTests.Helpers;
using Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml.Controls;

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml;

Expand Down Expand Up @@ -39,4 +43,57 @@ public async Task When_xBind_With_Cast_Default_Namespace()

Assert.AreEqual("Hello", SUT.tb.Text);
}

[TestMethod]
public async Task When_XBind_TargetDisposed_Test()
{
// load the view with the x:Bind
var SUT = new When_XBind_TargetDisposed();
var vm = SUT.ViewModel;
var wrSUT = new WeakReference(SUT);
await UITestHelper.Load(SUT);

// make sure it is disposed
SUT = null; // we must not keep any hard-reference to it

Check warning on line 57 in src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Given_xBind.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Given_xBind.cs#L57

Remove this useless assignment to local variable 'SUT'.
await UITestHelper.Load(new Border { Width = 50, Height = 50 });
if (!await WaitUntilCollected())
{
Assert.Fail("Timed out on waiting the view to be disposed");
}

// trigger an INotifyCollectionChanged update via ObservableCollection
vm.Items.Clear();

// and, it should not throw:
/* System.ObjectDisposedException: Cannot access a disposed object. Object name: 'Microsoft.UI.Xaml.Controls.StackPanel'.
at Android.Views.ViewGroup.RemoveAllViews()
at Microsoft.UI.Xaml.Controls.UIElementCollection.ClearCore()
at Microsoft.UI.Xaml.Controls.UIElementCollection.Clear()
at Microsoft.UI.Xaml.Controls.ItemsControl.UpdateItems(NotifyCollectionChangedEventArgs args)
at Microsoft.UI.Xaml.Controls.ItemsControl.OnItemsSourceSingleCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args, Int32 section)
at Microsoft.UI.Xaml.Controls.ItemsControl.OnItemsVectorChanged(IObservableVector`1 sender, IVectorChangedEventArgs e)
at Uno.UI.Extensions.VectorChangedEventHandlerExtensions.TryRaise(ValueTuple`2 handlers, IObservableVector`1 owner, IVectorChangedEventArgs args)
at Microsoft.UI.Xaml.Controls.ItemCollection.OnItemsSourceCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
at Microsoft.UI.Xaml.Controls.ItemCollection.<>c__DisplayClass33_0.<ObserveCollectionChangedInner>g__handler|0(Object s, NotifyCollectionChangedEventArgs e)
at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionReset()
at System.Collections.ObjectModel.ObservableCollection`1.ClearItems()
at System.Collections.ObjectModel.Collection`1.Clear()
at Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml.Given_xBind.When_XBind_TargetDisposed()
*/

async Task<bool> WaitUntilCollected()
{
var sw = Stopwatch.StartNew();
while (sw.ElapsedMilliseconds <= 1000 && wrSUT.Target is When_XBind_TargetDisposed)
{
GC.Collect();
GC.WaitForPendingFinalizers();

await Task.Delay(100);
}

return wrSUT.Target is not When_XBind_TargetDisposed;
}
}
}
9 changes: 8 additions & 1 deletion src/Uno.UI/UI/Xaml/Controls/ItemsControl/ItemsControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,14 @@ protected virtual _ViewGroup ResolveInternalItemsPanel(_ViewGroup itemsPanel)

private protected virtual void UpdateItems(NotifyCollectionChangedEventArgs args)
{
if (ItemsPanelRoot == null || !ShouldItemsControlManageChildren)
if (ItemsPanelRoot == null
|| !ShouldItemsControlManageChildren
#if __ANDROID__
// workaround for INCC callback on disposed object
// see: Given_xBind.When_XBind_TargetDisposed_Test()
|| (Handle == nint.Zero || ItemsPanelRoot.Handle == nint.Zero)
#endif
)
{
return;
}
Expand Down

0 comments on commit ae0ee5c

Please sign in to comment.