3

定義しましょう:

  • viewModel :TabViewModelクラス
  • ビュー :TabViewクラス

私はクラスのインスタンスをn 個持っているので、. クラスの 1 つのインスタンスがメッセージを送信するとき、それを独自のビューモデルで受信したいのですが、この 1 つだけです。TabViewTabViewModelTabView

mvvm light ツールキットの Messenger を理解しているので、次のようなものを使用する必要があります。

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

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

何に使えばoneTokenいいですか?

私が最初に考えたのは、ViewModel インスタンスを token として使用することでした。

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

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

ビューはDataContextが何であるかを知らないため、これは私には「mvvmフレンドリー」に思えます。しかし、このソリューションでは、メモリ リークが心配です。mvvm ライトでは、受信者は弱参照されますが、トークンはそうではありません ( Messenger クラスの WeakActionAndToken 構造体でわかるように) 。

トークンとして何を使用できますか? ビューモデル インスタンスは適切な選択ですか? また、それを使用した場合にメモリ リークを防ぐにはどうすればよいですか?


編集:可能な解決策

オプション 1 ( ethicallogicsの回答に基づく):

  1. ビューとビューモデルの両方でトークン プロパティ (文字列型や GUID 型など) を定義します。
  2. それらのいずれかの値を定義します(一意の値、たとえばビューモデルのコンストラクターで設定します)
  3. それらを XAML でバインドする
  4. メッセンジャー通話で使用する

オプション2(私が取ったもの):

viewmodel インスタンスをトークンとして使用します。

メモリ リークを防ぐには、weakReference にカプセル化する必要があります。2 つのトークンを比較する Messenger を操作するには、weakReference にEqualsメソッドを実装する必要があります (これは、クラスのデフォルトの .Net 実装の場合ではありませんWeakReference)。

だから私たちは持っています:

// 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);
    }
}

アドバンテージは、ビューとビューモデルの両方で軽量なコードであり、メモリ リークがありません。テストに成功しました。より良い解決策があれば、コメントしてください。

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 は同じトークンを持つ必要があるため、そのキーに対応する正確なデリゲートを Send メソッドで起動できます。

于 2014-01-04T18:30:57.873 に答える
0

MVVMLight を使用している場合は、コマンドを使用します。これは、適切な VM に移動することが保証されています。

VM で:

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

これにより、VM のプロパティである RelayCommand が作成されます。コマンドがビューによって呼び出されると、VM でメソッド DeletePreSale() が呼び出されますが、VM メソッド CanDeletePreSale() が true を返さない場合は、コマンドの呼び出しが許可されず、コマンドがバインドされているウィジェットが無効になります。に。

ビューで:

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

乾杯 -

于 2014-01-04T14:52:57.790 に答える