Hi, I’m running into a weird issue with a custom VisualElement and property binding that I can’t quite figure out.
Basically I’m trying to make a VisualElement to show/hide other elements based on the value of a property for context-sensitive options. The problem is that the binding seems to get lost somehow when I interact with certain child elements.
Here’s my code (it makes use of another element I created to mimic the IMGUI FadeGroup, but I’ve already tested it swapping that out for a standard VisualElement, setting the .style.display property instead of calling ChangeValue on my FadeGroup element, and I got the exact same behaviour):
public class ContextGroup<ContextType> : BindableElement, INotifyValueChanged<ContextType>
{
private ContextType _value;
private Dictionary<ContextType, FadeGroup> contexts;
public ContextGroup()
{
contexts = new Dictionary<ContextType, FadeGroup>();
UIElementsUtility.ApplySharedStyles(this);
}
public ContextGroup(SerializedProperty controlProperty)
{
contexts = new Dictionary<ContextType, FadeGroup>();
UIElementsUtility.ApplySharedStyles(this);
this.BindProperty(controlProperty);
}
public ContextType value
{
get => _value;
set
{
_value = value;
UpdateFadeGroups();
}
}
public void SetValueWithoutNotify(ContextType newValue)
{
_value = newValue;
UpdateFadeGroups();
}
public void AddContext(ContextType caseContext, params VisualElement[] contextElements)
{
var fadeGroup = new FadeGroup(false, 0.2f);
for (int i = 0; i < contextElements.Length; i++)
{
fadeGroup.Container.Add(contextElements[i]);
}
Add(fadeGroup);
contexts.Add(caseContext, fadeGroup);
}
private void UpdateFadeGroups()
{
if (contexts.ContainsKey(_value)){
var currentGroup = contexts[_value];
foreach (var group in contexts.Values)
{
group.ChangeValue(group == currentGroup);
}
}
else
{
Debug.LogWarning("Context group does not contain a context for the current value.");
}
}
}
I then create an instance of this in an editor I’m building like this:
var targetModeContextGroup = new ContextGroup<bool>(lookAtTargetModule.FindPropertyRelative("enabled"));
var singleTarget = new PropertyField();
singleTarget.BindProperty(lookAtTargetModule.FindPropertyRelative("singleTarget"));
var targetList = new PropertyField();
targetList.BindProperty(lookAtTargetModule.FindPropertyRelative("targetList"));
targetModeContextGroup.AddContext(true, singleTarget);
targetModeContextGroup.AddContext(false, targetList);
lookAtFadeGroup.Container.Add(targetModeContextGroup);
There’s also another PropertyField above this that is bound to the same property controlling the ContextGroup.
All in all, this shows up like this (checked):
(unchecked):
This all works perfectly at first, if the bool is true my singleTarget (an Object field) property is visible, if it’s false then the targetList (an array) shows up. I can also interact with the singleTarget object with no issues, changing it or removing it works normally. If, however, I set the bool to false and then click to expand the Target List array out, the binding between the enabled property and the ContextGroup just seems to disappear.
Not only does toggling the checkbox no longer switch between the fadegroup options, the value property in ContextGroup simply stops getting changed. It’s as if the internals of the binding system just forget about it.
I’ve tried it without the custom FadeGroup element, I’ve tried doing the binding after creating the instance in my editor class instead of in the constructor, and I’ve also tested having ContextGroup not be generic, just using the bool type exclusively, none of them made any difference. Am I doing something obviously wrong here, or is this a bug in the binding system?
Thanks!
EDIT: I’ve also found I can’t bind an Enum property to it? With other types it works as long as the Type of the property matches the generic , but for Enums I just get a message saying it’s not supported by the element.
EDIT 2: After looking into the Unity source code on GitHub, I think it has something to do with the UpdateArrayFoldout method in the UIElements PropertyField, which includes code for Unbinding a parent propertyField. Is this likely to just be a limitation of the current binding system?