让我们定义:
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 答案):
选项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上的轻量级代码,没有内存泄漏。测试成功。 如果您有更好的解决方案,请随时发表评论。
答案 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}"/>
干杯 -