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

Я пытаюсь научиться использовать 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>

MainViewModel code:

public partial class MainViewModel : PropertyChangedBase
{
}

xaml-файл MyControlView:

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

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

Скриншот ошибки: http://clip2net.com/s/1gtgt

я пытался

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

также. Также попробовал кал-ссылку:

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

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

Поскольку документация в основном предназначена для Silverlight, а иногда и для Caliburn, а не для CM, возможно, я неправильно реализовал загрузчик. Для этого тестового проекта это так: (с изменением .xaml в App.xaml)

public class BootStrapper : Bootstrapper<MainViewModel>
{
}

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


person diamondfish    schedule 24.10.2011    source источник
comment
-отредактированный пост с добавлением тега MVVM, добро пожаловать в S.O!   -  person EtherDragon    schedule 24.10.2011
comment
Проверьте ответ - я добавил раздел об экспорте типа. Это важное требование для c.m, чтобы найти ViewModel, связанную с представлением.   -  person EtherDragon    schedule 25.10.2011


Ответы (3)


РЕДАКТИРОВАТЬ - Новый (более полный) ответ ниже:

Хорошо, C.M делает для вас много вещей, все дело в том, чтобы ваши классы и xaml были подготовлены для того, чтобы C.M мог их найти. Как было сказано выше, я предпочитаю писать явный код, а не полагаться на неявные предположения кода фреймворком.

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

C.M обрабатывает назначение контекста данных от вашего имени для каждого из представлений, потому что вы указываете, к какой виртуальной машине привязываться, с помощью строки вроде 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", которая указывает точную модель представления для привязки этого представления.

Не забудьте экспортировать свой тип класса, иначе cm не сможет его найти.

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

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

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

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

person EtherDragon    schedule 24.10.2011
comment
Спасибо, что указали, как должно быть. Хотя я не могу заставить его работать. Пожалуйста, просмотрите мой оригинальный пост и проверьте обновленную часть. Хотелось бы это исправить :) - person diamondfish; 24.10.2011
comment
Вы на правильном пути - мы просто упускаем кое-что глупое. Добавлена ​​информация об экспорте. - person EtherDragon; 25.10.2011
comment
Гах, это просто не сработает: S Я пытался добавить класс MefBootstrapper и реализовал интерфейс IShell, но все равно ничего. Если у вас есть время проверить мой проект, не стесняйтесь сделать это :) johanbjarnle.se/temp/ CaliburnTest.rar - person diamondfish; 25.10.2011
comment
Если у вас или у кого-то еще есть время просмотреть его, а затем загрузить куда-нибудь отредактированную версию, это было бы здорово :) - person diamondfish; 25.10.2011
comment
caliburnmicro.codeplex.com/ В этом примере показано, как именно то, что вы после. Модель основного представления (или оболочки) с двумя моделями подпредставления внутри. - person EtherDragon; 25.10.2011
comment
Я использовал этот ответ для своего решения. Я получил представления для загрузки, но каким-то образом элементы управления в дочернем представлении не соответствуют свойствам в классе модели. Я использую NotifyPropertyChanged в качестве базового класса для дочерней модели представления, а также интерфейса iShell. Также с использованием MEFBootstrapper из примера CM. Без него не работал. ОБНОВЛЕНИЕ: пришлось использовать Binding Source=propname вместо Binding = propname - person Mounhim; 23.11.2011
comment
ОБНОВЛЕНИЕ: Странно: элемент управления моего пользовательского управления (дочерний) привязывается к свойству в модели основного представления, но не в модели детского представления. Я действительно потерялся здесь. Таким образом, MainViewModel.Prop1 показывает свое значение в Textblock в ChildView1, но ChildViewModel.Prop1 не показывает свое значение в Textblock в ChildView1. - person Mounhim; 23.11.2011
comment
Отличный ответ, он должен заменить руководство по началу работы / n00b от Caliburn :) - person Adam Plocher; 18.01.2015

Лучшим подходом является использование 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(...); }
}
person devdigital    schedule 24.10.2011
comment
Я заставил это работать. Но, похоже, он не так сильно использует C.M? Хотя хороший способ сделать это, большое спасибо! - person diamondfish; 25.10.2011
comment
Я также обнаружил, что не вижу интерфейс MyControl в MainView при редактировании в VS при использовании ContentControl. Есть ли способ сделать это? - person diamondfish; 25.10.2011
comment
Вы используете CM, то есть сопоставляете имя ContentControl с именем вашего свойства модели представления, находите представление, вводите представление в ContentControl и привязываете элементы управления этого представления к свойствам модели представления. Это рекомендуемый подход к просмотру композиции с помощью Caliburn.Micro. - person devdigital; 25.10.2011
comment
Что касается соглашений, которые выполняются внутри дизайнера визуальной студии, я не верю, что это возможно на данный момент, поэтому вам нужно будет редактировать интерфейсы каждого представления отдельно. - person devdigital; 25.10.2011

в файле 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
}
person Luca Corradi    schedule 04.04.2013
comment
Исправлено мое значение не может быть нулевой ошибкой при использовании Ninject в моем загрузчике. Большое спасибо :) - person Dave; 19.02.2014