C# 使用 MVVM 在 WPF 中创建新窗口的最佳方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2108949/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
The best approach to create new window in WPF using MVVM
提问by Budda
In the neighbour post: How should the ViewModel close the form?I've posted my vision how to close windows with MVVM usage. And now I have a question: how to open them.
在邻居帖子中:ViewModel 应该如何关闭表单?我已经发布了如何使用 MVVM 关闭窗口的愿景。现在我有一个问题:如何打开它们。
I have a main window (main view). If user clicks on the "Show" button then "Demo" window (modal dialog) should be displayed. What is a preferable way to create and open windows using MVVM pattern? I see two general approaches:
我有一个主窗口(主视图)。如果用户单击“显示”按钮,则应显示“演示”窗口(模式对话框)。使用 MVVM 模式创建和打开窗口的首选方法是什么?我看到两种一般方法:
The 1st one (probably the simplest). Event handler "ShowButton_Click" should be implemented in the code behind of the main window in way like this:
第一个(可能是最简单的)。事件处理程序“ShowButton_Click”应该在主窗口后面的代码中实现,如下所示:
private void ModifyButton_Click(object sender, RoutedEventArgs e)
{
ShowWindow wnd = new ShowWindow(anyKindOfData);
bool? res = wnd.ShowDialog();
if (res != null && res.Value)
{
// ... store changes if neecssary
}
}
- If we "Show" button state should be changed (enabled/disabled) we will need to add logic that will manage button state;
- The source code is very similar to "old-style" WinForms and MFC sources - I not sure if this is good or bad, please advise.
- Something else that I've missed?
- 如果我们“显示”按钮状态应该改变(启用/禁用),我们将需要添加管理按钮状态的逻辑;
- 源代码与“旧式”WinForms 和 MFC 源代码非常相似——我不确定这是好是坏,请指教。
- 还有什么我错过的吗?
Another approach:
另一种方法:
In the MainWindowViewModel we will implement "ShowCommand" property that will return ICommand interface of the command. Comman in turn:
在 MainWindowViewModel 中,我们将实现“ShowCommand”属性,该属性将返回命令的 ICommand 接口。依次命令:
- will raise "ShowDialogEvent";
- will manage button state.
- 将引发“ShowDialogEvent”;
- 将管理按钮状态。
This approach will be more suitable for the MVVM but will require additional coding: ViewModel class can't "show dialog" so MainWindowViewModel will only raise "ShowDialogEvent", the MainWindowView we will need to add event handler in its MainWindow_Loaded method, something like this:
这种方法更适合 MVVM,但需要额外的编码:ViewModel 类不能“显示对话框”所以 MainWindowViewModel 只会引发“ShowDialogEvent”,我们需要在 MainWindowView 的 MainWindow_Loaded 方法中添加事件处理程序,像这样:
((MainWindowViewModel)DataContext).ShowDialogEvent += ShowDialog;
(ShowDialog - similar to the 'ModifyButton_Click' method.)
(ShowDialog - 类似于 'ModifyButton_Click' 方法。)
So my questions are: 1. Do you see any other approach? 2. Do you think one of the listed is good or bad? (why?)
所以我的问题是: 1. 你看到任何其他方法吗?2. 您认为其中一项是好是坏?(为什么?)
Any other thoughts are welcome.
欢迎任何其他想法。
Thanks.
谢谢。
采纳答案by Benny Jobigan
I was thinking about this issue recently too. Here's an idea I had if you use Unityin your project as a 'container' or whatever for dependency injection. I guess normally you'd override App.OnStartup()
and create your model, view model, and view there, and give each the appropriate references. Using Unity, you give the container a reference to the model, then use the container to 'resolve' the view. The Unity container injects your view model, so you never directly instantiate it. Once your view is resolved, you call Show()
on it.
我最近也在考虑这个问题。如果您在项目中将Unity用作“容器”或用于依赖项注入的任何内容,这是我的一个想法。我想通常你会覆盖App.OnStartup()
并创建你的模型、查看模型和查看那里,并给每个适当的引用。使用 Unity,您可以为容器提供对模型的引用,然后使用容器来“解析”视图。Unity 容器会注入您的视图模型,因此您永远不会直接实例化它。一旦您的观点得到解决,您Show()
就可以调用它。
In an example video I watched, the Unity container was created as a local variable in OnStartup
. What if you created it as a public static readonly property in your App class? You could then use it in your main view model to create your new windows, automatically injecting whatever resources the new view needs. Something like App.Container.Resolve<MyChildView>().ShowDialog();
.
在我观看的示例视频中,Unity 容器被创建为OnStartup
. 如果您在 App 类中将其创建为公共静态只读属性会怎样?然后你可以在你的主视图模型中使用它来创建你的新窗口,自动注入新视图需要的任何资源。类似的东西App.Container.Resolve<MyChildView>().ShowDialog();
。
I suppose you could somehow mock the result of that call to the Unity container in your tests. Alternatively, perhaps you could write methods like ShowMyChildView()
in the App class, which basically just does what I described above. It might be easy to mock a call to App.ShowMyChildView()
since it would just return a bool?
, eh?
我想您可以在测试中以某种方式模拟对 Unity 容器的调用结果。或者,也许您可以编写类似于ShowMyChildView()
App 类中的方法,它基本上就是我上面描述的那样。模拟调用可能很容易,App.ShowMyChildView()
因为它只会返回 a bool?
,嗯?
Well, that might not really be any better than just using new MyChildView()
, but it's a little idea I had. I thought I'd share it. =)
好吧,这可能并不比仅使用 更好new MyChildView()
,但这是我的一个小想法。我以为我会分享它。=)
回答by adrianm
I use a controller which handles all information passing between views. All viewmodels use methods in the controller to request more information which can be implemented as dialogs, other views etc.
我使用一个控制器来处理视图之间传递的所有信息。所有视图模型都使用控制器中的方法来请求更多信息,这些信息可以实现为对话框、其他视图等。
It looks something like this:
它看起来像这样:
class MainViewModel {
public MainViewModel(IView view, IModel model, IController controller) {
mModel = model;
mController = controller;
mView = view;
view.DataContext = this;
}
public ICommand ShowCommand = new DelegateCommand(o=> {
mResult = controller.GetSomeData(mSomeData);
});
}
class Controller : IController {
public void OpenMainView() {
IView view = new MainView();
new MainViewModel(view, somemodel, this);
}
public int GetSomeData(object anyKindOfData) {
ShowWindow wnd = new ShowWindow(anyKindOfData);
bool? res = wnd.ShowDialog();
...
}
}
回答by Roboblob
Take a look at my current MVVM solution for showing Modal Dialogs in Silverlight. It solves most of the issues you mentioned yet its completely abstracted from platform specific things and can be reused. Also i used no code-behind only binding with DelegateCommands that implement ICommand. Dialog is basically a View - a separate control that has its own ViewModel and it is shown from the ViewModel of the main screen but triggered from the UI via DelagateCommand binding.
看看我当前用于在 Silverlight 中显示模态对话框的 MVVM 解决方案。它解决了你提到的大部分问题,但它完全从平台特定的东西中抽象出来,可以重用。我也没有使用代码隐藏,只与实现 ICommand 的 DelegateCommands 绑定。Dialog 基本上是一个视图 - 一个单独的控件,它有自己的 ViewModel,它从主屏幕的 ViewModel 显示,但通过 DelagateCommand 绑定从 UI 触发。
See full Silverlight 4 solution here Modal dialogs with MVVM and Silverlight 4
在此处查看完整的 Silverlight 4 解决方案使用 MVVM 和 Silverlight 4 的模态对话框
回答by jbe
My approach is similar to adrianm's. However, in my case the Controller never works with the concrete View types. The Controller is completely decoupled of the View - in the same way as the ViewModel.
我的方法类似于阿德里安的。但是,在我的情况下,控制器从不与具体的视图类型一起工作。Controller 与 View 完全解耦 - 与 ViewModel 的方式相同。
How this works can be seen in the ViewModel example of WPF Application Framework (WAF).
在WPF 应用程序框架 (WAF)的 ViewModel 示例中可以看到这是如何工作的。
.
.
Best Regards,
此致,
jbe
杰贝
回答by arconaut
Some MVVM frameworks (e.g. MVVM Light) make use of the Mediator pattern. So to open a new Window (or create any View) some View-specific code will subscribe to messages from the mediator and the ViewModel will send those messages.
一些 MVVM 框架(例如MVVM Light)使用了Mediator 模式。因此,要打开一个新窗口(或创建任何视图),一些特定于视图的代码将订阅来自中介的消息,而 ViewModel 将发送这些消息。
Like this:
像这样:
Subsription
订阅
Messenger.Default.Register<DialogMessage>(this, ProcessDialogMessage);
...
private void ProcessDialogMessage(DialogMessage message)
{
// Instantiate new view depending on the message details
}
In ViewModel
在视图模型中
Messenger.Default.Send(new DialogMessage(...));
I prefer to do the subscription in a singleton class, which "lives" as long as the UI part of the application does. To sum up: ViewModel passes messages like "I need to create a view" and the UI listens to those messages and acts on them.
我更喜欢在单例类中进行订阅,只要应用程序的 UI 部分“存在”。总结一下:ViewModel 传递诸如“我需要创建视图”之类的消息,UI 会侦听这些消息并对其采取行动。
There's no "ideal" approach though, for sure.
当然,没有“理想”的方法。
回答by Liero
I'm a bit late, but I find existing answers insufficient. I will explain why. In general:
我有点晚了,但我发现现有的答案不够。我会解释原因。一般来说:
- it's ok to access ViewModels from View,
- it's wrong to access Views from ViewModels, because it introduces circular dependency and makes the ViewModels hard to test.
- 可以从 View 访问 ViewModels,
- 从 ViewModels 访问 Views 是错误的,因为它引入了循环依赖并使 ViewModels 难以测试。
Benny Jobigan's anwer:
本尼·乔比根的回答:
App.Container.Resolve<MyChildView>().ShowDialog();
this actually does not solve anything. You are accessing your View from ViewModel in a tigtly coupled fashion. The only difference from new MyChildView().ShowDialog()
is that you went trough a layer of indirection. I don't see any advantage over directly calling the MyChildView ctor.
这实际上并不能解决任何问题。您正在以紧密耦合的方式从 ViewModel 访问您的视图。唯一的区别new MyChildView().ShowDialog()
是你通过了一个间接层。与直接调用 MyChildView ctor 相比,我没有看到任何优势。
It would be cleaner if you used interface for the view:
如果您使用视图界面会更干净:
App.Container.Resolve<IMyChildView>().ShowDialog();`
Now the ViewModel is not tigtly coupled to the view. However I find it quite impractical to create interface for each view.
现在 ViewModel 并没有紧密地耦合到视图。但是我发现为每个视图创建界面是不切实际的。
arconaut's anwer:
arconaut 的回答:
Messenger.Default.Send(new DialogMessage(...));
it's better. It seems like Messenger or EventAggregator or another pub/sub patterns are universal solution for everyhing in MVVM :) The disadvantage is that it's harder to debug or to navigate to DialogMessageHandler
. It's too indirect imho. For example, how would you read output form the Dialog? by modifying DialogMessage?
更好。似乎 Messenger 或 EventAggregator 或其他发布/订阅模式是 MVVM 中所有内容的通用解决方案 :) 缺点是更难调试或导航到DialogMessageHandler
. 恕我直言,这太间接了。例如,您将如何从对话框中读取输出?通过修改DialogMessage?
My Solution:
我的解决方案:
you can open window from MainWindowViewModel like this:
您可以像这样从 MainWindowViewModel 打开窗口:
var childWindowViewModel = new MyChildWindowViewModel(); //you can set parameters here if necessary
var dialogResult = DialogService.ShowModal(childWindowViewModel);
if (dialogResult == true) {
//you can read user input from childWindowViewModel
}
DialogService takes just dialog's ViewModel, so your viewmodels are totally independent from Views. At runtime, DialogService can find appropriate view (using naming convention for example) and shows it, or it can be easily mocked in unit tests.
DialogService 只需要对话框的 ViewModel,因此您的视图模型完全独立于视图。在运行时,DialogService 可以找到合适的视图(例如使用命名约定)并显示它,或者可以在单元测试中轻松模拟它。
in my case I use this interfaces:
在我的情况下,我使用这个接口:
interface IDialogService
{
void Show(IDialogViewModel dialog);
void Close(IDialogViewModel dialog);
bool? ShowModal(IDialogViewModel dialog);
MessageBoxResult ShowMessageBox(string message, string caption = null, MessageBoxImage icon = MessageBoxImage.No...);
}
interface IDialogViewModel
{
string Caption {get;}
IEnumerable<DialogButton> Buttons {get;}
}
where DialogButton specifies DialogResult or ICommand or both.
其中 DialogButton 指定 DialogResult 或 ICommand 或两者。