ListView binding to ObservableCollection does not update GUI

2

Posts

  • AneeAnee USMember

    Hi,

    I have strange issue with mvvm binding. Once viewmodel property changed it not reflecting on UI part.
    I tried with Device.BeginInvokeOnMainThread(()=> ...); . But still problem...

    Let me know how can handle this situation..

  • AdamPAdamP AUUniversity ✭✭✭✭✭

    @Anee - please don't post the same question in 3 old threads. Post a single new Question in the relevant forum and community members will be there to help you out if you provide a good description of what the issue is.

  • AneeAnee USMember

    @AdamP . Maybe its an old question the solution is not working on my scenario . ...

  • AdamPAdamP AUUniversity ✭✭✭✭✭

    @Anee exactly, you have a new scenario. Please post a new question with your explicit scenario, otherwise threads get convoluted. We can then focus on your scenario. Feel free to post a link back to this thread though if appropriate.

  • gauthamagauthama USMember
        Binding _isBusyBinding;
    

    it gives warning as private field _isBusyBinding is assigned, but never used. can anyone suggest me ways to resolve this warning.!

  • norgienorgie NOMember

    @JashPatel, make sure you're talking about the same thing. What people report as a bug is that adding items to the list at run-time doesn't seem to update the list. I've worked with xaml/wpf since it came out and never experienced any problem with the ListView/ObservableCollection on Windows.

    Guess what a surprise it was to discover that this bug still exist in Xamarin Forms almost two years after it was reported here. I'm using Xamarin Studio Version 5.10.3 (build 27) with Mono 4.2.3 on a MacBook Pro and I'm developing for iOS. It would be so nice if the Xamarin dev. team could fix this bug soon, because for an "old" wpf programmer this is rather annoying to say the least.

  • JashPatelJashPatel USMember

    @norgie, I respectfully disagree. If this "bug" has been in existence for 2 years, you should see a lot more complaints. I use Xamarin to develop Android and iOS apps. I use ObservableCollection extensively, and it's been working fine for me.

    As I pointed out my my previous post, in order to notify your UI when the "ListViewItems" properties is set you must raise the PropertyChanged event. Otherwise, the UI won't know about it; thus adding/deleting/removing items in the collection does nothing in the UI.

    Once the event is raise and the UI retrieves the property, it will monitor CollectionChanged event in the OvservableCollection.

  • NMackayNMackay GBInsider, University mod

    I remember the original bug but I've had no issues in iOS or Android with ObservableCollection binding for ages.

    I'm using VS2013 and Win10 and a Mini Mac for compiling iOS.

  • norgienorgie NOMember

    @JashPatel The following code is from a WPF project:

     <ListView ItemsSource="{Binding Source={StaticResource MessagesViewSource}}">
            <ListView.ItemTemplate>
                   <DataTemplate DataType="{x:Type Common:StatusMessage}">
                          <StackPanel Orientation="Horizontal" Margin="10,2">
                                  <StackPanel.Resources>
                                         <Style TargetType="TextBlock">
                                              <Style.Triggers>
                                                     <DataTrigger Binding="{Binding Type}" Value="{x:Static Common:StatusType.Failure}">
                                                           <Setter Property="Foreground" Value="Red" />
                                                      </DataTrigger>
                                               </Style.Triggers>
                                          </Style>
                                   </StackPanel.Resources>
                                   <TextBlock Text="{Binding Time, StringFormat=t}" />
                                    <TextBlock Text="{Binding Text}" Margin="10, 0, 0, 0" />
                           </StackPanel>
                    </DataTemplate>
           </ListView.ItemTemplate>
    </ListView>
    

    The collection:

    public ObservableCollection<StatusMessage> Messages { get; private set; }

    In the ViewModel's constructor:

    Messages = new ObservableCollection<StatusMessage>();

    Updating the observable collection:

    public void Handle(StatusInfoChanged message)
    {
         Messages.Add(message.StatusMessage);
         if(message.StatusMessage.Type == StatusType.Failure)
              StatusMessageArrived(this, EventArgs.Empty);
    }
    

    There is no, I repeat, no notification code here and this works right out of the box. Maybe you don't think this is a bug on Xamarin's part, but in my book it is because WPF was there long before Xamarin Forms.

    I guess we'll have to agree to disagree on this one.

  • HermannScharitzerHermannScharitzer ATUniversity
    edited May 2016

    @JashPatel said:
    This is not a bug, folks. You have to understand how binding works...

    When InitializeComponent() is called in the constructor, your XAML is loaded and all controls are initialized. At that time your listview gets it value from ListViewItems property, which happens to be null. So, nothing displays.

    Exactly that. Thank you for explaining this non-issue.

    Xamarin.Forms really could use an in-depth guide on data binding.

  • HermannScharitzerHermannScharitzer ATUniversity

    Already got my eye on it! I know Charles Petzold back from win32 API coding. Was rightfully called the windows API bible.

  • NMackayNMackay GBInsider, University mod

    @HermannScharitzer

    Caught his talk at Evolve and got a signed copy of his book.

  • abdullahtahan.7433abdullahtahan.7433 SAMember ✭✭

    guys i've implement a sample here which i'm using OnPropertyChanged to bind my control to other control but couldn't make it in the normal list it doesn't get notifier so when i changed it to ObservableCollection it works but what's wrong here why the list didn't get notified when i implement my interface ?

  • MattButlerMattButler USUniversity ✭✭

    This is still an issue...

  • abdullahtahan.7433abdullahtahan.7433 SAMember ✭✭

    one of my friend helped me out attached you can find a sample , what we had to do is to implement the OnPropertyChanged on the models of the list it self

  • Oliver.4228Oliver.4228 USMember
    edited June 2016

    Hi I've skimmed through the posts, but I had a similar problem.

    I wanted to simply bind a ObservableCollection class list of a custom class to a ListView container.

    I followed the two tutorials (Introduction to Xamarin.Forms and ListView Data Source) and I could see that the number of items in the list displayed were correct, but the binding data content wasn't displaying on the device.

    The trick that did it for me was setting the getter/setters for the custom class members. Here's the code that worked for me:

    My custom Page class Member varible and class declaration
    `

        // Create a class which will hold the itmes
        public class ListItem
        {
            // public string VideoTitle; <<<< !!!!! ------- THIS DID NOT BIND WELL ----- !!!!
            public string VideoTitle { get; set; }
            public string URL {get;set;}
            public string VideoID {get;set;}
        }
    
        // Create a list as a memeber.
        // This list will be "bounded" to the ListView container
        // also needs the statement uptop: " using System.Collections.ObjectModel; "
        ObservableCollection<ListItem> content_list = new ObservableCollection<ListItem>();
    

    `

    `

    public VideoListPage()
    {
    InitializeComponent();

            // Populate the list 
            content_list.Add(new ListItem { VideoTitle = "Test", URL = "https://www.google.co.uk/", VideoID = "1" });
            content_list.Add(new ListItem { VideoTitle = "Meh", URL = "https://www.google.co.uk/", VideoID = "2" });
            content_list.Add(new ListItem { VideoTitle = "Moo", URL = "https://www.google.co.uk/", VideoID = "3" });
    
            // ---  "Bind" the list to the ListView container. ---
            // In the .XAML file we know it's called ' x:Name="ContentList" '
    
            // First we assign the list of <ListItem> class to the ListView container
            ContentList.ItemsSource = content_list;
    
            // --- End of Binding --- //
    
            // Becauset the ObservableCollection class is bounded to the ListView, we can ammend the list after assigning
            // the current list, and the ListView container will AUTOMATICALLY update 
            content_list.Add(new ListItem() { VideoTitle = "New Item added after list binding", URL = "https://www.google.co.uk/", VideoID = "3" });
            content_list.Add(new ListItem() { VideoTitle = "Second Item added after list binding", URL = "https://www.google.co.uk/", VideoID = "3" });      
        }
    }
    

    `

    And the corresponding XAML

    <ListView x:Name="ContentList"> <ListView.ItemTemplate> <DataTemplate> <TextCell Text ="{Binding VideoTitle}" /> </DataTemplate> </ListView.ItemTemplate> </ListView>

    I hope this helps :)
    (p.s. I think this is my first post! woo)

  • Oliver.4228Oliver.4228 USMember
    edited June 2016

    Hi I had a similar problem and I had reached a solution.

    All I wanted to do was to bind an ObservableCollection class to a ListView, much like the tutorial on Xamarin website suggests. But somehow the APP deployed showed blank items, even though the number of items in the ListView were correct -- which suggested to me that its still a data binding problem.

    Another post somewhere suggested a layout problem, but what saved the day for me was placing the getters and setters for the custom class members which I wanted to bind to.

    // Create a class which will hold the itmes public class ListItem { // public string VideoTitle; <<<< !!!!! ------- THIS DID NOT BIND WELL ----- !!!! public string VideoTitle { get; set; } public string URL {get;set;} public string VideoID {get;set;} }

    My almost full CS code implementation was this:

    `public partial class VideoListPage : ContentPage
    {
    // Create a class which will hold the itmes
    public class ListItem
    {
    // public string VideoTitle; <<<< !!!!! ------- THIS DID NOT BIND WELL ----- !!!!
    public string VideoTitle { get; set; }
    public string URL {get;set;}
    public string VideoID {get;set;}
    }

        // Create a list as a memeber.
        // This list will be "bounded" to the ListView container
        // also needs the statement uptop: " using System.Collections.ObjectModel; "
        ObservableCollection<ListItem> content_list = new ObservableCollection<ListItem>();
    
        public VideoListPage()
        {
            InitializeComponent();
    
            // Populate the list 
            content_list.Add(new ListItem { VideoTitle = "Test", URL = "https://www.google.co.uk/", VideoID = "1" });
            content_list.Add(new ListItem { VideoTitle = "Meh", URL = "https://www.google.co.uk/", VideoID = "2" });
            content_list.Add(new ListItem { VideoTitle = "Moo", URL = "https://www.google.co.uk/", VideoID = "3" });
    
            // ---  "Bind" the list to the ListView container. ---
            // In the .XAML file we know it's called ' x:Name="ContentList" '
    
            // We assign the list of <ListItem> class to the ListView container
            ContentList.ItemsSource = content_list;
    
            // --- End of Binding --- //
    
            // Because the ObservableCollection class is now bounded to the ListView, we can ammend the list after assigning
            // the current list, and the ListView container will AUTOMATICALLY update 
            content_list.Add(new ListItem() { VideoTitle = "New Item added after list binding", URL = "https://www.google.co.uk/", VideoID = "3" });
            content_list.Add(new ListItem() { VideoTitle = "Second Item added after list binding", URL = "https://www.google.co.uk/", VideoID = "3" });      
        }
    }`
    

    And the corresponding XAML code

    <ListView x:Name="ContentList"> <ListView.ItemTemplate> <DataTemplate> <TextCell Text ="{Binding VideoTitle}" /> </DataTemplate> </ListView.ItemTemplate> </ListView>

    I hope this helps. Also my first post! (I think?)
    OK I can't seem to get the code snippet showing well on this comment, so I appologise for that.

  • ThomasMielkeThomasMielke DEMember ✭✭
    edited June 2016

    This is a rather crude solution. You don't need an ObservableCollection, if you first populate the collection and then assign it as items source. This can also be achived with a List or even an IEnumerable.

    I often see certain dependency objects confused these times: items and collections.

    If you use an ObservableCollection it's all about INotifyCollectionChanged. For binding the items of a collection, you have to implement INotifyPropertyChanged.

    public class ListItem : INotifyPropertyChanged
    {
        // public string VideoTitle; <<<< !!!!! ------- THIS DID NOT BIND WELL ----- !!!!
        // public string VideoTitle { get; set; } <-- this doesn't bind as dependency object as well!
    
        // this really binds your data model to the list view, meaning: if you 
        // change the VideoTitle of an item, it will actually update your view!
        private string videoTitle;
        public string VideoTitle
        {
            get
            {
                return videoTitle;
            }
            set
            {
                if (value != VideoTitle)
                {
                    videoTitle = value;
                    OnPropertyChanged();
                }
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    
        void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    INotifyCollectionChanged is easier to use because you usually won't have to add a handler to your collection: You rather would want to use a readily available INotifyCollectionChanged implementation of List<> called ObservableCollection.

  • DevelopersISEDDevelopersISED USMember ✭✭

    Try setting RowHeight to 1, even if you like me use HasUnevenRows to true ...

    This fixed the problem on Android for me.

    And dudes ... stop saying this is not a bug.
    Xamarin is a Cross Platform framework, if something works differently between platforms that's a bug for sure.

  • DevologyDevology GBMember ✭✭

    In my case my project is using MVVMLight with an IOC container, the IoC container doesn't set the BindingContext until after the page has been constructed, but the constructor was calling InitializeComponent as though it was.

    I guess InitializeComponent was binding my UI ListView (defined in the XAML) to a binding that didn't exist in the BindingContext yet!

    Even though the binding was set later by the IoC container, Xamarin.Forms didn't render changes to the list, although it did handle all the other simple bindings (i.e. Strings in the model to Labels in the UI).

    I did try to invoke InitializeComponent whenever the model changed, but this seemed inefficient.

    To work around this I modified the code-behind for the .xaml to defer calling InitalizeComponent until the BindingContext was explicitly set by the IoC container (by overriding the OnBindingContextChanged method) ...

        using System;
        using System.Collections.Generic;
        using PubSub;
        using Xamarin.Forms;
    
        namespace Presentation
        {
            public partial class MyPage : BasePage
            {
                public MyPage ()
                {
                    // NOTE - Do not call InitializeComponent in here - the main model is not injected yet and will cause
                    // the Xamarin.Forms list to not render the List, worse still if I add entities to my collecion the
                    // list does not update!
                }
    
                protected override void OnBindingContextChanged ()
                {
                    base.OnBindingContextChanged ();
                    InitializeComponent ();
                }
    
           }
        }
    

    Hopefully this will help someone out that has a similar initialisation lifecycle.

  • ThomasMielkeThomasMielke DEMember ✭✭

    It just doesn't seem right. If you have to do this, there is a fundamental flaw in you code.

  • DevologyDevology GBMember ✭✭

    Unfortunately the code was inherited and the lifecycle isn't 'typical' (it uses MvvmLite and a factory that creates the Views and Models, it creates the view first then sets the model afterwards - not in the constructor) - I'm just sharing this in case it helps someone else.

  • ThomasMielkeThomasMielke DEMember ✭✭

    Creating the model separately from the view can be troublesome. But this is just wrong and maybe only helps in your special case, possibly perpetuating a bug that might pop up at some other point in the future.

    Maybe you want to deliver some context? Model, collection, and how its been populated with data, so we can learn?

  • MikePintoMikePinto USMember

    @Oliver.4228 said:
    Hi I've skimmed through the posts, but I had a similar problem.

    I wanted to simply bind a ObservableCollection class list of a custom class to a ListView container.

    I followed the two tutorials (Introduction to Xamarin.Forms and ListView Data Source) and I could see that the number of items in the list displayed were correct, but the binding data content wasn't displaying on the device.

    The trick that did it for me was setting the getter/setters for the custom class members. Here's the code that worked for me:

    My custom Page class Member varible and class declaration
    `

        // Create a class which will hold the itmes
        public class ListItem
        {
            // public string VideoTitle; <<<< !!!!! ------- THIS DID NOT BIND WELL ----- !!!!
            public string VideoTitle { get; set; }
            public string URL {get;set;}
            public string VideoID {get;set;}
        }
    
        // Create a list as a memeber.
        // This list will be "bounded" to the ListView container
        // also needs the statement uptop: " using System.Collections.ObjectModel; "
        ObservableCollection<ListItem> content_list = new ObservableCollection<ListItem>();
    

    `

    `

    public VideoListPage()
    {
    InitializeComponent();

            // Populate the list 
            content_list.Add(new ListItem { VideoTitle = "Test", URL = "https://www.google.co.uk/", VideoID = "1" });
            content_list.Add(new ListItem { VideoTitle = "Meh", URL = "https://www.google.co.uk/", VideoID = "2" });
            content_list.Add(new ListItem { VideoTitle = "Moo", URL = "https://www.google.co.uk/", VideoID = "3" });
    
            // ---  "Bind" the list to the ListView container. ---
            // In the .XAML file we know it's called ' x:Name="ContentList" '
    
            // First we assign the list of <ListItem> class to the ListView container
            ContentList.ItemsSource = content_list;
    
            // --- End of Binding --- //
    
            // Becauset the ObservableCollection class is bounded to the ListView, we can ammend the list after assigning
            // the current list, and the ListView container will AUTOMATICALLY update 
            content_list.Add(new ListItem() { VideoTitle = "New Item added after list binding", URL = "https://www.google.co.uk/", VideoID = "3" });
            content_list.Add(new ListItem() { VideoTitle = "Second Item added after list binding", URL = "https://www.google.co.uk/", VideoID = "3" });      
        }
    }
    

    `

    And the corresponding XAML

    <ListView x:Name="ContentList"> <ListView.ItemTemplate> <DataTemplate> <TextCell Text ="{Binding VideoTitle}" /> </DataTemplate> </ListView.ItemTemplate> </ListView>

    I hope this helps :)
    (p.s. I think this is my first post! woo)

    As @Oliver.4228 said this was my EXACT issue:
    // public string VideoTitle; <<<< !!!!! ------- THIS DID NOT BIND WELL ----- !!!!

            public string VideoTitle { get; set; }
            public string URL {get;set;}
            public string VideoID {get;set;} 
    
  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    I don't use xaml... This is a little sample that demonstrate that observablecollection works with listview in XF https://github.com/acaliaro/TestBindingWithListView

  • masterolearymasteroleary USMember ✭✭

    My solution is to add something like this after you add items to the observable collection

    ListOfPeople.Add(NewPerson);
    PeopleList.MinimumHeightRequest = ListOfPeople.Count > 0 ? 0 : 200;
    // Forces empty list to redraw since it starts off with 0 height, otherwise you think its not drawing the new items the first time around when in fact it is

  • Hi everyone, I've some issues with the listview refresh. I try to explain:

    I have a listview which load data when appearing, using observable collection and binding item source. Scrolling the list, when the scroll stop, a timer start for three second that read and load data from database and update visible items of the listview.

    ListaArticoli is the listview itemsource:

                try
                {
                    foreach (PrezzoListino elem in cp.prezzi)
                    {
                        ListaArticoli[elem.index].LI00_PREZZOLISTINO = elem.prezzo;
                    }
                }
                catch (Exception ex)
                {
    
                }
    

    the itemsource is updated but the data will not show loaded data.. in fact, scrolling down and up (caming back to the updated items) the item appear correct (the listview is refreshed).

    Could anyone tell me any solution or idea?

    Thanks in advance

    Gianluca

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    Gianluca can you post a sample repo on github?

  • ThomasMielkeThomasMielke DEMember ✭✭
    edited October 2016

    How about making a dependency object checklist? I'll start with my number one cause for "does not update":

    1. Check, if you haven't forgotten to derive your data model from INotifyPropertyChanged:

      class MyItem : INotifyPropertyChanged
      {
      ...
      }

  • Hi ThomasMielke, you're right. I forget INotifyPropertyChanged implementation...

    I'll try to add it and post the result..

    Thanks everyone!

    Gianluca

  • Great! It works! It was INotifyPropertyChanged... now I add implementation and that's ok!

    Thanks a lot

    Gianluca

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    Gianluca you can use Fody for PropertyChanged...

  • Sorry AlessandroCaliaro..I don't know it

  • I'll take a look! Thanks

  • jk8jk8 USMember

    For me this ended up being that the model I was using for the generic ObservableCollection<> was not inheriting from INotifyPropertyChanged, the list was in my ViewModel but not the generic class model. Typical MVVM mistake.
    Hope this helps someone.

  • Dear Its not a BUG,
    you just have to implement the INotifyPropertyChanged interface in your model (or adapter you can say) class.
    And when the values changes it will update the ui.
    For example you have class named as Student who has name and image property, then the adapter class is as follows

    public class StudentModel : INotifyPropertyChanged
    {
    string profileImage = "";
    public event PropertyChangedEventHandler PropertyChanged;

        public string ProfileName { get; set; }
    
        public string ProfileImage
        {
            get
            {
                return profileImage;
            }
    
            set
            {
                profileImage= value;
                OnPropertyChanged("ProfileImage");
            }
        }
    
        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this,
                    new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    when the ProfileImage property changes (here we set image name) the list view ui updated.

    You just need to call add OnPropertyChanged thats it....
    I hope, I helped someone.
    Thank You
    --- HP

  • DonCB2BDonCB2B USMember ✭✭✭

    @JashPatel Thanks to point out right direction. By using collectionchanged, i can update my listview and show right display on my GUI.

2
Sign In or Register to comment.