2016-05-09 10 views
0

Я испытал странное поведение привязки, которое описано here. Я сделал много проблем, и я пришел к выводу, что наиболее вероятная проблема заключается в том, как я установил DataContext каждого из видов вкладок.WPF MVVM: настройка DataContext для вкладок Просмотров

У меня есть TabControl, чья ItemsSource привязана к списку ViewModels.

MainView: 
<TabControl ItemsSource="{Binding TabList}"> 
    <TabControl.Resources> 
     <DataTemplate DataType="{x:Type vm:Tab1ViewModel}"> 
      <v:Tab1 /> 
     </DataTemplate> 
    </TabControl.Resources> 
... 
</TabControl> 



MainViewModel: 
public ObservableCollection<TabViewModelBase> TabList { get; set; } 
pubic MainViewModel() 
{ 
    this.TabList = new ObservableCollection<TabViewModelBase>(); 
    this.TabList.Add(new Tab1ViewModel()); // Tab1ViewModel is derived from TabViewModelBase 
} 

Итак, теперь MainViewModel имеет список TabViewModelBase, который я считаю, это правильный способ MVVM, чтобы сделать это. Вид (Tab1) для TabViewModelBase определяется с использованием DataTemplate.

Здесь проблема:

Tab1: 
<UserControl.Resources> 
    <vm:Tab1ViewModel x:Key="VM" /> 
</UserControl.Resources> 
<UserControl.DataContext> 
    <StaticResourceExtension ResourceKey="VM" /> 
</UserControl.DataContext> 

Я думаю, что большинство людей будет делать это так, но ... Существует что-то ужасно неправильно с таким подходом!

В MainViewModel, я создал экземпляр Tab1ViewModel. В MainView я использовал DataTemplate, чтобы сообщить View, чтобы использовать Tab1 всякий раз, когда он видит Tab1ViewModel. Это означает, что MainView будет создавать объект класса Tab1.

Теперь Tab1 нуждается в DataContext сделать связываясь с его собственным Tab1ViewModel, поэтому мы используем StaticResource добавить одну Tab1ViewModel, за исключением того, что это новый экземпляр марки!

Мне нужно установить DataContext назад к оригиналу, который я создал в MainViewModel. Итак, как мне установить DataContext из Tab1 в пределах DataTemplate?

+1

просто удалите все ресурсы и код DataContext, который вы показываете для Tab1. ваш экземпляр Viewmodel находится в вашей коллекции, а DataTemplate определен в ваших вкладках. Свойства: – blindmeis

+0

. Тогда у моего 'Tab1' не будет' DataContext', и я не смог бы привязать между Tab1 и Tab1ViewModel'. – Jai

ответ

2

Вам не нужно указывать vm:Tab1ViewModel новый экземпляр вашего XAML. Вам также не нужно явно указывать DataContext. Каждый элемент вашего списка - ViewModel всякий раз, когда тип ViewModel соответствует типу, указанному в DataTemplate, будет отображаться часть view и с тем же DataContext, что и ViewModel. Например, если список содержит два объекта, как показано ниже:

public ObservableCollection<TabViewModelBase> TabList { get; set; } 
pubic MainViewModel() 
{ 
    this.TabList = new ObservableCollection<TabViewModelBase>(); 
    this.TabList.Add(new Tab1ViewModel1()); 
    this.TabList.Add(new Tab1ViewModel2()); 
} 

и ваш DataTemplate является:

<TabControl ItemsSource="{Binding TabList}"> 
<TabControl.Resources> 
    <DataTemplate DataType="{x:Type vm:Tab1ViewModel}"> 
     <v:Tab1 /> 
    </DataTemplate> 
    <DataTemplate DataType="{x:Type vm:Tab1ViewModel2}"> 
     <v:Tab2 /> 
    </DataTemplate> 
</TabControl.Resources> 

...

затем две вкладки будут Tab1 & выход дает Tab2 (список причин имеет 2 элемента). Tab1 будет иметь Tab1ViewModel1 как DataContext и и Tab2 будет иметь Tab1ViewModel2 как DataContext. Не обязательно указывать DataContext .

+0

Большое спасибо, я не могу поверить, что решение настолько простое. Теперь я чувствую, что ударяю себя по голове. - – Jai

+0

@Jai может иногда запутываться для DataTemplate, почти каждый делает это так, как вы пытались. вы можете найти бесчисленные уравнения на SO о том же. :) –

2

Просто дополнение к @ KyloRen отвечают: Это так называемый "ViewModel-первых подход". На основе вашей модели просмотра выбран режим просмотра -> сначала у вас есть viewmodel.

Однако вам даже не нужны данные для ваших просмотров. Это может раздражать, чтобы написать datatemplate для каждого представления.

Существует альтернативная реализация того же принципа «ViewModel-Первый»:

<TabControl ItemsSource="{Binding TabList}"> 
    <TabControl.ItemTemplate> 
    <DataTemplate> 
     <ContentPresenter Content={Binding Converter={ViewModelToViewConverter}} /> 
    </DataTemplate> 
    </TabControl.ItemTemplate> 
</TabControl> 

ViewModelToViewConverter принимает ViewModel и на основе именования создает вид для нее. Это особенно полезно в сценариях навигации по страницам, но это универсальный подход, который работает во многих ситуациях (навигация, списки, элементы управления, динамические презентации контента и т. Д.)

Пример конвертера можно найти here - просто замените IocContainer на Activator.CreateInstance

+0

На самом деле, я использую подход 'Activator', чтобы открыть новую' Windows'. Но иногда я чувствую, что это немного «хакерское». – Jai

+0

Если у вас есть веские причины для этого, например, вышеупомянутое соглашение namig, это не хаки. Если у вас есть соглашение об именах, это объединяет представления и режимы просмотра, и вам необходимо указать таблицы данных, которые снова объединяют представления и viewmodels, это своего рода нарушение принципа DRY – Liero

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