2
votes

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.

2
MessageObj should be inhertied from Bindable1SaeedSalehi
nowhere in that code do I see any attempt to add data to the Messages collectionJason
I'm adding data in the Messages collection in OnAppearing(), in the function LoadChat()Ayudh
Could you elaborate please 1SaeedSalehi? I cannot even think of what to google from that brief of a statement.Ayudh
@ayudh change public class MessageObj to public class MessageObj : Bindable because BindableProperies needs to be monitored for changes or collection changes1SaeedSalehi

2 Answers

1
votes

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.

2
votes

I Had a similar issue, what I found, after a lot of debugging and testing is that GUI is not updating because of the ItemsSource of the ListView and not because of the ObservableCollection.

Setting the ItemsSource to null and then set it again with the ObservableCollection made the GUI to update.
although I was hoping for much more elegeant solution I could not find one.

Device.BeginInvokeOnMainThread(() => {
    Context.WarmupOptions[0].IsChecked = !Context.WarmupOptions[0].IsChecked;
    mylist.ItemsSource = null;
    mylist.ItemsSource = Context.WarmupOptions;
});