Forum Xamarin.Forms

ListView not updating when adding items to it from another page

DacaramoDacaramo Member ✭✭

I'm trying to fill up or populate a ListView that is instantiated in PageA when the user clicks a button in PageB. I'm doing it sending a message with the MessageCenter in PageB and calling the MessageCenter.Subscribe() method in PageA (the Page where I want to add the new ViewCell or row to the ListView)... but it's not working.

I don't think that the problem is coming from the send/subscribe usage because I have alredy debugged the application and the collection (ObservableCollection) that i'm passing to the ListView.ItemSource property is indeed growning in size. Here you can see the class definitions:

PageA class definition:

PageA .xaml file:

PageB class definition:

I will appreciate a lot the help that you could provide! :smile:

Answers

  • LandLuLandLu Member, Xamarin Team Xamurai

    What does this Routine look like?
    I noticed that it contained a List<Exercise> property. If the Name and TimeDone belong to Exercise class, the listview's items source should be set to this List<Exercise> property like:

    RoutineListView.ItemsSource = arg.ExerciseList;
    

    If you want to display a grouped list, please refer to:
    https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/#groups
    The Routine class must inherit from List<Exercise> and <ListView IsGroupingEnabled="True"> needs to be set on the listview.

  • DacaramoDacaramo Member ✭✭

    Thanks for the quick reply @LandLu
    Routine is an Entity, Routine is the one that has the Name and TimesDone properties, not Exercise, here is the Class definition:

    Briefly, the page that has the problem with the Listview is the page where I want to display all the routines. Each Routine has a generic List<> of exercises, I'm not trying to group the data in that ListView. Routine is a Class that has an exercise list just because further, when any item of that ListView is tapped I want to display another page that has another ListView with the list of exercises of the tapped Routine.

  • LandLuLandLu Member, Xamarin Team Xamurai

    Could you please post a sample to help me reproduce this issue?

  • DacaramoDacaramo Member ✭✭

    Of course, thanks @LandLu , here is the WorkoutRoutineTab Class definition:

    using PumpFit.Entity;
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    
    namespace PumpFit
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class WorkoutRoutineTab : ContentPage
        {
            public ObservableCollection<Routine> Routines { set; get; } = new ObservableCollection<Routine>();
    
            public WorkoutRoutineTab()
            {
                InitializeComponent();
    
                routineListView.ItemsSource = Routines;
    
                MessagingCenter.Subscribe<AddExercisePage, Routine>(this, "FillRoutines", (messageSender, arg) =>
                {
                    Routines.Add(new Routine(arg.Name, arg.ExerciseList));
                    routineListView.ItemsSource = Routines;
                });
            }
    
            private async void NewRoutineButton_Clicked(object sender, EventArgs e)
            {
                await DisplayAlert("ALERT", Routines.Count.ToString() , "ok");
                await Navigation.PushAsync(new ExerciseBankTab() { Title = "" });
            }
    
            private async void RoutineListView_ItemTapped(object sender, ItemTappedEventArgs e)
            {
                var tappedRoutine = e.Item as Routine;
                if(tappedRoutine != null)
                {
                    await Navigation.PushAsync(new RoutineInfoPage(tappedRoutine));
                }
    
            }
        }
    }
    

    here is the AddExercisePage Class definition:

    using PumpFit.Entity;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using PumpFit;
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    
    namespace PumpFit
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class AddExercisePage : ContentPage
        {
            public Exercise newExercise;
    
            public AddExercisePage(Exercise selectedExercise)
            {
                InitializeComponent();
    
                newExercise = selectedExercise; 
    
                nameLabel.Text = newExercise.Name;
            }
    
            private async void CancelButton_Clicked(object sender, EventArgs e)
            {
                await Navigation.PopModalAsync();
            }
    
            private void AddButton_Clicked(object sender, EventArgs e)
            {
                if(setsEntry.Text != null && repsEntry.Text != null && restTimePicker.SelectedItem != null)
                {
                    if (int.TryParse(setsEntry.Text, out int sets) && int.TryParse(repsEntry.Text, out int reps) && sets > 0 && reps > 0)
                    {
                        List<Exercise> newExerciseList = new List<Exercise>()
                        {
                            new Exercise(newExercise.Name, newExercise.MuscleGroup, newExercise.ExerciseDifficulty, newExercise.Equipment, newExercise.Description, sets, reps, restTimePicker.SelectedItem.ToString())
                        };
    
                        MessagingCenter.Send<AddExercisePage, Routine>(this, "FillRoutines", new Routine(routineNameEntry.Text, newExerciseList));
                    }
                    else
                    {
                        DisplayAlert("ERROR", "You must only enter positive numbers", "OK");
                    }
                }
                else
                {
                    DisplayAlert("ERROR", "All fields must be set to add the exercise to the routine", "OK");
                }
            }
        }
    }
    

    And here is the Xaml code for the WorkoutRoutineTab Class:

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:d="http://xamarin.com/schemas/2014/forms/design"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                 xmlns:local="clr-namespace:PumpFit"
                 xmlns:custom="clr-namespace:PumpFit.Entity"
                 mc:Ignorable="d"
                 x:Class="PumpFit.WorkoutRoutineTab"
                 Title="Workout"
                 BackgroundColor="#343434">
    
        <StackLayout x:Name="routineStackLayout" Orientation="Vertical" HorizontalOptions="Center" VerticalOptions="Center" Margin="20,10">
            <ListView x:Name="routineListView" x:FieldModifier="public" SeparatorColor="#2C2C2C" RowHeight="100" 
                      SelectionMode="None" ItemTapped="RoutineListView_ItemTapped">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Grid RowDefinitions="2*,*" ColumnDefinitions="*,*">
                                <Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding Name}" TextColor="White" FontSize="Large" FontFamily="Ubuntu"/>
                                <Label Grid.Row="1" Grid.Column="0" Text="{Binding TimesDone}" TextColor="#9F9F9F" FontSize="Body" FontFamily="Ubuntu"/>
                                <Label Grid.Row="1" Grid.Column="1" Text="{Binding TimesDone}" TextColor="#9F9F9F" FontSize="Body" FontFamily="Ubuntu"/>
                            </Grid>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <Button x:Name="newRoutineButton" Text="New routine" FontSize="Body" FontFamily="Geo" 
                    BackgroundColor="#2C2C2C" TextColor="#87BC72" Clicked="NewRoutineButton_Clicked"/>
        </StackLayout> 
    
    </ContentPage>
    
  • LandLuLandLu Member, Xamarin Team Xamurai


    Try to reproduce it with the code snippet but the items are added as expected.
    I changed the background color to red so that the white color text could be shown up.
    If the issue persists on your side, please offer a whole small project here so that I could run it to see the specific issue.

  • DacaramoDacaramo Member ✭✭
    edited July 2020

    This GIF is kind of huge, but it is exactly what's happening:

    After adding the new 2 routines from the AddExercisePage, when returning to the first page (The WorkoutRoutineTab Page) the ListView must have two Viewcells or rows when I return to it)

  • LandLuLandLu Member, Xamarin Team Xamurai

    Without your sample, I don't know what happened on your side.

  • DacaramoDacaramo Member ✭✭

    So I post all of the classes and everything? Ahhh Allright so your saying that I should attach the files, ok on my way

  • DacaramoDacaramo Member ✭✭
    edited July 2020

    That's the whole project, without the .Android or .IOS It should run just like in my PC, whitout the icons and the images cause those are in the drawable ressources of the Android proyect I think

  • DacaramoDacaramo Member ✭✭
    edited July 2020

    @LandLu it is running fine? or should I send it with the .android and .IOS proyects?

  • LandLuLandLu Member, Xamarin Team Xamurai

    It's better to post a whole sample that reveals this issue.
    I could debug it directly on my side to see the effect.
    Besides, try to reproduce this issue with a project instead of your real program.

  • DacaramoDacaramo Member ✭✭
    edited July 2020

    @LandLu I find out what was causing the problem, sorry to bother you.
    I don't know if you have noticed but I was setting the ItemsSource property twice and the first time that I've setted it was after that I have used the empty ObservableCollection<>() constructor (in the same line that i'm declaring the Routines property)

    public partial class WorkoutRoutineTab : ContentPage
        {
            public ObservableCollection<Routine> Routines { set; get; } = new ObservableCollection<Routine>();
    
            public WorkoutRoutineTab()
            {
                InitializeComponent();
    
                routineListView.ItemsSource = Routines; //HERE
    
                MessagingCenter.Subscribe<AddExercisePage, Routine>(this, "FillRoutines", (messageSender, arg) =>
                {
                    Routines.Add(new Routine(arg.Name, arg.ExerciseList));
                    routineListView.ItemsSource = Routines; //AND HERE
                });
            }
    
            private async void NewRoutineButton_Clicked(object sender, EventArgs e)
            {
                await DisplayAlert("ALERT", Routines.Count.ToString() , "ok");
                await Navigation.PushAsync(new ExerciseBankTab() { Title = "" });
            }
    
            private async void RoutineListView_ItemTapped(object sender, ItemTappedEventArgs e)
            {
                var tappedRoutine = e.Item as Routine;
                if(tappedRoutine != null)
                {
                    await Navigation.PushAsync(new RoutineInfoPage(tappedRoutine));
                }
    
            }
        }
    

    So what I've done is that I've erased the first routineListView.ItemsSource = Routines and i've only leaved the one who is just inside the lambda expression of the MessaginCenter.Subscribe<>() method

    I don't really know why that would prevent the ListView to update itself but know it's working! Could you please explain to me the strange behaviour? It is because the class constructor is called more than once or something like that?
    Thanks for your patiance, I've started to learn Xamarin some weeks ago.

  • DacaramoDacaramo Member ✭✭
    edited July 2020

    .

  • LandLuLandLu Member, Xamarin Team Xamurai

    It won't make any difference if you reset the ItemsSource again using the same Routines.
    Routines won't be cleared when the messaging center comes as it only initializes at the beginning.
    I need an entire sample instead of the code snippet here to reproduce your issue/environment.
    I don't know why it erases the items source without it.

Sign In or Register to comment.