Page 1 of 1

ListBox.ScrollIntoView does not appear to be working directly after a CollectionChanged event

Posted: 23 Mar 2018, 15:40
by XaeroDegreaz
When attempting to scroll an item into view with ListBox.ScrollIntoView() after a CollectionChanged event is raised by an ObservableCollection, the method ScrollIntoView throws an exception: NoesisException: Null item added to collection.

Here's the code:
    public class ChatScreen : UserControl, INotifyPropertyChanged
    {
        public ObservableCollection<ChatMessageModel> Messages { get; } = new ObservableCollection<ChatMessageModel>();
        public TextBox SendText { get; private set; }
        public ListBox ListBox { get; private set; }
        public string Message { get; set; }
        public event PropertyChangedEventHandler PropertyChanged;

        public ChatScreen()
        {
            Initialized += onInitialized;
            GUI.LoadComponent( this, "Assets/Ui/Chat/ChatScreen.xaml" );
        }

        private void onInitialized( object sender, EventArgs eventArgs )
        {
            DataContext = this;
            SendText = (TextBox) FindName( "SendText" );
            ListBox = (ListBox) FindName( "ListBox" );
            SendText.TextInput += onMessageSend;
            Messages.CollectionChanged += onNewMessage;
        }

        private void onNewMessage( object sender, NotifyCollectionChangedEventArgs e )
        {
            var item = e.NewItems[0]; //# Confirmed not null through debugging
            ListBox.ScrollIntoView( item );
        }

        private void onMessageSend( object sender, TextCompositionEventArgs textCompositionEventArgs )
        {
            if ( Message != null && Message.Trim().Length > 0 )
            {
                Messages.Add( new ChatMessageModel( "XaeroDegreaz", $"{Message}" ) );
                SendText.Text = null;
            }
        }

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged( [CallerMemberName] string propertyName = null )
        {
            PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
        }
    }

    public class ChatMessageModel
    {
        public string Username { get; }
        public string Message { get; }
        public string FormattedMessage => $"{Username}: {Message}";

        public ChatMessageModel( string username, string message )
        {
            Username = username.Trim();
            Message = message.Trim();
        }
    }
I believe it has something to do with the item not yet being rendered in the list during that frame, so something internal to the ListBox isn't aware of a child to scroll to. I was able to mitigate this by adding a helper MonoBehaviour with a coroutine that waits until the end of the frame before calling ScrollIntoView on the ListBox:
    public class ChatHelper : MonoBehaviour
    {
        private static ChatHelper instance { get; set; }

        private void Awake()
        {
            instance = this;
        }

        public static void ScrollIntoView( ListBox listBox, object item )
        {
            instance.StartCoroutine( instance.doWork( listBox, item ) );
        }

        private IEnumerator doWork( ListBox listBox, object item )
        {
            yield return new WaitForEndOfFrame();
            listBox.ScrollIntoView( item );
        }
    }
I just thought I'd bring this up, since it seems like it doesn't exactly function the way it does in standard WPF applications. Is there a better way to handle scrolling to newly added items with Noesis? Perhaps I'm missing something really obvious. Thanks!

Re: ListBox.ScrollIntoView does not appear to be working directly after a CollectionChanged event

Posted: 27 Mar 2018, 17:16
by sfernandez
Could you please report this problem to our bugtracker?
We will review WPF behavior and implement it the same way.