Table of Contents

Class UIElementBinderRegistry

Namespace
PopcorningGuineaPig.Engine.Scenes
Assembly
PopcorningGuineaPig.Engine.dll

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 null and 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:

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

elementType Type
binder IPropertyBinder

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

elementType Type
binder IPropertyBinder

Returns

bool

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

element object
propertyName string
value object

Returns

bool

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

value object

Returns

object