Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory leak in WPF app due to DelegateCommand

I just finished desktop apps written in WPF and c# using MVVM pattern. In this app I used Delegate Command implementation to wrap the ICommands properties exposed in my ModelView. The problem is these DelegateCommands prevent my ModelView and View from being garbage collected after closing the view. So it stays larking until I terminate the whole application. I profile the application I find it’s all about delegatecommand that keeping the modelview in memory. How could I avoid this situation and is this in nature of mvvm pattern, or it’s about my implantation of the pattern?. Thanks.

Edit: this is small but complete portion of how i implement MVVM pattern

First: CommandDelegte class

class DelegateCommand:ICommand
{
    private Action<object> execute;
    private Predicate<object> canExcute;
    public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }
        this.execute = execute;
        this.canExcute = canExecute;
    }
    public bool CanExecute(object parameter)
    {
        if (this.canExcute != null)
        {
            return canExcute(parameter);
        }
        return true;
    }

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


    public void Execute(object parameter)
    {
        this.execute(parameter);
    }
}

Second: ModelView Class

public class ViewModel:DependencyObject, INotifyPropertyChanged
{
    private DelegateCommand printCommand;

    public ICommand PrintCommand
    {
        get
        {
            if (printCommand == null)
            {
                printCommand = new DelegateCommand(Print, CanExecutePrint);
            }
            return printCommand;
        }
    }
    void Print(object obj)
    {
        Console.WriteLine("Print Command");

    }
    bool CanExecutePrint(object obj)
    {
        return true;
    }


    public event PropertyChangedEventHandler PropertyChanged;
    private void OnProeprtyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Third: Window code behind

public MainWindow()
    {
        InitializeComponent();
        base.DataContext = new ViewModel();
    }

Forth: My XAML

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Window.InputBindings>
    <KeyBinding Key="P" Modifiers="Control" Command="{Binding Path=PrintCommand}"/>
</Window.InputBindings>
<StackPanel>
    <Button Content="Print - Ctrl+P" Width="75" Height="75" Command="{Binding Path=PrintCommand}"/>
</StackPanel>

like image 499
Abdullah BaMusa Avatar asked Jun 15 '10 10:06

Abdullah BaMusa


3 Answers

In your case, what contains a reference to what?

  1. DelegateCommand contains a reference to ViewModel - its execute and canExecute properties contain references to a methods of the ViewModel instance.

  2. ViewModel contains a reference to DelegateCommand - its PrintCommand property.

  3. The view contains any number of references to the ViewModel.

  4. The CommandManager contains a reference to DelegateCommand in its RequerySuggested event.

That last reference is a special case: CommandManager uses a WeakReference in its RequerySuggested event, so despite the fact that DelegateCommand registers for that event, it can still be garbage-collected.

Given all this, you shouldn't be having a problem. If the view gets disposed, neither the ViewModel nor the DelegateCommand should be reachable.

You say you've profiled the application and DelegateCommand is holding a reference to ViewModel. It seems to me that the logical next question should be: what's holding a reference to DelegateCommand? It shouldn't be CommandManager. Do you have something else in your application that's referencing your commands?

like image 118
Robert Rossney Avatar answered Nov 03 '22 03:11

Robert Rossney


I think that in this code there is a circular reference which is causing the ViewModel to never be garbage collected.

I know this is an old question, but I will point out that some implementations of DelegateCommand or RelayCommand hold a WeakReference to the action. Your use of the DelegateCommand here is typical, but unfortunately will cause memory leaks with this implementation because when the ViewModel's method is passed into the DelegateCommand's constructor, a reference to the class containing that method is automatically captured by the delegate.

If you implemented IDispose on your ViewModel and cleared the references to the DelegateCommands explicitly in Dispose, then you could continue to use this implementation. Your view that's constructing your ViewModel would also have to Dipose of it, however.

like image 24
Malaise Avatar answered Nov 03 '22 02:11

Malaise


After reading this post, I then came across a web page that had some relating information. It is a page on CodePlex called Memory Leak caused by DelegateCommand.CanExecuteChanged Event.

Reported by : huetter
Updated by : dschenkelman

When profiling my application I noticed that plenty of EventHandlers had never been deregistered from DelegateCommand's CanExecuteChanged-Event. So those EventHandlers were never been garbage-collector, which caused a severe memory leak.

As registering CanExecuteChanged-EventHandles is done outside application code scope I had expected them to be deregistered automatically as well. At this point I thought this might as well be a ThirdParty WPF control issue, but digging further I read a blog post stating that "WPF expects the ICommand.CanExecuteChanged-Event to apply WeakReferences for EventHandlers". I had a look into RoutedCommand, and noticed it uses WeakReferences as well.

I adapted DelegateCommand to use an implementation similar to RoutedCommand's CanExecuteChanged-Event, and the memory leak was gone. The same is true for CompositeCommand.

Closed Nov 3, 2009 at 6:28 PM by This issue was fixed in the Prism-v2.1 release, so the Workitem is closed now. Prism 2.1 can be downloaded from here:
http://www.microsoft.com/downloads/details.aspx?FamilyID=387c7a59-b217-4318-ad1b-cbc2ea453f40&displaylang=en

like image 1
Sheridan Avatar answered Nov 03 '22 02:11

Sheridan