C# Databinding Textblock, PropertyChanged stays null

104 Views Asked by At

Im trying to change 2 textblocks with data binding. The propertyChanged is always null, so the ui wont update.

This is my model code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MovieApp.Models
{
    public class MovieModel : INotifyPropertyChanged
    {
        string original_title, overview;
        public event PropertyChangedEventHandler PropertyChanged;

        public string Original_Title {
            get
            {
                return original_title;
            }
            set
            {
                original_title = value;
                onPropertyChanged(nameof(Original_Title));
            } 
        }
        public string Overview
        {
            get
            {
                return overview;
            }
            set
            {
                overview = value;
                onPropertyChanged(nameof(Overview));
            }
        }
        protected void onPropertyChanged(string propertyName)
        {
            if(PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
            //PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

    }
}

The mainview.xaml.cs:

using MovieApp.API;
using MovieApp.Models;
using MovieApp.Processor;
using System.Windows;

namespace MovieApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        //private readonly MovieModel movieModel = new MovieModel(); 

        public MainWindow()
        {
            InitializeComponent();
            ApiCaller.InitializeClient();
            // DataContext = movieModel;
        }

        private async void previousImageButton_Click(object sender, RoutedEventArgs e)
        {
            int id = 484718;
            await MovieProcessor.LoadMovie(id);
       
        }

        private async void nextImageButton_Click(object sender, RoutedEventArgs e)
        {
            int id = 527774;
            await MovieProcessor.LoadMovie(id);
           
        }
    }
}


and the maindwindow.xaml:

<Window x:Class="MovieApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MovieApp.Models"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <local:MovieModel x:Key="movieModel" />
    </Window.Resources>
    
    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        
        <Button x:Name="previousImageButton" Padding="15" Margin="15" Click="previousImageButton_Click">Previous</Button>
        <StackPanel Grid.Row="1">
            <TextBlock Text="{Binding  Source={StaticResource movieModel}, Path=Original_Title}"  ></TextBlock>
            <TextBlock Text="{Binding  Source={StaticResource movieModel}, Path=Overview }"></TextBlock>
        </StackPanel>
        <Button Grid.Row="2" x:Name="nextImageButton" Padding="15" Margin="15" Click="nextImageButton_Click">Next</Button>
    </Grid>
</Window>

EDIT: Added the movieprocessor code:

using MovieApp.API;
using MovieApp.Models;
using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace MovieApp.Processor
{
    class MovieProcessor    
    {
        public static async Task<MovieModel> LoadMovie(int id)
        {
            
            string url = $"movie/{id}?api_key=77e7d2ef687aedca2119680778f1d619&language=en-US";
            using (HttpResponseMessage response = await ApiCaller.httpClient.GetAsync(url))
            {
                if (response.IsSuccessStatusCode)
                {
                    MovieModel movie = await response.Content.ReadAsAsync<MovieModel>();
                    Console.WriteLine(movie.Original_Title);
                    Console.WriteLine(movie.Overview);
                    return movie;
                }
                else
                {
                    throw new Exception(response.ReasonPhrase);
                }

            }
        }
    }
}


I have no idea what could be wrong. I tried multiple things but nothing seemed to work for me. I tried adding datacontext but that didnt work either. I let it commented in my code so anyone can see it.

2

There are 2 best solutions below

1
user2250152 On

If your MovieProcess class sets the value of MovieModel.Original_Title and MovieModel.Overview property then you have to ensure that MovieProcess is accessing the same instance of MovieModel as your view (xaml).

Instead of using StaticResource movieModel assing DataContext in code behind.

private readonly MovieModel movieModel = new MovieModel(); 
public MovieProcessor MovieProcessor { get; set; }
public MainWindow()
{
    InitializeComponent();
    ApiCaller.InitializeClient();
    DataContext = movieModel;
    MovieProcessor = new MoviewProcessor(moviewModel);
}

Xaml

<StackPanel Grid.Row="1">
    <TextBlock Text="{Binding Original_Title}" />
    <TextBlock Text="{Binding Overview }" />
</StackPanel>

MovieProcessor class

public class MovieProcessor
{
    private readonly MovieModel movieModel;
    public MovieProcessor(MovieModel movieModel)
    {
        this.movieModel = movieModel;
    }

    public async Task LoadMovie(int id)
    {
        ...
        movieModel.Original_Title = <loaded_movie_title>;
        movieModel.Overview = <loaded_movie_overview>;
        ... 
    }
}
0
DasiyTian_1203 On

I bind the command to the Buttons in the MovieProcessor to show data in Maindwindow with StaticResource movieModel, below is my code:

NotifyObject.cs

 public class NotifyObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

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

MyCommand.cs

public class MyCommand : ICommand
{
    private Func<object, bool> _canExecute;
    private Action<object> _execute;

    public event EventHandler CanExecuteChanged
    {
        add
        {
            if (_canExecute != null)
            {
                CommandManager.RequerySuggested += value;
            }
        }
        remove
        {
            if (_canExecute != null)
            {
                CommandManager.RequerySuggested -= value;
            }
        }
    }


    public bool CanExecute(object parameter)
    {
        if (_canExecute == null) return true;
        return _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        if (_execute != null && CanExecute(parameter))
        {
            _execute(parameter);
        }
    }

    public MyCommand(Action<object> execute) : this(execute, null)
    {
    }
    public MyCommand(Action<object> execute, Func<object, bool> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }
}

MovieProcessor.cs

 public class MovieProcessor:NotifyObject
{       
    private MovieModel vm;
    public MovieModel VM
    {
        get { return vm; }
        set
        {
            vm = value;
            OnPropertyChange("VM");
        }
    }
    public MovieModel LoadMovie(int id)
    {
        //....
    }


    private MyCommand _cmd1;
    public MyCommand Cmd1
    {
        get
        {
            if (_cmd1 == null)
                _cmd1 = new MyCommand(new Action<object>
                (
                    o =>
                    {
                        int id = 484718;
                        LoadMovie(id);
                    }
                ));
            return _cmd1;
        }
    }

    private MyCommand _cmd2;
    public MyCommand Cmd2
    {
        get
        {
            if (_cmd2 == null)
                _cmd2 = new MyCommand(new Action<object>
                (
                    o =>
                    {
                        int id = 527774;
                        LoadMovie(id);
                    }
                ));
            return _cmd2;
        }
    }
}

MainWindow.xaml

   <Window.Resources>
    <local:MovieProcessor x:Key="movieModel" />
</Window.Resources>
<Grid >

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

    <Button x:Name="previousImageButton" Padding="15" Margin="15" Command="{Binding Source={StaticResource movieModel}, Path=Cmd1}">Previous</Button>
    <StackPanel Grid.Row="1">
        <TextBlock Text="{Binding  Source={StaticResource movieModel}, Path=VM.Original_Title}"  ></TextBlock>
        <TextBlock Text="{Binding  Source={StaticResource movieModel}, Path=VM.Overview }"></TextBlock>
    </StackPanel>

    <Button Grid.Row="2" x:Name="nextImageButton" Padding="15" Margin="15" Command="{Binding Source={StaticResource movieModel}, Path=Cmd2}">Next</Button>

</Grid>