ICommand binding causing UI memory leak in a WPF application

I am creating a WPF application that connects to a SQL Server database using LINQ to SQL.

The main application window contains a ListView containing a series of detailed views. The ItemSource ListView bound to a set of detailed view model objects displayed as a property in the root view model. Each detailed view model object contains several ICommand properties, as well as a property representing the detailed model object, which in turn provides various data fields displayed in the user interface.

Analysis with the ANTS memory profiler shows that leaked objects are those contained in the object of the detailed model and some user interface classes to which they are attached. Instances of these objects from previous updates are not garbage collected.

ANTS has a tool that allows the user to track link chains to determine why unwanted memory is stored. When I use it, I find that all the chains that appear have ICommand in them. Accordingly, I removed the ICommand insult and found that the memory leak disappeared.

Unfortunately, I need ICommand implement some important functions. I am really confused about how it refers to the part model object in the first place - these are two completely separate instance variables in the detailed view model object.

Here is the constructor of the detail view model object (the RootViewModel link is used for callbacks in some methods related to ICommands. Initially, I suspected that this might cause a circular chain of links, which might be the cause of the problem, but deleting it seems to have no effect .)

 public CarDataViewModel(CarData carDataItem, RootViewModel parentViewModel) { _parentViewModel = parentViewModel; CarDataModel = carDataItem; CompetingCheckboxStatus = CarDataModel.CurrentCar.Competing; AcknowledgeAlarm = new ParameterlessCommand(AcknowledgeAlarmClicked); Acknowledge = new ParameterlessCommand(AcknowledgeClicked); ShowReport = new ParameterlessCommand(ShowReportClicked); Cancel = new ParameterlessCommand(CancelClicked); } 

Here xaml where the bindings are configured - AcknowledgeAlarm is ICommand, CarDataModel is the part model object:

 <ListView x:Name="itemGridView"Grid.Row="1"ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding CarDataViewModels}" IsSynchronizedWithCurrentItem="True" Margin="0,0,0,0"> <ListView.ItemTemplate> <DataTemplate> </DataTemplate.Resources> <Button Command="{Binding AcknowledgeAlarm}"> <Border DataContext="{Binding CarDataModel}" BorderBrush="{StaticResource GrayFadeBrush}" Background="White" BorderThickness="5"> <Grid> . . . 
+7
source share
2 answers

The CanExecuteChanged event CanExecuteChanged is probably involved in the leak.

WPF expects ICommand implementations to use weak references to event handlers. You are using a regular .NET event that uses strong links that can cause this leak.

The way you instantiate the ParameterlessCommand seems to imply that CanExecute will always be true, and you don't need this event at all. Are you really shooting an event anywhere or OnCanExecuteChanged unused code?

If not, replace the event definition:

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

Thus, the event does not store any handlers, and the view model avoids having a strong link to user interface elements.

If you need to raise an event, the simplest solution would be to use CommandManager.RequerySuggested , which corresponds to the expected event slice 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 already done so) and use this instead of having individual NameChanged events, etc. for each property. This is because the logic in WPF relating to individual properties causes a memory leak when there is a link from the view model back to the user interface elements: http://support.microsoft.com/kb/938416

AFAIK you need to implement INotifyPropertyChanged , even if in fact you do not have any changes.


My guess is that resolving either of these two issues will lead to the leak disappearing: an incorrectly implemented CanExecuteChanged calls a strong link from the view model for viewing, which is precisely the fact that the absence of INotifyPropertyChanged causes a leak.

But itโ€™s nice to fix both problems; not just one of them.

+10
source

One of the โ€œnewโ€ problems that programmers must deal with when using MVVM is that ViewModel does not automatically clean itself. If you set a property that causes the view to load, then change this property, the old object will not necessarily be automatically deleted. It can sit on the GC for a long time before it is cleaned.

It is especially tempting to use LINQ to create ViewModel collections, but you have to be very careful. LINQ will return new instances of your objects every time it is called, potentially creating memory leaks and state problems.

All that said, I do not see enough information in this matter to help you determine the source of the leak (and too much unnecessary information). Itโ€™s best to standardize the ViewModel creation pattern using Factory or something similar, so that you only generate a new instance of ViewModel when you intend, and that you can track instances and kill them as needed.

+2
source

All Articles