Невозможно добавить два пользовательских элемента управления в одном представлении.

У меня есть представление списка, в котором есть два дочерних представления. Одним из них является представление просмотра, а другим - представление редактирования. Вот как я определил представление List (Parent). Обратите внимание, что я хочу, чтобы два дочерних UserControl занимали разное пространство в Parent.

<UserControl x:Class="RelayAnalysis_UI.Views.Relays.RelayListView"
    ....

    <ContentControl x:Name="GroupDetail" Grid.Row="2" />
    <TabControl x:Name="Items" Grid.Column="0" Style="{StaticResource TabControlStyle}" Margin="5,0,0,0"/>
</UserControl>

Затем в моей модели представления я активирую эти элементы на основе ответов пользователя следующим образом

** Посмотреть модель **

[Export(typeof(RelayListViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class RelayListViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<Group> {
    ....
    public void Edit() { //Requested Edit
        RelayEditViewModel viewModel = TryAndLocateViewModel(SelectedRelay.Group.Rack.Id, SelectedRelay.Group.Id);
        ActivateItem(viewModel);
    }
    ....

    public void ViewGroupDetail(Relay relay) { //Requested View
        GroupDetailViewModel viewModel = container.GetExportedValue<GroupDetailViewModel>();
        ActivateItem(viewModel);
    }

Приведенный выше код работает, но подробное представление загружается в пространство вкладок (пространство, предназначенное для представления редактирования). На самом деле, ActivateItem (viewModel) действительно выбирает правильный тип представления для отображения, но он загружен не в том месте для представления просмотра, то есть представление отображения загружается в пространство редактирования представления на экране. Конечно, я упускаю некоторые очевидные вещи.

Таким образом, как мы можем получить два UserControl, определенных в Parent UserControl для активации в своем собственном пространстве?

Редактировать - 1:

Вот два снимка экрана, которые показывают, где мне нужно загрузить Edit и Detail View соответственно.

Редактировать экран загружен

Просмотр экрана загружен (не должен загружаться в области редактирования

Как вы можете видеть на втором скриншоте, подробный вид загружается как в области сведений, так и в области редактирования (вкладки). Я не хочу, чтобы Детальный Вид появлялся только в Детальной Области. Область редактирования предназначена только для представления редактирования.

Вот код, который я использовал для создания снимков экрана.

Главный вид, в котором находятся оба вида

<UserControl x:Class="RelayAnalysis_UI.Views.Relays.RelayListView"
    <Grid>
         ....
                     <ContentControl x:Name="GroupDetail" HorizontalContentAlignment="Left" 
                                        cal:View.Context="GroupDetail" cal:View.Model="{Binding ActiveItem}"/>
                    <TabControl x:Name="Items" Grid.Column="0" Style="{StaticResource TabControlStyle}" Margin="5,0,0,0"
                                cal:View.Context="RelayEdit" cal:View.Model="{Binding ActiveItem}"/>
     </Grid>
</UserControl>

Редактировать 2: Я думаю, что я очень близок, чтобы заставить его работать. В соответствии с вашими предложениями я изменил основной (родительский) контейнер, как показано ниже.

<UserControl x:Class="RelayAnalysis_UI.Views.Relays.RelayListView"
            <ContentControl x:Name="GroupDetail" HorizontalContentAlignment="Left" />
                <TabControl x:Name="Items" Grid.Column="0" Style="{StaticResource TabControlStyle}" Margin="5,0,0,0" />

Экран редактирования и подробные экраны теперь отображаются на своих местах. Однако Detail ViewModels OnActivate не вызывается, поэтому я получаю пустой подробный вид без заполненных переменных. Вся загрузка поля представления сведений выполняется с помощью переопределения OnActivate(). Вот как определяется моя GroupDetailViewModel

[Export(typeof(GroupDetailViewModel))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class GroupDetailViewModel : Screen {
    ...
    protected override void OnActivate() {
        base.OnActivate();
    ..
    } 

Так что, конечно, мне не хватает какого-то атрибута. Или мне придется вызвать какой-либо метод в GroupDetailViewModel, чтобы загрузить детали вручную?

1 ответ

Решение

Удален оригинальный ответ, потому что он был длинным и не очень помог

Редактировать:

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

<ContentControl x:Name="GroupDetail" HorizontalContentAlignment="Left" 
    cal:View.Context="GroupDetail" 
    cal:View.Model="{Binding ActiveItem}"/>
<TabControl x:Name="Items" Grid.Column="0" Style="{StaticResource  TabControlStyle}" Margin="5,0,0,0" 
    cal:View.Context="RelayEdit" 
    cal:View.Model="{Binding ActiveItem}"/>

Учитывая ВМ с именем RelayEditViewModel активируется через ActivateItem() CM попытается загрузить следующие представления:

RelayEdit.GroupDetail для управления контентом

RelayEdit.RelayEdit для вкладки

Увидеть:

http://caliburnmicro.codeplex.com/wikipage?title=View%2FViewModel%20Naming%20Conventions&referringTitle=Documentation

...

Если вы попытаетесь загрузить другую ViewModel, то же самое соглашение будет применяться, чтобы найти представление

GroupDetailViewModel результаты в

GroupDetail.GroupDetail для управления контентом

GroupDetail.RelayEdit для вкладки

Похоже, это не то, что вы хотите (и я не уверен, почему что-то вообще загружается - в каком пространстве имен находятся ваши представления? Вы настроили локатор представлений?)

Я все еще пытаюсь разобраться с необходимой вам поддержкой жизненного цикла, но звучит так, будто вы хотите, чтобы представление редактирования управлялось жизненным циклом (так как вы хотите поддержку типа загрузки / сохранения / защиты), но подробное представление должно читаться только и не волнует, если он закрыт без охраны

В этом случае вы, вероятно, захотите добавить свойство в вашу ViewModel, которое будет содержать ссылку на модель представления детали, но не активировать ее... просто установите свойство без вызова ActivateItem(vm)

пример:

[Export(typeof(RelayListViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class RelayListViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<Group> {
....
// Backing field + prop to hold the details view - the content control will bind to this
private IScreen _details;
public IViewAware Details { get { } set { } } // Implement standard NotifyOfPropertyChange here for this property

public void Edit() { //Requested Edit
    RelayEditViewModel viewModel = TryAndLocateViewModel(SelectedRelay.Group.Rack.Id, SelectedRelay.Group.Id);
    ActivateItem(viewModel);
}
....

public void ViewGroupDetail(Relay relay) { //Requested View
    GroupDetailViewModel viewModel = container.GetExportedValue<GroupDetailViewModel>();
    // Instead of activating, just assign the VM to the property and make sure Details calls NotifyOfPropertyChange to let CM know to start the binding logic
    Details = viewModel;
}

Тогда в вашем XAML

<!-- Just bind the details view to the Details property -->
<ContentControl x:Name="Details" HorizontalContentAlignment="Left" /> 
<!-- Leave this as-is, as it's working ok -->
<TabControl x:Name="Items" Grid.Column="0" Style="{StaticResource  TabControlStyle}" Margin="5,0,0,0" /> 

(Я предположил, что вы используете TabControlСоглашения по умолчанию выше, но подправить, если необходимо)

Вы можете использовать одну и ту же виртуальную машину как для деталей, так и для вида редактирования, если вы установили Context собственность соответственно.

Дайте мне знать, если это поможет

Редактировать:

Просто чтобы ответить на вопрос о MVVM и сцеплении и т.д...

Все, что вы делаете, это составление более сложной модели представления из нескольких более простых моделей представления (и, следовательно, более сложное представление из нескольких более простых представлений). Пока ваша ссылка на подробности VM не является конкретным типом, между VM существует очень слабая связь. Вы можете назначить ЛЮБОЙ тип viewmodel, который реализует этот интерфейс в Detail свойство на основной ВМ и CM попытается найти представление для него и построить интерфейс. Это прекрасно (вы можете использовать свой IoC, чтобы получить тип для окна сведений, если это необходимо)

Если для просмотра ваших данных необходим жизненный цикл, вы должны унаследовать от Screen, но я сомневаюсь, что ваше представление подробностей нуждается в активации (так как это просто представление подробностей и только готово), поэтому просто реализуем IViewAware и наследуя от PropertyChangedBase Сделаю. Однако представление редактирования должно иметь жизненный цикл и поэтому должно наследоваться от Screen.

Conductor уже содержит ActiveItem и обеспечивает управление жизненным циклом для дочерних элементов, активированных через ActivateItem()все, что вы делаете, это создаете дополнительное свойство 'bolt-on' для вашего проводника, которое ссылается на дополнительный vm (т.е. вам нужно ActiveItem а также Details)

Другие вопросы по тегам