mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 18:26:39 +02:00
CmdPal: Clean up ListItemViewModels when we no longer need them (#41169)
_We already fixed one leak, yes, but what about second leak?_ We already clean up `ListItemViewModel`s for a page when the page is navigated away from. However, if the page updates it's items, we would never actually `Cleanup` the old items. We'd just lose them, and never unregister their event handlers. The objects would just leak forever. This builds on the work in #41166, to do two things: * Cleanup items that were removed from our list, when we actually update `Items`. This involved a change to `Toolkit.ListHelpers`, to let us know which items were removed from the list during `InPlaceUpdateList` * Cleanup items that are thrown out when we cancel a FetchItems. Those items were constructed, and might have registered event handlers, even if we never actually put them into `Items`. _Targets #41166_ Closes #39837 Tested with the evil sample from #41158, and loading thousands and thousands of items no longer causes us to leak memory like we're Deepwater Horizon.
This commit is contained in:
@@ -65,12 +65,32 @@ public partial class ListHelpers
|
||||
public static void InPlaceUpdateList<T>(IList<T> original, IEnumerable<T> newContents)
|
||||
where T : class
|
||||
{
|
||||
InPlaceUpdateList(original, newContents, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the contents of `original` in-place, to match those of
|
||||
/// `newContents`. The canonical use being:
|
||||
/// ```cs
|
||||
/// ListHelpers.InPlaceUpdateList(FilteredItems, FilterList(ItemsToFilter, TextToFilterOn));
|
||||
/// ```
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Any type that can be compared for equality</typeparam>
|
||||
/// <param name="original">Collection to modify</param>
|
||||
/// <param name="newContents">The enumerable which `original` should match</param>
|
||||
/// <param name="removedItems">List of items that were removed from the original collection</param>
|
||||
public static void InPlaceUpdateList<T>(IList<T> original, IEnumerable<T> newContents, out List<T> removedItems)
|
||||
where T : class
|
||||
{
|
||||
removedItems = [];
|
||||
|
||||
// we're not changing newContents - stash this so we don't re-evaluate it every time
|
||||
var numberOfNew = newContents.Count();
|
||||
|
||||
// Short circuit - new contents should just be empty
|
||||
if (numberOfNew == 0)
|
||||
{
|
||||
removedItems.AddRange(original);
|
||||
original.Clear();
|
||||
return;
|
||||
}
|
||||
@@ -92,6 +112,7 @@ public partial class ListHelpers
|
||||
for (var k = i; k < j; k++)
|
||||
{
|
||||
// This item from the original list was not in the new list. Remove it.
|
||||
removedItems.Add(original[i]);
|
||||
original.RemoveAt(i);
|
||||
}
|
||||
|
||||
@@ -120,6 +141,7 @@ public partial class ListHelpers
|
||||
while (original.Count > numberOfNew)
|
||||
{
|
||||
// RemoveAtEnd
|
||||
removedItems.Add(original[original.Count - 1]);
|
||||
original.RemoveAt(original.Count - 1);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user