Control de pestañas WPF y selección de MVVM
prism tabcontrol (3)
El ejemplo de código siguiente creará una pestaña dinámica utilizando MVVM.
XAML
<TabControl Margin="20" x:Name="tabCategory"
ItemsSource="{Binding tabCategory}"
SelectedItem="{Binding SelectedCategory}">
<TabControl.ItemTemplate>
<DataTemplate>
<HeaderedContentControl Header="{Binding TabHeader}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding TabContent}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Clase de modales
TabCategoryItem representa cada elemento de la pestaña. En dos propiedades, TabHeader mostrará un título de pestaña y TabContent contiene el contenido / control para completar cada pestaña.
Public Class TabCategoryItem
Public Property TabHeader As String
Public Property TabContent As UIElement
End Class
Clase VM
Public Class vmClass
Public Property tabCategory As ObjectModel.ObservableCollection(Of TabCategoryItem)
Public Property SelectedCategory As TabCategoryItem
End Class
El siguiente código llenará y enlazará el contenido. Estoy creando dos pestañas, tab1 y tab2. Ambas pestañas contendrán cuadros de texto. Puede utilizar cualquier elemento de UIelemento en lugar de cuadros de texto.
Dim vm As New vmClass
vm.tabCategory = New ObjectModel.ObservableCollection(Of TabCategoryItem)
''VM.tabCategory colection will create all tabs
vm.tabCategory.Add(New TabCategoryItem() With {.TabHeader = "Tab1", .TabContent = new TextBlock().Text = "My first Tab control1"})
vm.tabCategory.Add(New TabCategoryItem() With {.TabHeader = "Tab2", .TabContent = new TextBlock().Text = "My first Tab control2"})
mywindow.DataContent = vm
Tengo un TabControl en una aplicación MVVM WPF . Se define de la siguiente manera.
<TabControl Style="{StaticResource PortfolioSelectionTabControl}" SelectedItem="{Binding SelectedParameterTab}" >
<TabItem Header="Trades" Style="{StaticResource PortfolioSelectionTabItem}">
<ContentControl Margin="0,10,0,5" Name="NSDetailTradeRegion" cal:RegionManager.RegionName="NSDetailTradeRegion" />
</TabItem>
<TabItem Header="Ccy Rates" Style="{StaticResource PortfolioSelectionTabItem}">
<ContentControl Margin="0,10,0,5" Name="NSDetailCcyRegion" cal:RegionManager.RegionName="NSDetailCcyRegion" />
</TabItem>
<TabItem Header="Correlations / Shocks" Style="{StaticResource PortfolioSelectionTabItem}">
<ContentControl Name="NSDetailCorrelationRegion" cal:RegionManager.RegionName="NSDetailCorrelationRegion" />
</TabItem>
<TabItem Header="Facility Overrides" Style="{StaticResource PortfolioSelectionTabItem}" IsEnabled="False">
<ContentControl Name="NSDetailFacilityOverrides" cal:RegionManager.RegionName="NSDetailFacilityOverrides" />
</TabItem>
</TabControl>
Por lo tanto, el contenido de cada elemento de la pestaña tiene su propia vista asociada. Cada una de esas vistas tiene el MEF [Export]
MEF y está asociada con la región relevante a través del descubrimiento de vistas, por lo que el código anterior es todo lo que necesito para tener la pestaña de control de carga y cambiar entre ellas. Todos hacen referencia al mismo objeto ViewModel compartido detrás de ellos y, por lo tanto, todos interactúan a la perfección.
Mi problema es que cuando el usuario navega a la ventana principal, quiero que el control de pestaña se ajuste de forma predeterminada al segundo elemento de pestaña. Esto es lo suficientemente fácil de hacer cuando la ventana se carga por primera vez, especificando en XAML IsSelected="True"
en TabItem número 2. Es menos fácil hacerlo cuando el usuario se aleja de la pantalla y luego vuelve a la pantalla.
Pensé en tener una propiedad SelectedItem={Binding SelectedTabItem}
en el control de pestañas, por lo que pude establecer programáticamente la pestaña seleccionada en ViewModel, pero el problema es que no tengo conocimiento de los objetos TabItem en ViewModel tal como están declarados anteriormente en solo el XAML, por lo que no tengo ningún objeto TabItem para pasar a la propiedad de establecimiento.
Una de las ideas que tuve fue que las Vistas secundarias (que forman el contenido de cada uno de los elementos de las pestañas anteriores) tengan un estilo en el nivel de Control de usuario de su XAML, como se indica a continuación.
<Style TargetType={x:Type UserControl}>
<Style.Triggers>
<DataTrigger Property="IsSelected" Value="True">
<Setter Property="{ElementName={FindAncestor, Parent, typeof(TabItem)}, Path=IsSelected", Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
Sé que el bit del buscador no es correcto; Acabo de ponerlo allí para especificar mi intención, pero no estoy seguro de la sintaxis exacta. Básicamente, para que cada UserControl tenga un desencadenante que escuche una propiedad en ViewModel (no estoy seguro de cómo distinguiría cada UserControl diferente, ya que obviamente no todos pueden escuchar la misma propiedad o se seleccionarían simultáneamente cuando la propiedad esté configurada para Es cierto, pero tener una propiedad para cada control de usuario parece feo) y luego encuentra su contenedor de TabItem principal y establece el valor de IsSelected en verdadero.
¿Estoy en el camino correcto con una solución aquí? ¿Es posible hacer lo que estoy pensando? ¿Hay una solución más ordenada?
Si observa la página de TabControl
Class en MSDN, encontrará una propiedad llamada SelectedIndex
que es un int
. Por lo tanto, simplemente agregue una propiedad int
en su modelo de vista y Bind
a la propiedad TabControl.SelectedIndex
y luego puede seleccionar la pestaña que desee en cualquier momento del modelo de vista:
<TabControl SelectedIndex="{Binding SelectedIndex}">
...
</TabControl>
ACTUALIZACIÓN >>>
Configurar una pestaña de ''inicio'' es aún más fácil usando este método:
En vista modelo:
private int selectedIndex = 2; // Set the field to whichever tab you want to start on
public int SelectedIndex { get; set; } // Implement INotifyPropertyChanged here
Solo para su información, pasé por el mismo problema donde agrego pestañas dinámicamente usando la fuente ObservableCollection pero la última pestaña agregada no se selecciona. He hecho los mismos cambios que Sheridan dijo para seleccionar Tab según SelectedIndex. Ahora la última pestaña agregada se selecciona, pero no se estaba enfocando. Así que para enfocar la pestaña tenemos que agregar el conjunto de Binding IsAsync propiedad True.
<TabControl ItemsSource="{Binding Workspaces}" Margin="5" SelectedIndex="{Binding TabIndex, Mode=OneWay,UpdateSourceTrigger=PropertyChanged, IsAsync=True}">