-3

I'm trying to dev a WPF app which displays 3 views that we can switch using a side menu.

The first View displays articles. I've tried this view in another project on its own and it works just fine.

My issue:

The problem is that when I use it in my navigation (with the exact same code) the Images or Titles won't appear unless I click on another view first then click back.

Here's my current code:

Model: (Article)

    public class Article : INotifyPropertyChanged
    {
        int id;
        string title;
        string link;
        BitmapImage image;

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public Article(int id, string title, string link, BitmapImage image)
        {
            Id = id;
            Title = title;
            Link = link;
            Image = image;
        }

        public int Id
        {
            get => id;
            set { if (value != id) { id = value; NotifyPropertyChanged(); } }
        }

        public string Title
        {
            get => title;
            set { if (value != title) { title = value; NotifyPropertyChanged(); } }
        }

        public string Link
        {
            get => link;
            set { if (value != link) { link = value; NotifyPropertyChanged(); } }
        }

        public BitmapImage Image
        {
            get => image;
            set { if (value != image) { image = value; NotifyPropertyChanged(); } }
        }
    }

ViewModel: (ArticlesViewModel)

public class ArticlesViewModel : INotifyPropertyChanged
    {
        Article[] articles;

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public AccueilViewModel()
        {
            loadArticle();
        }

        public Article[] Articles
        {
            get => articles;
            set { if (value != articles) { articles = value; NotifyPropertyChanged(); } }
        }

         private async void loadArticle()
        {
            var client = new WordPressClient(App.WordpressLink);

            try
            {
                var asychArticles = GetArticles(client);
                await asychArticles;
                var articles = asychArticles.Result;

                int i = 0;
                int a;

                foreach (WordPressPCL.Models.Post p in articles)
                {
                    a = p.FeaturedMedia.Value;

                    var media = GetMedia(client, a);
                    await media;

                    BitmapImage bimage = App.getImage(media.Result.SourceUrl);

                    articleImg[i].Source = bimage;
                    articleTitle[i].Content = p.Title.Rendered.ToUpper();
                    articleLink[i] = p.Link;

                    i++;
                    if (i == 4) break;
                }
            }
            catch (System.Net.Http.HttpRequestException e)
            {

            }
        }
        static async Task<IEnumerable<WordPressPCL.Models.Post>> GetArticles(WordPressClient client)
        {
            var posts = await client.Posts.GetAll();
            return posts;
        }
        static async Task<WordPressPCL.Models.MediaItem> GetMedia(WordPressClient client, int number)
        {
            var media = await client.Media.GetByID(number);
            return media;
        }
    }

View: (ArticlesView)

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="10*"/>
            <RowDefinition Height="5*"/>
        </Grid.RowDefinitions>

        <Grid Name="Article1" Grid.Row="0" Margin="10,10,10,10" DataContext="{Binding Articles[0]}">
            <Grid.RowDefinitions>
                <RowDefinition Height="5*"/>
                <RowDefinition Height="1*"/>
            </Grid.RowDefinitions>

            <Image Name="Image1" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
            <Button Name="Titre1" Grid.Row="1" Content="{Binding Title}"/>

        </Grid>

        <Grid Grid.Row="1">

            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Grid Name="Article2" Grid.Column="0"  Margin="10,10,10,10" DataContext="{Binding Articles[1]}">
                <Grid.RowDefinitions>
                    <RowDefinition Height="5*"/>
                    <RowDefinition Height="1*"/>
                </Grid.RowDefinitions>

                <Image Name="Image2" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
                <Button Name="Titre2" Grid.Row="1" Content="{Binding Title}"/>

            </Grid>

            <Grid Name="Article3" Grid.Column="1"  Margin="10,10,10,10" DataContext="{Binding Articles[2]}">
                <Grid.RowDefinitions>
                    <RowDefinition Height="5*"/>
                    <RowDefinition Height="1*"/>
                </Grid.RowDefinitions>

                <Image Name="Image3" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
                <Button Name="Titre3" Grid.Row="1" Content="{Binding Title}"/>

            </Grid>

            <Grid Name="Article4" Grid.Column="2" Margin="10,10,10,10" DataContext="{Binding Articles[3]}">
                <Grid.RowDefinitions>
                    <RowDefinition Height="5*"/>
                    <RowDefinition Height="1*"/>
                </Grid.RowDefinitions>

                <Image Name="Image4" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
                <Button Name="Titre4" Grid.Row="1" Content="{Binding Title}"/>

            </Grid>

        </Grid>
    </Grid>

