Подтвердить что ты не робот

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

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

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

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

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

В принципе, это будет MVVM соответствующим:

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

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

4b9b3361

Ответ 1

Это не 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 content" });
        Tabs.Add(new TabItem { Header = "Two", Content = "Two 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>

(Note, if you want different stuff in different tabs, use [TG42]. Either each tab view model should be its own class, or create a custom [TG43] to pick the correct template.)

О, смотрите, 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>

Ответ 2

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

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

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

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

И здесь вы можете увидеть специальность Призма. Представление создается с помощью ViewModel. В моем случае я разрешаю ViewModel через контейнер Inversion of Control (например, 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 );
}

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

Ответ 3

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

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

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

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

Ответ 4

Возможно, так:

<UserControl x:Class="Test_002.Views.MainView"
         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:local="clr-namespace:Test_002.Views"
         xmlns:generalView="clr-namespace:Test_002.Views.General"
         xmlns:secVIew="clr-namespace:Test_002.Views.Security"
         xmlns:detailsView="clr-namespace:Test_002.Views.Details"
         mc:Ignorable="d" 
         d:DesignHeight="400" d:DesignWidth="650">
<Grid>
    <DockPanel>
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" Margin="2,5">
            <Button Command="{Binding btnPrev}" Content="Prev"/>
            <Button Command="{Binding btnNext}" Content="Next"/>
            <Button Command="{Binding btnSelected}" Content="Selected"/>
        </StackPanel>
        <TabControl>
            <TabItem Header="General">
                <generalView:GeneralView></generalView:GeneralView>
            </TabItem>
            <TabItem Header="Security">
                <secVIew:SecurityView></secVIew:SecurityView>
            </TabItem>
            <TabItem Header="Details">
                <detailsView:DetailsView></detailsView:DetailsView>
            </TabItem>
        </TabControl>
    </DockPanel>
</Grid>

Думаю, это самый простой способ. Совместим ли MVVM?