I have pieces of data that are tightly coupled, for example a string and an enum. With help from this post - I have created a custom VisualElement, which displays this data, and this works great!
My CustomVisualElement implements from INotifyValueChanged<string> and INotifyValueChanged<myEnum>
This is Fine and works if I want to register call backs for ChangeEvent<string> or ChangeEvent<myEnum>. However I have cases where I want to just check if anything changed, and I don’t care about the specific data.
I could wrap my data in a struct
struct MyStruct{
string text;
myEnum enum;
}
and implement INotifyValueChanged<MyStruct>, however anytime I need to fire the ChangeEvent
newvalue = new MySruct(){text=TextValue, enum=EnumValue}
using (var newEvent = ChangeEvent<MyStruct>.GetPooled(currentvalue, newValue))
{
newEvent.target = this;
SendEvent(newEvent);
}
currentvalue = newValue
new’ing the struct is a bit meh, especialy when I don’t care about the current/new value.
Is there a better way to Send a change event without the attached gubbins (previous value, new value)
As we don’t have a non-generic base class for ChangeEvent, what you have there with your MyStruct is what I’d do as well. But I am curious why you would never care about the new value. Ideally, your CustomVisualElement would not be the one performing the logic on value change - instead, the user/owner of CustomVisualElement would use your ChangeEvent to perform the necessary actions.
My only other suggestion is to choose a value type, like int or string, and have CustomVisualElement send out a ChangeEvent with T as this simple type. It could be a hash or some other representation of your string/enum pair, only intended for outside users to know when something has changed.
My use case is as followed:
When the user edits a field (either the enum or text) in the CustomVisualElement this enables the “Save” button in the window.
When the user clicks the “Save” button, the string/enum values inside the CustomVisualElements get serialised into an object.
(I do not wish to bind directly, more of a delayed bind requiring input from the user)
Maybe a better pattern would be to store the string/enum values in the owner window; however there are multiple CustomVisualElements in the tree. How would I know whichCustomVisualElement text/enum was changed. As far as I am aware, there is no Owner parameter passed into the callback, so you don’t know which VE it came from?
Currently, when saving I iterate through all the (cached) CustomVisualElements and serialise their field values into an array.
All events have these properties: target - element on which the event occured (so your CustomVisualElement if you’re still sending the ChangeEvent<MyStruct>) currentTarget - element currently handling the event (the element you called RegisterCallback<ChangeEvent<MyStruct>>() on)
Since you need to retroactively find the current values of your fields when the user presses Save, you can just register for ChangeEvent and ChangeEvent on your container/root element and enable the Save button whenever you get any event.
I would just link all your fields to a central array via reference and index instead of relying on the values within the UI elements themselves. When you press Save, you can just serialize this array.
Sorry for the late reply I was just letting the new implementation settle.
This is how it was, and what lead me to write this forum post. As it meant I would have repeat code, the code once in the callback for string, and then again in the Enum. I could foresee this leading to complications in the future and wanted to know if there was a more generic ChangeEvent which I could use for all
I have done this now, and for my purpose it seems fine
I had to change the struct to a class ass otherwise it meant I would have to new up a new object anytime something was changed
private void TextChanged(ChangeEvent<string> evt)
{
OldData.Text = evt.previousValue;
Data.Text = evt.newValue;
//as a struct:
//Data = new Data() { Text = evt.newValue, myEnum = Data.myEnum };
using (var newEvent = ChangeEvent<Data>.GetPooled(OldData, Data))
{
newEvent.target = this;
SendEvent(newEvent);
}
}