3

让我们定义:

  • 一个 viewModel :TabViewModel
  • 一个视图:TabView

我有该类的n个实例TabView,等等nTabViewModel. 当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-friendly”,因为视图不知道 DataContext 是什么。但是使用这个解决方案,我担心内存泄漏:在 mvvm light 中,接收者是弱引用的,但令牌不是(正如您将在Messenger 类的 WeakActionAndToken 结构中看到的那样。

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


编辑:可能的解决方案

选项 1(基于伦理逻辑的答案):

  1. 在视图和视图模型上定义一个 Token 属性(例如字符串或 GUID 类型)
  2. 定义其中一个的值(一个唯一的值,例如在视图模型的构造函数中设置它)
  3. 在 XAML 中将它们绑定在一起
  4. 在 Messenger 通话中使用它们

选项2(我采取的那个):

使用 viewmodel 实例作为 Token。

为了防止内存泄漏,我们必须将它封装在一个弱引用中。为了与比较 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 是视图和视图模型上的轻量级代码,没有内存泄漏。测试成功。如果您有更好的解决方案,请随时发表评论。

4

2 回答 2

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 必须具有相同的 Token 以便可以在 Send 方法上触发与该键对应的确切委托。

于 2014-01-04T18:30:57.873 回答
0

如果您使用的是 MVVMLight,请使用命令。可以保证转到正确的 VM。

在虚拟机中:

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

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

在视图中:

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

干杯 -

于 2014-01-04T14:52:57.790 回答