由于 DelegateCommand 而导致 WPF 应用程序内存泄漏
我刚刚使用 MVVM 模式完成了用 WPF 和 C# 编写的桌面应用程序。在此应用程序中,我使用 Delegate Command 实现来包装 ModelView 中公开的 ICommands 属性。问题是这些 DelegateCommands 阻止我的 ModelView 和 View 在关闭视图后被垃圾收集。所以它会一直保持活跃状态,直到我终止整个应用程序。我分析了该应用程序,我发现它全部与将模型视图保留在内存中的委托命令有关。 我怎样才能避免这种情况,这是 mvvm 模式的本质,还是与我植入该模式有关?谢谢。
编辑:这是我如何实现 MVVM 模式的小但完整的部分
第一:CommandDelegte 类
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);
}
}
第二:ModelView 类
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));
}
}
}
第三:窗口代码
public MainWindow()
{
InitializeComponent();
base.DataContext = new ViewModel();
}
第四:我的 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>
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>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
就您而言,什么包含对什么的引用?
DelegateCommand
包含对ViewModel
的引用 - 其execute
和canExecute
属性包含对ViewModel
实例。ViewModel
包含对DelegateCommand
的引用 - 其PrintCommand
属性。视图包含对
ViewModel
的任意数量的引用。CommandManager
在其RequerySuggested
事件中包含对DelegateCommand
的引用。最后一个引用是一种特殊情况:
CommandManager
在其RequerySuggested
事件中使用WeakReference
,因此尽管DelegateCommand
> 注册该事件,它仍然可以被垃圾收集。考虑到这一切,你应该不会有问题。如果视图被释放,则
ViewModel
和DelegateCommand
都将无法访问。您说您已经分析了应用程序,并且
DelegateCommand
持有对ViewModel
的引用。在我看来,合乎逻辑的下一个问题应该是:什么持有对DelegateCommand
的引用?它不应该是CommandManager
。您的应用程序中是否还有其他内容引用您的命令?In your case, what contains a reference to what?
DelegateCommand
contains a reference toViewModel
- itsexecute
andcanExecute
properties contain references to a methods of theViewModel
instance.ViewModel
contains a reference toDelegateCommand
- itsPrintCommand
property.The view contains any number of references to the
ViewModel
.The
CommandManager
contains a reference toDelegateCommand
in itsRequerySuggested
event.That last reference is a special case:
CommandManager
uses aWeakReference
in itsRequerySuggested
event, so despite the fact thatDelegateCommand
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 theDelegateCommand
should be reachable.You say you've profiled the application and
DelegateCommand
is holding a reference toViewModel
. It seems to me that the logical next question should be: what's holding a reference toDelegateCommand
? It shouldn't beCommandManager
. Do you have something else in your application that's referencing your commands?我认为在此代码中存在循环引用,导致 ViewModel 永远不会被垃圾收集。
我知道这是一个老问题,但我会指出 DelegateCommand 或 RelayCommand 持有对操作的 WeakReference。此处对 DelegateCommand 的使用是典型的,但不幸的是,此实现会导致内存泄漏,因为当 ViewModel 的方法传递到 DelegateCommand 的构造函数时,委托会自动捕获对包含该方法的类的引用。
如果您在 ViewModel 上实现了 IDispose 并在 Dispose 中显式清除了对 DelegateCommands 的引用,那么您可以继续使用此实现。但是,构建 ViewModel 的视图也必须将其丢弃。
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.
读完这篇文章后,我发现了一个包含一些相关信息的网页。这是 CodePlex 上一个名为 DelegateCommand.CanExecuteChanged 事件导致的内存泄漏的页面。
报告人:huetter
更新者:dschenkelman
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