Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Memory leak in ListView #1242

Open
4nonym0us opened this issue Sep 30, 2024 · 0 comments
Open

Memory leak in ListView #1242

4nonym0us opened this issue Sep 30, 2024 · 0 comments
Labels
bug Something isn't working

Comments

@4nonym0us
Copy link

4nonym0us commented Sep 30, 2024

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.

private void OnLoaded(object sender, RoutedEventArgs e)
{
Loaded -= OnLoaded; // prevent memory leaks
// Setup initial ViewState and hook into View property changes
var descriptor = DependencyPropertyDescriptor.FromProperty(
System.Windows.Controls.ListView.ViewProperty,
typeof(System.Windows.Controls.ListView)
);
descriptor?.AddValueChanged(this, OnViewPropertyChanged);
UpdateViewState(); // set the initial state
}

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 4nonym0us added the bug Something isn't working label Sep 30, 2024
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant