Class UIElementBinderRegistry
Static lookup table mapping UI element Type to an IPropertyBinder that knows how to write its bindable PGPSCN properties without runtime GetProperty(string) reflection at the dispatch site.
Replaces the legacy element.GetType().GetProperty(name).SetValue(...)
chain previously used in ApplyBinding(object, string, object, BindingDefinition) and
SceneLoader's binding setup. That chain was both:
- silently broken (most bindable fields were private — reflection returned
nulland the write skipped), and - not trim-safe (the trimmer could not prove which properties on which types remained reachable).
The registry fixes both problems: the engine self-registers its built-in element types with typed setter maps (so PGPSCN bindings actually fire), and game-defined custom UI elements opt in by passing their own IPropertyBinder via Register(Type, IPropertyBinder).
Two registration paths:
- Option A — RegisterReflective<T>() creates a
reflection-based binder whose
typeof(T).GetProperty(name)site is statically pinned byT's DynamicallyAccessedMembersAttribute annotation. Cheap; fine when the target type has public properties for its bindable surface. - Option B — Register(Type, IPropertyBinder) accepts a hand-authored PropertyBinder<T> with explicit setters. Zero reflection at runtime. Used for game-defined custom UI.
public static class UIElementBinderRegistry
- Inheritance
-
UIElementBinderRegistry
- Inherited Members
Methods
Clear()
Removes every entry from the registry. Intended for test
setup/teardown only — production code should register once at
startup, before Game.RunGame.
public static void Clear()
Register(Type, IPropertyBinder)
Register an explicit IPropertyBinder for
elementType. Used for the Option B path where a
game-defined UI element supplies a hand-authored PropertyBinder<T>.
Idempotent: registering the same IPropertyBinder instance twice is a no-op. Registering a different instance under an already-used Type throws.
public static void Register(Type elementType, IPropertyBinder binder)
Parameters
elementTypeTypebinderIPropertyBinder
RegisterReflective<T>()
Register a reflection-backed binder for T.
The DynamicallyAccessedMembersAttribute annotation on
the type parameter flows through every call site to the
typeof(T).GetProperty reflection inside
UIElementBinderRegistry.ReflectivePropertyBinder<T>, so the trimmer keeps
T's public properties.
public static void RegisterReflective<T>() where T : class
Type Parameters
T
TryGetBinder(Type, out IPropertyBinder?)
Look up the binder for elementType. Returns
true and the binder when registered. Returns false when
no binder is registered for the type (caller logs a diagnostic).
public static bool TryGetBinder(Type elementType, out IPropertyBinder? binder)
Parameters
elementTypeTypebinderIPropertyBinder
Returns
TrySet(object, string, object)
Dispatch helper used by ApplyBinding(object, string, object, BindingDefinition) and
SceneLoader's binding setup. Looks up the binder for
element's runtime Type, unwraps the
PGPSCN-deserialized value (JsonElement → CLR
primitive), and dispatches. Returns true if the property was
set; false if no binder is registered or the binder did not
recognize the property.
public static bool TrySet(object element, string propertyName, object value)
Parameters
Returns
UnwrapValue(object)
Unwrap PGPSCN deserialization artifacts to CLR primitives. After Deserialize<TValue>(string, JsonSerializerOptions) runs against PGPSCN files, primitive values land as JsonElement, not as string / int / etc. Setter authors should not have to know this — the registry unwraps before dispatch so binders work against ordinary CLR types.
Localization source values (already string) pass through unchanged.
public static object UnwrapValue(object value)
Parameters
valueobject