Как связать TabControl с коллекцией ViewModels? - PullRequest
63 голосов
/ 13 апреля 2011

В основном я имею в своем MainViewModel.cs:

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

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

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

По сути, это будет уместно для MVVM:

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

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

Ответы [ 3 ]

126 голосов
/ 13 апреля 2011

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

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

Вот виртуальная машина и модель, которая представляет вкладку:

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, чтобы выбрать правильный шаблон.)

Oh lookUserControl внутри шаблона данных:

<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>
20 голосов
/ 14 апреля 2011

В Prism вы обычно делаете вкладку управляющей областью, чтобы вам не приходилось управлять связанными коллекциями страниц вкладок.

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

Теперь представления можно добавлять, регистрируя себя вРегион MainRegion:

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

И здесь вы можете увидеть специальность Prism.Вид создается экземпляром 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 );
}

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

1 голос
/ 15 августа 2017

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

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

Tab - это перечисление в моем TabItemViewModel, и TabItemConverter преобразует его в реальный пользовательский интерфейс.

В TabItemConverter просто получите значение и верните необходимый пользовательский контроль.

...