从View向ViewModel发送消息:什么是适当的令牌?

时间:2014-01-04 14:24:42

标签: c# wpf mvvm mvvm-light messenger

让我们定义:

  • viewModel:TabViewModel
  • 视图:TabView

我有TabView类的 n 个实例,以及TabViewModel n 个实例。 当TabView类的一个实例发送消息时,我希望它由自己的视图模型接收,只有这个

据我了解mvvm light工具包的Messenger,我应该使用类似的东西:

// in the view
Messenger.Default.Send(new RefreshMessage(/*...*/), oneToken);

// in the viewmodel 
Messenger.Default.Register<RefreshMessage>(this, oneToken, MyViewModelMethod);

我应该对oneToken使用什么?

我的第一个想法是使用ViewModel实例作为标记:

// in the view
Messenger.Default.Send(new RefreshMessage(/*...*/), this.DataContext);

// in the viewmodel 
Messenger.Default.Register<RefreshMessage>(this, **this**, MyViewModelMethod);

这对我来说似乎是“mvvm友好”,因为视图不知道什么是DataContext。 但是使用这个解决方案,我担心内存泄漏:在mvvm中,收件人是弱引用的,但令牌不是(正如您将在WeakActionAndToken struct of the Messenger class中看到的那样。

我可以使用什么作为代币? viewmodel实例是一个不错的选择,如果我使用它,如何防止内存泄漏?


编辑:可能的解决方案

选项1(基于 ethicallogics 答案):

  1. 在视图和viewmodel上定义Token属性(例如,类型为string或GUID)
  2. 定义其中一个的值(一个唯一值,例如在viewmodel的构造函数中设置它)
  3. 在XAML中将它们绑定在一起
  4. 在Messenger通话中使用它们
  5. 选项2(我采取的那个):

    将viewmodel实例用作Token。

    为防止内存泄漏,我们必须将其封装在weakReference中。为了使用比较2个令牌的Messenger,weakReference应该实现Equals方法(这不是WeakReference类的默认.Net实现的情况。)

    所以我们有:

    // in the view
    Messenger.Default.Send(new RefreshMessage(), new EquatableWeakReference(this.DataContext));
    

    // in the viewmodel 
    Messenger.Default.Register<RefreshMessage>(this, new EquatableWeakReference(this), ApplyRefreshMessage);
    

    我实施了EquatableWeakReference课程如下:

    /// <summary>
    /// A weak reference which can be compared with another one, based on the target comparison.
    /// </summary>
    public class EquatableWeakReference : IEquatable<EquatableWeakReference>
    {
        private WeakReference reference;
        private int targetHashcode;
    
        public EquatableWeakReference(object target)
        {
            if (target == null)
                throw new ArgumentNullException("target");
            reference = new WeakReference(target);
            targetHashcode = target.GetHashCode();
        }
    
        public override bool Equals(object obj)
        {
            return Equals(obj as EquatableWeakReference);
        }
    
        /// <summary>
        /// As Equals is overriden, we must provide an override for GetHashCode.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return targetHashcode;
        }
    
        public bool Equals(EquatableWeakReference other)
        {
            if (other == null
                || !reference.IsAlive
                || !other.reference.IsAlive)
                return false; // we assume that if both references are not alive, the result is inconclusive : let's say false.
            return this.reference.Target.Equals(other.reference.Target);
        }
    }
    

    Advantage是view和viewmodel上的轻量级代码,没有内存泄漏。测试成功。 如果您有更好的解决方案,请随时发表评论。

2 个答案:

答案 0 :(得分:1)

Token是View传递给ViewModel的对象唯一值,它们都使用相同的Token。像

  

查看

public partial class MainWindow : Window
{
    readonly string Token;
    public MainWindow()
    {
        Token = Guid.NewGuid().ToString();
        InitializeComponent();
        DataContext = new MainViewModel(Token);
    }
}
  

视图模型

public class MainViewModel 
{
    readonly string Token;

    public MainViewModel(string token)
    {
        Token = token;
    }
}

实际上Token背后的逻辑是当我们将一个委托注册到Messenger时。它确实有内部字典,而这个Token充当该字典中的密钥。 View及其ViewModel必须具有相同的令牌,以便可以在Send方法上触发与该键对应的确切委托。

答案 1 :(得分:0)

如果您正在使用MVVMLight,请使用命令。这保证会转到正确的VM。

在VM中:

    this.DeletePreSaleCommand = new RelayCommand(() => this.DeletePreSale(), () => this.CanDeletePreSale());

这会创建一个RelayCommand,它是VM上的一个属性;当View调用Command时,它将调用VM上的DeletePreSale()方法,但如果VM方法CanDeletePreSale()没有返回true,则不允许调用该命令,并且将禁用命令绑定的小部件到。

在视图中:

        <telerik:RadButton Grid.Row="3" Width="200" Command="{Binding DeletePreSaleCommand}"/>

干杯 -