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 :

Notifié le ViewModel à modification d'une propriété d'une instance [MVVM]


Sujet :

Windows Presentation Foundation

  1. #1
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2010
    Messages
    188
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Août 2010
    Messages : 188
    Points : 129
    Points
    129
    Par défaut Notifié le ViewModel à modification d'une propriété d'une instance
    Bonjour.

    J'ai dans un premier temps un formulaire de données client. Celui-ci possède une Grid pour laquelle j'ai mis en DataContext l'instance de mon client récupérer de mon ViewModel.

    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    <UserControl ...>
        <UserControl.DataContext>
            <ViewModels:MyViewModel />
        </UserControl.DataContext>
     
        <Grid>
            <Grid DataContext="{Binding Path=Customer}">
                <TextBox Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged}" />
            </Grid>
            <Button Command="{Binding Path=SaveCommand}" />
        </Grid>
    </UserControl>

    Le problème est que la modification de la propriété FirstName de mon Client ne se répercute pas. J'ai placer un point d'arrêt dans le setter mais il ne passe pas à l'intérieur.

    Code csharp : 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
     
    public class MyViewModel : NotificationObject
    {
        public MyViewModel()
        {
            this.SaveCommand = new DelegateCommand(this.Save, this.CanSave);
        }
     
        public ICustomer Customer
        {
            get { return this.customer; }
            set
            {
                this.customer = value;
                this.SaveCommand.RaiseCanExecuteChanged();
            }
        }
     
        public DelegateCommand SaveCommand { get; private set; }
     
        public bool CanSave()
        {
            return this.Customer != null && !string.IsNullOrWhiteSpace(this.Customer.FirstName)
        }
     
        public void Save ()
        {
        }
    }

    Pour info, je travail avec les bibliothèque de Prism pour la gestion MVVM.

    Merci de votre aide

  2. #2
    Expert confirmé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Novembre 2009
    Messages
    2 025
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Novembre 2009
    Messages : 2 025
    Points : 5 462
    Points
    5 462
    Par défaut
    Il faut préciser que le BindingMode=TwoWay.

  3. #3
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2010
    Messages
    188
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Août 2010
    Messages : 188
    Points : 129
    Points
    129
    Par défaut
    Mode TwoWay ne fonctionne pas

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    DataContext="{Binding Path=Customer, Mode=TwoWay}"
    Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"

  4. #4
    Membre habitué
    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
    Points : 152
    Points
    152
    Par défaut
    Dans ton code je ne vois aucun appel du genre NotifyPropertyChanged ou RaisePropertyChanged pour dire à ta vue que le contexte a changé.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public ICustomer Customer
        {
            get { return this.customer; }
            set
            {
                this.customer = value;
                RaisPropertyChanged("Customer");
                this.SaveCommand.RaiseCanExecuteChanged();
            }
        }
    Cette réponse vous a aidé ?
    Problème résolu ?

  5. #5
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2010
    Messages
    188
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Août 2010
    Messages : 188
    Points : 129
    Points
    129
    Par défaut
    Certes en effet je n'ai pas mis ce code, mais celui-ci ne sert qu'à mettre à jour du ViewModel vers la View. Hors mon problème se situe dans l'autre sens.

  6. #6
    Membre habitué
    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
    Points : 152
    Points
    152
    Par défaut
    Tu peux mettre le code de ta classe Customer ? J'imagine que ça doit ressembler à ça :

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Customer : ICustomer
    {
            private string _firstName;
            public string FirstName
            {
                get { return _firstName; }
                set
                {
                    _firstName= value;
                    RaisePropertyChanged("FirstName");
                }
            }
    }
    Cette réponse vous a aidé ?
    Problème résolu ?

  7. #7
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2010
    Messages
    188
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Août 2010
    Messages : 188
    Points : 129
    Points
    129
    Par défaut
    C'est à peu près ça sans le RaisePropertyChanged("FirstName");

    Cette classe Customer sert pour d'autre projet non forcement WPF, donc aucune dépendance à Prism ou autre méthodologie PropertyChanged de ce genre.

    Mes propriétés sont ainsi toutes écrites de cette façon (Et il n'y à que ça des propriétés dans cette classe)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public string FirstName { get; set; }

  8. #8
    Membre habitué
    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
    Points : 152
    Points
    152
    Par défaut
    J'ai recrée un petit projet pour reproduire ce que tu fais :

    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
    public class Customer
        {
            private string _firstName;
            public string FirstName
            {
                get { return _firstName; }
                set { _firstName = value; }
            }
        }
     
        public class MyViewModel
        {
            public MyViewModel()
            {
                Customer = new Customer();
                Customer.FirstName = "toto";
            }
     
            private Customer _customer;
            public Customer Customer
            {
                get { return _customer; }
                set
                {
                    _customer = value;
                }
            }
        }

    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
    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
            xmlns:ViewModels="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525">
        <Window.DataContext>
            <ViewModels:MyViewModel />
        </Window.DataContext>
     
        <Grid>
            <Grid DataContext="{Binding Path=Customer}">
                <TextBox Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
            </Grid>
        </Grid>
    </Window>

    Ma propriété FirstName change bien quand je la modifie. Du coup je vois pas où est le soucis...
    Cette réponse vous a aidé ?
    Problème résolu ?

  9. #9
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2010
    Messages
    188
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Août 2010
    Messages : 188
    Points : 129
    Points
    129
    Par défaut
    Bon du coup je vient de faire d'autres tests, non concluant mais ai une idée pour détourner le problème.

    M'abonner à l'évènement TextChanged de tout mes champs en utilisant Systeme.Windows.Interactivity pour rester en MVVM.

    De ce fait je vais avoir un commande supplémentaire qui un fois activée avec la méthode ci-dessus, va appelé le code suivant dont j'ai besoin.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this.SaveCommand.RaiseCanExecuteChanged();
    Oui j'ai oublié de préciser que mon but étais de rentrer dans le set de la propriété Customer pour appeler cette méthode et ainsi dégriser mon Bouton Save; Dans la condition ou les champs FirstName et LastName sont bien remplis.

  10. #10
    Expert confirmé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Novembre 2009
    Messages
    2 025
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Novembre 2009
    Messages : 2 025
    Points : 5 462
    Points
    5 462
    Par défaut
    C'est peut etre simplement le nom de la variable qui est mal écrit.
    N'y a-t-il pas d'erreur de binding dans la fenetre de sortie de VS ?

  11. #11
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2010
    Messages
    188
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Août 2010
    Messages : 188
    Points : 129
    Points
    129
    Par défaut
    Aucunes erreurs de ce côté.

    J'ai forcer ma méthode CanSave à vrai pour avoir mon bouton disponible. Après avoir cliqué sur celui-ci, ma méthode Save renvoi bien le bon Customer, qui plus est, avec les informations modifiées. C'est juste l'appel à la méthode RaiseCanExecuteChanged() citée précédemment qui me gêne.

  12. #12
    Expert confirmé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Novembre 2009
    Messages
    2 025
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Novembre 2009
    Messages : 2 025
    Points : 5 462
    Points
    5 462
    Par défaut
    Si tu as mis un breakpoint sur le setter de firstName et qu'il ne s'arrete jamais, je ne vois pas comment firstName peut avoir la bonne valeur quand tu clics sur ton bouton!

  13. #13
    Membre habitué
    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
    Points : 152
    Points
    152
    Par défaut
    Citation Envoyé par Monkey56 Voir le message
    Oui j'ai oublié de préciser que mon but étais de rentrer dans le set de la propriété Customer pour appeler cette méthode et ainsi dégriser mon Bouton Save; Dans la condition ou les champs FirstName et LastName sont bien remplis.
    Ah ! Ça change tout Moi je pensais que tu ne rentrais pas dans FirstName. Tu n'as pas besoin d'utiliser RaiseCanExecuteChanged(); juste la méthode CanSave() et tout se fait automatiquement.

    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
    35
    36
    37
    38
    39
    40
    41
    public class MyViewModel
        {
            public MyViewModel()
            {
                Customer = new Customer();
                Customer.FirstName = "toto";
            }
     
            private Customer _customer;
            public Customer Customer
            {
                get { return _customer; }
                set
                {
                    _customer = value;
                }
            }
     
            RelayCommand _saveCommand;
            public ICommand SaveCommand
            {
                get
                {
                    if (_saveCommand == null)
                    {
                        _saveCommand = new RelayCommand(Save, CanSave);
                    }
                    return _saveCommand;
                }
            }
     
            public bool CanSave(object o)
            {
                return Customer.FirstName.Equals("titi");
            }
     
            public void Save(object o)
            {
     
            }
        }
    Cette réponse vous a aidé ?
    Problème résolu ?

  14. #14
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2010
    Messages
    188
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Août 2010
    Messages : 188
    Points : 129
    Points
    129
    Par défaut
    En effet mon Breakpoint ne se situe pas dans FirstName mais Customer dans mon ViewModel.

    Je vois que je me suis mal exprimer, je réexplique donc.

    Je possède un formulaire ainsi qu'un ViewModel. Ce dernier possède un instance de Customer qui utilisé en tant que DataContext de ma vue. Pour la suite, les différent champ utilise des Binding sur les propriété de mon Customer.
    J'avais en tête qu'une fois un champ de Customer changé, le setter de ce dernier situé dans le ViewModel serais appelé me permettant ainsi d’exécuter la méthode RaiseCanExecuteChanged de ma commande afin de forcer à refaire le test sur la méthode CanExecute associée : ici CanSave.
    Hors je ne passe jamais dans le setter.
    Dans mes tests unitaires, une propriété de Customer changé, la méthode CanSave me renvoi toujours le résultat attendu.

    Euhh par contre en interne, nous avons une charte qui nous dit de rester sur Prism pour tout ce qui est gestion du MVVM, donc je n'utilise que les DelegateCommand . Le fonctionnement est différent des RelayCommand.

  15. #15
    Membre habitué
    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
    Points : 152
    Points
    152
    Par défaut
    Ok, dans ce cas une solution peut être de faire une nouvelle propriété dans MyViewModel de ce type là :

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public string FirstName
    {
         get { return Customer.FirstName; }
         set
         { 
             Customer.FirstName = value;
             SaveCommand.RaiseCanExecuteChanged();
         }
    }

    Certes, ça fait du travail en plus mais ça devrait fonctionner.

    Autre solution :http://stackoverflow.com/a/7353704/1668669
    Cette réponse vous a aidé ?
    Problème résolu ?

  16. #16
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2010
    Messages
    188
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Août 2010
    Messages : 188
    Points : 129
    Points
    129
    Par défaut
    Mince, moi qui voulais tout faire pour éviter cela .

    Bon ba je suppose que la je n'ai plus d'autres choix.

    Ou bien j'attache des AddEventHandler pour chacune des propriété du mon Customer. Dès qu'une propriété est modifié, elle appelle RaiseCanExecuteChanged.

    J'ai à peu près le code en tête. Je test cela de suite et revient.

  17. #17
    Expert confirmé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Novembre 2009
    Messages
    2 025
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Novembre 2009
    Messages : 2 025
    Points : 5 462
    Points
    5 462
    Par défaut
    Ah je comprends mieux le soucis
    En effet si tu modifies simplement le FirstName ca ne peut pas appeler le setter de Customer puisque tu ne modifie pas sa valeur, mais une de ses propriétés

    Et si tu n'as pa le droit de modifier le code de Customer la solution de Lordinaire peut etre une solution si tu bindes directement sur le nouveau firstItem. En gros tu va faire un genre de wrapper partout ou tu utilises des objets "metier" que tu n'as pas le droit de toucher... Je sais pas si tu gagnes quelque chose . Enfin surtout qu'implenter InotifyPropertyChanged n'a rien avoir avec wpf et donc l'utiliser n'est pas bien dangereux! (enfin si tu restes dans un projet ou chaque milisecond n'est pas precieux! Mais dans ce cas là ca serait ptetre pas du C#.

  18. #18
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2010
    Messages
    188
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Août 2010
    Messages : 188
    Points : 129
    Points
    129
    Par défaut
    La nouvelle idée est la suivante.

    Etant donné que je passe au moins une fois dans le setter, et que ma classe Customer ne sert que pour diffusé les valeurs FirstName ... et autres, je vient de mettre en place le code suivant

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public ICustomer Customer
            {
                get { return this.customer; }
                set
                {
                    this.customer = value;
                    foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(this.customer))
                    {
                        propertyDescriptor.AddValueChanged(this, (sender, e) => { this.SaveCommand.RaiseCanExecuteChanged(); });
                    }
                }
            }
    Simple à chaque fois que mon instance de Customer change, je m'abonne à toutes les propriétés pour qu'elle appellent la méthode si elles changent.

    Malheureusement, cela n'a pas fonctionné .

    Je tente de corriger le bug mais sinon voila l'idée.

    Si celle-ci vous parait lourde, avertissez moi.

  19. #19
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2010
    Messages
    188
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Août 2010
    Messages : 188
    Points : 129
    Points
    129
    Par défaut
    Bug corrigé

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    propertyDescriptor.AddValueChanged(this.customer, (sender, e) => { this.SaveCommand.RaiseCanExecuteChanged(); });
    Merci en tout cas pour votre aide qui m'ont conduit au résultat voulu

  20. #20
    Expert confirmé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Novembre 2009
    Messages
    2 025
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Novembre 2009
    Messages : 2 025
    Points : 5 462
    Points
    5 462
    Par défaut
    Il vaudrait mieux que tu test si l'objet n'est pas null avant, et que tu te désabonne à tout tes petis event. (Donc ne pas utiliser une méthode anonyme). Tu risques d'avoir des surprises si le customer change beaucoup

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 1
    Dernier message: 14/12/2013, 00h05
  2. Réponses: 10
    Dernier message: 23/03/2011, 16h10
  3. Réponses: 10
    Dernier message: 01/12/2010, 08h26
  4. Binding sur une proprité d'une propriété
    Par al2000 dans le forum Windows Forms
    Réponses: 0
    Dernier message: 21/06/2010, 17h24
  5. Eval d'une propriété d'une classe dans une classe
    Par bizet dans le forum ASP.NET
    Réponses: 4
    Dernier message: 28/10/2008, 09h43

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