【发布时间】:2017-01-09 11:03:03
【问题描述】:
编辑:添加具体示例来阐明我想要实现的目标。
申请方案如下:
为了使代码更简单,我将使用普通的 Messenger 类来代替 Prism 的事件聚合器。元组包含 Id 和字符串负载。
public static class Messenger
{
public static event EventHandler<Tuple<int, string>> DoWork;
public static void RaiseDoWork(int id, string path)
{
DoWork?.Invoke(null, new Tuple<int, string>(id, path));
}
}
模型实例订阅 messenger 以了解何时开始工作(如果 Id 正确),并在工作完成时通知视图模型。
public class Model
{
public int id;
public Model(int id)
{
this.id = id;
Messenger.DoWork += (sender, tuple) =>
{
if (tuple.Item1 != this.Id)
{
return;
}
var result = tuple.Item2 + " processed with id " + this.id;
this.OnWorkCompleted(result);
};
}
public event EventHandler<string> WorkCompleted;
private void OnWorkCompleted(string path)
{
this.WorkCompleted?.Invoke(null, path);
}
}
UserControlResult负责payload处理和结果输出。为了使代码更简单,我们只跟踪输出而不是将其放在 UI 上。所以 XAML 将是默认的。
代码隐藏:
public partial class UserControlResult : UserControl
{
private ResultViewModel viewModel;
public UserControlResult()
{
this.InitializeComponent();
}
public void Init(int id)
{
this.viewModel = new ResultViewModel(id);
this.DataContext = this.viewModel;
}
}
视图模型:
public class ResultViewModel
{
private Model model;
public ResultViewModel(int id)
{
this.model = new Model(id);
this.model.WorkCompleted += path =>
{
Trace.WriteLine(path);
};
}
}
UserControlButtons 包含按钮,其中一个应该通过信使开始处理UserControlResult 中的模型。为了使代码更简单,让我们省略命令实现,只显示它的处理程序。
代码隐藏:
public partial class UserControlButtons : UserControl
{
private ButtonsViewModel viewModel;
public UserControlButtons()
{
this.InitializeComponent();
}
public void Init(int id)
{
this.viewModel = new ButtonsViewModel(id);
this.DataContext = this.viewModel;
}
}
视图模型:
public class ButtonsViewModel
{
private int id;
public ButtonsViewModel(int id)
{
this.id = id;
}
// DelegateCommand implementation...
private void StartWorkingCommandHandler()
{
Messenger.RaiseDoWork(this.id, "test path");
}
}
UserControlParent 包含UserControlResult 和UserControlButtons。他唯一的职责是将 ID 传递给他们,因此他甚至不需要视图模型。
Xaml:
<StackPanel>
<uc:UserControlResult x:Name="UserControlResult" />
<uc:UserControlButtons x:Name="UserControlButtons" />
</StackPanel>
代码隐藏:
public partial class UserControlParent : UserControl
{
public UserControlParent()
{
this.InitializeComponent();
}
public void Init(int id)
{
this.UserControlResult.Init(id);
this.UserControlButtons.Init(id);
}
}
最后,MainWindow 包含两个UserControlParent 实例。它的作用是为它们分配不同的 Id。
Xaml:
<StackPanel>
<uc:UserControlParent x:Name="UserControlParent1" />
<uc:UserControlParent x:Name="UserControlParent2" />
</StackPanel>
代码隐藏:
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
this.UserControlParent1.Init(111);
this.UserControlParent2.Init(222);
}
}
这将起作用:按下UserControlButtons 中的按钮将开始在UserControlResult 模型中工作,并且UserControlParent 都将正确且独立地工作,这要归功于Id。
但我相信这个调用 Init 方法的链违反了 MVVM,因为代码隐藏(在 MVVM 中是 View)应该不 strong> 了解有关 Id 值的任何信息(与 MVVM 中的 Model 相关)。说到这里,我确信 Id 不是视图模型的一部分,因为它在 UI 中没有任何表示。
如何在不违反 MVVM 的情况下将 Id 值从顶部窗口传递到“最深”视图模型?
原始问题
这是由 3 个 UserControls 组成的 WPF 应用程序:
UserControl3 是UserControl2 内容的一部分。我在开发和使用Prism的过程中保留MVVM。
我需要从UserControl1 的view-model 调用UserControl3 中自定义类的方法(就MVVM 而言是model)。自定义类不能是单例的限制。我想通过以下方式之一做到这一点:
使用 Prism 的事件聚合器。
UserControl1view-model 是发布者,UserControl3model 是订阅者。为此,我需要在Window中创建唯一 ID,并将其传递给UserControl1和UserControl3。在
Window中创建服务实例并将其传递给UserControl1和UserControl3。那么UserControl1只会调用这个实例的方法。Window将UserControl2实例传递给UserControl1。UserControl1中的View-model只会调用UserControl2的方法,然后会调用UserControl3的方法,以此类推。
似乎 2 和 3 方法违反了 MVVM。您将如何解决这种情况?
【问题讨论】:
-
发送命令或引发事件感觉像是没有将类紧密耦合在一起的更好方法......这两个概念之间的主要区别是:命令相当“实时”或目前任务同时事件是发生在过去的事情。
-
如何有一个服务方法“in”一个控件?我建议您将服务方法放在其他地方,并让父视图模型将其传递给任何需要它们的视图模型(或者如果您不进行自动化测试,则将其设为全局单例)。任何对这个问题的解决方案即使承认存在控制也是错误的。控件不应该知道 Web 服务的存在;他们应该只知道他们的 DataContexts 的属性。
-
@EdPlunkett 我应该澄清一下:服务是指在
UserControl3的视图模型中存储为变量的普通类实例,因此它是@ MVVM 的 model 一部分987654368@。例如。该类接收图像的路径,异步加载和处理图像,然后将图像返回给UserControl3的view-model。问题是从UserControl1的视图模型正确启动这个过程。 -
@kayess 我建议您在谈论事件时指的是 1 个选项...但是如何使用命令在 不同 UserControls 的视图模型之间进行消息传递?当然,如果我理解正确的话。
-
@Sam 你一直在谈论控件。不要谈论控制,不要考虑控制。我认为您将您的程序视为其中包含视图模型的控件的集合,然后您尝试弄清楚视图模型如何进行通信。那不是 MVVM。在 MVVM 中,您的程序是视图模型的集合,然后您拥有显示它们的视图。设计视图模型关系。我再说一遍:“任何解决这个问题的方法,即使承认存在控件也是错误的。”