ListBox.ScrollIntoView does not appear to be working directly after a CollectionChanged event
Posted: 23 Mar 2018, 15:40
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:
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:
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!
Here's the code:
Code: Select all
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();
}
}
Code: Select all
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 );
}
}