DataBinding et asynchrone

Pas de mise à jour

Le problème exposé dans ce sujet a été résolu.

Bonjour,

Je suis en train de réaliser une application universelle pour Windows 10, qui utilise l'API Jamendo. J'essaie de suivre le pattern MVVM mais je rencontre un problème.

J'ai un modèle Artist qui contient des informations comme le nom, le site web ou l'ID d'un artiste. J'ai un ViewModel HomeVoiewModel contenant une liste d'artistes appelée RandomArtists. Je veux ensuite afficher le nom de chacun de ces artistes dans une vue Home.

Le problème est que je récupère les infos sur l'artiste de cette façon :

 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
public Artist (int Id)
        {
            Debug.WriteLine("Getting artist with id " + Id.ToString());

            var requestUri = new Uri(Constants.API_URL + "artists/?format=json&id=" + Id + "&client_id=" + Constants.CLIENT_ID);
            var request = (HttpWebRequest)WebRequest.Create(requestUri);

            request.BeginGetResponse(r =>
            {
                var httpRequest = (HttpWebRequest)r.AsyncState;
                var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);

                using (var reader = new StreamReader(httpResponse.GetResponseStream()))
                {
                    var response = reader.ReadToEnd();

                    JsonObject root = JsonObject.Parse(response);

                    if (root.GetNamedObject("headers").GetNamedNumber("code") != 0)
                    {
                        throw new Exception(root.GetNamedObject("headers").GetNamedString("error_message"));
                    }
                    else
                    {
                        var results = root.GetNamedArray("results");
                        var result = results.First().GetObject();
                        this.ID = Int32.Parse(result.GetNamedString("id"));
                        Debug.WriteLine("ID is " + this.ID);
                        this.Name = result.GetNamedString("name");
                        this.Website = result.GetNamedString("website");
                        this.JoinDate = Constants.DateFromstring(result.GetNamedString("joindate"));
                        this.ImageUrl = result.GetNamedString("image");
                        this.ShortUrl = result.GetNamedString("shorturl");
                        this.Shareurl = result.GetNamedString("shareurl");
                    }
                }
            }, request);
        }

Ma vue, n'affiche pas le nom de l'artiste. Je pense que ça vient du fait que la requête mets du temps à se faire et que ma vue essaie d'obtenir le nom de l'artiste alors qu'on ne l'a pas encore obtenu, mais ma vue devrait se mettre à jour quand même une fois qu'on l'a reçu, non ?

Je vous mets tout mon code ici, si besoin :

  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
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Windows.Data.Json;

namespace Jamendo.Model
{
    public class Artist : INotifyPropertyChanged
    {
        private int _id;

        /// <summary>
        /// Identifiant unique de l'artiste
        /// </summary>
        public int ID
        {
            get
            {
                return _id;
            }
            private set
            {
                _id = value;
                RaisePropertyChanged("ID");
            }

        }

        private string _name;

