Как связать 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.
Ответы
Ответ 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?