如何从单独的视图模型调用用户控件上的函数?
在我的场景中,我有一个带有ScoreDisplay UserControl的“主”视图:
<local:ScoreDisplay/>
此控件将显示我游戏中 n 玩家数量的得分。主视图模型连接到游戏控制器,为其提供分数更新。我需要将这些更新的分数提供给UserControl(并运行一些动画,非平凡的逻辑等)。
我看到了几个选项:
创建“Scores”依赖项属性并将其绑定到视图模型的分数集合。我用这种方法看到的唯一问题是无论何时修改它,我都需要去看看,看看有什么变化,这样我才能运行适当的动画。这样做当然是可能的,但似乎并不“正确”。
让ViewModel在UserControl上调用“UpdateScore”函数。当然,唯一的问题是ViewModel不应该知道有关View的任何信息,因此不应该有必要的参考来执行此操作。
让UserControl在视图模型上注册“ScoreUpdated”事件。这似乎是最好的选择,但我不知道如何获取ViewModel的引用以注册该事件。
哪个选项(如果有的话)是正确的方法?如果(2)或(3),我该如何实现呢?
编辑:
要明确的是,乐谱集合中的值正在发生变化(集合本身保持不变)。我可以在得分int周围放一个包装器并听取PropertyChanged,但同样,这似乎是一个简单问题的过于复杂的方法。如果那是最好的解决方案,请告诉我!
编辑2:
UpdateScore是一个函数(理论上)接受更新得分的索引和要添加到该玩家得分的值(它可以接受整个得分)。然后它会使玩家的挂钩沿着一条小路移动到新的位置。
每当玩家获得积分时就会被调用(这是一个Cribbage游戏,所以这种情况会发生很多)。视图模型附加到游戏控制器,该游戏控制器引发事件以通知VM玩家已经获得积分。视图模型基本上只需要将此信息传递给ScoreDisplay
以进行显示/动画等。
答案 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
的更多示例,请参阅以下答案:
答案 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以提供所需的任何数据。