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

Posted

技术标签:

【中文标题】使用 WPF 和 Caliburn.Micro 在视图中添加多个视图【英文标题】:Add multiple views inside a view using WPF and Caliburn.Micro 【发布时间】:2011-12-14 08:22:14 【问题描述】:

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

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

另一个视图,带有视图模型:MyControlViewModel

<UserControl x:Class="ProjectName.Views.MyControlView"
         ...>
<Grid>
    ...
</Grid>
</UserControl>

如果我只是添加视图,它不会检测到它有一个具有适当名称的视图模型。我怎样才能将它绑定到它?

我尝试过使用不同的引导程序并使用类似 cal:Bind.Model="path/classname/merge of the two" 之类的东西。已尝试将其添加到主视图和用户控件(MyControlView)。我非常感谢有关此事的任何帮助。我几乎被卡住了,我真的很想使用 Caliburn.Micro :)

最好的问候, 钻石鱼

编辑:我仍然无法让它工作,问题似乎出在引导程序或其他东西上。但为了澄清,这是我正在为一个测试项目运行的代码。

MainView 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>

MainViewModel 代码:

public partial class MainViewModel : PropertyChangedBase


MyControlView xaml:

<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>

MyControlView 代码:

public class MyControlViewModel : PropertyChangedBase

    public string MyProp
    
        get  return "Working"; 
    

错误截图:http://clip2net.com/s/1gtgt

我试过了

cal:Bind.Model="Test.ViewModels.MyControlViewModel" 

也是。还尝试了 cal-reference:

xmlns:cal="http://www.caliburnproject.org"

我的项目截图http://clip2net.com/s/1gthM

由于文档主要是针对 silverlight,有时是针对 Caliburn 而不是 CM,因此我可能错误地实现了引导程序。对于这个测试项目,它就像这样:(使用 App.xaml 中的 .xaml-change)

public class BootStrapper : Bootstrapper<MainViewModel>


请帮帮我!似乎这是我缺少的一些基本东西:)

【问题讨论】:

-编辑帖子以包含 MVVM 标签,欢迎来到 S.O! 检查分析器 - 我添加了关于导出类型的部分。这是 c.m 找到与 View 相关的 ViewModel 的重要要求。 【参考方案1】:

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

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

所以,默认 C.M 项目中的引导程序就可以了。

public class AppBootstrapper : Bootstrapper<MainViewModel>

    // ... You shouldn't need to change much, if anything

“Bootstrapper”部分非常重要,它指示应用启动时哪个 ViewModel 是您的第一个屏幕或主屏幕。

[Export(Typeof(MainViewModel))]
public class MainViewModel : Screen,  IShell

    [ImportingConstructor]
    public MainViewModel(YourFirstViewModel firstViewModel, YourSecondViewModel secondviewModel) // etc, for each child ViewModel
    
    

[ImportingConstructor] 中,除了指定 MainViewModel 需要存在其他 ViewModel 之外,您无需执行任何操作。在我的特殊情况下,我喜欢我的 MainViewModel 是一个容器,并且只是容器,事件逻辑在其他地方处理。但是您可以在这里轻松地使用您的 Handle 逻辑 - 但这是另一个讨论。

现在每个子视图模型也需要导出自己,以便 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>

XAMl 或其中一个子视图

<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>

这里到底发生了什么?

嗯,C.M 在引导程序中看到 MainViewModel 是起点,因为指定 public class AppBootstrapper : Bootstrapper&lt;MainViewModel&gt; 的行。 MainViewModel 要求其构造函数中需要 YourFirstViewModelYourSecondViewModel(以及其他 ViewModel),因此 C.M 构造每一个。所有这些 ViewModel 最终都会出现在 IoC 中(让您以后的生活更轻松 - 再次,完全是另一个讨论)。

C.M 代表您将数据上下文分配给每个视图,因为您可以使用 cal:Bind.Model="Your.Namespace.ViewModels.YourFirstViewModel" 之类的行指定要绑定到哪个 VM

运气好的话,这应该可以帮助您入门。另请参阅 C.M 示例项目 Caliburn.Micro.HelloEventAggregator,因为它完全符合您的要求(尽管它被描述为事件聚合器演示,这也非常有用 - 但再次讨论)

(尊敬的原始答案,如下)

你需要这样做:

<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>

注意cal:Bind.Model="Your.Namespace.Here.YourViewModel" 行,它指定了要绑定此视图的确切视图模型。

不要忘记导出你的类类型,否则 c.m 找不到它。

[Export(typeof(YourViewModel))]
public class YourViewModel : IShell

    ...

然后您可以嵌套您认为合适的用户控件。这是利用 C.M 的一种非常好的方法,您会发现它具有高度可扩展性。唯一的弱点是 View 和 ViewModel 必须在同一个项目中(据我所知)。但这种方法的优势在于,如果您愿意,您可以将 View 和 View Model 类分隔到不同的命名空间(在同一个项目中),以保持事物井井有条。

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

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

【讨论】:

感谢您说明它应该是怎样的。虽然,我无法让它工作。请查看我的原始帖子并检查更新的部分。很想解决这个问题:) 你在正确的轨道上 - 我们只是错过了一些愚蠢的东西。添加了关于导出的信息。 呸,这行不通:S 我尝试添加一个 MefBootstrapper 类并实现了 IShell 接口,但仍然没有。如果您有时间查看我的项目,请随时查看 :) johanbjarnle.se/temp/CaliburnTest.rar caliburnmicro.codeplex.com/… 这个例子展示了如何做你所追求的。一个内部有两个子视图模型的主视图模型(或外壳)。 很好的答案,这应该取代 Caliburn 的入门/n00b 指南 :)【参考方案2】:

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

MainView.xaml

<ContentControl x:Name="MyControlViewModel" />

MainViewModel.cs

// 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(...); 

【讨论】:

我得到了这个工作。但它似乎没有充分利用 C.M?虽然这样做的好方法,但非常感谢! 我还发现在VS中使用ContentControl进行编辑时,在MainView中看不到MyControl的界面。有没有办法做到这一点? 您正在使用 CM,这就是将 ContentControl 的名称与您的视图模型属性的名称相匹配、定位视图、将视图注入 ContentControl 并将该视图的控件绑定到查看模型属性。这是使用 Caliburn.Micro 查看合成的推荐方法。 至于Visual Studio Designer内部运行的约定,我认为目前还不可能,因此您需要分别编辑每个视图的界面。【参考方案3】:

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

protected override object GetInstance(Type service, string key)

    if (service == null && !string.IsNullOrWhiteSpace(key))
    
        service = Type.GetType(key);
        key = null;
    
    // the rest of method

【讨论】:

修复了在我的引导程序中使用 Ninject 时我的值不能为空的错误。非常感谢:)

以上是关于使用 WPF 和 Caliburn.Micro 在视图中添加多个视图的主要内容,如果未能解决你的问题,请参考以下文章

使用 Caliburn.Micro 的单实例 WPF 应用程序

使用 Caliburn.Micro 4.0.x 和 WPF 的对话框

WPF Caliburn.Micro 和 TabControl 与 UserControls 问题

WPF MVVM(Caliburn.Micro+Metro)-配置Caliburn.Micro

C# WPF MVVM开发框架Caliburn.Micro入门介绍①

关于使用 Caliburn.Micro MVVM WPF 进行视图导航的建议