Search Issue Tracker
By Design
Votes
0
Found in
6000.0.10f1
Issue ID
UUM-75794
Regression
No
Data bindings update each frame when using ListView
How to reproduce
1. Open the attached “DataBindingExampleBasic.zip” project
2. Open “SampleScene”
3. Open the Profiler window under Window/Analysis/Profiler
4. Enter Play mode
5. Observe the Profiler’s Main Thread’s “Update Binding” section
Expected result: ListView data bindings are not updated every frame
Actual result: ListView data bindings are updated every frame
Reproducible in: 6000.0.10f1
Could not test with: 2021.3.40f1, 2022.3.38f1 (Project has dependencies on latest versions of select packages, which are unsupported in earlier streams)
Reproducible on: Windows 10, Windows 11
Not reproducible on: No other environments tested
Add comment
All about bugs
View bugs we have successfully reproduced, and vote for the bugs you want to see fixed most urgently.
Latest issues
- Spot Light with 'Box' Shape stops working when it collides with Mesh
- Accepting Asset Store EULA endless loop in Package Manager window
- “InvalidOperationException: Sequence contains no matching element“ error when reenabling a custom Debug Draw Mode
- Prefab that contains a layout group is marked as changed by the Editor when opened
- Emission animation does not appear when the "Emission Map" parameter is set to black in a Lit material
Resolution Note:
This is behaving as designed. What happens in this case is that when the AutoAssign mode is used on the ListView, the itemsSource will be used as the dataSource for the items. Each item will also prepend the dataSourcePath with the correct index for the item. We do it this way to ensure that changes on an item is propagated back to the itemsSource.
In your case, the itemsSource you are using is a C# array, which does not implement our change tracking interfaces, so the system will interpret that source as volatile and will update the bindings every frame even if no changes are reported.
We are missing an observable collection type that would implement those interface. We did not have it ready in time and did not want to delay releasing the system as a whole.
To reduce the amount of binding updates on a ListView, you will need to use an observable collection. I've included a partial example here that derives from `System.Collections.ObjectModel.ObservableCollection`. It's partial because I haven't overridden all the relevant methods (i.e. ClearItems) to opt-in change tracking.
Code:
using System;
using System.Collections.ObjectModel;
using Unity.Properties;
using UnityEngine.UIElements;
public sealed class CustomObservableCollection<T> : ObservableCollection<T>, INotifyBindablePropertyChanged
where T:INotifyBindablePropertyChanged
{
static CustomObservableCollection()
{
// Since this is a custom IList<T> implementation, we need to register it.
// This property bag will not take additional properties into account.
PropertyBag.Register(new IndexedCollectionPropertyBag<CustomObservableCollection<T>, T>());
}
public event EventHandler<BindablePropertyChangedEventArgs> propertyChanged;
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
if (item is INotifyBindablePropertyChanged handler)
handler.propertyChanged += ItemChanged;
RequestRefresh();
}
protected override void RemoveItem(int index)
{
if (Items[index] is INotifyBindablePropertyChanged handler)
handler.propertyChanged -= ItemChanged;
base.RemoveItem(index);
RequestRefresh();
}
protected override void SetItem(int index, T item)
{
if (Items[index] is INotifyBindablePropertyChanged previousHandler)
previousHandler.propertyChanged -= ItemChanged;
base.SetItem(index, item);
if (item is INotifyBindablePropertyChanged currentHandler)
currentHandler.propertyChanged += ItemChanged;
RequestRefresh();
}
private void ItemChanged(object sender, BindablePropertyChangedEventArgs e)
{
// Here, if we knew the index of the item, we could request a smaller refresh.
RequestRefresh();
}
private void RequestRefresh()
{
// Sending an empty bindingId to tell the system to update all sub-bindings.
propertyChanged?.Invoke(this, new BindablePropertyChangedEventArgs());
}
}
Hope this helps!