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

Feature Request DropdownField Data Pair Support Rather Than Just String

Discussion in 'UI Toolkit' started by stevphie123, Jun 13, 2023.

  1. stevphie123

    stevphie123

    Joined:
    Mar 24, 2021
    Posts:
    74
    Dropdownfields currently only accept List<string> for it's choices/menus, which is fine for most cases, but once we want to add another layer of validation via id in our system it requires us to roll our own, which also fine I guess but can be a bit of repetitive after some time.

    But wouldn't it be nice if we can do data pairing e.g: menu (string, T) or similar concept to keyvaluepair in c# ?

    Just a thought of mine, thanks!
     
  2. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,820
    You can use a custom data type with the PopupField.

    Code (csharp):
    1. using System.Collections.Generic;
    2. using UnityEngine.UIElements;
    3.  
    4. public class MyData
    5. {
    6.     public string name;
    7.     public int id;
    8. }
    9.  
    10. public class MyPopupField : PopupField<MyData>
    11. {
    12.     public MyPopupField() : base(GenerateExampleData(), 0)
    13.     {
    14.         formatSelectedValueCallback = FormatItem;
    15.         formatListItemCallback = FormatItem;
    16.     }
    17.  
    18.     static string FormatItem(MyData myData)
    19.     {
    20.         return myData.name;
    21.     }
    22.  
    23.     static List<MyData> GenerateExampleData()
    24.     {
    25.         return new List<MyData>
    26.         {
    27.             new MyData { name = "First", id = 1 },
    28.             new MyData { name = "Second", id = 2 },
    29.             new MyData { name = "Third", id = 3 },
    30.         };
    31.     }
    32. }
     
  3. stevphie123

    stevphie123

    Joined:
    Mar 24, 2021
    Posts:
    74
    I honestly did not know that PopupField can do that o_O

    Thanks Karl!
     
    karl_jones likes this.
  4. Skolwind

    Skolwind

    Joined:
    Aug 28, 2014
    Posts:
    8
    I have tried the example and it does not work for me - popup field is showing up but changes does not serialize, it works fine if I use PopupField<string> or PopupField<int>

    here is my code:
    Code (csharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEditor;
    4. using UnityEditor.UIElements;
    5. using UnityEngine;
    6. using UnityEngine.UIElements;
    7.  
    8. [Serializable]
    9. public class MyData
    10. {
    11.    public string name;
    12.    public int id;
    13. }
    14.  
    15. [CustomPropertyDrawer(typeof(MyData))]
    16. public class MyDataEditor : PropertyDrawer
    17. {
    18.    public override VisualElement CreatePropertyGUI(SerializedProperty property)
    19.    {
    20.      var field = new MyPopupField();
    21.      field.BindProperty(property);
    22.      return field;
    23.    }
    24. }
    25.  
    26. public class MyMonoBehaviour : MonoBehaviour
    27. {
    28.    public MyData myData;
    29. }
    30.  
    31. public class MyPopupField : PopupField<MyData>
    32. {
    33.    public MyPopupField() : base(GenerateExampleData(), 0)
    34.    {
    35.      formatSelectedValueCallback = FormatItem;
    36.      formatListItemCallback = FormatItem;
    37.    }
    38.  
    39. static string FormatItem(MyData myData)
    40. {
    41.    return myData.name;
    42. }
    43.  
    44. static List<MyData> GenerateExampleData()
    45. {
    46.    return new List<MyData>
    47.    {
    48.      new MyData { name = "First", id = 1 },
    49.      new MyData { name = "Second", id = 2 },
    50.      new MyData { name = "Third", id = 3 },
    51.    };
    52. }
    53. }
     
  5. oscarAbraham

    oscarAbraham

    Joined:
    Jan 7, 2013
    Posts:
    431
    Hi! The binding system only supports some basic types when binding a single field. Namely types that are explicitly named in this enum. :/ So your MyData class isn't supported. You'd need to "bind" the field manually, listening for changes in both the serializedProperty and the field to sync the data, or you'd need to do something a bit more custom. It's hard to say without knowing your actual use case.
     
  6. Skolwind

    Skolwind

    Joined:
    Aug 28, 2014
    Posts:
    8
    I see, so it is by design. I just want to work with ids but show user the names (which can be changed), now that I thing about it I don't need to store names in place, like so

    Code (CSharp):
    1. public class MyIdAttribute : PropertyAttribute {}
    2.  
    3. [CustomPropertyDrawer(typeof(MyIdAttribute))]
    4. public class MyDataEditor : PropertyDrawer
    5. {
    6.     public override VisualElement CreatePropertyGUI(SerializedProperty property)
    7.     {
    8.         var idToName = new IdToName();
    9.         var field = new MyPopupField(idToName, idToName.GetIds());
    10.         field.BindProperty(property);
    11.         return field;
    12.     }
    13. }
    14.  
    15. public class MyMonoBehaviour : MonoBehaviour
    16. {
    17.     [MyId] public int myId;
    18. }
    19.  
    20. public sealed class MyPopupField : PopupField<int>
    21. {
    22.     public MyPopupField(IdToName idToName, List<int> choices) : base(choices, 0)
    23.     {
    24.         formatSelectedValueCallback = idToName.Convert;
    25.         formatListItemCallback = idToName.Convert;
    26.     }
    27. }
    28.  
    29. public class IdToName
    30. {
    31.     private Dictionary<int, string> idToNameDict = new() { {1, "First"}, {2, "Second"}, { 3, "Third" } } ;
    32.     public string Convert(int id) => idToNameDict[id];
    33.     public List<int> GetIds() => idToNameDict.Keys.ToList();
    34. }
    35.