2011-10-24 4 views
12

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

<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: Я до сих пор не могу заставить его работать, проблема, как представляется, в bootstrapper или что-то еще. Но просто для уточнения, вот мой код, который я запускаю для тестового проекта.

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" 

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

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

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

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

public class BootStrapper : Bootstrapper<MainViewModel> 
{ 
} 

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

+0

- отредактированный пост, чтобы включить тег MVVM, добро пожаловать в SO! – EtherDragon

+0

Проверьте anser - я добавил раздел об экспорте этого типа. Это важное требование для c.m найти ViewModel, связанную с представлением. – EtherDragon

ответ

16

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

Ok, CM делает много вещей для вас, это все о получении классов и xaml, подготовленный для CM, чтобы найти его. Как было сказано выше, я предпочитаю писать код явно, а не полагаться на неявные кодовые предположения с помощью фреймворка.

Итак, 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 здесь, но это еще одно обсуждение.

Теперь каждый ребенок View Model также должен экспортировать себя, поэтому 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 ручки присвоения DataContext, от вашего имени, к каждому из представлений, поскольку необходимо указать, какие VM связываться с с линией, как cal:Bind.Model="Your.Namespace.ViewModels.YourFirstViewModel"

если повезет, то вы должны получить работу. Также обратитесь к примеру CM проекта Caliburn.Micro.HelloEventAggregator, поскольку он точно выполняет поиск (хотя это описано как демоверсия Event Aggregator, что также очень полезно - но опять же, другое обсуждение)

(Оригинальный ответ для почтения, ниже)

Вы должны сделать это:

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

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

+0

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

+1

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

+0

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

16

Лучшим подходом является использование 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(...); } 
} 
+0

У меня это получилось. Но, похоже, он не использует C.M? Хотя это хороший способ сделать это, спасибо большое! – diamondfish

+0

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

+2

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

1

в файле 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 
} 
+0

Исправлено мое значение не может быть пустой ошибкой при использовании Ninject в моем загрузчике. Большое спасибо :) – Dave

Смежные вопросы