Actualisation d'une vue C#

Rafraîchir une vue dont le modèle est dans un autre projet

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour, je rencontre un petit problème concernant l'actualisation d'une de mes vues.

La classe représentant ma vue est une WindowsForm et possède :

  • Divers champs avec des informations entrées par l'utilisateur
  • Un 'ModelBuilder' qu'elle instancie elle-même avec les informations ci-dessus
  • Une barre de progression et un label étant censés s'actualiser durant la construction du modèle
  • Un bouton start

Mon ModelBuilder contient :

  • Une liste d'objet, possédant tous un nom
  • Une méthode build() qui crée un modèle global en itérant sur les objets ci-dessus

Mon objectif est le suivant : Lorsque l'utilisateur appuie sur le bouton Start, la méthode build() est appelée, et construit le modèle. La vue s'actualise en temps réel en affichant l'objet en cours de création, ainsi que la progression global de la construction.

Mon problème est le suivant : Les deux classes n'étant pas dans le même projet, la vue (WindowsForm) possède une référence sur le modèle, mais pas l'inverse (pas possible sinon dépendance circulaire).

Une idée ?

Édité par Coyote

+0 -0

Salut,

Je suppose que tu travailles avec C# (projet Windows Forms), dans ce cas il faudrait implémenter l'interface INotifyPropertyChanged (lien) sur le modèle de vue et ensuite appeler NotifyPropertyChanged lorsque les propriétés du modèle d'affichage sont modifiées. Par exemple, il suffit d'implémenter INotifyPropertyChanged et appeler la méthode NotifyPropertyChanged pour les propriétés du bouton Start.

+1 -0
Auteur du sujet

Salut, oui il s'agit bien du C# j'avais oublié de le préciser. Je regarde ton lien tout de suite, merci !

Edit :

Hmmm je ne sais pas si tu as bien saisi mon problème ou si j'ai mal compris ta réponse. Côté windowsForm, après le start, voilà ce que j'ai en gros :

1
2
3
4
5
private void Start_Click(object sender, EventArgs e)
{
   this.model = new Model(champA, champB, champC, etc...);
   this.model.build(); 
}

Côté Model avec les modifs que tu m'as conseillées :

 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
// Model.cs
// attributs & propriétés
public event PropertyChangedEventHandler PropertyChanged;
private Substation currentSubstation;

public Substation CurrentSubstation
{
  get
  {
     return this.currentSubstation;
  }
  set
  {
     if (value != this.currentSubstation)
     {
        this.currentSubstation = value;
        NotifyPropertyChanged();
     } 
  }
}

// methods

public void build(){
   foreach(string uri in SubstationUri){
      Substation s = new Substation(new Uri(uri));
      this.currentSubstation = s;
      NotifyPropertyChanged();
   }
}

private void NotifyPropertyChanged(String propertyName = "")
   {
       if (PropertyChanged != null)
       {
           PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
           // Impossible de communiquer avec la vue pour lui demander de se rafraichir ! :(
       }
   }

Le problème est que côté WindowsForm, la vue est toujours dans son "this.model.build()" et est incapable de réagir.

Édité par RedTheTweet

+0 -0
1
2
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
// Impossible de communiquer avec la vue pour lui demander de se rafraichir ! :(

La vue sait qu’elle doit s’abonner à l’évènement PropertyChanged, puisqu'elle va "scanner" toutes les classes implémentant l’interface INotifyPropertyChanged (la donnée à rafraîchir est spécifiée par le nom de la propriété passée en argument de l’évènement). Bref, c'est transparent pour toi.

1
2
3
4
5
6
7
public void build(){
   foreach(string uri in SubstationUri){
      Substation s = new Substation(new Uri(uri));
      this.currentSubstation = s;
      NotifyPropertyChanged();
   }
}

Attention, tel qu’est écrit ce code la vue va être notifiée qu’une propriété du nom de build doit être rafraichie, or build est une méthode et non une propriété, ça ne fonctionnera pas (sans compter que visiblement c’est currentSubstraction que tu souhaites rafraîchir, or le setter de la propriété CurrentSubstation possède déjà un appel vers NotifyPropertyChanged, il te suffit d’affecter la propriété directement plutôt que le backing field).

Maintenant, pour que toute cette mécanique fonctionne il te faut créer, côté vue, des liens entre tes classes implémentant INotifyPropertyChanged et tes contrôles Winforms (via des BindingSource). Là-dessus je te renvoie vers la MSDN ou ton moteur de recherche favori parce que ça fait des siècles que je n’ai pas fait de Winforms =/

Édité par Truitemouth

Auteur du sujet

Effectivement, l'appel de la fonction NotifyPropertyChanged() est de trop dans ma méthode build, j'ai donc :

1
2
3
4
5
6
public void build(){
   foreach(string uri in SubstationUri){
      Substation s = new Substation(new Uri(uri));
      this.currentSubstation = s;
   }
}

Par contre concernant le liens entre mon Model (classe implémentant INOtifyPropertyChanged) et ma vue, il ne peut être fait que dans un sens (vue vers model) car la référence inverse n'existe pas. Je vais regarder ça sur le MSDN tout de suite, j'espère que c'est faisable :)

BlueShark, j'up mon code dans quelques minutes. Merci !

Edit :

Honnêtement j'ai du mal à saisir le code du lien que tu m'as envoyé BlueShark, voici l'équivalent que je devrais avoir :

1
2
3
4
5
6
private void Start_Click(object sender, EventArgs e)
{
  this.model = new ModelBuilder(......);
  this.modelTimeText.DataBindings.Add(new Binding("Text", ??, "??"));
  this.model.build();
}

Édité par RedTheTweet

+0 -0
1
this.currentSubstation = s;

Attention, il faut que tu affectes ton s à la propriété et non au backing field (CurrentSubstation et non currentSubstation), car en l'état l’événement PropertyChanged ne sera pas déclenché.

Le lien que t'a donné Blue Shark dans son premier lien illustre très bien ce mécanisme de binding.

Auteur du sujet

Merci truitemouth, c'est modifié ! J'ai mi un point d'arrêt à l'intérieur du NotifyPropertyChanged et j'y arrive bien! Erreur d’inattention :/

Il ne me reste plus qu'à relié correctement la vue…


Pour résumer mon code actuel :

ModelBuilder.cs :

 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
        public event PropertyChangedEventHandler PropertyChanged;
        private Substation currentSubstation;

        public Substation CurrentSubstation
        {
            get
            {
                return this.currentSubstation;
            }
            set
            {
                if (value != this.currentSubstation)
                {
                    this.currentSubstation = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private void NotifyPropertyChanged(String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public void Build()
        {
            foreach (string substationUri in substationsUri)
            {                 
                Substation substation = new Substation(new Uri(substationUri));
                this.CurrentSubstation = substation;
                this.substations.Add(substation);
            } 
        }

View.cs

1
2
3
4
5
6
 private void StartImportation_Click(object sender, EventArgs e)
 {
    this.model = new ModelBuilder(....);
    this.timeLabel.DataBindings.Add(new Binding("Text", this.model, "CurrentSubstation.name"));
    this.model.Build();
 }

Il me manque quelque chose mais quoi ?

Édité par RedTheTweet

+0 -0
Auteur du sujet

Apparemment le Label a besoin d'un Update() manuel pour être mi à jour graphiquement… J'ai utilisé le même code pour binder ma progressBar et tout fonctionne très bien, par contre le Label ne veut rien savoir !

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte