我刚刚使用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();
}
Forth:我的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>
答案 0 :(得分:9)
在你的情况下,什么包含对什么的引用?
DelegateCommand
包含对ViewModel
的引用 - 其execute
和canExecute
属性包含对ViewModel
实例的方法的引用。
ViewModel
包含对DelegateCommand
- PrintCommand
属性的引用。
该视图包含对ViewModel
。
CommandManager
在DelegateCommand
事件中包含对RequerySuggested
的引用。
最后一个参考是一个特殊情况:CommandManager
在WeakReference
事件中使用了RequerySuggested
,所以尽管DelegateCommand
注册了该事件,它仍然可以垃圾收集。
鉴于这一切,你应该没有问题。如果视图被释放,则ViewModel
和DelegateCommand
都不可访问。
您说您已对该应用进行了分析,DelegateCommand
正在引用ViewModel
。在我看来,合乎逻辑的下一个问题应该是:什么引用了DelegateCommand
?它不应该是CommandManager
。您的应用程序中是否还有其他引用命令的内容?
答案 1 :(得分:1)
阅读这篇文章之后,我偶然发现了一个有相关信息的网页。它是CodePlex上的一个名为Memory Leak caused by DelegateCommand.CanExecuteChanged Event的页面。
报告人:huetter
更新:dschenkelman
在分析我的应用程序时,我发现有很多EventHandler 从未被DelegateCommand取消注册 CanExecuteChanged的事件。所以那些EventHandler从未出现过 垃圾收集器,导致严重的内存泄漏。
注册CanExecuteChanged-EventHandles是在外面完成的 应用程序代码范围我曾预计它们会被取消注册 自动也是。在这一点上,我认为这也可能是 一个ThirdParty WPF控件问题,但进一步挖掘我读了一篇博客 帖子说“WPF需要ICommand.CanExecuteChanged-Event 为EventHandlers应用WeakReferences“。我看了一下 RoutedCommand,并注意到它也使用了WeakReferences。
我调整了DelegateCommand以使用类似的实现 RoutedCommand的CanExecuteChanged-Event,以及内存泄漏 不见了。 CompositeCommand也是如此。
2009年11月3日下午6:28关闭此问题已修复 Prism-v2.1发布,所以Workitem现在关闭了。棱镜2.1可以 从这里下载:
http://www.microsoft.com/downloads/details.aspx?FamilyID=387c7a59-b217-4318-ad1b-cbc2ea453f40&displaylang=en
答案 2 :(得分:1)
我认为在这段代码中有一个循环引用,导致ViewModel永远不会被垃圾回收。
我知道这是一个老问题,但我要指出,DelegateCommand或RelayCommand的某些实现持有对该操作的WeakReference。您在这里使用DelegateCommand是典型的,但遗憾的是这会导致内存泄漏,因为当ViewModel的方法传递给DelegateCommand的构造函数时,对包含该方法的类的引用会自动被捕获代表。
如果您在ViewModel上实现了IDispose并在Dispose中明确清除了对DelegateCommands的引用,那么您可以继续使用此实现。但是,您构建ViewModel的视图也必须使用它的Dipose。