The problem of memory leakage when closing the window

The essence of my program is to create a window for all screens through the tray. In this case, the main program flow ends when the tray is closed. The bottom line is that when creating and closing a window, you need to clear the memory before the next one. As I did not try to find and understand, but I have a leak. RAM consumption increases each time.

public class TrayIconViewModel : INotifyPropertyChanged
{
    // Commands for user interactions
    public ICommand CreateWindowCommand { get; }
    public ICommand ExitCommand { get; }

    private bool _disposed;

    public TrayIconViewModel()
    {
        // Initializing commands
        CreateWindowCommand = new RelayCommand(async () => await CreateWindowAsync());
        ExitCommand = new RelayCommand(ExitApp);
    }

    // Asynchronous method to create a new window
    private async Task CreateWindowAsync()
    {
        await WindowManager.Instance.CreateAndShowWindowAsync();
    }

    // Method to terminate the application
    private void ExitApp()
    {
        // Gracefully shut down the application using the desktop lifetime instance
        var desktopLifetime = Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime;
        desktopLifetime?.Shutdown(); // Gracefully shut down the application
        Environment.Exit(0); // Forcibly terminate the process (if needed)
    }

    // Implementation of INotifyPropertyChanged to notify UI about property changes
    public event PropertyChangedEventHandler? PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}



public sealed class ScreenManager
{
    // Singleton instance of ScreenManager
    private static readonly Lazy<ScreenManager> Lazy = new(() => new ScreenManager());
    public static ScreenManager Instance => Lazy.Value;

    private ScreenManager() { }

    // Retrieve all screens available without caching as they may change
    public List<Screen> GetAllScreens()
    {
        List<Screen> screens;
        var tempWindow = new MainWindow(); // Temporary window to access screens
        try
        {
            screens = tempWindow.Screens.All.ToList();
            if (screens.Count == 0)
            {
                throw new InvalidOperationException("Unable to retrieve screen information.");
            }
        }
        finally
        {
            tempWindow.Close(); // Always close the temporary window
        }
        return screens;
    }
}

public sealed class WindowManager : IDisposable
{
    // Singleton instance of WindowManager
    private static readonly Lazy<WindowManager> Lazy = new(() => new WindowManager());
    public static WindowManager Instance => Lazy.Value;

    // Weak reference to the main window to avoid memory leaks
    private WeakReference<MainWindow>? _mainWindowRef;
    private bool _disposed;

    // Structure to track window state (open/closed)
    private struct WindowState
    {
        public bool IsOpen;
        public bool EventsUnsubscribed;
    }

    private WindowState _windowState;

    // Destructor to ensure proper disposal
    ~WindowManager()
    {
        Dispose(false);
    }

    // Method to create and show the main window asynchronously
    public async Task CreateAndShowWindowAsync()
    {
        if (_disposed) throw new ObjectDisposedException(nameof(WindowManager));

        if (!_windowState.IsOpen || (_mainWindowRef != null && !_mainWindowRef.TryGetTarget(out _)))
        {
            await Dispatcher.UIThread.InvokeAsync(() =>
            {
                // Get all screens and find the main screen
                var screens = ScreenManager.Instance.GetAllScreens();
                var mainScreen = screens.FirstOrDefault(s => s.Bounds.X <= 0 && s.Bounds.Y <= 0)
                                 ?? throw new InvalidOperationException("Main screen not found.");

                // Combine screen bounds to get the total screen space
                var combinedBounds = screens.Aggregate(new PixelRect(), (current, screen) => current.Union(screen.Bounds));

                // Create and initialize the new window
                var newWindow = new MainWindow();
                InitializeWindow(newWindow, combinedBounds, mainScreen);
                SubscribeToWindowEvents(newWindow);

                // Save the window reference and mark it as open
                _mainWindowRef = new WeakReference<MainWindow>(newWindow);
                newWindow.Show();
                _windowState.IsOpen = true;
                _windowState.EventsUnsubscribed = false;

                Console.WriteLine("Window successfully created and displayed.");
            });
        }
        else if (_mainWindowRef != null && _mainWindowRef.TryGetTarget(out var existingWindow))
        {
            await Dispatcher.UIThread.InvokeAsync(() => existingWindow.Activate());
        }
    }

    // Method to initialize window properties and position
    private void InitializeWindow(MainWindow window, PixelRect bounds, Screen mainScreen)
    {
        window.Width = bounds.Width / mainScreen.PixelDensity;
        window.Height = bounds.Height / mainScreen.PixelDensity;
        window.Position = new PixelPoint(bounds.X, bounds.Y);
        window.Topmost = true;
        window.ExtendClientAreaToDecorationsHint = true;
        window.ExtendClientAreaTitleBarHeightHint = -1;
        window.CanResize = false;
        window.ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.NoChrome;
    }

    // Subscribe to window events like close and keypress
    private void SubscribeToWindowEvents(MainWindow window)
    {
        window.Closed += OnWindowClosed;
        window.KeyDown += MainWindow_KeyDown;
        Console.WriteLine("Subscribed to window events.");
    }

    // Unsubscribe from window events to prevent memory leaks
    private void UnsubscribeFromWindowEvents(MainWindow? window)
    {
        if (window != null && !_windowState.EventsUnsubscribed)
        {
            window.Closed -= OnWindowClosed;
            window.KeyDown -= MainWindow_KeyDown;
            _windowState.EventsUnsubscribed = true;
            Console.WriteLine("Unsubscribed from window events.");
        }
    }

    // Event handler for window close event
    private void OnWindowClosed(object? sender, EventArgs e)
    {
        CloseWindow();
    }

    // Event handler for key press event (closes window on Escape)
    private void MainWindow_KeyDown(object? sender, KeyEventArgs e)
    {
        if (e.Key == Key.Escape)
        {
            CloseWindow();
        }
    }

    // Method to close the window and clean up resources
    private void CloseWindow()
    {
        if (_windowState.IsOpen && _mainWindowRef != null && _mainWindowRef.TryGetTarget(out var window))
        {
            Dispatcher.UIThread.InvokeAsync(() =>
            {
                UnsubscribeFromWindowEvents(window);
                window.Close();
            });

            _windowState.IsOpen = false;
            DisposeWindow();

            // Force garbage collection to clean up resources
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            Console.WriteLine("Garbage collection performed after window closure.");
        }
    }

    // Method to dispose of the window and release resources
    private void DisposeWindow()
    {
        if (_mainWindowRef != null && _mainWindowRef.TryGetTarget(out var window))
        {
            UnsubscribeFromWindowEvents(window);
            window.Close();
            (window as IDisposable)?.Dispose(); // Explicitly dispose resources
            _mainWindowRef = null;
            Console.WriteLine("Window references cleared.");
        }

        _windowState.IsOpen = false;
    }

    // Dispose pattern implementation to clean up resources
    private void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                DisposeWindow();
            }

            _disposed = true;
        }
    }

    // Public method to dispose resources
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

If you don’t use GC, it’s even sadder.

New contributor

callsign_332 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

2

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật