Search Unity

Question How to use the userData attribute with events?

Discussion in 'UI Toolkit' started by kork, Jun 24, 2020.

  1. kork

    kork

    Joined:
    Jul 14, 2009
    Posts:
    280
    In our game we dynamically create a list of buttons depending on what options a player has. What I would like to do is something like this:

    Code (CSharp):
    1. foreach( var option in options) {
    2.    var button = new Button();
    3.    // ...
    4.    // remember which option belongs to each button
    5.    button.userData = option;
    6.    button.clicked += OnButtonClicked;
    7. }
    8.  
    9. // ...
    10.  
    11. private void OnButtonClicked() {
    12.     // now i would like to have my option back
    13.     // but at this time i only know that a button was
    14.    // clicked, not which one. If this function would take
    15.    // a parameter with the event or the clicked element I could
    16.    // use it.
    17. }
    I could solve this by using a lambda function like this:

    Code (CSharp):
    1. foreach( var option in options) {
    2.    var button = new Button();
    3.    // ...
    4.    button.clicked += () => {  // do something with the option here }
    5. }
    But then I cannot un-register the function from the
    clicked
    event anymore. Also what is the point of having a
    userData
    attribute if you cannot access it in events because you don't know which element fired the event. I seem to be missing something here. Could someone shed some light on how the
    userData
    property is supposed to be used together with events?
     
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Button.clicked would be an Action<Button> rather than an Action, which would allow OnButtonClicked to take a Button argument.

    You should probably also not name the class Button, since that's a built-in Unity thing! I got confused and tried to look for .userData in the Button API page before I saw that you were new()ing it.
     
  3. kork

    kork

    Joined:
    Jul 14, 2009
    Posts:
    280
    @Baste you mean this is how the UI Elements API should be, right? Because right now it is just an
    Action
    , not an
    Action<Button>
    .
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Oh whoops, forgot which subforum I was in.

    So, yeah, disregard that.
     
  5. ErnestSurys

    ErnestSurys

    Joined:
    Jan 18, 2018
    Posts:
    94
    You can use nested methods like so:

    Code (CSharp):
    1. foreach (var option in options)
    2. {
    3.     var button = new Button();
    4.     button.clicked += OnButtonClicked;
    5.     resetAllButtons += () => button.clicked -= OnButtonClicked;
    6.  
    7.     void OnButtonClicked()
    8.     {
    9.         Debug.Log($"Clicked on: {option}");
    10.     }
    11. }
    or Action variable if you use .NET 3.5
    Code (CSharp):
    1. foreach (var option in options)
    2. {
    3.     var button = new Button();
    4.     Action onButtonClicked = () =>
    5.     {
    6.         Debug.Log($"Clicked on: {option}");
    7.     };
    8.    
    9.     button.clicked += onButtonClicked;
    10.     resetAllButtons += () => button.clicked -= onButtonClicked;
    11. }
    You can also overwrite the on-click callback like this:
    Code (csharp):
    1. button.clickable = new Clickable(OnButtonClicked);
     
  6. kork

    kork

    Joined:
    Jul 14, 2009
    Posts:
    280
    @ErnestSurys thank you, overwriting the clickable looks like the thing I need.

    The thing with the
    resetAllButtons
    event handler seems to just shift the problem elsewhere, because you need to remove your nested function from that event somehow, or you will probably build a memory leak by this.

    Still i think if one would have an
    Action<Event> 
    or
    Action<Button>
    as the
    clicked
    event signature one would need less code acrobatics.

    That being said, I'll roll with the
    clickable
    approach for now. Thank you again @ErnestSurys .
     
    uDamian and ErnestSurys like this.