"A code-behind Panel has no Style.Rotate shortcut — rotate it with a PanelTransform"
▸ SYMPTOM
You have a pure C# Panel subclass (not razor markup) that you want to rotate from code — a self-rotating minimap arrow, a spinning indicator, a dial. In razor you could just write style="transform: rotate(Xdeg)" as a plain string, but from a code-behind Panel there is no obvious settable rotation property. There is no documented, easily-discoverable Style.Rotate = <float> shortcut to reach for.
▸ CAUSE
The razor transform string and a code-behind Panel take different paths to the same styling system. Razor parses the CSS string for you; from code you have to construct the transform object yourself. The rotation API exists, but it's reached through a builder object (PanelTransform) rather than a single scalar property, so it isn't discoverable by guessing a Style.Rotate name.
▸ FIX
Build a PanelTransform, add the rotation, and assign it to Style.Transform:
var t = new PanelTransform();
t.AddRotation( 0f, 0f, angleDeg ); // pitch, yaw, roll — roll is the 2D screen rotation
Style.Transform = t;Assign this every frame the angle can change — do it in a Tick() / OnUpdate override on the Panel subclass. This is an established, verified pattern for a self-rotating code-driven UI element (shared UI libraries ship Panel subclasses that rotate themselves every tick this exact way), not a one-off hack.
Finding a private UI API shape without decompiling
When you need a Sandbox.UI API shape you can't find in the docs, search for an existing call site before reverse-engineering it:
- Grep the wider codebase — not just the current project. Shared/house UI libraries live outside the game project, and sibling projects often already have a working
Style.Transform/PanelTransformcall site you can copy. A working precedent beats reverse-engineering a private API every time one exists. - Decompiled-binary archaeology is a last resort. A
grep -afor a symbol name (e.g.set_Rotate/AddRotation) in the engine DLL confirms such members exist, but it doesn't tell you the real C# call pattern — the argument types, or whether it's a standalone property versus something reached only through a builder object — and it's slow. - Cheap fallback when no precedent turns up: deliberately assign a value of the wrong type to the suspected property and let the compiler's type-mismatch error tell you the real type it wants.
▸ WHY IT WORKS
Style.Transform accepts a PanelTransform — a composable transform builder — rather than a scalar. AddRotation appends a rotation to it, and reassigning the whole transform each tick keeps the rotation live as the angle changes. Because it's the same object the engine's own rotating UI elements use, it composes correctly with the rest of the panel's layout instead of fighting it.
- Published