Skip to content
New issue

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

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

Already on GitHub? Sign in 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant