Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/311.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在MVVM WPF中打开新窗口_C#_Wpf_Mvvm - Fatal编程技术网

C# 在MVVM WPF中打开新窗口

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

我有一个按钮,我将这个按钮绑定到ViewModel中的一个命令,比如说
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吗