繁体   English   中英

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

[英]Invoke user control function from view model

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

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

<local:ScoreDisplay/>

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

我看到一些选择:

  1. 创建一个“分数”依赖项属性,并将其绑定到分数的视图模型集合。 我看到的这种方法的唯一问题是,只要对其进行了修改,就需要查看更改后的内容,以便可以运行适当的动画。 这样做当然是可能的,但似乎并不“正确”。

  2. 让ViewModel在UserControl上调用“ UpdateScore”函数。 当然,唯一的问题是ViewModel不应该对View一无所知,因此不应该具有执行此操作所需的引用。

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

哪种选择是正确的方法? 如果是(2)或(3),如何实现呢?

编辑:

需要明确的是,分数集合中的正在更改(集合本身保持不变)。 我可以在分数int周围放一个包装纸,然后听PropertyChanged,但是同样,对于一个简单的问题,这似乎过于复杂。 如果这是最好的解决方案,请告诉我!

编辑2:

UpdateScore是一个功能(理论上),它接受更新分数的索引以及要添加到该玩家分数中的值(它可以接受整个分数)。 然后,它使玩家的钉子沿着a脚轨迹移动到新位置。

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

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

Mediator模式有多个实施例,但是我最喜欢XAML Guy的实现,它很简单明了The Mediator Pattern

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

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

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

一个用于UserControl和Window的ViewModel或单独的ViewModel

我找到了一种方法来做到这一点:

我做了一个我的视图模型类型的依赖属性:

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;

Anatoliy Nikolaev在上面的回答是一个很好的答案。

作为另一种选择,我还建议您调查事件汇总器。 这是一个很好的模式,有很多用途。 他们都将获得对事件聚合器的引用,然后一个可以发布一个事件,另一个可以接收到该事件并可以采取措施。 如果在您的应用程序中启用了类似的功能,则对于多个ViewModel以完全分离的方式进行通信将变得微不足道。 随着额外的奖励测试变得简单,您可以轻松地模拟出Event Aggregator以提供所需的任何数据。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM