Xamarin forms understanding ObservableCollection binding context

Mike

I'm having some issue getting my ObservableCollection to bind to alexrainman CarouselView. After reading some basic articles I created my view model:

public class PostObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        string postOwner = string.Empty;
        string id = string.Empty;
        string profileimage = string.Empty;
        string post = string.Empty;
        List<string> postimages = null;

        public string PostOwner
        {
            set
            {
                if (postOwner != value)
                {
                    postOwner = value;
                    OnPropertyChanged("PostOwner");
                }
            }
            get
            {
                return postOwner;
            }

        }

        public string Id {

            set
            {
                if (id != value)
                {
                    id = value;
                    OnPropertyChanged("Id");
                }
            }
            get
            {
                return id;
            }
        }


        public string Post
        {
            set
            {
                if (post != value)
                {
                    post = value;
                    OnPropertyChanged("Post");
                }
            }
            get
            {
                return post;
            }
        }
        public string ProfileImage
        {
            set
            {
                if (profileimage != value)
                {
                    profileimage = value;
                    OnPropertyChanged("ProfileImage") ;
                }
            }
            get
            {
                return profileimage;
            }

        }
        public List<string> PostImages
        {
            set
            {
                if (postimages != value)
                {
                    postimages = value;
                    OnPropertyChanged("PostImages");
                }
            }
            get
            {
                return postimages;
            }
        }

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

    }

I retrieve my data via a REST call to my server:

public static bool GetMyPostData(ref ObservableCollection<PostObject> myPosts, string groupid, string apikey)
 {
       try
            {
                string newURL = URL + "GetPosts";

                using (HttpClient client = new HttpClient())
                {

                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    MultipartFormDataContent formdata = new MultipartFormDataContent
                    {
                        { new StringContent(apikey), "apikey" },
                        { new StringContent(groupid), "groupid" }
                    };

                    HttpResponseMessage response = client.PostAsync(newURL, formdata).Result;  // Blocking call! Program will wait here until a response is received or a timeout occurs.

                    if (response.IsSuccessStatusCode)
                    {
                        try
                        {

                            myPosts = response.Content.ReadAsAsync<ObservableCollection<PostObject>>().Result;



                        }
                        catch (Exception e)
                        {
                            Debug.WriteLine(e);
                            return false;
                        }
                    }
                }
                return true;

            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                return false;
            }
}

Which works I get my data correctly, now I set up my Binding context like so:

 ObservableCollection<PostObject> GroupPosts = new ObservableCollection<PostObject>();

       public Posts (GroupInfo ginfo)
        {
            InitializeComponent ();

                GroupTitle.Text = ginfo.Title;
                 CurrentGroupInfo = ginfo;
                GetDataPosts();

            BindingContext = GroupPosts;


       }
  public void GetDataPosts()
        {
            try
            {

                GroupPosts.Clear();

                if (RestController.GetMyPostData(ref GroupPosts, CurrentGroupInfo.Id.ToString(), apikey))
                {
                    Debug.WriteLine("Data downloaded");

                }
            }
            catch(Exception e)
            {
                Debug.WriteLine(e.Message);
            }

And finally I have my XAML set up like this:

<?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:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin"
             xmlns:cv="clr-namespace:CarouselView.FormsPlugin.Abstractions;assembly=CarouselView.FormsPlugin.Abstractions"
              NavigationPage.HasNavigationBar="True"
             NavigationPage.HasBackButton="False"
             NavigationPage.BackButtonTitle="Back"
             x:Class="forms.Posts">

    <NavigationPage.TitleView>
        <StackLayout Orientation="Horizontal" VerticalOptions="Center" Spacing="10" >
            <Label x:Name="GroupTitle" TextColor="White" FontSize="Medium"/>
        </StackLayout>
    </NavigationPage.TitleView>

    <ContentPage.ToolbarItems>
        <ToolbarItem Name="iconexample" Icon="settings.png" Priority="0" Order="Primary" />
    </ContentPage.ToolbarItems>

    <ContentPage.Content>
        <cv:CarouselViewControl x:Name="carousel"
        ItemsSource="{Binding PostImages}"
        ShowArrows="true"
        ShowIndicators="true"
        Orientation="Horizontal">
       </cv:CarouselViewControl>


    </ContentPage.Content>
</ContentPage>

However I get an error

Unhandled Exception:

System.NullReferenceException: Object reference not set to an instance of an object.

So I'm not sure what I'm missing or I need to read up on this a little more? any help would be great.

Alexey Strakh

You want to do a few changes here:

  1. Change the field definition to a property, you won't be able to bind to the field:
public ObservableCollection<PostObject> GroupPosts { get; } = new ObservableCollection<PostObject>();
  1. If you updating the reference then you have to raise property changed event, so your property definition should look like that:
private ObservableCollection<PostObject> _groupPosts = new ObservableCollection<PostObject>();
public ObservableCollection<PostObject> GroupPosts
{
    get { return _groupPosts; }
    set 
    { 
        _groupPosts = value; 
        RaisePropertyChanged(.....); // here you should notify your binding that value has changed
    }
}
  1. Because you are trying to pass this list by reference (ref parameter), you won't be able to compile it with a property so it's better just to return value from your data provider and then apply it:
GroupPosts.Clear();
var newData = RestController.GetMyPostData(CurrentGroupInfo.Id.ToString(), apikey);
GroupPosts = newData;

it's a bad practice to pass the observable collection to an underlying data provider because it will limit it to operate on UI thread only (otherwise after updating the collection on non-ui thread you can crash the app). But this is a top for another post :)

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related