Search code examples
c#listviewdata-bindinguwpobservablecollection

The listview don't get update when the observable collection changes


this the listview

    <ListView x:Name="task_list" SelectionChanged="task_list_SelectionChanged"  Grid.Row="0" Grid.Column="1"  Background="Transparent">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="local:TaskTodo">
                <TextBlock Width="100" Foreground="White" Text="{x:Bind NameTask}"  />
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

And the view model

public class TaskTodo : INotifyPropertyChanged
{
    private string _NameTask, _Reminder, _Date, _Priority, _NameList, _Description;
    private int _Id, _ParentTask;
    
    private DateTimeOffset _NextRep;
    public TaskTodo()
    {        
      _NameTask =   String.Empty;
      _Reminder  =  String.Empty;
      _Date =       String.Empty;
      _Priority  =  String.Empty;
      _NameList  =  String.Empty;
      _Description = String.Empty;
     _ParentTask =  -1;
     _Id = 1;
    }

    Resource_Manager rm = new Resource_Manager();
    public event PropertyChangedEventHandler PropertyChanged;
    TaskSqliteDataAccess TaskSqlite = new TaskSqliteDataAccess();

    private void NotifyPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    public int TaskId { get { return _Id; }  set { _Id = value; NotifyPropertyChanged(TaskId.ToString()); } }
    public string NameTask { get { return _NameTask; } set { _NameTask = value; NotifyPropertyChanged(NameTask); } }
    public string Reminder { get { return _Reminder; } set { _Reminder = value; NotifyPropertyChanged(FormatDateReminder); } }
    public string NameList {  
        get 
        {
            if ( _NameList == string.Empty || _NameList == null)
            {
                return rm.GetDefaultNamelist;
            }
            return _NameList;

            }
        set { _NameList = value; NotifyPropertyChanged(NameList); }
    }

       
    public string Date { get { return _Date ; } set { _Date = value; NotifyPropertyChanged(FormatDate); } }
    public string FormatDate
    {
        get
        {
            var cultureInfo = new CultureInfo("en-US");
            DateTimeOffset DueDate;
            
            if (Date != string.Empty && Date != null)
            {
                DueDate = DateTimeOffset.Parse(Date, cultureInfo);
                return String.Format(" {0},{1} {2}", DueDate.DayOfWeek, DueDate.Day, DueDate.ToString("MMM"));
            }
            else                   
                return rm.GetDefaultDate;             
        }
         
}
    public string Priority { get { return _Priority; } set { _Priority = value; NotifyPropertyChanged(Priority); } }
    public string Description { get { return _Description;  } set { _Description = value; NotifyPropertyChanged(Description); } } 
    public DateTimeOffset NextRep { get { return _NextRep; } set { _NextRep= value; NotifyPropertyChanged(NextRep.ToString()); } }      
    public int  ParentTask { get { return _ParentTask; } set { _ParentTask = value; NotifyPropertyChanged(TaskId.ToString()); } }      
    public string FormatTimeReminder
    {
        get {
            DateTime Time;
            var cultureInfo = new CultureInfo("en-US");
            if ( Reminder != string.Empty && Reminder !=  null )
            {
                Time = DateTime.Parse (Reminder, cultureInfo);
                return string.Format(" Remind me at {0}:{1}", Time.Hour, Time.Minute);
            }
                          
            return rm.GetDefaultReminder;
            
        } 
    }
    public string FormatDateReminder
    {
        get
        {
            DateTimeOffset Date;

            if(DateTimeOffset.TryParse(_Reminder, out Date) == true)
            {
                return string.Format(" {0},{1} {2}", Date.DayOfWeek, Date.Day, Date.ToString("MMM"));

            }

            return Reminder;

        }
    }
    public ObservableCollection<TaskTodo> GetTasks() => TaskSqlite.GetTaskDB();
 
    public void AddTask(TaskTodo task) => TaskSqlite.AddTaskDB(task); 

    public void UpdateTask() => TaskSqlite.UpdateData(TaskId, NameTask, Date, Reminder, Priority, NameList, Description, NextRep);
  
    public ObservableCollection<TaskTodo> GetSubtasks(TaskTodo taskTodo) => TaskSqlite.GetSubtasks(taskTodo);

For example if I change the NameTask property in the form, the property changes , and if bind that property to the texblock from the form, the texblock have the new value but the the texblock of the listview don't

enter image description here

StackPanel Name="TaskForm"  Grid.Column="2" Width="340" Background="#04000E" HorizontalAlignment="Right" Visibility="Collapsed" >
        <Button FontSize="20" Name="quit_TaskForm" Content="x" Click="quit_TaskForm_Click" HorizontalAlignment="Right" Background="Transparent"></Button>

        <TextBox Name="NameTaskForm" HorizontalAlignment="Center" FontSize="20" 
                 TextChanged="NameTaskForm_TextChanged" LostFocus="NameTaskForm_LostFocus" Foreground="White" 
                 DataContext="{Binding ElementName=task_list, Path=SelectedItem}" 
                 Text="{Binding Path=NameTask, Mode=TwoWay}"
                 Style="{StaticResource TextBoxStyle1}" BorderThickness="0"  />
       
    
        </StackPanel>

this the code of the event when the text of the texbox from the form changes

 private void NameTaskForm_TextChanged(object sender, TextChangedEventArgs e)
    {
        if ((task_list.SelectedItem as TaskTodo)!= null)
        {
            (task_list.SelectedItem as TaskTodo).NameTask = NameTaskForm.Text;
            (task_list.SelectedItem as TaskTodo).UpdateTask();

           
        }

        
    }

Solution

  • NotifyPropertyChanged Method Takes the name of the property, not the value of it ... NotifyPropertyChanged(NameTask); this should be change to NotifyPropertyChanged(nameof(NameTask));

    as in this line

     public string NameTask { get { return _NameTask; } set { _NameTask = value; NotifyPropertyChanged(NameTask); } }
    

    should be like this

    public string NameTask { get { return _NameTask; } set { _NameTask = value; NotifyPropertyChanged(nameof(NameTask)); } }
    

    so you have to edit all your code whenever you call NotifyPropertyChanged to give it the name of the property you change ... or you can make the compiler automatically get the caller property name just use CallerMemberName attribute

    using System.Runtime.CompilerServices;
    //...
    void OnPropertyChanged([CallerMemberName] string name = null)
    {
       PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
    

    and call it without passing any value and the compiler automatically get the caller name

    as following

    public string NameTask 
    { 
      get { return _NameTask; } 
      set 
      {
         _NameTask = value;
         NotifyPropertyChanged(); // without passing any value
       }
    }