Je suis un nouvel utilisateur de WPF et j'essaye d'envelopper ma tête autour du modèle. En particulier, j'essaie de modéliser un TabControl.WPF TabControl Templating
Je suis confus par les contrôles qui sont réellement générés lorsque le TabControl est lié aux données. Ce dont j'ai besoin, c'est que chaque onglet ait une copie séparée des contrôles dans sa section de contenu. Ce que j'ai trouvé, c'est qu'il semble qu'un seul ensemble de contrôles sont créés, et lorsque l'utilisateur clique d'un onglet à l'autre, seules les données liées changent.
Description du projet:
L'exemple de projet est constitué d'une seule vue avec un TabControl. Dans le modèle de contenu de TabControl est un ListBox. La vue est liée à un objet de la classe TabBindingViewModel. Cette classe a une propriété appelée Tabs, qui est une collection ObservableCollection d'objets TabViewModel. La classe TabViewModel a deux propriétés: TabHeader et Guids. TabViewModel.TabHeader est une chaîne qui contient le texte qui apparaîtra sur l'onglet, et TabViewModel.Guids est une ObservableCollection de chaînes.
Une fois lié, chaque TabViewModel de la collection TabBindingViewModel.Tabs doit générer une tabulation dans l'objet TabControl avec l'en-tête de l'onglet étant la propriété TabViewModel.TabHeader. Le contenu de chaque onglet doit être le ListBox rempli avec la collection de chaînes dans la collection TabViewModel.Guids.
Problème
Tout cela semble rendre/bind bien, si vous changez l'état d'un ListBox (par exemple le défilement ou la sélection d'un élément), puis changer d'onglet, il semble que l'Etat porte sur le ListBox sur le nouvel onglet que vous venez de sélectionner. Cela me fait supposer qu'il n'y a qu'une seule instance de ListBox (au lieu de 5 instances séparées) et lorsque vous changez d'onglet, les données sont la seule chose qui change.
Dans un autre scénario, au lieu de lier une collection à un modèle, je définis explicitement chaque objet TabItem avec son propre ListBox dans le xaml. Cette fois, lorsque je clique d'un onglet à l'autre, chaque ListBox conserve son propre état.
Suis-je correct dans mes hypothèses? Et si oui, comment puis-je atteindre le résultat décrit dans le deuxième scénario tout en profitant de la modélisation? (Ceci est un exemple simplifié.Mon contenu du monde réel est beaucoup plus complexe.)
Merci d'avance pour toute aide!
Le code source de mon exemple est répertorié ci-dessous.
Voir Classes
1. TabBinding
<Window x:Class="TabBindingQuestion.View.TabBinding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TabBindingQuestion.ViewModel"
Title="TabTemplateView"
Height="344" Width="618">
<Window.Resources>
<vm:TabBindingViewModel x:Key="Data" />
</Window.Resources>
<Grid DataContext="{StaticResource Data}">
<TabControl Width="Auto"
Height="Auto"
ItemsSource="{Binding Tabs}"
IsSynchronizedWithCurrentItem="True">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding TabHeader}" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ListBox Width="Auto"
Height="Auto"
ItemsSource="{Binding Guids}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
</Grid>
</Window>
ViewModel Classes
2. ViewModelBase
using System.ComponentModel;
namespace TabBindingQuestion.ViewModel
{
class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(object sender, string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
}
}
}
}
3.TabBindingViewModel
using System.Collections.ObjectModel;
namespace TabBindingQuestion.ViewModel
{
class TabBindingViewModel : ViewModelBase
{
#region Members
private ObservableCollection<TabViewModel> _Tabs;
public ObservableCollection<TabViewModel> Tabs
{
get { return _Tabs; }
set
{
_Tabs = value;
OnPropertyChanged(this, "Tabs");
}
}
#endregion
#region Constructor
public TabBindingViewModel()
{
var tabs = new ObservableCollection<TabViewModel>();
for (int i = 1; i <= 5; i++)
{
tabs.Add(new TabViewModel() { TabHeader = "Tab " + i.ToString() });
}
Tabs = tabs;
}
#endregion
}
}
4. TabViewModel
using System;
using System.Collections.ObjectModel;
namespace TabBindingQuestion.ViewModel
{
class TabViewModel : ViewModelBase
{
#region Members
private string _TabHeader;
public string TabHeader
{
get { return _TabHeader; }
set
{
_TabHeader = value;
OnPropertyChanged(this, "TabHeader");
}
}
private ObservableCollection<string> _Guids;
public ObservableCollection<string> Guids
{
get { return _Guids; }
set
{
_Guids = value;
OnPropertyChanged(this, "Guids");
}
}
#endregion
#region Constructors
public TabViewModel()
{
var guids = new ObservableCollection<string>();
for (int i = 1; i < 100; i++)
{
guids.Add(Guid.NewGuid().ToString());
}
Guids = guids;
}
#endregion
}
}
Merci pour votre réponse. Ce que vous proposez n'est cependant pas une solution viable dans mon cas. Comme le contenu de chaque onglet est beaucoup plus complexe dans le scénario réel, et comprend un contrôle tiers complexe. En outre, une quantité potentiellement importante de données est liée au contrôle tiers et je ne peux pas me permettre de continuer à relier les données chaque fois qu'un utilisateur accède à un autre onglet. – BigFunger
Si je définis explicitement 5 onglets ayant chacun un contenu identique (à l'exception des données qui leur sont liées), j'obtiens la fonctionnalité que je recherche. Cependant, je veux que la fonctionnalité soit dynamique en fonction de la taille de ma collection, et je ne veux évidemment pas dupliquer ce code. J'ai du mal à croire qu'il n'y a pas moyen de reproduire cela en utilisant un modèle. – BigFunger
Hrrm vous pourriez essayer le code trouvé ici: http://www.pluralsight-training.net/community/blogs/eburke/archive/2009/04/30/keeping-the-wpf-tab-control-from-destroying- its-children.aspx Je l'utilise pour empêcher un TabControl de détruire ses enfants lorsqu'il est hors de vue et le même principe pourrait s'appliquer pour maintenir l'état de chaque onglet. Je n'ai pas le temps de le tester maintenant, mais vous pourriez être en mesure de comprendre quelque chose avec – Rachel