Emixam23
Emixam23

Reputation: 3954

ObservableCollection<> and ListView binding issue

I'm coming today because I'm not sure to understand something about the Binding in C#. I'm currently making a stupid app to help myself with my classes and their locations. Some others information also comes in the app.

So I have this design, which is a simple ListView of my 4 classes.

MainPage.xaml :

<ContentPage.Content>
    <AbsoluteLayout>
        <Label
            AbsoluteLayout.LayoutBounds="0.5, 0, 1, 0.1"
            AbsoluteLayout.LayoutFlags="All"
            Text="My CSULB Schedule" />
        <ListView x:Name="CoursesListView" ItemsSource="{Binding Courses}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="2*" />
                            <RowDefinition Height="6*" />
                            <RowDefinition Height="2*" />
                        </Grid.RowDefinitions>
                        <!--  TOP  -->
                        <Grid Grid.Row="0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="2*" />
                                <ColumnDefinition Width="6*" />
                                <ColumnDefinition Width="2*" />
                            </Grid.ColumnDefinitions>
                            <Label
                                Grid.Column="0"
                                HorizontalTextAlignment="Center"
                                Text="{Binding DaysToString}"
                                VerticalTextAlignment="Center" />
                            <Label
                                Grid.Column="1"
                                HorizontalTextAlignment="Center"
                                Text="{Binding Title}"
                                VerticalTextAlignment="Center" />
                            <Label
                                Grid.Column="2"
                                HorizontalTextAlignment="Center"
                                Text="{Binding TypeAndNumber}"
                                VerticalTextAlignment="Center" />
                        </Grid>
                        <!--  Top  -->
                        <!--  Center  -->
                        <ListView Grid.Row="1" ItemsSource="{Binding Sections}">
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.RowDefinitions>
                                            <ColumnDefinition Width="25*" />
                                            <ColumnDefinition Width="5*" />
                                            <ColumnDefinition Width="25*" />
                                        </Grid.RowDefinitions>
                                    </Grid>
                                    <Label
                                        Grid.Column="0"
                                        HorizontalTextAlignment="Center"
                                        Text="{Binding Location}"
                                        VerticalTextAlignment="Center" />
                                    <Label
                                        Grid.Column="1"
                                        HorizontalTextAlignment="Center"
                                        Text="{Binding TeacherName}"
                                        VerticalTextAlignment="Center" />
                                    <Label
                                        Grid.Column="2"
                                        HorizontalTextAlignment="Center"
                                        Text="{Binding Time}"
                                        VerticalTextAlignment="Center" />
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
                        <!--  Center  -->
                        <!--  Bottom  -->
                        <Grid Grid.Row="2">
                            <Grid.RowDefinitions>
                                <ColumnDefinition Width="1*" />
                                <ColumnDefinition Width="8*" />
                                <ColumnDefinition Width="1*" />
                            </Grid.RowDefinitions>
                            <!-- Useless stuff at the moment -->
                        </Grid>
                        <!--  Bottom  -->
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </AbsoluteLayout>
</ContentPage.Content>

In the CS part, I have this:

MainPage.xaml.cs :

public partial class MainPage : ContentPage
{
    public ObservableCollection<Course> Courses { get; set; }

    public MainPage()
    {
        base.BindingContext = this;
        Debug.WriteLine("1.");
        InitCoursesList();
        Debug.WriteLine("2.");
        InitializeComponent();
        Debug.WriteLine("3.");
        //CoursesListView.ItemsSource = Courses;
    }

