PsychotiCoder
PsychotiCoder

Reputation: 87

xamarin forms : can't bind data

I'm trying to create an app which retrieves data from my api but I just don't know why the binding doesn't work. Here's the C# code

 public partial class PageData : ContentPage
{
    TodoList tdl { get; set; }
    public PageData()
    {
        tdl = new TodoList();
        this.BindingContext = tdl;
        InitializeComponent();
    }

    private async void ContentPage_Appearing(object sender, EventArgs e)
    {
        var httpClientHandler = new HttpClientHandler();
        httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
        HttpClient client = new HttpClient(httpClientHandler);
        var WebAPIUrl = @"http://192.168.x.xx:65xxx/api/data";
        var uri = new Uri(WebAPIUrl); 
        var response = await client.GetAsync(uri);
        if (response.IsSuccessStatusCode)
        {
            string responseBody = await response.Content.ReadAsStringAsync();
            var tmp = JsonConvert.DeserializeObject<List<TodoItem>>(responseBody);
            tdl.todoLists = new System.Collections.ObjectModel.ObservableCollection<TodoItem>(tmp);
        }
       // MyListView.ItemsSource = tdl.todoLists;
    }
}

It works when I use that last line I commented but it kind of feel like "cheating" as this isn't the best practice when using MVVM. I know there's a way around that but I just dont know what I'm doing wrong. thanks.

and here is the xaml code :

<ContentPage.Content>
    <ListView x:Name="MyListView" ItemsSource="{Binding todoLists}" RowHeight="70">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <StackLayout Orientation="Horizontal" Spacing="20" Padding="5">
                        <StackLayout Orientation="Vertical">
                            <Label Text="To do: " FontAttributes="Bold" FontSize="17"></Label>
                            <Label Text="Is completed: "  FontAttributes="Bold" FontSize="17"></Label>
                        </StackLayout>
                        <StackLayout Orientation="Vertical">
                            <Label Text="{Binding name}" FontSize="17"></Label>
                            <Label Text="{Binding isCompleted}" FontSize="17"></Label>
                        </StackLayout>

                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage.Content>

here's my MVVM class :

using ExamenAout.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;

namespace ExamenAout.MVVM
{
    public class TodoList : INotifyPropertyChanged
    {
        private ObservableCollection<TodoItem> _todoLists { get; set; }

        public ObservableCollection<TodoItem> todoLists
        {
            get { return this._todoLists; }
            set
            {
                this._todoLists = value;
                OnPropertyRaised("todoList");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyRaised(string PropertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
            }
        }
    }
}

here's the TodoItem class :

    using System;
using System.Collections.Generic;
using System.Text;

namespace ExamenAout.Models
{
    public class TodoItem
    {
        public Guid userid { get; set; }
        public string name { get; set; }
        public bool isCompleted { get; set; }
    }
}

Upvotes: 0

Views: 221

Answers (2)

Arthur Rey
Arthur Rey

Reputation: 3056

To expand on Jack Hua's answer, you could also use CallerMemberNameAttribute. As the documentation explains:

Implementing the INotifyPropertyChanged interface when binding data. This interface allows the property of an object to notify a bound control that the property has changed, so that the control can display the updated information. Without the CallerMemberName attribute, you must specify the property name as a literal.

You can use it as such:

using System.Runtime.CompilerServices;

private void OnPropertyRaised([CallerMemberName]string propertyName = "")
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

This makes it so the property's name you're setting is automatically passed

public ObservableCollection<TodoItem> todoLists
{
    get => _todoLists;
    set
    {
        _todoLists = value;
        OnPropertyRaised(); // You don't need to pass "todoLists" here
    }
}

Upvotes: 1

nevermore
nevermore

Reputation: 15816

Next time you can use nameof expression to prevent this mistake:) :

public ObservableCollection<TodoItem> todoLists
{
    get { return this._todoLists; }
    set
    {
        this._todoLists = value;
        OnPropertyRaised(nameof(todoLists));
    }
}

Upvotes: 1

Related Questions