C# 使用WPF和Caliburn.Micro在视图中添加多个视图

C# 使用WPF和Caliburn.Micro在视图中添加多个视图,c#,wpf,mvvm,caliburn.micro,caliburn,C#,Wpf,Mvvm,Caliburn.micro,Caliburn,我正在尝试学习使用Caliburn.Micro和WPF。如何在一个视图中添加多个视图 <Window x:Class="ProjectName.Views.MainView" ...> <Grid> <views:MyControlView /> </Grid> </Window> MyControlView xaml: <Window x:Class="Test.Views.MainView

我正在尝试学习使用Caliburn.Micro和WPF。如何在一个视图中添加多个视图

<Window x:Class="ProjectName.Views.MainView"
         ...>
<Grid>
        <views:MyControlView  />
</Grid>
</Window>
MyControlView xaml:

<Window x:Class="Test.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
    xmlns:views="clr-namespace:Test.Views"
    Title="MainWindow" Height="360" Width="640">
<Grid>
    <views:MyControlView />
</Grid>
<UserControl x:Class="Test.Views.MyControlView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
         cal:Bind.Model="Test.MyControlViewModel"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <TextBlock Text="{Binding MyProp}"/>
</Grid>
错误的屏幕截图:

我试过了

cal:Bind.Model="Test.ViewModels.MyControlViewModel" 
还有。还尝试了cal参考:

xmlns:cal="http://www.caliburnproject.org"
我的项目截图

由于文档主要是针对silverlight的,有时是针对Caliburn的,而不是针对CM的,所以我可能错误地实现了引导程序。对于这个测试项目,它是这样的:(App.xaml中的.xaml更改)

公共类引导程序:引导程序
{
}
请帮帮我!这似乎是我缺少的一些基本东西:)

编辑-新的(更完整的)答案如下:

好的,C.M为您做了很多事情,这都是为了让您的类和xaml为C.M找到它做好准备。如上所述,我更喜欢显式地编写代码,而不是依赖于框架的隐式代码假设

因此,来自默认C.M项目的引导程序是很好的

public class AppBootstrapper : Bootstrapper<MainViewModel>
{
    // ... You shouldn't need to change much, if anything
}
[ImportingConstructor]
中,除了指定MainViewModel需要存在其他ViewModel之外,无需执行任何操作。在我的特殊情况下,我希望我的MainViewModel是一个容器,并且仅是容器,事件逻辑在别处处理。但是你也可以很容易地在这里处理你的逻辑-但那是另外一段时间的讨论

现在,每个子视图模型也需要导出它们自己,以便C.M知道在哪里可以找到它们

[Export(Typeof(YourFirstViewModel))]
public class YourFirstViewModel : IShell
{
    // VM properties and events here
}
如果只使用默认构造函数,则无需指定导入构造函数

现在,这些视图的每个视图都类似于:

<UserControl x:Class="Your.Namespace.MainView"
             xmlns:views="clr-namespace:Your.Namespace.Views"
             xmlns:cal="http://www.caliburnproject.org"
             cal:Bind.Model="Your.Namespace.ViewModels.MainViewModel"
             MinWidth="800" MinHeight="600">
    <StackPanel x:Name="RootVisual">
        <views:YourFirstView />
        <views:YourSecondView />
        <!-- other controls as needed -->
    </StackPanel>
</UserControl>
请注意行
cal:Bind.Model=“Your.Namespace.Here.YourViewModel”
,它指定了要将此视图绑定到的确切视图模型

别忘了导出类类型,否则c.m找不到它

[Export(typeof(YourViewModel))]
public class YourViewModel : IShell
{
    ...
}
然后,您可以根据需要嵌套用户控件。这是使用C.M的一个非常好的方法,您会发现它具有高度的可扩展性。唯一的缺点是视图和ViewModel必须在同一个项目中(据我所知)。但这种方法的优点是,如果愿意,可以将视图和视图模型类分离到不同的名称空间(在同一个项目中),以保持组织

实际上,作为对c.m的评论,我更喜欢这种方法,即使我不必嵌套View UserControls等等。我宁愿明确地声明视图是必须的(并且仍然让C.M处理IoC中的所有繁重工作),而不是让C.M从隐含的代码中“弄明白”


