#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 в моем загрузчике. Большое спасибо 🙂