Search code examples
c#wpfmvvmuser-controlsdatacontext

WPF View not binding unless I switch view before


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.


Solution

  • 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.(?)