ICommand binding causing UI memory leak in WPF application

前端 未结 2 1270
轮回少年
轮回少年 2020-12-16 19:28

I\'m building a WPF app that connects to a SQL Server database using LINQ to SQL.

The main window of the app contains a ListView containing a series of

相关标签:
2条回答
  • 2020-12-16 19:57

    One of the "new" challenges that programmers must deal with when using MVVM is that the ViewModel doesn't automatically clean itself up. If you set a property which causes a view to load, then change that property, the old object is not necessarily automatically disposed. It may sit on the GC for a long time before it's cleaned up.

    It's especially tempting to use LINQ to build your ViewModel collections, but you must be very careful. LINQ will return new instances of your objects on each call, potentially creating memory leaks and state problems.

    All that said, I don't see nearly enough information in this question to help you identify the source of your leak (and far too much irrelevant information). Your best bet is to standardize your ViewModel creation pattern, using Factory or something similar, so that you are only generating a new instance of a ViewModel when you intend to and so that you can keep track of instances and kill them as needed.

    0 讨论(0)
  • 2020-12-16 20:11

    The CanExecuteChanged event handler is likely implicated in the leak.

    WPF expects ICommand implementations to use weak references to the event handlers. You're using a normal .NET event which uses strong references, which can cause this leak.

    The way you are creating the ParameterlessCommand instance seems to imply that CanExecute will always be true, and you don't need the event at all. Are you actually firing the event anywhere, or is OnCanExecuteChanged unused code?

    If not, replace the event definition with:

    public event EventHandler CanExecuteChanged { add {} remove {} }
    

    This way the event does not store any handlers, and the view model avoids having a strong reference to the UI elements.

    If you need to raise the event, the easiest solution is to use CommandManager.RequerySuggested, which matches the weak event semantics expected for ICommand:

    public event EventHandler CanExecuteChanged {
        add {
            CommandManager.RequerySuggested += value;
        }
        remove {
            CommandManager.RequerySuggested -= value;
        }
    }
    

    Another thing you should do is implement INotifyPropertyChanged in your view model (if you haven't done so already), and use that instead of having individual NameChanged etc. events for each property. This is because the logic in WPF dealing with the individual properties causes memory leaks when there is a reference from the view model back to the UI elements: http://support.microsoft.com/kb/938416

    AFAIK you need to implement INotifyPropertyChanged even if you don't actually have any change events.


    My guess is that fixing either of these two problems will make the leak disappear: the incorrectly implemented CanExecuteChanged causes a strong reference from view model to view, which is exactly the circumstance under which the lack of INotifyPropertyChanged causes a leak.

    But it's a good idea to fix both issues; not just one of them.

    0 讨论(0)
提交回复
热议问题