行为不一致:带有关闭功能的MVVM RelayCommandWPF捕获了局部变量

时间:2018-12-07 20:32:30

标签: c# closures mvvm-light

我试图了解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处理关闭的方式。

有任何猜想吗?

1 个答案:

答案 0 :(得分:0)

问题是传递给命令的闭包最终被垃圾回收了。归功于this Stack Overflow answer和这个MVVMLight documentation item

您传递给RelayCommand的命令action和enable函数与弱引用一起存储,因此,除非保留RelayCommand之外的东西,否则它们将在某些时候被垃圾回收。解决方案是,如果您的操作或启用函数是闭包,则使用keepTargetAlive构造函数参数。