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

#c# #wpf #mvvm #caliburn.micro #caliburn

#c# #wpf #mvvm #caliburn.micro #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.Модель=»путь / имя_класса / слияние двух». Я попытался добавить это в mainview и в usercontrol (MyControlView). Я ОЧЕНЬ благодарен за любую помощь в этом вопросе. Я в значительной степени застрял, и я действительно хочу использовать Caliburn.Микро 🙂

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

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

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, я, возможно, неправильно внедрил загрузчик. Для этого тестового проекта это выглядит так: (с изменением .xaml в App.xaml)

 public class BootStrapper : Bootstrapper<MainViewModel>
{
}
 

Пожалуйста, помогите мне здесь! Похоже, мне не хватает некоторых базовых вещей 🙂

Комментарии:

1. -отредактированный пост, чтобы включить тег MVVM, добро пожаловать в S.O!

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

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

Если повезет, это должно помочь вам начать. Также обратитесь к проекту C.M example 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 и тому подобное. Я бы предпочел явно объявить виртуальную машину, к которой привязан вид (и при этом позволить C.M выполнять всю тяжелую работу в IoC), чем позволить C.m «разобраться» из подразумеваемого кода.

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

Комментарии:

1. Спасибо, что указали, как это должно быть. Хотя я не могу заставить его работать. Пожалуйста, просмотрите мой оригинальный пост и проверьте обновленную часть. Хотелось бы, чтобы это было исправлено 🙂

2. Вы на правильном пути — мы просто упускаем что-то глупое. Добавлена информация об экспорте.

3. Блин, это просто не сработает: S Я попытался добавить класс MefBootstrapper и реализовал интерфейс IShell, но все равно ничего. Если у вас есть время, чтобы проверить мой проект, пожалуйста, не стесняйтесь это сделать 🙂 johanbjarnle.se/temp/CaliburnTest.rar

4. caliburnmicro.codeplex.com/… Этот пример показывает, как сделать именно то, что вам нужно. Основная модель представления (или оболочка) с двумя моделями вложенных представлений внутри.

5. Отличный ответ, это должно заменить руководство 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(...); }
}
 

Комментарии:

1. Я заставил это работать. Но, похоже, он не использует C.M так сильно? Хотя это хороший способ сделать это, большое спасибо!

2. Я также обнаружил, что не вижу интерфейс MyControl в MainView при редактировании в VS при использовании ContentControl. Есть ли способ сделать это?

3. Вы используете CM, то есть сопоставляете имя ContentControl с именем вашего свойства модели представления, размещаете представление, вводите представление в ContentControl и привязываете элементы управления этого представления к свойствам модели представления. Это был бы рекомендуемый подход к просмотру композиции с помощью Caliburn. Микро.

4. Что касается соглашений, выполняемых внутри Visual Studio designer, я не верю, что это возможно на данный момент, поэтому вам нужно будет редактировать интерфейсы каждого представления отдельно.

Ответ №3:

в файле App.xaml.cs в методе getInstance добавьте следующие строки

 protected override object GetInstance(Type service, string key)
{
    if (service == null amp;amp; !string.IsNullOrWhiteSpace(key))
    {
        service = Type.GetType(key);
        key = null;
    }
    // the rest of method
}
 

Комментарии:

1. Исправлена ошибка, из-за которой мое значение не может быть нулевым при использовании Ninject в моем загрузчике. Большое спасибо 🙂