I had to go through and fix scaling on a whole bunch of WinForms programs, at least 20 of them, written by different people in different styles. Lots of user controls, splitters, anchors, docking, panels, custom controls, dynamic layout code, etc. It took a lot of experimenting, but I think I've come up with a good way of handling it.
This answer is what got me started in the right direction: Trying to make WinForms look good in 4K but forms too large after using AutoScaleMode.Dpi?
The problem is the LayoutManager tends to mangle the layout if you have anything slightly complicated. It's really a problem with calling SuspendLayout() and then doing stuff and then ResumeLayout(). (This also plays havoc with anchors when you mix user controls with TabControl. But that's a separate issue.)
The key is to move the AutoScaleDimension and AutoScaleMode properties on the form outside of the SuspendLayout()/ResumeLayout(), so everything will be properly laid out before it scales. Since the form designer orders statements however it wants to, just remove those two lines from the .Designer.cs file, and move them to right after the InitializeComponent() method in the constructor.
The other important part is to set all your user controls AutoScaleMode to Inherit, not font. That way everything gets scaled all at once instead of doing a scale in the user control, and then rescaling stuff when it's added to the form.
Before changing AutoScaleMode on the form, I visit all the controls recursively, and anything which isn't docked and has an anchor other than Top|Left, I temporarily set the anchor to Top|Left, and then restore it back to its original value after setting AutoScaleMode.
Doing those three things gets me about 90% of the way, and almost everything works automagically. Together, these 3 things ensure that everything is scaled one time, all together, and to the same proportions. Any deviation from this pattern seems to lead to chaos in the layout.
It's also a good idea to PInvoke user32.dll SetProcessDPIAware() at the beginning of the application. This seems to allow programmatic scaling to work even at 150%. I haven't had any luck making it behave properly when setting SetProcessDpiAwareness() or SetProcessDpiAwarenessContext(), they both seem to lead to layout chaos no matter what I do.