15

我正在尝试学习将 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 :)

最好的问候,钻石鱼

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

主视图 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>

主视图模型代码:

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

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

4

3 回答 3

17

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

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

因此,默认 CM 项目中的 Bootstrapper 就可以了。

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 逻辑 - 但这是另一个讨论。

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

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

这到底是怎么回事?

好吧,CM 在引导程序中看到 MainViewModel 是起点,因为指定public class AppBootstrapper : Bootstrapper<MainViewModel>. MainViewModel要求在其构造函数中需要 aYourFirstViewModelYourSecondViewModel(和其他 ViewModel),因此 CM 构造每个。所有这些 ViewModel 最终都出现在 IoC 中(让您以后的生活更轻松 - 再次,完全是另一个讨论)。

CM 代表您处理将数据上下文分配给每个视图,因为您指定要绑定到哪个 VM,例如cal:Bind.Model="Your.Namespace.ViewModels.YourFirstViewModel"

运气好的话,这应该可以帮助您入门。另请参阅 CM 示例项目Caliburn.Micro.HelloEventAggregator,因为它完全符合您的要求(尽管它被描述为 Event Aggregator 演示,这也非常有用 - 但同样,另一个讨论)

(以下为尊敬的原始答案)

你需要这样做:

<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"指定要将此视图绑定到的确切视图模型的行。

不要忘记导出你的类类型,否则cm找不到。

[Export(typeof(YourViewModel))]
public class YourViewModel : IShell
{
    ...
}

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

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

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

于 2011-10-24T18:15:17.313 回答
17

更好的方法是ContentControl在您的主视图上使用,并将其命名为与您MainViewModel的类型为MyControlViewModel. 例如

主视图.xaml

<ContentControl x:Name="MyControlViewModel" />

主视图模型.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(...); }
}
于 2011-10-24T19:44:08.257 回答
1

在文件 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
}
于 2013-04-04T12:42:08.030 回答