Skip to content

Commit

Permalink
Add workaround so ItemsRepeater doesn't leak the result of DataTempla…
Browse files Browse the repository at this point in the history
  • Loading branch information
Kinnara committed Aug 16, 2020
1 parent ba20188 commit 31b4427
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private void UnListenToCollectionChanges()
{
if (m_vector is INotifyCollectionChanged incc)
{
incc.CollectionChanged -= OnCollectionChanged;
CollectionChangedEventManager.RemoveHandler(incc, OnCollectionChanged);
}
}

Expand All @@ -122,7 +122,7 @@ private void ListenToCollectionChanges()
Debug.Assert(m_vector != null);
if (m_vector is INotifyCollectionChanged incc)
{
incc.CollectionChanged += OnCollectionChanged;
CollectionChangedEventManager.AddHandler(incc, OnCollectionChanged);
}
}

Expand Down
56 changes: 55 additions & 1 deletion test/ModernWpfTestApp/ApiTests/RepeaterTests/RepeaterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public void VerifyCurrentAnchor()
@"<controls:ItemsRepeaterScrollHost Width='400' Height='600'
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:controls='using:ModernWpf.Controls'>
xmlns:controls='http://schemas.modernwpf.com/2019'>
<controls:ItemsRepeaterScrollHost.Resources>
<DataTemplate x:Key='ItemTemplate' >
<TextBlock Text='{Binding}' Height='50'/>
Expand Down Expand Up @@ -615,5 +615,59 @@ public void VerifyUIElementsInItemsSource()
}
});
}


[TestMethod]
public void VerifyRepeaterDoesNotLeakItemContainers()
{
ObservableCollection<int> items = new ObservableCollection<int>();
for(int i=0;i<10;i++)
{
items.Add(i);
}

ItemsRepeater repeater = null;

RunOnUIThread.Execute(() =>
{
var template = (DataTemplate)XamlReader.Parse("<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' "
+ "xmlns:local='clr-namespace:MUXControlsTestApp.Samples;assembly=MUXControlsTestApp'>"
+ "<local:DisposableUserControl Number='{Binding}'/>"
+ "</DataTemplate>");
Verify.IsNotNull(template);
Verify.AreEqual(0, MUXControlsTestApp.Samples.DisposableUserControl.OpenItems, "Verify we start with 0 DisposableUserControl");
repeater = new ItemsRepeater() {
ItemsSource = items,
ItemTemplate = template,
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left
};
Content = repeater;
});

IdleSynchronizer.Wait();

RunOnUIThread.Execute(() =>
{
Verify.IsGreaterThanOrEqual(MUXControlsTestApp.Samples.DisposableUserControl.OpenItems, 10, "Verify we created at least 10 DisposableUserControl");
// Clear out the repeater and make sure everything gets cleaned up.
Content = null;
repeater = null;
});

IdleSynchronizer.Wait();

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Verify.AreEqual(0, MUXControlsTestApp.Samples.DisposableUserControl.OpenItems, "Verify we cleaned up all the DisposableUserControl that were created");
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<UserControl
x:Class="MUXControlsTestApp.Samples.DisposableUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">

<Grid>
<TextBlock Text="{x:Bind Number}"/>
</Grid>
</UserControl>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Threading;
using System.Windows;
using System.Windows.Controls;

// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236

namespace MUXControlsTestApp.Samples
{
public sealed partial class DisposableUserControl : UserControl
{

public int Number
{
get { return (int)GetValue(NumberProperty); }
set { SetValue(NumberProperty, value); }
}

public static readonly DependencyProperty NumberProperty =
DependencyProperty.Register("Number", typeof(int), typeof(DisposableUserControl), new PropertyMetadata(-1));


public static int OpenItems { get { return _counter; } }
private static int _counter = 0;

public DisposableUserControl()
{
Interlocked.Increment(ref _counter);
this.InitializeComponent();
}

~DisposableUserControl()
{
Interlocked.Decrement(ref _counter);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<StackPanel>
<TextBlock>DataTemplate Sample:</TextBlock>
Expand Down Expand Up @@ -98,7 +99,7 @@
Width="150">
<ScrollViewer>
<controls:ItemsRepeater
x:Name="repeater3"
x:Name="repeater4"
ItemsSource="{Binding Data}"
Layout="{StaticResource stackLayout}">
<controls:ItemsRepeater.ItemTemplate>
Expand All @@ -123,5 +124,21 @@
</ScrollViewer>
</controls:ItemsRepeaterScrollHost>
</StackPanel>
<StackPanel Grid.Column="4">
<TextBlock>DataTemplate clear count checking:</TextBlock>
<controls:ItemsRepeaterScrollHost Height="400" Width="150">
<ScrollViewer>
<controls:ItemsRepeater x:Name="repeater3"
ItemsSource="{x:Bind Numbers}"
Layout="{StaticResource stackLayout}">
<controls:ItemsRepeater.ItemTemplate>
<DataTemplate>
<local:DisposableUserControl Number="{Bind number, Mode=OneTime}"/>
</DataTemplate>
</controls:ItemsRepeater.ItemTemplate>
</controls:ItemsRepeater>
</ScrollViewer>
</controls:ItemsRepeaterScrollHost>
</StackPanel>
</Grid>
</controls:Page>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using ModernWpf.Controls;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
Expand All @@ -12,9 +13,17 @@ namespace MUXControlsTestApp.Samples
public partial class ItemTemplateDemo
{
public List<int> Data { get; set; }
public List<MyData> Numbers { get; } = new List<MyData>();

public ItemTemplateDemo()
{
Data = Enumerable.Range(0, 1000).ToList();

for(int i=0;i<10;i++)
{
Numbers.Add(new MyData(i));
}

DataContext = this;
InitializeComponent();
}
Expand All @@ -25,6 +34,16 @@ private void OnSelectTemplateKey(RecyclingElementFactory sender, SelectTemplateE
}
}

public class MyData
{
public int number;

public MyData(int number)
{
this.number = number;
}
}

public class MySelector : DataTemplateSelector
{
public DataTemplate TemplateOdd { get; set; }
Expand Down
21 changes: 14 additions & 7 deletions test/ModernWpfTestApp/Utilities/RunOnUIThread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,26 @@ public static void Execute(Dispatcher dispatcher, Action action)
}
}

public async Task WaitForTick()
public static void WaitForTick()
{
var renderingEventFired = new TaskCompletionSource<object>();
var renderingEvent = new ManualResetEvent(false);

EventHandler renderingCallback = (sender, arg) =>
EventHandler renderingHandler = (object sender, EventArgs args) =>
{
renderingEventFired.TrySetResult(null);
renderingEvent.Set();
};
CompositionTarget.Rendering += renderingCallback;

await renderingEventFired.Task;
RunOnUIThread.Execute(() =>
{
CompositionTarget.Rendering += renderingHandler;
});

renderingEvent.WaitOne();

CompositionTarget.Rendering -= renderingCallback;
RunOnUIThread.Execute(() =>
{
CompositionTarget.Rendering -= renderingHandler;
});
}

}
Expand Down
2 changes: 1 addition & 1 deletion test/TestAppUtils/BindExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public BindExtension(string path)
public PropertyPath Path { get; set; }

[DefaultValue(BindingMode.OneTime)]
public BindingMode Mode { get; set; } = BindingMode.OneWay;
public BindingMode Mode { get; set; } = BindingMode.OneTime;

[DefaultValue(null)]
public IValueConverter Converter { get; set; }
Expand Down

0 comments on commit 31b4427

Please sign in to comment.