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

  1. 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!

Add comment

Log in to post comment

All about bugs

View bugs we have successfully reproduced, and vote for the bugs you want to see fixed most urgently.