    private void InitCoursesList()
    {
        Debug.WriteLine("1.1");
        this.Courses = new ObservableCollection<Course>()
        {
            new Course()
            {
                Days = new List<Course.Day>() { Course.Day.M, Course.Day.W },
                Title = "Object Oriented Programming",
                Type = "CECS",
                Number = "274",
                Sections = new List<Section>()
                {
                    new Section()
                    {
                        Location = "VEC-419",
                        TeacherName = "Foss S",
                        Time = "1-1:50pm"
                    },
                    new Section()
                    {
                        Location = "ECS-414",
                        TeacherName = "Foss S",
                        Time = "2-3:15pm"
                    }
                }
            },
            new Course()
            {
                Days = new List<Course.Day>() { Course.Day.M, Course.Day.W },
                Title = "Adv Artificial Intelligence",
                Type = "CECS",
                Number = "551",
                Sections = new List<Section>()
                {
                    new Section()
                    {
                        Location = "VEC-331",
                        TeacherName = "Todd Ebert",
                        Time = "3:30-4:45pm"
                    }
                }
            },
            new Course()
            {
                Days = new List<Course.Day>() { Course.Day.Tu, Course.Day.Th },
                Title = "Operating Syetems",
                Type = "CECS",
                Number = "326",
                Sections = new List<Section>()
                {
                    new Section()
                    {
                        Location = "VEC-330",
                        TeacherName = "Lam S",
                        Time = "9:30-10:20am"
                    },
                    new Section()
                    {
                        Location = "ECS-416",
                        TeacherName = "Lam S",
                        Time = "10:30-11:45am"
                    }
                }
            },
            new Course()
            {
                Days = new List<Course.Day>() { Course.Day.Tu, Course.Day.Th },
                Title = "C++ for Java Developers",
                Type = "CECS",
                Number = "282",
                Sections = new List<Section>()
                {
                    new Section()
                    {
                        Location = "VEC-518",
                        TeacherName = "Nachawati S",
                        Time = "11-11:50am"
                    },
                    new Section()
                    {
                        Location = "ECS-403",
                        TeacherName = "Nachawati S",
                        Time = "12-1:15pm"
                    }
                }
            },
        };
        Debug.WriteLine("1.2");
    }
}

Everything is coming in the good way, I mean the display of the Debug.WriteLine();. The thing I don't understand now it's why my listview is empty? For me, it doesn't make sense but everyone knows that, often, the problem comes between the computer and the chair so.. Where am I wrong?

I bind my main ListView which is <ListView x:Name="CoursesListView" ...> with my public ObservableCollection<Course> Courses { get; set; }. I fill it, initalize everything, I set the binding context but at the end.. The page is empty, why?

Also I was asking to myself another question. If I bind a collection to a list, can I bind another list inside of every element of this list view? If you are confuse, take a look at the List<Section> Sections in every Course object. In the xaml part, I have a list in the center part of each element.

I hope my question is clear, maybe it's a stupid question, maybe this question is interesting, I don't know but for sure, I didn't and I still don't understand something I think.

Thank for your help.

PS: Tell me if you want any edit.

Upvotes: 0

Views: 169

Answers (2)

Michael Puckett II
Michael Puckett II

Reputation: 6749

Your code will work if you change the Courses property to a dependency property.

public ObservableCollection<Course> Courses
    {
        get { return (ObservableCollection<Course>)GetValue(CoursesProperty); }
        set { SetValue(CoursesProperty, value); }
    }

public static readonly DependencyProperty CoursesProperty =
        DependencyProperty.Register(nameof(Courses), typeof(ObservableCollection<Course>), typeof(MainPage));

The reason is, as stated in an answer already, is that you're re-creating the ObservableCollection and breaking the binding to the old one because there is no PropertyChanged event fired when you re-assigned it. Since this is a UIElement it is best to just make this a DependencyProperty rather than implementing INotifyPropertyChanged, which you also have the choice of doing. Then you would need to write out a full Courses property call PropertyChanged in the setter of the Courses property.

Furthermore you need to update your binding statement as well... It needs to look at the MainPage rather than the DataContext.

ItemsSource="{Binding Courses, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainPage}}}

Or name your MainPage x:Name="mainPage" and use this binding...

ItemsSource="{Binding Courses, ElementName=mainPage}"

Upvotes: 1

Tim Rutter
Tim Rutter

Reputation: 4679

You're creating the ObservableCollection in your InitCoursesList function. The binding, which will have already bound to Courses with a value of null, won't update when you create the ObservableCollection because you're not raising the PropertyChanged event (see INotifyPropertyChanged) so any additions to the collection won't register. The solution is to create the collection when the containing class is created:

public ObservableCollection<Course> Courses { get; set; } = new ObservableCollection<Course>();

And then just populate it in the initialise function:

private void InitCoursesList()
{
    Courses.Add(new Course { etc });
}

And of course, if you wanted to bind a second list (ObservableCollection) you can do that. In your Course class you'd simply have an ObservableCollection and bind that to whatever control you want to use to view that data.

Upvotes: 3

Related Questions