C# 在MVVM WPF中打开新窗口
我有一个按钮,我将这个按钮绑定到ViewModel中的一个命令,比如说C# 在MVVM WPF中打开新窗口,c#,wpf,mvvm,C#,Wpf,Mvvm,我有一个按钮,我将这个按钮绑定到ViewModel中的一个命令,比如说OpenWindowCommand。当我点击按钮时,我想打开一个新窗口。但创建窗口实例并从视图模型显示窗口违反了MVVM。我已经创建了如下界面 interface IWindowService { void showWindow(object dataContext); } class WindowService : IWindowService { public void showWindow(object
OpenWindowCommand
。当我点击按钮时,我想打开一个新窗口。但创建窗口实例并从视图模型显示窗口违反了MVVM。我已经创建了如下界面
interface IWindowService
{
void showWindow(object dataContext);
}
class WindowService : IWindowService
{
public void showWindow(object dataContext)
{
ChildWindow window=new ChildWindow();
window.DataContext=dataContext;
window.Show();
}
}
而WindowService
实现这个接口,就像
interface IWindowService
{
void showWindow(object dataContext);
}
class WindowService : IWindowService
{
public void showWindow(object dataContext)
{
ChildWindow window=new ChildWindow();
window.DataContext=dataContext;
window.Show();
}
}
在这个类中,我指定了ChildWindow
。因此,该类与显示ChildWindow
紧密结合。当我想显示另一个窗口时,我必须用相同的接口和逻辑实现另一个类。如何使该类成为泛型类,以便只传递任何窗口的实例,而该类将能够打开任何窗口
我没有使用任何内置的MVVM框架。我读过很多关于StackOverflow的文章,但我找不到任何解决方案。也许您可以传递窗口类型 尝试使用
Activator.CreateInstance()
见以下问题:
chakrit的解决方案:
// determine type here
var type = typeof(MyClass);
// create an object of the type
var obj = (MyClass)Activator.CreateInstance(type);
您可以编写如下函数:
class ViewManager
{
void ShowView<T>(ViewModelBase viewModel)
where T : ViewBase, new()
{
T view = new T();
view.DataContext = viewModel;
view.Show(); // or something similar
}
}
abstract class ViewModelBase
{
public void ShowView(string viewName, object viewModel)
{
MessageBus.Post(
new Message
{
Action = "ShowView",
ViewName = viewName,
ViewModel = viewModel
});
}
}
类视图管理器
{
无效显示视图(ViewModelBase viewModel)
其中T:ViewBase,new()
{
T视图=新的T();
view.DataContext=viewModel;
view.Show();//或类似的内容
}
}
抽象类ViewModelBase
{
public void ShowView(字符串视图名、对象视图模型)
{
邮递(
新消息
{
Action=“ShowView”,
ViewName=ViewName,
ViewModel=ViewModel
});
}
}
确保ViewBase具有DataContext属性。(您可以继承UserControl)
一般来说,我会制作某种消息总线,让ViewManager侦听请求查看的消息。ViewModels将发送一条消息,要求显示视图和数据。然后,ViewManager将使用上面的代码
要防止调用ViewModel了解视图类型,可以将视图的字符串/逻辑名称传递给ViewManager,并让ViewManager将逻辑名称转换为类型。一种可能性是:
class WindowService:IWindowService
{
public void showWindow<T>(object DataContext) where T: Window, new()
{
ChildWindow window=new T();
window.Datacontext=DataContext;
window.show();
}
}
classwindowservice:IWindowService
{
public void showWindow(对象DataContext),其中T:Window,new()
{
childwindowwindow=newt();
window.Datacontext=Datacontext;
window.show();
}
}
然后你可以这样做:
windowService.showWindow<Window3>(windowThreeDataContext);
windowService.showWindow(windowThreeDataContext);
有关新约束的详细信息,请参见
注意:
new()约束仅适用于窗口将具有无参数构造函数的情况(但我认为在这种情况下这不应该是问题!)。在更一般的情况下,请参阅以了解可能性。在将DataConNext绑定到的窗口中使用contentpresenter。
然后为DataContext定义一个Datatemplate,以便wpf可以呈现DataContext。类似于我的
因此,您只需要一个带有ContentPresenter的单子窗口:
<Window x:Class="ChildWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowStartupLocation="CenterOwner" SizeToContent="WidthAndHeight">
<ContentPresenter Content="{Binding .}">
</ContentPresenter>
</Window>
您说“创建窗口实例并从视图模型显示窗口违反了MVVM”。这是正确的
您现在正在尝试创建一个接口,该接口采用VM指定的视图类型。这也是一种违反。您可能已经抽象出接口背后的创建逻辑,但仍然在请求从VM中创建视图
虚拟机应该只关心创建虚拟机。如果您真的需要一个新窗口来承载新的VM,那么就提供一个您已经完成的界面,但是这个界面不需要查看。你为什么需要这个视角?大多数(VM优先)MVVM项目使用隐式数据模板将视图与特定VM关联。虚拟机对它们一无所知
像这样:
class WindowService:IWindowService
{
public void ShowWindow(object viewModel)
{
var win = new Window();
win.Content = viewModel;
win.Show();
}
}
显然,您需要确保在app.xaml中设置了VM->View隐式模板,以使其正常工作。这只是标准的VM-first-MVVM
例如:
我发现公认的解决方案非常有用,但在实际尝试时,我发现它没有能力使UserControl(由VM->View mapping生成的视图)停靠在宿主窗口中,以占据它提供的整个区域。因此,我扩展了解决方案以包括以下功能:
public Window CreateWindowHostingViewModel(object viewModel, bool sizeToContent)
{
ContentControl contentUI = new ContentControl();
contentUI.Content = viewModel;
DockPanel dockPanel = new DockPanel();
dockPanel.Children.Add(contentUI);
Window hostWindow = new Window();
hostWindow.Content = dockPanel;
if (sizeToContent)
hostWindow.SizeToContent = SizeToContent.WidthAndHeight;
return hostWindow;
}
这里的技巧是使用DockPanel来承载从VM转换的视图
然后,如果希望窗口的大小与其内容的大小相匹配,请使用前面的方法,如下所示:
var win = CreateWindowHostingViewModel(true, viewModel)
win.Title = "Window Title";
win.Show();
或者,如果窗口大小固定,则如下所示:
var win = CreateWindowHostingViewModel(false, viewModel)
win.Title = "Window Title";
win.Width = 500;
win.Height = 300;
win.Show();
我可能错了,但我很确定您需要where T:ViewBase,new()
以便在函数中创建泛型类型的新对象?洛杉矶:这是一个同时回答的案例——当我写我的答案时,你的答案不在那里,然后我重新振作起来,发现你在这方面打败了我!抱歉Erno:)@DavidEdey-:)np这不是race@ErnodeWeerd在您的情况下,我仍然需要引用viewmodel中的视图。我不必在那里创建实例,但至少我必须引用视图。这不是MVVM吗?@DTsawant-不,你不需要。阅读我的最后一点答案。为什么需要不同的窗口类型?窗口只是视图的容器。只需使用一个通用窗口,并像映射VM->View一样使用隐式DataTemplates。VM对视图一无所知的原因之一是,可以使用多个视图以不同的方式在ViewModel中显示数据。此方法使视图和视图模型具有1:1映射。如果MyView是窗口,则此解决方案将不起作用。它将抛出一个“无法将窗口设置为样式”的错误,我希望vw:MyView
应为UserControl
类型,而不是window
@Ehsan类型,无论您的视图(即xaml)在何处定义。windowService.showWindow(windowThreeDataContext);这个语句在viewmodel中,它包含视图的名称。它不是MVVM吗