Hey, I have a custom List Element (it creates elements and adds a callback when they are clicked to change the _selectedElement
property, nothing else is important).
[UxmlElement]
[Serializable]
public partial class ListElement : BindableElement
{
private readonly ObjectPool<VisualElement> pool;
private readonly List<VisualElement> templates = new();
private IList items = new List<object>();
public ListElement()
{
this.pool = new ObjectPool<VisualElement>(() => this.itemTemplate!.Instantiate().Children().FirstOrDefault());
}
[UxmlAttribute("item-template")]
[CreateProperty]
public VisualTreeAsset itemTemplate { get; set; }
[CreateProperty]
public IList itemsSource
{
get => this.items;
set
{
Debug.Log($"setting list-items to {value}");
if (!this.items.Equals(value) || this.items.Count != value.Count)
{
this.items = value;
this.Rebuild();
}
this.NotifyPropertyChanged(nameof(this.itemsSource));
}
}
private int _selectedElement = -1;
[UxmlAttribute("selected-element")]
[CreateProperty]
public int selectedElement
{
get => this._selectedElement;
set
{
_selectedElement = value;
Debug.Log($"Changing selected element to {value}");
this.NotifyPropertyChanged((nameof(this._selectedElement)));
}
}
private void Rebuild()
{
Debug.Log("rebuilding list");
if (this.itemTemplate == null)
{
throw new InvalidOperationException($"{nameof(this.itemTemplate)} has not be set");
}
Debug.Log($"We have {items.Count} items");
// Add anything new
for (var i = this.templates.Count; i < this.itemsSource.Count; i++)
{
var item = this.pool.Get();
this.Add(item);
this.templates.Add(item);
item.dataSource = this.itemsSource;
item.dataSourcePath = PropertyPath.FromIndex(i);
var i1 = i;
item.RegisterCallback<ClickEvent>(evt =>
{
selectedElement = i1;
Debug.Log("Clicked an element");
});
}
// Remove shrinking
for (var i = this.templates.Count - 1; i >= this.itemsSource.Count; i--)
{
var item = this.templates[i];
this.Remove(item);
this.templates.RemoveAt(i);
this.pool.Release(item);
}
}
}
Together with a a datasource:
public class DayState : ScriptableObject, INotifyBindablePropertyChanged
{
public List<TimeSlotSO> timeSlots;
private int _selectedTimeSlot = -1;
[CreateProperty] public int SelectedTimeSlot
{
get => _selectedTimeSlot;
set
{
Debug.Log($"Data source time slot changed to {value}");
_selectedTimeSlot = value;
TimeSlotSelected.Invoke();
}
}
}
In the UXML I am binding as such:
<UI.ListElement.ListElement name="ListElement" item-template="project://database/Assets/UI/Panels/DailyActions/TimeSlotElement.uxml?fileID=9197481963319205126&guid=3c25f402937f84b47b0ce3cd06e44b39&type=3#TimeSlotElement">
<Bindings>
<engine:DataBinding property="selectedElement" data-source-path="SelectedTimeSlot" binding-mode="TwoWay" />
<engine:DataBinding property="itemsSource" data-source-path="timeSlots" binding-mode="ToTarget" />
</Bindings>
</UI.ListElement.ListElement>
I know the binding works, since the second binding works (the one where I bind itemsSource), the items are displayed.
However, when I click an element, only Changing selected element to
is logged in the console, i.e. the handler in the VisualElement itself.
However, Data source time slot changed to
is not logged and the value in the data source also doesn’t change. Am I missing something?