我试图了解MVVM RelayCommand的一些奇怪行为,该行为的动作是一个捕获局部变量的闭包。
最小可行代码示例:
using GalaSoft.MvvmLight.CommandWpf;
namespace WpfApplication3
{
public partial class MainWindow
{
public RelayCommand DoIt { get; }
int i = 0;
public MainWindow()
{
DoIt = new RelayCommand( () =>
{
System.Console.WriteLine( "doing it!" );
button.Content = (++i).ToString();
} );
InitializeComponent();
}
}
}
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight">
<Button x:Name="button" Content="Hit me" Command="{Binding DoIt, RelativeSource={RelativeSource AncestorType=Window}}"/>
</Window>
当您点击“打我”按钮时,标签将更改为一个数字,此数字在以后的每次点击中都会递增。
由于i
仅由RelayCommand
动作使用,因此我想将声明作为局部变量移到构造函数中。但是当我这样做时,我得到一个非常奇怪的行为:该命令根本不会触发,或者触发一次然后停止。
有趣的是,如果我不使用RelayCommand
并将闭包连接到按钮的Click
事件,无论我在何处定义i
都可以使用。因此,它一定是RelayCommand
处理关闭的方式。
有任何猜想吗?
答案 0 :(得分:0)
问题是传递给命令的闭包最终被垃圾回收了。归功于this Stack Overflow answer和这个MVVMLight documentation item。
您传递给RelayCommand
的命令action和enable函数与弱引用一起存储,因此,除非保留RelayCommand
之外的东西,否则它们将在某些时候被垃圾回收。解决方案是,如果您的操作或启用函数是闭包,则使用keepTargetAlive
构造函数参数。