Как привязать TabControl к коллекции ViewModels?

в основном у меня есть в моей MainViewModel.cs:

ObservableCollection<TabItem> MyTabs { get; private set; }

однако мне нужно как-то уметь не только создавать вкладки, но и загружать содержимое вкладок и связывать их с соответствующими viewmodels при сохранении MVVM.

в принципе, как я могу получить usercontrol для загрузки в качестве содержимого tabitem и подключить этот usercontrol к соответствующей viewmodel. Часть, которая затрудняет это, - ViewModel не должна создавать фактические элементы просмотра, верно? Или может?

в принципе, это будет MVVM подходит:

UserControl address = new AddressControl();
NotificationObject vm = new AddressViewModel();
address.DataContext = vm;
MyTabs[0] = new TabItem()
{
    Content = address;
}

Я спрашиваю только потому, что хорошо, я создаю представление (AddressControl) из ViewModel, которое для меня звучит как MVVM no-no.

3 ответов


Это не MVVM. Не следует создавать элементы пользовательского интерфейса в модели представления.

вы должны привязать ItemsSource вкладки к вашей коллекции ObservableCollection, и это должно содержать модели с информацией о вкладках, которые должны быть созданы.

вот VM и модель, которая представляет собой вкладку:

public sealed class ViewModel
{
    public ObservableCollection<TabItem> Tabs {get;set;}
    public ViewModel()
    {
        Tabs = new ObservableCollection<TabItem>();
        Tabs.Add(new TabItem { Header = "One", Content = "One's content" });
        Tabs.Add(new TabItem { Header = "Two", Content = "Two's content" });
    }
}
public sealed class TabItem
{
    public string Header { get; set; }
    public string Content { get; set; }
}

и вот как выглядят привязки в окне:

<Window x:Class="WpfApplication12.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <ViewModel
            xmlns="clr-namespace:WpfApplication12" />
    </Window.DataContext>
    <TabControl
        ItemsSource="{Binding Tabs}">
        <TabControl.ItemTemplate>
            <!-- this is the header template-->
            <DataTemplate>
                <TextBlock
                    Text="{Binding Header}" />
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <!-- this is the body of the TabItem template-->
            <DataTemplate>
                <TextBlock
                    Text="{Binding Content}" />
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>
</Window>

(обратите внимание, если вы хотите разные вещи различные вкладки, используйте DataTemplates. Либо модель представления каждой вкладки должна быть собственным классом, либо создать пользовательский DataTemplateSelector чтобы выбрать правильный шаблон.)

смотри, UserControl на шаблоне данные:

<TabControl
    ItemsSource="{Binding Tabs}">
    <TabControl.ItemTemplate>
        <!-- this is the header template-->
        <DataTemplate>
            <TextBlock
                Text="{Binding Header}" />
        </DataTemplate>
    </TabControl.ItemTemplate>
    <TabControl.ContentTemplate>
        <!-- this is the body of the TabItem template-->
        <DataTemplate>
            <MyUserControl xmlns="clr-namespace:WpfApplication12" />
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>

в Prism вы обычно делаете tab control областью, так что вам не нужно контролировать коллекцию связанных вкладок.

<TabControl 
    x:Name="MainRegionHost"
    Regions:RegionManager.RegionName="MainRegion" 
    />

теперь представления могут быть добавлены путем регистрации себя в регионе MainRegion:

RegionManager.RegisterViewWithRegion( "MainRegion", 
    ( ) => Container.Resolve<IMyViewModel>( ).View );

и здесь вы можете увидеть специальность Prism. Вид instanciated купить в ViewModel. В моем случае я разрешаю ViewModel через инверсию контейнера управления (например, Unity или MEF). В ViewModel получает вид вводят через инъекция конструктора и устанавливает себя в качестве контекста данных представления.

альтернативой является регистрация типа представления в контроллере региона:

RegionManager.RegisterViewWithRegion( "MainRegion", typeof( MyView ) );

использование этого подхода позволяет создавать представления позже во время выполнения, например, контроллером:

IRegion region = this._regionManager.Regions["MainRegion"];

object mainView = region.GetView( MainViewName );
if ( mainView == null )
{
    var view = _container.ResolveSessionRelatedView<MainView>( );
    region.Add( view, MainViewName );
}

поскольку вы зарегистрировали тип представления, представление помещается в правильную область.


У меня есть конвертер для развязки пользовательского интерфейса и ViewModel, вот точка ниже:

<TabControl.ContentTemplate>
    <DataTemplate>
        <ContentPresenter Content="{Binding Tab,Converter={StaticResource TabItemConverter}"/>
    </DataTemplate>
</TabControl.ContentTemplate>

вкладка-это enum в моем TabItemViewModel и TabItemConverter преобразовать его в реальный UI.

в TabItemConverter, просто получить значение и вернуть usercontrol вам нужно.