简体   繁体   English

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

[英]Add multiple views inside a view using WPF and Caliburn.Micro

I'm trying to learn using Caliburn.Micro with WPF. 我正在尝试使用带有WPF的Caliburn.Micro学习。 How can I add multiple views inside a view? 如何在视图中添加多个视图?

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

Another view, with viewmodel: MyControlViewModel 另一个视图,使用viewmodel:MyControlViewModel

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

If i just add the view, it won't detect that it has a viewmodel with the appropriate name. 如果我只是添加视图,它将不会检测到它具有具有适当名称的viewmodel。 How can i bind this to it? 我怎么能把它绑在上面呢?

I have tried out with different bootstrappers and using something like cal:Bind.Model="path/classname/merge of the two". 我已尝试使用不同的bootstrappers并使用类似cal:Bind.Model =“path / classname / merge of the two”。 Have tried to add that to the mainview and to the usercontrol (MyControlView). 试图将其添加到主视图和usercontrol(MyControlView)。 I'm VERY grateful for any help regarding this matter. 我非常感谢有关此事的任何帮助。 I'm pretty much stuck, and I really want to use Caliburn.Micro :) 我几乎卡住了,我真的想用Caliburn.Micro :)

Best Regards, diamondfish 最好的问候,钻石鱼

Edit: I still can't get it to work, the problem seems to be in the bootstrapper or something else. 编辑:我仍然无法让它工作,问题似乎是在引导程序或其他东西。 But just to clarify, here is my code I'm running for a testproject. 但只是为了澄清,这是我的代码,我正在运行testproject。

MainView xaml: 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 code: MainViewModel代码:

public partial class MainViewModel : PropertyChangedBase
{
}

MyControlView xaml: 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 code: MyControlView代码:

public class MyControlViewModel : PropertyChangedBase
{
    public string MyProp
    {
        get { return "Working"; }
    }
}

Screenshot of the error: http://clip2net.com/s/1gtgt 错误的屏幕截图: http//clip2net.com/s/1gtgt

I have tried 我试过了

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

as well. 同样。 Also tried the cal-reference: 还尝试了cal-reference:

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

Screenshot of my project http://clip2net.com/s/1gthM 我的项目http://clip2net.com/s/1gthM的屏幕截图

Since the documentation mostly is for silverlight and sometimes is for Caliburn and not CM, I might have implemented the bootstrapper wrong. 由于文档主要用于silverlight,有时用于Caliburn而不是CM,我可能已经错误地实现了bootstrapper。 For this test-project, it's just like this: (with the .xaml-change in App.xaml) 对于这个测试项目,它就像这样:(在App.xaml中使用.xaml-change)

public class BootStrapper : Bootstrapper<MainViewModel>
{
}

Please help me out here! 请帮帮我吧! It seems like it is some basic stuff I'm missing :) 好像这是我缺少的一些基本内容:)

EDIT - New (more complete) Answer Below: 编辑 - 新(更完整)答案如下:

Ok, CM is doing a lot of stuff for you, it's all about getting your classes and xaml prepared for CM to be able to find it. 好的,CM正在为你做很多事情,这都是为了让你的类和xaml为CM准备好找到它。 As said above, I prefer to be write code explicit, rather than rely in implicit code assumptions by the framework. 如上所述,我更喜欢将代码显式化,而不是依赖于框架的隐式代码假设。

So, the Bootstrapper, from the default CM project is just fine. 所以,来自默认CM项目的Bootstrapper就好了。

public class AppBootstrapper : Bootstrapper<MainViewModel>
{
    // ... You shouldn't need to change much, if anything
}

