2010-11-11 7 views
1

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

Répondre

1

Oui la réutilise TabControl commandes si elles sont les mêmes lors du passage des onglets. Il permet également de décharger/recharger les contrôles si nécessaire, ce qui entraîne la réinitialisation des contrôles non liés. Par exemple, si Tab1 a une ListBox avec l'ItemA sélectionné et que vous sélectionnez ItemB et que vous passez d'un onglet à l'autre sans la listbox, lorsque vous revenez à Tab1, ItemA est à nouveau sélectionné.

La meilleure chose à faire est de lier vos propriétés d'interface utilisateur à quelque chose dans le code derrière. Par exemple, votre TabControl peut contenir une liste de TabItemViewModel et chaque ViewModel doit contenir des propriétés représentant l'état de votre interface utilisateur, telles que ListBox.SelectedItems ou CheckBox.IsChecked.

+0

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

+0

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

+0

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