I have been away from Unity for about 2 years and coming back now, with a new project and I’m now evaluating UI workflows.
My Idea was to go with UI Toolkit as the stylesheets make it easy to stay consistent with the UI and I thought since it now supports runtime bindings and so on …
So here I am, trying to create a custom control with binding.
The documentation Unity - Manual: Bind a custom control features a quite bad example IMHO, as it actually does not really show how custom bindable controls work, it is just defered to the inner ObjectField.
You see, my example is simpler… in that regard:
[UxmlElement("WidgetSwitcher")]
public partial class WidgetSwitcher : BindableElement, INotifyValueChanged<int>
{
private int _activeWidgetIndex;
public WidgetSwitcher()
{
RegisterCallback<GeometryChangedEvent>(_ => UpdateVisibility());
UpdateVisibility();
}
private void UpdateVisibility()
{
for (int i = 0; i < childCount; i++)
{
this[i].style.display = i == _activeWidgetIndex ? DisplayStyle.Flex : DisplayStyle.None;
}
}
public void SetValueWithoutNotify(int newValue)
{
_activeWidgetIndex = newValue;
UpdateVisibility();
}
int INotifyValueChanged<int>.value
{
get => _activeWidgetIndex;
set
{
if (_activeWidgetIndex == value) return;
var previous = _activeWidgetIndex;
SetValueWithoutNotify(value);
using (var evt = ChangeEvent<int>.GetPooled(previous, value)) SendEvent(evt);
this.NotifyPropertyChanged("value");
}
}
}
A widget switcher, that just switches between it’s children. so It is supposed to bind to a int property.
It’s inheriting from BindableElement and INotifyValueChanged and in the UI Builder I set it to bind to a int property
<WidgetSwitcher name="WidgetSwitcher" data-source-type="MyDataType, Game02.Runtime" data-source-path="typeIndex">
<ui:Button text="Button" />
<ui:Label text="Label" />
<ui:Toggle label="Toggle" />
</WidgetSwitcher>
a label next to it shows me, that the int property is set correctly while the WidgetSwitcher just shows me the first child.
So, maybe I need to add a property with a [UxmlAttribute] to my widget and do the binding there?
Sadly for a property I cannot set up the binding in the UI Builder, but for a public field I can, so going with that:
[UxmlElement("WidgetSwitcher")]
public partial class WidgetSwitcher : BindableElement, INotifyValueChanged<int>
{
[UxmlAttribute("active-widget-index")]
public int _activeWidgetIndex;
...
and with that binding … it “works” … but also not right away. I press play, my UI is loaded and populated and the labels are all fine, but the widget switchers are all showing buttons (index=0), but when I then adjust the size of the Game-Panel inside the editor, then all of the Widgets get refreshed I guess and it shows the correct ones.
But ultimately it cannot be the solution to have it a public field, because then I have no way to do things in the setter (which also likely is the issue with the field approach only updating after receiving a refresh)
I guess I’m doing something super simple wrong, but I have no idea what.
