Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Don't fire Callback when one UI control is updated from it's Binding

Discussion in 'UI Toolkit' started by KristofferH, Dec 21, 2021.

  1. KristofferH

    KristofferH

    Joined:
    Oct 27, 2012
    Posts:
    51
    I have one Slider and one FloatField that both can updates a (the same) value on a script on a GameObject. I use Bindings to bind the value to the two UI controls so that when you update one control the other is also updated to reflect the value. But the problem is that this fires the Callback on both controls, even if I just change one of them.
    I'm setting the value through a method because I need to do some other stuff with that value as well when the value is updated. So the flow should be like this:
    1. Drag the Slider.
    2. The slider Callback calles SetValue with the slider value as a parameter and my GameObject component uses this value to calculate some other values and updates the bound field "value".
    3. The Binding to the FloatField then reads the updated "value" variable and updates the FloatField value in the UI to reflect the updated value.
      1. Note: it should also work the same way the other way around: if you update the FloatField the same thing happens and then the Slider is updated through the Binding to reflect the changed value.
    But every time the "value" is changed the controls are firing their callbacks every time.

    I'm also using Timeline to animate the "value" but if my window is opened when the animation plays the controls are updated through the binding and the callbacks are fired again every frame and that causes problem as well.

    Here is the code for the binding and the callbacks:

    Code (CSharp):
    1. SerializedObject serializedController = new SerializedObject(controller);
    2. SerializedProperty value = serializedController.FindProperty("value");
    3.  
    4. Slider valueSlider = controllerRow.Q<Slider>("value-slider");
    5. FloatField valueField = controllerRow.Q<FloatField>("value-field");
    6.  
    7. valueSlider.BindProperty(value);
    8. valueField.BindProperty(value);
    9.  
    10. valueSlider.RegisterCallback<ChangeEvent<float>>(evt => {
    11.     controller.SetValue(evt.newValue);
    12.     controller.SetInclude(true);
    13.     });
    14.  
    15. valueField.RegisterCallback<ChangeEvent<float>>(evt => {
    16.     controller.SetValue(evt.newValue);
    17.     controller.SetInclude(true);
    18.     });
    How can I make this work?
     
  2. Xan_9

    Xan_9

    Joined:
    Oct 7, 2020
    Posts:
    31
    Try using
    controller.SetValueWithoutNotify(evt.newValue);


    Then the Change Event won't fire for the target controller.
     
  3. KristofferH

    KristofferH

    Joined:
    Oct 27, 2012
    Posts:
    51
    'controller' is my own code, it does not have a SetValueWithoutNotify method, it is not part of the UI Toolkit. It is the Binding that is causing the callback to fire, so using SetValueWithoutNotify() wouldn't help there either.
     
  4. uMathieu

    uMathieu

    Unity Technologies

    Joined:
    Jun 6, 2017
    Posts:
    384
    ChangeEvent are triggered when the value inside a VisualElement field has changed. This can mean the change can come from either user interaction or the binding system.

    Since in your case both elements are bound to the same value, you can register to only one of them and receive the event, no matter where the change is coming from. eg: User edits the slider, binding writes value to the object, then the binding on the floatFields picks it up and update the floatField's value.
     
  5. KristofferH

    KristofferH

    Joined:
    Oct 27, 2012
    Posts:
    51
    Yes, I understand why this is happening, what I was wondering is how do I get around this, in order to achieve the effect I want? :)
     
  6. Monogenesis

    Monogenesis

    Joined:
    Dec 30, 2019
    Posts:
    56
    Could you set the displayed value in the respective other control to the new value without notify? Something like this:
    1. valueSlider.RegisterCallback<ChangeEvent<float>>(evt => {
    2. controller.SetValue(evt.newValue);
    3. controller.SetInclude(true);
    4. valueField.SetValueWithoutNotify(evt.newValue);
    5. });

    6. valueField.RegisterCallback<ChangeEvent<float>>(evt => {
    7. controller.SetValue(evt.newValue);
    8. controller.SetInclude(true);
    9. valueSlider.SetValueWithoutNotify(evt.newValue);
    10. });