Search code examples
listviewxamarinxamarin.formsxamarin.androidsendbird

Xamarin Forms Listview Not Updating GUI


I am trying to create a chatting page. There's a listview whose itemssource is an ObservableCollection. Everything seems to go smoothly until an item is added to the ObservableCollection. The added item does not immediately appear on the screen and only after touching the screen does it show up.

In fact, I have the same issue as stated here: https://forums.xamarin.com/discussion/18631/listview-binding-to-observablecollection-does-not-update-gui/p2

I believe one of the solutions stated in the thread (Page 2) has something to do with INotifyPropertyChanged. After many attempts of trying to apply the solution to fit my needs, I am unable to do so. So could you please guide me in the correct direction.

My code is summarized as follows:

public partial class Chat : ContentPage
{
    private ObservableCollection<MessageObj> Messages = new ObservableCollection<MessageObj>();

    public Chat(string name, string imageURL, int UID)
    {
        msgList.ItemsSource = Messages; //msgList is ListView in XAML
    }

    public class MessageObj
    {
        public string Mess { get; set; }
        public TextAlignment textAlignment { get; set; }

        public MessageObj(string Mess, TextAlignment textAlignment)
        {
            this.Mess = Mess;
            this.textAlignment = textAlignment;
        }
    }
}

I've also read: Xamarin Forms ListView ObservableCollection not updating I think I am changing the collection so I shouldn't be facing this issue but I am. Any help/insight would be greatly appreciated!

I have added a gist containing my code for reference which shows my now commented attempts at different solutions: https://gist.github.com/Dobermensch/49ee9d8adb9a83a38790b691c857691d

Edit: Added a little bit of more initially omitted code to gist

Edit2: FYI, I am not using the MVVM pattern.


Solution

  • As you clarified in comments, the problem is with the messages received during the OnMessageReceived callback from the SendBird API. From reading their documentation, they do not appear to invoke the callback synchronously with the UI thread.

    Xamarin applications can be multi-threaded, and there is one main, UI thread which must be involved in all updates to the UI. In a lot of apps, the code you write will all run on the UI thread, unless you do things like Task.Run() or await <some async task>.ConfigureAwait(false) to force work off of the UI thread. So normally you don't have to worry about UI vs non-UI threads. Callbacks from user interaction with the UI, like button clicks, also run on the UI thread. Callbacks from non-UI sources may or may not be on the UI thread (depending on implementation and luck), and I suspect that's the case here.

    Depending on the platform you're working on, different things can go wrong if you attempt to change the UI from code that is not the UI thread. Sometimes you get a crash (typical iOS response), or just won't update right away (common Android behavior).

    So what I would try is to force the collection update to happen on the UI thread, using Device.BeginInvokeOnMainThread, as such:

            ch.OnMessageReceived = (BaseChannel baseChannel, BaseMessage baseMessage) => {
                Device.BeginInvokeOnMainThread(() =>
                {
                    Messages.Add(new MessageObj(((UserMessage)baseMessage).Message, TextAlignment.Start));
                    msgList.ScrollTo(Messages.Last(), ScrollToPosition.End, false);
                };
            };
    

    That will tell the UI thread to do the work of adding the MessageObj to Messages, so the various PropertyChanged events will also be handled on the UI thread, which will know how to actually update the UI.