IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Windows Presentation Foundation Discussion :

[MVVM] TabControl et ViewModel


Sujet :

Windows Presentation Foundation

  1. #1
    Membre éclairé Avatar de koyot3
    Inscrit en
    Avril 2007
    Messages
    693
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 693
    Par défaut [MVVM] TabControl et ViewModel
    Bonjour à tous,

    Voici la suite de mon précédent post (gestion des usercontrols).
    J'utilise désormais la MVVM Light Toolkit de Galasoft.

    Mon application est décomposée ainsi
    - 1 View principale reliée
    - 2 Views sous format de UserControl

    Ma View principale possède un menu (Action 1, Action 2). et un grid :

    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    <Grid x:Name="LayoutRoot">
       <Grid>       
          <Menu  Name="Choix"  HorizontalAlignment="Left" VerticalAlignment="Top"  BorderThickness="2">
             <MenuItem Header="Choix">
                <MenuItem Header="View1" Command="{Binding ChangeToView1}" />
                <MenuItem Header="View2" Command="{Binding ChangeToView2}"/>
             </MenuItem>
         </Menu>
       </Grid>
     
       <Grid  Margin="0,30,0,0">             
          <ContentControl Content="{Binding MaVmSelect}" Margin="0,20" />
       </Grid>

    En gros, lors du choix, la propriété MaVmSelect sur la ViewModel principale est changée et donc, j'affiche la bonne View correctement bindée...

    Aujourd'hui, mon objectif est d'ouvrir ce UserControl dans un TabControlItem.
    Et c'est là que je sèche... Je pense pouvoir réutiliser mes DataTemplates afin de respecter au max le pattern

    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    <DataTemplate  DataType="{x:Type vm:ViewModel1}">
       <v:View1/>
    </DataTemplate>
    <DataTemplate  DataType="{x:Type vm:ViewModel2}">
       <v:View2/>
    </DataTemplate>

    Si quelqu'un avait une idée pour la suite...

    Merci d'avance

  2. #2
    Membre éclairé Avatar de koyot3
    Inscrit en
    Avril 2007
    Messages
    693
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 693
    Par défaut
    Bon j'avance sur le sujet...
    J'ai créé une collection sur ma ViewModel principal contenant des ViewModels tel que

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    VmCollect.Add(new ViewModel1());
    VmCollect.Add(new ViewModel2());

    En définissant un DataTemplate sur le <TabControl.ItemTemplate> j'arrive à afficher mes deux onglets.

    Seulement voilà, je voudrais que le client puisse ajouter des onglets à la volée.
    Par exemple, s'il clic sur un bouton, un nouveau onglet apparaît avec la ViewModel1 comme template par exemple...

    Comment faire pour chaque view ait une instance indépendante des autres ?

    Par avance, merci de votre aide !!

  3. #3
    Membre éprouvé
    Profil pro
    Consultant informatique
    Inscrit en
    Septembre 2009
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Septembre 2009
    Messages : 99
    Par défaut
    Tu peux essayer d'utiliser un ContentControlSelector qui choisira quel DataTemplate utiliser.
    http://msdn.microsoft.com/fr-ca/libr...eselector.aspx

  4. #4
    Membre éclairé Avatar de koyot3
    Inscrit en
    Avril 2007
    Messages
    693
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 693
    Par défaut
    merci pour ton lien...
    en fait en réutilisant mon ancien bout de code, moyennant quelques modifications j'ai réussi à obtenir un truc convenable...
    seul hic, lorsque j'ouvre un onglet de type ViewModel1 (la View1 est chargée dans un nouveau tabitem), le client a la possibilité de de refaire la même opération....

    On se retrouver alors avec 2 onglets avec deux View1 à l'intérieur.. seul hic, on utilise qu'une seule instance de la ViewModel1 alors que j'aimerais en avoir deux par exemple .....
    C'est ça que je n'arrive pas à gérer !!

  5. #5
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par koyot3 Voir le message
    merci pour ton lien...
    en fait en réutilisant mon ancien bout de code, moyennant quelques modifications j'ai réussi à obtenir un truc convenable...
    seul hic, lorsque j'ouvre un onglet de type ViewModel1 (la View1 est chargée dans un nouveau tabitem), le client a la possibilité de de refaire la même opération....

    On se retrouver alors avec 2 onglets avec deux View1 à l'intérieur.. seul hic, on utilise qu'une seule instance de la ViewModel1 alors que j'aimerais en avoir deux par exemple .....
    C'est ça que je n'arrive pas à gérer !!
    Je pense que le problème n'est bien expliqué là.

    Est ce que tu veux dire :

    Comment faire pour ne pas créer un TabItem correpondant à un ViewModel si au moins un TabItem lui est déjà associé dans le TabControl ?

  6. #6
    Membre éclairé Avatar de koyot3
    Inscrit en
    Avril 2007
    Messages
    693
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 693
    Par défaut
    En fait, oui je pense que je me suis mal expliqué...

    Si j'ai bien compris le principe, 1 ViewModel = 1 View...
    Dans mon cas, j'ai 2 ViewModels qui correspondent à 2 UserControls

    Quand je clique sur un bouton, j'ai le UserControl1 qui s'insère dans un TabItem de mon TabControl. Jusqu'à là tout marche nickel !

    Le hic, c'est que si je reclique sur le même bouton, un deuxième TabItem se rajoute avec le même UserControl. Ca c'est le comportement attendu.

    Mais mes deux UserControls pointent vers le même ViewModel : ce qui fait que quand je modifie une valeur sur le 1er, elle est répercutée sur le 2ème...

    Exemple :
    Model1 :
    une propriété Nom

    ViewModel1 :
    une propriété qui retourne un objet Model1

    View1 :
    une textblock bindée sur le nom de l'objet Model1 renvoyé par ViewModel1

    View1 n°2 :
    même composant que la précédante. Or si je change le nom ici, ça le change dans l'autre, ce qui parait normal.

    J'imaginerais donc une solution pour créer une collection d'objet dans ma ViewModel mais comment faire pour dire que lors du binding on pointe vers tel ou tel objet ?
    Stocker le nombre d'onglet en cours et créer une propriété qui renvoie l'objet à l'index suivant dans la collection ?

    Merci d'avance !!

  7. #7
    Invité
    Invité(e)
    Par défaut
    L'ajout d'un item dans le TabControl se fait-il via une collection telle que ObservableCollection ?

    Comment construis-tu tes ViewModels ?
    Est-tu sûr d'utiliser le constructeur de chaque ViewModel pour envoyer une instance différente ?

  8. #8
    Membre éclairé Avatar de koyot3
    Inscrit en
    Avril 2007
    Messages
    693
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 693
    Par défaut
    Oui c'est ça. J'ai une collection de ViewModel sur ma Viewmodel principale. Chaque item de la collection correspond à un onglet.

    Mes ViewModels sont construites par le biais du ViewModelLocator (singleton).

    Mais je pense que mon erreur vient de vouloir dupliquer des ViewModels dynamiquement alors que je pense que c'est les objets d'une VIewModels qui doivent se dupliquer ...

    Mais j'essaie de voir si j'ai la meilleure approche...

    Merci en tout cas pour ton intérêt...

  9. #9
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par koyot3 Voir le message
    Mes ViewModels sont construites par le biais du ViewModelLocator (singleton).
    Si tes ViewModels autre le ViewModel principal sont construits avec la logique du ViewModelLocator alors c'est normal que tu ais la même instance du ViewModel.

    Donc le mieux de n'avoir que le ViewModel principal qui soit créé par le ViewModelLocator. Pour le ViewModel dont tu es susceptibles d'avoir plusieurs instances différentes tu passes par une commande qui t'instancies une nouvelle instance en utilisant le constructeur par défaut.

  10. #10
    Membre éclairé Avatar de koyot3
    Inscrit en
    Avril 2007
    Messages
    693
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 693
    Par défaut
    oula ça commence à devenir complexe mais j'aime bien ton idée...

    par contre , je ne vois pas trop comment la mettre en place, sachant que la View1 est bindée en automatique sur la classe ViewModel1.

    Crée une nouvelle instance depuis la ViewModel principale, ok, mais ensuite dire d'ajouter une View bindée dessus, j'ai l'impression que ça casse mon système pour ajouter dynamiquement des onglets

  11. #11
    Membre éprouvé
    Profil pro
    Consultant informatique
    Inscrit en
    Septembre 2009
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Septembre 2009
    Messages : 99
    Par défaut
    Et quelque chose de simple comme ça, ne pourrait pas faire l'affaire ?

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
    public class MainViewModel : ViewModelBase
        {
            public ObservableCollection<ItemViewModel> MyList { get; set; }
     
            RelayCommand addTabCommand;
            public ICommand AddTabCommand
            {
                get
                {
                    if (addTabCommand == null)
                    {
                        addTabCommand = new RelayCommand(param => this.AddTab(),
                            param => true);
                    }
                    return addTabCommand;
                }
            }
     
            private void AddTab()
            {
                ItemViewModel newTab = new ItemViewModel();
                MyList.Add(newTab);
                RaisePropertyChanged("MyList");
            }
     
            public MainViewModel()
            {
                MyList = new ObservableCollection<ItemViewModel>();
                MyList.Add(new ItemViewModel());
                MyList.Add(new Item2ViewModel());
                RaisePropertyChanged("MyList");
            }
        }

    En rajoutant les DataTemplate qui vont bien pour lier tes ViewModels à tes Views.

    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    <DataTemplate DataType="{x:Type vm:ItemViewModel}">
          <local:ItemView />
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:Item2ViewModel}">
          <local:Item2View />
    </DataTemplate>

    La view :

    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    <StackPanel DataContext="{Binding}">
            <Button Command="{Binding AddTabCommand}" Content="+" />
            <TabControl ItemsSource="{Binding MyList}" />
    </StackPanel>

  12. #12
    Invité
    Invité(e)
    Par défaut
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
    public class MainViewModel : ViewModelBase
        {
            public ObservableCollection<ItemViewModel> MyList { get; set; }
     
            RelayCommand addTabCommand;
            public ICommand AddTabCommand
            {
                get
                {
                    if (addTabCommand == null)
                    {
                        addTabCommand = new RelayCommand(param => this.AddTab(),
                            param => true);
                    }
                    return addTabCommand;
                }
            }
     
            private void AddTab()
            {
                ItemViewModel newTab = new ItemViewModel();
                MyList.Add(newTab);
                RaisePropertyChanged("MyList");
            }
     
            public MainViewModel()
            {
                MyList = new ObservableCollection<ItemViewModel>();
                MyList.Add(new ItemViewModel());
                MyList.Add(new Item2ViewModel());
                RaisePropertyChanged("MyList");
            }
        }

    Le code ci-dessus pourrait marcher mais pas la peine de déclencher l'évènement PropertyChanged sur la proprieté MyList vu que t'utilises une ObservableCollection.

    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    <DataTemplate DataType="{x:Type vm:ItemViewModel}">
          <local:ItemView />
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:Item2ViewModel}">
          <local:Item2View />
    </DataTemplate>

    Avec les DataTemplate définis comme ça tu risques d'avoir des problèmes au niveau des visuels des items et leur contenus. J'ai l'impression que c'est pas comme ça que ça devrait être pour que ça marche avec un TabControl. Bon si ça marche avec une routine que t'aurais déjà mis en place tant mieux. Moi je passerai par des DataTemplateSelector.

  13. #13
    Membre éprouvé
    Profil pro
    Consultant informatique
    Inscrit en
    Septembre 2009
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Septembre 2009
    Messages : 99
    Par défaut
    Citation Envoyé par h2s84 Voir le message
    Avec les DataTemplate définis comme ça tu risques d'avoir des problèmes au niveau des visuels des items et leur contenus. J'ai l'impression que c'est pas comme ça que ça devrait être pour que ça marche avec un TabControl. Bon si ça marche avec une routine que t'aurais déjà mis en place tant mieux. Moi je passerai par des DataTemplateSelector.
    Ce n'est certainement pas la solution la plus "dynamique" mais elle peut convenir. J'utiliserai moi aussi un DataTemplateSelector (comme proposé plus haut) mais il n'a pas l'air d'en vouloir

  14. #14
    Invité
    Invité(e)
    Par défaut
    Un exemple extrait d'un de mes TP :
    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    <Window x:Class="WpfApplicationTabContol.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplicationTabContol"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <local:TabControlItemTemplateSelector x:Key="TabControlItemTemplateSelector" />
            <local:TabControlItemContentTemplateSelector x:Key="TabControlItemContentTemplateSelector" />
        </Window.Resources>
        <Grid>
            <TabControl x:Name="tabControl" 
                        HorizontalAlignment="Stretch" 
                        ItemTemplateSelector="{StaticResource TabControlItemTemplateSelector}">
                <TabControl.Resources>
                    <Style TargetType="TabItem">
                        <Setter Property="ContentTemplateSelector" Value="{StaticResource TabControlItemContentTemplateSelector}" />
                    </Style>
                </TabControl.Resources>
            </TabControl>
        </Grid>
    </Window>

    Un DataTemplateSelctor pour les pour les headers des TabItems :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    public class TabControlItemTemplateSelector : DataTemplateSelector
        {
            public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
            {
                DataTemplate selectedDataTemplate = null;
                if (item is View1Model) selectedDataTemplate = Application.Current.FindResource("View1DataTemplate") as DataTemplate;
                else if (item is View2Model) selectedDataTemplate = Application.Current.TryFindResource("View2DataTemplate") as DataTemplate;
     
                return selectedDataTemplate;
            }
        }


    Un DataTemplateSelector pour les contenus des TabItems :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    public class TabControlItemContentTemplateSelector : DataTemplateSelector
        {
            public override DataTemplate SelectTemplate(object item, DependencyObject container)
            {
                DataTemplate selectedDataTemplate = null;
                if (item is View1Model) selectedDataTemplate = Application.Current.FindResource("View1ContentDataTemplate") as DataTemplate;
                else if (item is View2Model) selectedDataTemplate = Application.Current.TryFindResource("View2ContentDataTemplate") as DataTemplate;
     
                return selectedDataTemplate;
            }
        }

Discussions similaires

  1. [WPF - MVVM] Communications entre ViewModels
    Par NeoKript dans le forum Windows Presentation Foundation
    Réponses: 9
    Dernier message: 06/02/2012, 14h06
  2. MVVM TabControl, TabItem, UserControl
    Par mathrb dans le forum Windows Presentation Foundation
    Réponses: 6
    Dernier message: 25/08/2010, 21h20
  3. [MVVM] Binding à un ViewModel depuis le code-behind
    Par Gigi070 dans le forum Windows Presentation Foundation
    Réponses: 23
    Dernier message: 29/03/2010, 17h46

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo