views:

122

answers:

3

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>

A: 

Without seeing any code it's very difficuilt give a proper answer. Please show us your implementation and usage of the delegate command.

DHN
@DHN: I’ve edited my question to include small but complete portion of how I implement the MVVM pattern
Abdullah BaMusa
A: 

I've not used WPF etc. before or used the MVVM pattern, but if you're trying to avoid memory leaks you should see : http://msdn.microsoft.com/en-us/library/ee658248.aspx. Specifically see the section about event handling causing leaks.

apoorv020
+4  A: 

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?

Robert Rossney
@Robert Rossney: very excellent point to start with, but how about binding i do in the view to properties and commands comes from ViewModel which will subscribe to PropertyChanged event to be notified, are these will be managed by WPF framework? Is there any concern from these binding operations? Thanks..
Abdullah BaMusa
To answer that question definitively I'd have to poke around with Reflector, but I'm reasonably certain that the binding contains references only to its source and its target, and nothing else has a reference to the binding. So if the garbage collector can't find the view or view model from the root, the fact that they're both referencing and referenced by a binding won't affect whether or not they get garbage-collected. But you're using a profiler: does it tell you what's got a reference to `DelegateCommand`?
Robert Rossney
Yes, it tells me that DelegateCommand has a reference in ModelView and marked as WeakReference but they never being collected, I know that because I leave profile running for a while and take a new snapshot i find them there. Implementing IDisposible and setting all command to null in Dispose result to nothing.Continue...
Abdullah BaMusa
I'm not sure how to effectively release these DelegateCommand references, but I'm sure I'm missing something and any help will be appreciated as this leak annoying me because when I look at Processes tab in Windows Task Manager I see my application not release what it gets from memory but keeps growing. Thanks..
Abdullah BaMusa
As far as I understand the CommandManager does not contain a reference to the DelegateCommand instance but to the view:The view subscribes to the CanExecuteChanged event of the DelegateCommand (that would make the DelegateCommand contain a reference to the view) but the DelegateCommand passes that reference to the CommandManager.Am I wrong?
Alex Janzik
@Alex: No, I don't think you're wrong. I wasn't looking at this closely enough. From what Abdullah has just posted, I don't think it's the CommandManager that's the source of the problem here. DelegateCommand's not getting garbage-collected because the view model is holding a reference to it. What's holding a reference to the view model?
Robert Rossney