The section `Bootstrapper' is very important, is it indicates which ViewModel is your first, or main screen, when the app starts up. “Bootstrapper”部分非常重要,它指示应用程序启动时哪个ViewModel是您的第一个或主屏幕。

[Export(Typeof(MainViewModel))]
public class MainViewModel : Screen,  IShell
{
    [ImportingConstructor]
    public MainViewModel(YourFirstViewModel firstViewModel, YourSecondViewModel secondviewModel) // etc, for each child ViewModel
    {
    }
}

In the [ImportingConstructor] you don't need to do anything other than specify that the MainViewModel requires the presence of the other ViewModels. [ImportingConstructor] ,除了指定MainViewModel需要存在其他ViewModel之外,您不需要执行任何操作。 In my particular case, I like my MainViewModel to be a container, and container only, the event logic is handled elsewhere. 在我的特定情况下,我喜欢我的MainViewModel是一个容器,只有容器,事件逻辑在别处处理。 But you could just as easily have your Handle logic here - but that's a while other discussion. 但你可以很容易地在这里使用你的Handle逻辑 - 但这是其他一些讨论。

Now each child View Model also needs to export themselves so CM knows where to find them. 现在每个子视图模型也需要自己导出,因此CM知道在哪里找到它们。

[Export(Typeof(YourFirstViewModel))]
public class YourFirstViewModel : IShell
{
    // VM properties and events here
}

No need to specify an Importing Constructor if you are just using a default constructor. 如果您只是使用默认构造函数,则无需指定导入构造函数。

Now, each of your Views for these will look something like: 现在,您对这些视图的每个视图将如下所示:

<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 or one of the child-views 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>

What the heck is actually going on here? 到底是怎么回事?

Well, CM sees in the bootstrapper, that MainViewModel is the starting point because of the line specifying public class AppBootstrapper : Bootstrapper<MainViewModel> . 好吧,CM在引导程序中看到,由于指定public class AppBootstrapper : Bootstrapper<MainViewModel>的行,MainViewModel是起点。 MainViewModel requires that a YourFirstViewModel and YourSecondViewModel (and other ViewModels) are required in it's constructor, so CM constructs each one. MainViewModel要求在其构造函数中需要YourFirstViewModelYourSecondViewModel (以及其他ViewModel),因此CM构造每个。 All of these ViewModels end up in the IoC (making your life much easier later - again, a whole other discussion). 所有这些ViewModel最终都会出现在IoC中(以后会让您的生活变得更轻松 - 再次,整个其他讨论)。

CM handles assigning the datacontext, on your behalf, to each of the views because you specify which VM to bind to with the line like cal:Bind.Model="Your.Namespace.ViewModels.YourFirstViewModel" CM处理代表您为每个视图分配datacontext,因为您指定要绑定到哪个VM的行如cal:Bind.Model="Your.Namespace.ViewModels.YourFirstViewModel"

With any luck, that should get you started. 运气好的话,应该让你开始吧。 Also refer to the CM example project Caliburn.Micro.HelloEventAggregator as it does exactly what you are looking for (Although, it's described as an Event Aggregator demo, which is also very useful - but again, another discussion) 另请参阅CM示例项目Caliburn.Micro.HelloEventAggregator因为它完全符合您的要求(虽然,它被描述为事件聚合器演示,这也非常有用 - 但同样,另一个讨论)

(Original Answer for reverence, below) (尊敬的原始答案,见下文)

You need to do this: 你需要这样做:

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

Notice the line cal:Bind.Model="Your.Namespace.Here.YourViewModel" which specifies the exact View Model to bind this View to. 注意行cal:Bind.Model="Your.Namespace.Here.YourViewModel" ,它指定将此视图绑定到的确切视图模型。

Don't forget to export your class type, or cm can't find it. 不要忘记导出您的类类型,否则cm无法找到它。

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

Then you can nest your User Controls as you see fit. 然后,您可以根据需要嵌套用户控件。 It's a very good way to make use of CM, and you will find it highly scalable. 这是一种使用CM的非常好的方法,你会发现它具有高度的可扩展性。 The only weakness is that the View and ViewModel must be in the same project (as far as I can tell). 唯一的缺点是View和ViewModel必须在同一个项目中(据我所知)。 But the strength of this approach is you can separate the View and View Model classes into different Namespaces (within the same project) if you wish, to keep things organized. 但是这种方法的优点是,如果您愿意,可以将View和View Model类分离到不同的命名空间(在同一个项目中),以保持组织有序。

As a commentary on cm I prefer this method, actually, even if I don't have to nest View UserControls and such. 作为对cm的评论,我更喜欢这种方法,实际上,即使我不需要嵌套View UserControl等。 I would rather explicitly declare witch VM a View is bound to (and still let CM handle all the heavy lifting in IoC) than let cm "figure it out" from implied code. 我宁愿明确地声明一个View被绑定的女巫VM(并且仍然允许CM处理IoC中所有繁重的工作),而不是让cm从隐含代码中“弄明白”。

Even with a good framework: explicit code is more maintainable than implied code. 即使有一个好的框架:显式代码比隐含代码更易于维护。 Specifying the bound View Model has the benefit of clearly stating what your data context is expected to be, so you won't need to guess later. 指定绑定的视图模型的好处是可以清楚地说明您的数据上下文是什么,因此您不需要稍后猜测。

A better approach is to use ContentControl on your main view, and give it the same name as a public property on your MainViewModel which is of type MyControlViewModel . 更好的方法是在主视图上使用ContentControl ,并在MainViewModel为其提供与MyControlViewModel类型相同的公共属性。 Eg 例如

MainView.xaml MainView.xaml

<ContentControl x:Name="MyControlViewModel" />

MainViewModel.cs 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(...); }
}

in file App.xaml.cs, in method GetInstance add the following lines 在文件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 } 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM