UBind is a value binding component for Unity, which is used to quickly realize the association binding between UI and logical data.
Official QQ Group:1070645638
-
- 2.1. Component
- 2.1.1. Common Component Binder
- 2.1.2. Property Binder
- 2.1.3. Type Binder
- 2.2. Programming
- 2.2.1. MonoBehaviour automatic binding
- 2.2.2. MonoBehaviour manual binding
- 2.2.3. Manually bind any objects
- 2.1. Component
-
- 3.1. Passive callback mode
- 3.2. Active update mode
-
- 4.1. Attribute
- 4.1.1. Bind Value Attribute
- 4.1.2. Bind Type Attribute
- 4.2. Data Container
- 4.3. Data Binder
- 4.4. Data Converter
- 4.5. Component Binder
- 4.6. Bind Updater
- 4.7. Bind Map
- 4.1. Attribute
-
- 6.1. Custom Data Binder
- 6.2. Custom Component Binder
- 6.3. Custom Binder Editor
- 6.4. Custom Data Converter
- Support two-way binding
- Support multiple data sources and multiple target objects
- Support any property and field binding of any component
- Support automatic type conversion when the data source and target data type are different
- Provide a large number of commonly used property binders for built-in components
- Binding of properties and fields of any runtime object
- Provide data containers to divide data usage domains by groups
- Extensible custom data binder, custom type converter
You can use the built-in binder component to directly set the properties of common UI components as data sources or data targets:
If you need to operate a custom component, or a component that does not provide a dedicated binder, you can use the more general PropertyBinder, and you can bind any property field of any component with a simple setting:
If you need to bind a data class to the corresponding UI, you only need to use TypeBinder, specify the assembly and class name of the data class, and then bind the corresponding properties and fields to the UI in turn, and then provide the data logic In the code, the code binds the data source:
public class UBindSamplePlayerData
{
public string Name;
public int Exp;
public float Stat;
public float PlayTime;
}
public class UBindSampleGameManager
{
public UBindSamplePlayerData Player;
public void Awake()
{
Player = new UBindSamplePlayerData() {Name = "Player",};
UBind.BindSource("PlayerData", Player);
}
}
By inheriting BindableMonoBehaviour, the ability to automatically handle the binding and unbinding of properties and fields is obtained.
Use BindValue Attribute to mark the need to bind basic type data:
public class ExampleMonoBehaviour : BindableMonoBehaviour
{
[BindValueSource("ValueKey")]
public string ValueSource;
[BindValueTarget("ValueKey")]
public string ValueTarget;
}
Use BindType Attribute to mark the need to bind class and structure data:
public class ExampleData
{
public string Value;
}
public class ExanokeMonoBehaviour : BindableMonoBehaviour
{
[BindTypeSource("TypeKey")]
public ExampleData DataSource;
[BindTypeTarget("TypeKey")]
public ExampleData DataTarget;
}
For custom MonoBehaviour objects that cannot be inherited, you can add the following code in the OnEnable / OnDisable method to manually call the binding and unbinding interfaces of BindMap, and you can get the same as automatic binding effect:
public class ExanokeMonoBehaviour : MonoBehaviour
{
[BindValueSource("ValueKey")]
public string ValueSource;
[BindValueTarget("ValueKey")]
public string ValueTarget;
public void OnEnable()
{
UBind.RegisterMap(this);
}
public void OnDisable()
{
UBind.DeRegisterMap(this);
}
}
By calling various overloaded interfaces of UBind, you can bind values, attributes, fields, class objects, and structure objects at runtime. If you need to bind and unbind at a specific time, you need Cache the returned DataBinder object by itself:
public class ExampleData
{
public string Value;
}
public class ExampleClass
{
public string ValueSource;
public string ValueTarget;
public ExampleData TypeSource;
public ExampleData TypeTarget;
private DataBinder _runtimeValueSourceBinder;
private DataBinder _runtimeValueTargetBinder;
private DataBinder _runtimeTypeSourceBinder;
private DataBinder _runtimeTypeTargetBinder;
public void BindTest()
{
_runtimeValueSourceBinder = UBind.BindSource<string>("ValueKey", () => ValueSource);
_runtimeValueTargetBinder = UBind.BindTarget<string>("ValueKey", value => ValueTarget = value);
_runtimeTypeSourceBinder = UBind.BindSource("TypeKey", TypeSource);
_runtimeTypeTargetBinder = UBind.BindTarget("TypeKey", TypeTarget);
}
public void UnBindTest()
{
_runtimeValueSourceBinder.UnBind();
_runtimeValueTargetBinder.UnBind();
_runtimeTypeSourceBinder.UnBind();
_runtimeTypeTargetBinder.UnBind();
}
}
It should be noted that in the above example, the data source and the data target each generate a binder, which is the default and recommended way to use it in order to support multiple data sources and multiple data targets. However, the binding of the value class (RuntimeValueBinder<T>) can also be used to simplify the call in the following way, which will return the data source binder and data target binder at the same time:
public class ExampleClass
{
public string Source;
public string Target;
private DataBinder _sourceBinder;
private DataBinder _targetBinder;
public void BindTest()
{
(_sourceBinder, _targetBinder) = UBind.Bind<string>("Key", () => Source, value => Target = value);
}
public void UnBindTest()
{
_sourceBinder.UnBind();
_targetBinder.UnBind();
}
}
OnValueChangedTrigger -> DataBinder(Source) --> DataConverter --> DataBinder(Target)
The default recommended mode, at this time the NeedUpdate property of DataBinder is false, the data is refreshed by the value change callback provided by the data source, notified to the data source binder, and broadcast to all The data target binder is then converted into target data bears through the data converter.
BindUpdater -> DataBinder(Source) --> DataConverter --> DataBinder(Target)
When the data source does not have the ability to actively trigger data changes, the framework provides a unified update cycle to actively detect the value changes, but this mode will inevitably cause performance loss. It should be used as little as possible, which is more suitable for rapid prototyping. In this mode, the NeedUpdate property of DataBinder is true, and the AddListener() and RemoveListener() methods need to be implemented to achieve specific callback registration and cancellation . The framework triggers the UpdateSource() method of all data sources in the BindUpdater component on a periodic basis to achieve data change detection and data update broadcasting.
It is used to mark attributes and fields that need to be bound in a class that has the ability to handle binding relationships. It is only recommended to bind common basic data types. The marked object will dynamically create a RuntimeValueBinder for processing.
Unlike BindValueAttribute, it is used to mark custom class and structure type objects, and RuntimeTypeBinder is dynamically created for processing.
The data container is used to maintain a set of Data Binder, which can contain multiple data sources and data destinations. Each data container is independent of each other.
The data binder is used to bind data to specific objects and its properties, and can be used to receive and broadcast data.
Property | Description |
---|---|
Target | Data target object. |
Context | The Key value of the data container, the default is Default, if there is no special requirement, you can keep the default setting. |
Key | The Key value of the target data is the unique identification of the data in a single data container. |
Direction | Data transfer direction, Source identifies the current binder as the data source, as the data broadcast sender, and Target identifies the current binder as the data receiver. |
UpdateType | For target objects that do not provide data change callbacks, the data adopts the Update mode to detect whether changes have occurred. At this time, the update timing needs to be specified. |
When the data types of the data source and the data target are different, the data converter corresponding to (sourceType, targetType) will be used to try the conversion.
The implementation of the default converter CommonConverter is as follows, which can be replaced by modifying DataConverter.Default:
Convert.ChangeType(object data, Type type);
You can register specific types of custom converters in advance using the following interfaces as needed:
DataConvert.Register(Type sourceType, Type targetType, DataConverter dataConverter);
The Unity Component version of the data binder needs to realize generic binding with a specific binder to realize the binding of component data.
Based on the MonoBehaviour life cycle implementation, it is used to uniformly maintain the update cycle of DataBinder that needs to actively update data.
Used to cache all the binding structure information of the objects marked by BindAttribute.
Component | Binding object type | Default binding property | Binding property type |
---|---|---|---|
Text Binder | Text | text | string |
Text FontSize Binder | Text | fontSize | int |
Text Format Value Binder | Text | text | float |
InputField Binder | InputField | text | string |
Slider Binder | Slider | value | float |
Scrollbar Binder | Scrollbar | value | float |
Dropdown Binder | Dropdown | value | int |
Dropdown List Binder | Dropdown | options | List<Dropdown.OptionData> |
CanvasGroup Binder | CanvasGroup | alpha | float |
Toggle Binder | Toggle | value | bool |
Color Binder | Graphic | color | Color |
Image Binder | Image | sprite | Sprite |
Image FillAmount Binder | Image | fillAmount | float |
RawImage Binder | RawImage | texture | Texture |
Property Binder | Component | ||
Type Binder | Component |
Implement a runtime data binder for a specific object and property by inheriting DataBinder:
public class RuntimeImageFillAmountBinder : DataBinder<Image, float>
{
public override float Value
{
get => Target.fillAmount;
set => Target.fillAmount = value;
}
}
If you need to use it as a component, you need to additionally implement the corresponding ComponentBinder:
[AddComponentMenu("Data Binding/Image FillAmount Binder")]
public class ImageFillAmountBinder : ComponentUpdateBinder<Image, float, RuntimeImageFillAmountBinder>
{
}
If the component needs to maintain the basic component style and extend custom properties and styles, it needs to implement the corresponding ComponentBinderEditor, which is generically associated with the binder component and the runtime binder:
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(ImageFillAmountBinder)), UnityEditor.CanEditMultipleObjects]
public class ImageFillAmountBinderEditor : ComponentBinderEditor<Image, float, RuntimeImageFillAmountBinder>
{
}
#endif
If you need to transfer different data types and the data is not what Convert.ChangeType() can handle, you need a custom data converter that implements this type conversion:
public class CustomDataConverter : DataConverter
{
public object To(object data, Type targetType)
{
object result;
// To do something...
return result;
}
}
Before you need to use these binders, such as when the game is initialized, register a custom binder:
var sourcetType = sourceData.GetType();
var targetType = targetData.GetType();
DataConverter.Register((sourceType, targetType), new CustomDataConverter());
- OnValueChanged is only available for data sources.
- All parameters must be configured in the editor mode. Modifying the parameters during operation is invalid.
- TypeBinder temporarily only supports simple data classes with single-level property.
- Although the component supports automatic conversion of different data types, it is still recommended to pass the same data type directly to optimize performance.
- The data between multiple data sources will not be synchronized. If the data sources also need to be synchronized, the Direction must be set to Both.
- PropertyBinder and TypeBinder rely on reflection to achieve, but due to the property encapsulation of some native components, direct reflection to modify the field does not refresh the display. It may be necessary to manipulate the corresponding properties or customize a dedicated binder.
- TMP Text Binder
- TMP Text Format Value Binder