从视图模型调用用户控制函数
本文关键字:控制 函数 用户 调用 视图 模型 | 更新日期: 2023-09-27 17:55:05
如何从单独的视图模型调用用户控件上的函数?
在我的场景中,我有一个"主"视图与ScoreDisplay UserControl:
<local:ScoreDisplay/>
这个控件将显示n玩家在我的游戏中的分数。主视图模型连接到游戏控制器,为其提供分数更新。我需要得到这些更新的分数到UserControl(并运行一些动画,非琐碎的逻辑,等)
我看到一些选项:
创建一个"Scores"依赖属性,并将其绑定到视图模型的分数集合。我认为这种方法的唯一问题是,无论何时修改它,我都需要查看更改的内容,以便运行适当的动画。这样做当然是可能的,但似乎不"正确"。
让ViewModel调用UserControl上的"UpdateScore"函数。唯一的问题,当然,是ViewModel不应该知道任何关于视图,所以不应该有必要的引用来做这个
让UserControl注册视图模型上的"ScoreUpdated"事件。这似乎是最好的选择,但我不知道如何获得对ViewModel的引用,以便注册事件。
如果有的话,哪个选项是正确的方法?如果(2)或(3),我如何实现这一点?
编辑:要明确的是,分数集合中的值正在改变(集合本身保持不变)。我可以在score int周围放一个包装器,然后收听PropertyChanged,但是,对于一个简单的问题,这似乎是一种过于复杂的方法。如果这是最好的解决方案,请让我知道!编辑2:UpdateScore是一个函数,(理论上)接受更新分数的索引和要添加到玩家分数上的值(它可以接受整个分数)。然后,它会使玩家的木桩沿着木桩轨道移动到新的位置。
每当玩家获得点数时,它就会被调用(这是一款克里比奇游戏,所以这种情况经常发生)。视图模型附加到一个游戏控制器,该控制器引发一个事件,通知VM玩家已获得积分。视图模型基本上只需要将此信息传递给ScoreDisplay
以显示/动画等
在这种情况下,我们可以应用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);
在您的例子中,值在ViewModel中传递ScoreData
,它将被更改。
有关使用模式Mediator
的更多示例,请参阅以下答案:
一个ViewModel用于UserControl和Window或单独的ViewModel
Anatoliy Nikolaev的回答很好。
作为另一个选项,我还建议查看事件聚合器。这是一个很好的模式,有很多用途。它们都将获得对Event Aggregator的引用,然后一个可以发布事件,另一个可以接收事件并采取行动。如果你在你的应用程序中启用这样的东西,那么多个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;