wpf - ¿Cómo puedo vincular un TabControl a una colección de ViewModels?
mvvm tabitem (3)
En Prism, generalmente haces que la pestaña controle una región para que no tengas que tomar el control de la colección de páginas de pestañas atadas.
<TabControl
x:Name="MainRegionHost"
Regions:RegionManager.RegionName="MainRegion"
/>
Ahora las vistas se pueden agregar registrándose en la región MainRegion:
RegionManager.RegisterViewWithRegion( "MainRegion",
( ) => Container.Resolve<IMyViewModel>( ).View );
Y aquí puedes ver una especialidad de Prism. La Vista es instanciada por ViewModel. En mi caso, resuelvo ViewModel a través de un contenedor de inversión de control (por ejemplo, Unity o MEF). El ViewModel obtiene la Vista inyectada a través de la inyección del constructor y se establece como el contexto de datos de la Vista.
La alternativa es registrar el tipo de vista en el controlador de región:
RegionManager.RegisterViewWithRegion( "MainRegion", typeof( MyView ) );
El uso de este enfoque le permite crear las vistas más tarde durante el tiempo de ejecución, por ejemplo, mediante un controlador:
IRegion region = this._regionManager.Regions["MainRegion"];
object mainView = region.GetView( MainViewName );
if ( mainView == null )
{
var view = _container.ResolveSessionRelatedView<MainView>( );
region.Add( view, MainViewName );
}
Como ha registrado el tipo de Vista, la vista se coloca en la región correcta.
Básicamente tengo en mi MainViewModel.cs:
ObservableCollection<TabItem> MyTabs { get; private set; }
Sin embargo, tengo que ser capaz de no solo crear las pestañas, sino también tener el contenido de pestañas cargado y vinculado a sus modelos de vista apropiados mientras mantengo MVVM.
Básicamente, ¿cómo puedo obtener un control de usuario para cargar como el contenido de un tabitem y tener ese control de usuario conectado a un modelo de vista apropiado? La parte que hace esto difícil es que ViewModel no debe construir los elementos de la vista real, ¿verdad? O puede?
Básicamente, ¿esto sería MVVM apropiado?
UserControl address = new AddressControl();
NotificationObject vm = new AddressViewModel();
address.DataContext = vm;
MyTabs[0] = new TabItem()
{
Content = address;
}
Solo pregunto porque, bueno, estoy construyendo una View (AddressControl) desde dentro de ViewModel, lo que para mí suena como un MVVM no-no.
Esto no es MVVM. No debería crear elementos UI en su modelo de vista.
Debería vincular ItemsSource de la pestaña a su ObservableCollection, y eso debería contener modelos con información sobre las pestañas que deberían crearse.
Aquí están la VM y el modelo que representa una página de pestañas:
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; }
}
Y así es como se ven los enlaces en la ventana:
<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>
(Tenga en cuenta que si desea elementos diferentes en pestañas diferentes, use DataTemplates
. O bien el modelo de vista de cada pestaña debe ser de su propia clase, o cree un DataTemplateSelector
personalizado para elegir la plantilla correcta).
Oh, mira, un UserControl dentro de la plantilla de datos:
<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>
Tengo un convertidor para desacoplar la interfaz de usuario y ViewModel, ese es el punto a continuación:
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Tab,Converter={StaticResource TabItemConverter}"/>
</DataTemplate>
</TabControl.ContentTemplate>
La pestaña es una enumeración en mi TabItemViewModel y el TabItemConverter la convierte a la IU real.
En TabItemConverter, solo obtenga el valor y Devuelva el control de usuario que necesita.