Добавьте несколько представлений внутри представления с помощью WPF и Caliburn.Микро

Я пытаюсь научиться использовать Caliburn.Микро с WPF. Как добавить несколько представлений в представление?

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

другой вид, с viewmodel: MyControlViewModel

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

если я просто добавлю представление, он не обнаружит, что у него есть viewmodel с соответствующим именем. Как я могу привязать это к нему?

Я пробовал с разными загрузчиками и использовал что-то вроде cal:Bind.Model= "путь/имя класса/слияние двух". Попробовали добавить к mainview и к usercontrol (MyControlView). Я очень благодарен за любую помощь в этом вопросе. Я в значительной степени застрял, и я действительно хочу использовать Caliburn.Micro:)

С Наилучшими Пожеланиями, diamondfish

Edit: я все еще не могу заставить его работать, проблема, похоже, в загрузчике или что-то еще. Но чтобы уточнить, вот мой код, который я запускаю для testproject.

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-ссылку:

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

скриншот моего проекта http://clip2net.com/s/1gthM

поскольку документация в основном предназначена для silverlight, а иногда для Caliburn, а не CM, возможно, я неправильно реализовал загрузчик. Для этого тест-проекта, это так же, как это: (с .в XAML-изменения в приложение.язык XAML)

public class BootStrapper : Bootstrapper<MainViewModel>
{
}

пожалуйста, помогите мне здесь! Кажется, это некоторые основные вещи, которые мне не хватает:)

3 ответов


EDIT-новый (более полный) ответ ниже:

хорошо, 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 требует присутствия других ViewModels. В моем конкретном случае мне нравится, чтобы моя 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>

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<MainViewModel>. MainViewModel требует YourFirstViewModel и YourSecondViewModel (и другие ViewModels) требуются в его конструкторе, поэтому C. M создает каждый из них. Все эти ViewModels заканчиваются в МОК (что делает вашу жизнь намного проще позже - опять же, целая другая дискуссия).

C. M обрабатывает назначение datacontext от вашего имени каждому из представлений, потому что вы указываете, к какой виртуальной машине привязываться с помощью строки, такой как cal:Bind.Model="Your.Namespace.ViewModels.YourFirstViewModel"

если повезет, это должно получить вас начатый. Также обратитесь к примеру проекта 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.м не может найти ее.

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

затем вы можете вложить свои пользовательские элементы управления, как вы считаете нужным. Это очень хороший способ использовать C. M, и вы найдете его очень масштабируемым. Единственный слабость заключается в том, что View и ViewModel должны быть в одном проекте (насколько я могу судить). Но сила этого подхода заключается в том, что вы можете разделить классы View и View Model на разные пространства имен (в одном проекте), если хотите, чтобы все было организовано.

в качестве комментария к c.m я предпочитаю этот метод, на самом деле, даже если мне не нужно nest View UserControls и тому подобное. Я бы предпочел явно объявить witch VM, что представление обязательно (и все же пусть C. M обрабатывает всю тяжелую работу в IoC), чем пусть c.m "выяснить это" из подразумеваемого кода.

даже с хорошей структурой: явный код более ремонтопригоден, чем подразумеваемый код. Указание модели связанного представления позволяет четко указать, каким должен быть контекст данных, поэтому вам не нужно будет угадывать позже.


лучший подход-использовать 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(...); }
}

в приложении file.код 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
}