从视图模型调用用户控制功能

时间:2014-03-29 19:32:48

标签: c# wpf mvvm

如何从单独的视图模型调用用户控件上的函数?

在我的场景中,我有一个带有ScoreDisplay UserControl的“主”视图:

<local:ScoreDisplay/>

此控件将显示我游戏中 n 玩家数量的得分。主视图模型连接到游戏控制器,为其提供分数更新。我需要将这些更新的分数提供给UserControl(并运行一些动画,非平凡的逻辑等)。

我看到了几个选项:

  1. 创建“Scores”依赖项属性并将其绑定到视图模型的分数集合。我用这种方法看到的唯一问题是无论何时修改它,我都需要去看看,看看有什么变化,这样我才能运行适当的动画。这样做当然是可能的,但似乎并不“正确”。

  2. 让ViewModel在UserControl上调用“UpdateScore”函数。当然,唯一的问题是ViewModel不应该知道有关View的任何信息,因此不应该有必要的参考来执行此操作。

  3. 让UserControl在视图模型上注册“ScoreUpdated”事件。这似乎是最好的选择,但我不知道如何获取ViewModel的引用以注册该事件。

  4. 哪个选项(如果有的话)是正确的方法?如果(2)或(3),我该如何实现呢?

    编辑:

    要明确的是,乐谱集合中的正在发生变化(集合本身保持不变)。我可以在得分int周围放一个包装器并听取PropertyChanged,但同样,这似乎是一个简单问题的过于复杂的方法。如果那是最好的解决方案,请告诉我!

    编辑2:

    UpdateScore是一个函数(理论上)接受更新得分的索引和要添加到该玩家得分的值(它可以接受整个得分)。然后它会使玩家的挂钩沿着一条小路移动到新的位置。

    每当玩家获得积分时就会被调用(这是一个Cribbage游戏,所以这种情况会发生很多)。视图模型附加到游戏控制器,该游戏控制器引发事件以通知VM玩家已经获得积分。视图模型基本上只需要将此信息传递给ScoreDisplay以进行显示/动画等。

3 个答案:

答案 0 :(得分:3)

在这种情况下,我们可以应用Mediator模式,如果成功,它将不需要该事件。

Mediator 模式有几个实施例,但我最喜欢 XAML Guy 的实现,它简单明了 - {{3} }。

Implementation code

public static class Mediator
{
    static IDictionary<string, List<Action<object>>> pl_dict = new Dictionary<string, List<Action<object>>>();

    static public void Register(string token, Action<object> callback)
    {
        if (!pl_dict.ContainsKey(token))
        {
            var list = new List<Action<object>>();
            list.Add(callback);
            pl_dict.Add(token, list);
        }
        else
        {
            bool found = false;
            foreach (var item in pl_dict[token])
                if (item.Method.ToString() == callback.Method.ToString())
                    found = true;
            if (!found)
                pl_dict[token].Add(callback);
        }
    }

    static public void Unregister(string token, Action<object> callback)
    {
        if (pl_dict.ContainsKey(token))
        {
            pl_dict[token].Remove(callback);
        }
    }

    static public void NotifyColleagues(string token, object args)
    {
        if (pl_dict.ContainsKey(token))
        {
            foreach (var callback in pl_dict[token])
                callback(args);
        }
    }
}

通过调解员进行的沟通如下:

Mediator.NotifyColleagues("NameOfYourAction", ObjectValue);

在这种情况下,我们会通知NameOfYourAction您需要为他传递ObjectValue。为了 NameOfYourAction 成功收到数据,有必要在课堂上或ViewModel中注册,如下所示:

private void NameOfYourAction_Mediator(object args)
{
    MyViewModel viewModel = args as MyViewModel;

    if (viewModel != null)
        viewModel.PropertyA = someValue;
}

// Somewhere, may be in constructor of class
Mediator.Register("NameOfYourAction", NameOfYourAction_Mediator);

在您的情况下,该值在ViewModel中传递ScoreData,其中将更改。

有关使用模式Mediator的更多示例,请参阅以下答案:

The Mediator Pattern

答案 1 :(得分:0)

我找到一种方式来执行此操作:

我创建了我的视图模型类型的依赖项属性:

public GameViewModel BaseViewModel
{
    get { return (GameViewModel)GetValue(baseViewModelProperty); }
    set { SetValue(baseViewModelProperty, value); }
}
public static readonly DependencyProperty baseViewModelProperty =
    DependencyProperty.Register("BaseViewModel", typeof(GameViewModel), typeof(ScoreDisplay), new PropertyMetadata(null, RegisterForScoreChange));

并将我的XAML更改为:

<local:ScoreDisplay BaseViewModel="{Binding}"/>

然后在依赖属性的PropertyChanged事件处理程序中,我能够连接我的事件。

(e.NewValue as GameViewModel).ScoreUpdated += (d as ScoreDisplay).UpdateScore;

答案 2 :(得分:0)

Anatoliy Nikolaev上面的回答很好。

作为另一种选择,我还建议调查Event Aggregator。这是一个伟大的模式,有很多用途。他们都会获得对Event Aggregator的引用,然后可以发布一个事件而另一个可以接收它并可以采取行动。如果在应用程序中启用此类内容,则多个ViewModel以完全分离的方式进行通信变得微不足道。并且随着额外的奖励测试变得简单,因为您可以轻松地模拟您的Event Aggregator以提供所需的任何数据。