How to associate TabControl with the ViewModels collection?

Basically, I have in my MainViewModel.cs:

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

However, I need to somehow not only create the tabs, but also load the contents of the tab and bind them to the corresponding view modes while saving the MVVM.

Basically, how can I get a usercontrol to load as the contents of a tabitem and this usercontrol is connected to the corresponding viewmodel. The part that makes this difficult is that the ViewModel doesn't have to create the actual view elements, does it? Or maybe it?

Basically, this will be MVVM appropriate:

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

I just ask, because well, I build a View (AddressControl) from the ViewModel, which for me sounds like MVVM no-no.

+65
wpf mvvm tabcontrol prism-4 tabitem
Apr 13 2018-11-11T00:
source share
4 answers

This is not MVVM. You should not create user interface elements in the view model.

You must associate the ItemsSource tabs with your ObservableCollection, and this should contain models with information about the tabs that need to be created.

Here is the virtual machine and model that represents the tab:

 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; } } 

And here are the bindings in the window:

 <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.)

Oh, see UserControl inside the data template:

 <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> 
+127
Apr 13 2018-11-11T00:
source share

In Prism, you usually make a tab control a scope so that you don't have to control the collection of tab-bound tabs.

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

Now you can add views by registering in the MainRegion region:

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

And here you can see the specialty of Prism. A view is created using the ViewModel. In my case, I enable ViewModel through the Inversion of Control container (e.g. Unity or MEF). The ViewModel receives the view introduced through the constructor installation and sets itself as the context for the data view.

An alternative is to register the type of the view in the region controller:

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

Using this approach, you can create views later at runtime, for example. using the controller:

 IRegion region = this._regionManager.Regions["MainRegion"]; object mainView = region.GetView( MainViewName ); if ( mainView == null ) { var view = _container.ResolveSessionRelatedView<MainView>( ); region.Add( view, MainViewName ); } 

Since you registered the view type, the view is placed in the correct area.

+21
Apr 14 '11 at 7:00
source share

I have a converter for decoupling the user interface and ViewModel, as follows:

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

A tab is an enumeration in my TabItemViewModel, and a TabItemConverter converts it to a real interface.

In TabItemConverter, just get the value and return the user control you need.

+1
Aug 15 '17 at 7:31 on
source share

Perhaps so:

 <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> 

I think this is the easiest way. Is MVVM compatible?

0
Aug 21 '19 at 7:36
source share



All Articles