MainWindow.xaml:

<Window.Resources>

        <DataTemplate x:Name="ArticlesViewTemplate" DataType="{x:Type viewmodels:ArticlesViewModel}">
            <views:ArticlesView DataContext="{Binding}"/>
        </DataTemplate>
        <DataTemplate x:Name="Feature2ViewTemplate" DataType="{x:Type viewmodels:Feature2ViewModel}">
            <views:Feature2View DataContext="{Binding}"/>
        </DataTemplate>
        <DataTemplate x:Name="Feature3ViewTemplate" DataType="{x:Type viewmodels:Feature3ViewModel}">
            <views:Feature3View DataContext="{Binding}"/>
        </DataTemplate>

</Window.Resources>
<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="4*" />
        </Grid.ColumnDefinitions>

        <Grid Grid.Column="0">
            <StackPanel Grid.Row="1" VerticalAlignment="Top" Margin="20,20,20,0">
                <Button Content="Articles" Click="F1Button_Click"/>
                <Button Content="F2" Click="F2Button_Click"/>
                <Button Content="F3" Click="F3Button_Click"/>
            </StackPanel>
        </Grid>

        <ContentControl Grid.Column="1" Content="{Binding}"/>
    </Grid>

MainWindow.xaml.cs:

public partial class MainWindow : Window
    {
        ArticlesViewModel avm;
        Feature2ViewModel f2vm;
        Feature3ViewModel f3vm;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void F1Button_Click(object sender, RoutedEventArgs e)
        {
            if (avm == null) avm = new AccueilViewModel();
            DataContext = avm;
        }

        private void F2Button_Click(object sender, RoutedEventArgs e)
        {
            if (f2vm == null) f2vm = new CatalogueViewModel();
            DataContext = f2vm;
        }

        private void F3Button_Click(object sender, RoutedEventArgs e)
        {
            if (f3vm == null) f3vm = new MesFormationsViewModel();
            DataContext = f3vm;
        }
    }

To sum up:

  • Click on Articles -> Nothing is displayed
  • Click on F2 then Articles -> Nothing is displayed
  • Click on Articles then F2 then Articles -> Images and Titles correctly displayed!

Any ideas on where the problem might be coming from? Thank you.

  • *//fills "articles", everything works fine here* ... in fact this is a problem ... imagine code like `Articles = new Article[3]` ... what is binded to `Grid Name="Article3"` DataContext? ... **null** – Selvin Jun 19 '19 at 14:14
  • 1
    becuase `new Article[3]` is the same as `new Article[] {null,null,null}` ... and only when you are changeing whole `Articles` property data is binded not when you are doing `Articles[1] = new Article { ... }` ... if you wana binding on items change then maybe you should use `ObservableCollection` – Selvin Jun 19 '19 at 14:18
  • Your attempt at translating the class names for your ViewModels makes your code very confusing. It actually could make it near impossible to know for sure whether you mistakenly use a wrong type name in your XAML or whether this was just a translation accident... –  Jun 19 '19 at 14:20
  • Ok so until the articles are loaded the datacontext is null and I need to notify my view when it has changed? – Omaewamushindeiru Jun 19 '19 at 14:20
  • No, that's not what Selvin was talking about. A simple array does not have the functionality required for the view to be able to get notified about the array content changing. Use a collection type such as ObservableCollection instead of arrays or List, so the view will be able to respond to collection changes... –  Jun 19 '19 at 14:23
  • Yeah that's what I understood, I thought the "NotifyPropertyChanged" function was enough. Why is it not ? – Omaewamushindeiru Jun 19 '19 at 14:27
  • I updated my ViewModel on the post – Omaewamushindeiru Jun 19 '19 at 14:31
  • It worked thank you. – Omaewamushindeiru Jun 19 '19 at 15:01

1 Answers1

0

So the problem seemed to be coming from the fact that I was using an array. At the start the DataContext was null so the images and titles even though they were loaded in the modelview were not displayed on the view.

The problem was fixed by using an ObservableCollection instead of the array. If I understood correctly, NotifyPropertyChanged() is apparently not notifying the view when the array is at first null.(?)