wpfui icon indicating copy to clipboard operation
wpfui copied to clipboard

Memory leak in ListView

Open 4nonym0us opened this issue 5 months ago • 0 comments

Describe the bug

OnLoaded method of the ListView from WPF-UI uses DependencyPropertyDescriptor to invoke AddValueChanged, but never unregisters the event handler using RemoveValueChanged, which leads to a memory leak because a strong refence is created to the component. https://github.com/lepoco/wpfui/blob/4ac96867797267d566b82fbbfcab0c867353afaa/src/Wpf.Ui/Controls/ListView/ListView.cs#L67-L78

To Reproduce

An instance of a ListView from WPF-UI stays in memory forever because a strong reference to the component is created and never removed.

public class UnitTest1
{
    [Fact]
    public Task TestWpfUiControlsListView()
    {
        return TestListViewMemoryLeakAsync<Wpf.Ui.Controls.ListView>();
    }

    [Fact]
    public Task TestSystemWindowsControlListView()
    {
        return TestListViewMemoryLeakAsync<System.Windows.Controls.ListView>();
    }

    public async Task TestListViewMemoryLeakAsync<T>() where T : ListView, new()
    {
        WeakReference? wr = null;

        await StartSTATask(() =>
        {
            var listView = new T();

            wr = new WeakReference(listView);

            listView.RaiseEvent(new RoutedEventArgs(FrameworkElement.LoadedEvent));
            listView.RaiseEvent(new RoutedEventArgs(FrameworkElement.UnloadedEvent));
        });

        GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);

        Assert.NotNull(wr);
        Assert.False(wr.IsAlive);
    }

    private static Task StartSTATask(Action action)
    {
        var tcs = new TaskCompletionSource<object>();
        var thread = new Thread(() =>
        {
            try
            {
                action();
                tcs.SetResult(new object());
            }
            catch (Exception e)
            {
                tcs.SetException(e);
            }
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        return tcs.Task;
    }
}

image

Expected behavior

 ListView should subscribe to Unloaded event and call RemoveValueChanged.

Screenshots

No response

OS version

All.

.NET version

All.

WPF-UI NuGet version

3.0.5 - latest (4.0.0-rc.2)

Additional context

No response

4nonym0us avatar Sep 30 '24 15:09 4nonym0us