即使有一个好的框架:显式代码比隐含代码更易于维护。指定绑定视图模型的好处是可以清楚地说明您的数据上下文预期是什么,因此以后无需猜测。

更好的方法是在主视图上使用
ContentControl
,并将其命名为
MainViewModel
上的公共属性,该属性的类型为
MyControlViewModel
。例如

MainView.xaml

<ContentControl x:Name="MyControlViewModel" />

在文件App.xaml.cs中,在方法GetInstance中添加以下行


-编辑后的帖子包括MVVM标签,欢迎来到S.O!检查anser-我添加了一个关于导出类型的部分。这是c.m查找与视图相关的ViewModel的一个重要要求。感谢您说明它应该是怎样的。好吧,我不能让它工作。请查看我原来的帖子,并检查更新的部分。我很想把这个修好:)你走对了-我们只是错过了一些愚蠢的事情。添加了关于Export.Gah的信息,它就是不起作用:我尝试添加一个MefBootStraper类并实现了IShell接口,但仍然没有任何效果。如果您有时间查看我的项目,请随时查看:)此示例显示了如何准确地执行您想要的操作。一个主视图模型(或外壳),里面有两个子视图模型。回答得很好,这应该取代Caliburn的入门/n00b指南:)我让它工作起来了。但它似乎没有那么多地利用C.M?虽然这是一个很好的方法,但非常感谢!我还发现,在使用ContentControl时,在VS中编辑时,在MainView中看不到MyControl的界面。有没有办法做到这一点?您正在使用CM,这就是将ContentControl的名称与视图模型属性的名称相匹配,定位视图,将视图注入ContentControl,并将该视图的控件绑定到视图模型属性。这将是使用Caliburn.Micro查看合成的推荐方法。至于visual studio designer内部正在运行的约定,我认为目前不可能实现,因此您需要单独编辑每个视图的接口。修复了在引导程序中使用Ninject时我的值不能为null的错误。非常感谢:)
[Export(Typeof(MainViewModel))]
public class MainViewModel : Screen,  IShell
{
    [ImportingConstructor]
    public MainViewModel(YourFirstViewModel firstViewModel, YourSecondViewModel secondviewModel) // etc, for each child ViewModel
    {
    }
}
[Export(Typeof(YourFirstViewModel))]
public class YourFirstViewModel : IShell
{
    // VM properties and events here
}
<UserControl x:Class="Your.Namespace.MainView"
             xmlns:views="clr-namespace:Your.Namespace.Views"
             xmlns:cal="http://www.caliburnproject.org"
             cal:Bind.Model="Your.Namespace.ViewModels.MainViewModel"
             MinWidth="800" MinHeight="600">
    <StackPanel x:Name="RootVisual">
        <views:YourFirstView />
        <views:YourSecondView />
        <!-- other controls as needed -->
    </StackPanel>
</UserControl>
<UserControl x:Class="Your.Namespace.Views.YourFirstView"
             xmlns:cal="http://www.caliburnproject.org"
             cal:Bind.Model="Your.Namespace.ViewModels.YourFirstViewModel"
             MinWidth="800" MinHeight="600">
    <Grid x:Name="RootVisual">
        <!-- A bunch of controls here -->
    </Grid>
</UserControl>
<UserControl x:Class="Your.Namespace.Here.YourView"
             xmlns:cal="http://www.caliburnproject.org"
             cal:Bind.Model="Your.Namespace.Here.YourViewModel"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="1024">
  <YourControlLayout />
</UserControl>
[Export(typeof(YourViewModel))]
public class YourViewModel : IShell
{
    ...
}
<ContentControl x:Name="MyControlViewModel" />
// Constructor
public MainViewModel()
{
  // It would be better to use dependency injection here
  this.MyControlViewModel = new MyControlViewModel();     
}

public MyControlViewModel MyControlViewModel
{
  get { return this.myControlViewModel; }
  set { this.myControlViewModel = value; this.NotifyOfPropertyChanged(...); }
}
protected override object GetInstance(Type service, string key)
{
    if (service == null && !string.IsNullOrWhiteSpace(key))
    {
        service = Type.GetType(key);
        key = null;
    }
    // the rest of method
}