        /// <summary>
        /// Nom de l'artiste
        /// </summary>
        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                RaisePropertyChanged("Name");
            }
        }

        private string _website;

        /// <summary>
        /// Adresse du site web de l'artiste
        /// </summary>
        public string Website
        {
            get
            {
                return _website;
            }
            set
            {
                _website = value;
                RaisePropertyChanged("Website");
            }
        }

        private DateTime _joinDate;

        /// <summary>
        /// Date d'inscription de l'artiste
        /// </summary>
        public DateTime JoinDate
        {
            get
            {
                return _joinDate;
            }
            set
            {
                _joinDate = value;
                RaisePropertyChanged("JoinDate");
            }
        }

        private string _imageurl;

        /// <summary>
        /// Adresse de l'image de profil de l'artiste
        /// </summary>
        public string ImageUrl
        {
            get
            {
                return _imageurl;
            }
            set
            {
                _imageurl = value;
                RaisePropertyChanged("ImageUrl");
            }
        }

        private string _shortUrl;

        /// <summary>
        /// Adresse pour le partage de la page de l'artiste
        /// </summary>
        public string ShortUrl
        {
            get
            {
                return _shortUrl;
            }
            set
            {
                _shortUrl = value;
                RaisePropertyChanged("ShortUrl");
            }
        }

        private string _shareUrl;

        /// <summary>
        /// Adresse de la page de l'artiste sur Jamendo.com
        /// </summary>
        public string Shareurl
        {
            get
            {
                return _shareUrl;
            }
            set
            {
                _shareUrl = value;
                RaisePropertyChanged("ShareUrl");
            }
        }

        public Artist (int Id)
        {
            Debug.WriteLine("Getting artist with id " + Id.ToString());

            var requestUri = new Uri(Constants.API_URL + "artists/?format=json&id=" + Id + "&client_id=" + Constants.CLIENT_ID);
            var request = (HttpWebRequest)WebRequest.Create(requestUri);

            request.BeginGetResponse(r =>
            {
                var httpRequest = (HttpWebRequest)r.AsyncState;
                var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);

                using (var reader = new StreamReader(httpResponse.GetResponseStream()))
                {
                    var response = reader.ReadToEnd();

                    JsonObject root = JsonObject.Parse(response);

                    if (root.GetNamedObject("headers").GetNamedNumber("code") != 0)
                    {
                        throw new Exception(root.GetNamedObject("headers").GetNamedString("error_message"));
                    }
                    else
                    {
                        var results = root.GetNamedArray("results");
                        var result = results.First().GetObject();
                        this.ID = Int32.Parse(result.GetNamedString("id"));
                        Debug.WriteLine("ID is " + this.ID);
                        this.Name = result.GetNamedString("name");
                        this.Website = result.GetNamedString("website");
                        this.JoinDate = Constants.DateFromstring(result.GetNamedString("joindate"));
                        this.ImageUrl = result.GetNamedString("image");
                        this.ShortUrl = result.GetNamedString("shorturl");
                        this.Shareurl = result.GetNamedString("shareurl");
                    }
                }
            }, request);
        }

        protected void RaisePropertyChanged(string propertyName)
        {
            Debug.WriteLine("Just changed " + propertyName);

            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

 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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
using Jamendo.Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Jamendo.Presentation
{
    public class HomeViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<Artist> _randomArtists;

        public ObservableCollection<Artist> RandomArtists
        {
            get
            {
                return _randomArtists;
            }
            set
            {
                _randomArtists = value;
                RaisePropertyChanged("RandomArtists");
            }
        }

        private string _greeter;

        public string Greeter
        {
            get
            {
                return _greeter;
            }
            set
            {
                _greeter = value;
                RaisePropertyChanged("Greeter");
            }
        }

        public HomeViewModel ()
        {
            Greeter = "Hey bro !";

            RandomArtists = new ObservableCollection<Artist>();

            var artist = new Artist(471621);
            Debug.WriteLine(artist.Name);

            RandomArtists.Add(artist);
            RandomArtists.Add(new Artist(475635));
        }

        protected void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

 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
<Page
    x:Class="Jamendo.Pages.Home"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Jamendo.Pages"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:model="using:Jamendo.Model"
    xmlns:vm="using:Jamendo.Presentation"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <ScrollViewer>
            <Grid>
                <TextBlock Text="{x:Bind ViewModel.Greeter, Mode=OneWay}"/>
                <ItemsControl Margin="0, 100" ItemsSource="{x:Bind ViewModel.RandomArtists, Mode=OneWay}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate x:DataType="model:Artist">
                            <TextBlock>
                                <Run>Name : </Run>
                                <Run Text="{x:Bind Name, Mode=OneWay}"></Run>
                            </TextBlock>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
                <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="140,64,0,0" VerticalAlignment="Top" Click="button_Click"/>
            </Grid>
        </ScrollViewer>
    </Grid>
</Page>

 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
using Jamendo.Presentation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

// Pour plus d'informations sur le modèle d'élément Page vierge, voir la page http://go.microsoft.com/fwlink/?LinkId=234238

namespace Jamendo.Pages
{
    /// <summary>
    /// Une page vide peut être utilisée seule ou constituer une page de destination au sein d'un frame.
    /// </summary>
    public sealed partial class Home : Page
    {
        public HomeViewModel ViewModel { get; set; }

        public Home()
        {
            this.InitializeComponent();

            this.ViewModel = new HomeViewModel();
        }

        private async void button_Click(object sender, RoutedEventArgs e)
        {
            ViewModel.Greeter = "Plop !";
            ViewModel.RandomArtists.Add(new Model.Artist(357612));
        }
    }
}

Voilà, merci d'avance pour votre aide ! :)

+0 -0

Je ne vois pas d'où vient ton problème, puisque tu utilises bien des ObservableCollection, mais j'ai quelques remarques sur ton code.

Concernant tes RaisePropertyChanged, n'utilise pas des chaines de caractère "en dur". Utilise soit nameof(NomDeTapropriété), soit réimplémente une fonction du genre

1
2
3
4
5
6
7
8
public void RaiseProperty([CallerMemberName] name = "")
{
RaisePropertyChanged(name);
}

// Et on s'en sert comme ça
private int maProp;
public int MaProp { set { maProp = value; RaiseProperty(); }}

Tu trouvera un exemple plus complet (et surtout plus exact) sur le net.

D'autre part, il vaudrait mieux utiliser un conteneur IoC pour instancier ton ViewModel. Dans ton xaml, tu aurais

1
DataContext="{Binding MonViewModel Source={StaticResources MonLocator}}"

et dans ton code behind

1
2
3
InitializeComponents();
// Un truc dans le genre, je ne suis pas sur pour l'ordre des paramètre
DataContextChanged += (e, args) => ViewModel = e.NewValue;

La j'ai pas trop le temps d'examiner ton code, j'essaierais de prendre un peu de temps ce soir si tu n'as pas résolu ton problème.

EDIT : De plus, ton programme plante. Tu essaies de modifier des propriété bindées par l'UI, sauf que tu les modifier dans un thread différent du thread de l'UI.

+0 -0

Merci pour tes conseils. J'ai essayé de mettre en place la conteneur IoC, mais je n'en ai jamais utilisé, ton code ne marche pas (j'ai remplacé e.NewValue par args.NewValue, vu que le premier n'existait pas). Est-ce qu'il faut faire autre chose ?

C'est normal, y'a d'autre trucs à mettre en place, des bibliothèques existent pour te simplifier la tâche. Notamment Mvvm light qui est assez réputé. Je te conseille de l'utiliser. Elle permet de simplifier la mise en place du pattern MVVM. Notamment grâce à la classe ViewModelBase qui abstrait tout ce qui concerne INotifyPropertyChanged. Elle propose aussi un système de messages pour que les view model communiquent entre eux. Et elle propose aussi un conteneur IoC (pour inversion de contrôle).

Mais le plus gros soucis viens du fait que tu modifies des propriétés utilisé par l'UI. Je ne comprends pas que ça ne plante pas chez toi.

Une meilleure façon de faire serait d'avoir une classe qui se chargerais d'instancier un artiste. ici il explique bien le principe avec sa classe Client et ServiceClient. C'est un cours pour WP8, mais le principe de MVVM est le même.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

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