This is a back-port of the bug fix from .NET 4.8 (BUG 378542) and its original description:
Adjustments for controls for smooth operation in HighDPI PerMonitorV2 mode are usually straight-forward, since the basic infrastructure for this is already implemented in the control class.
Basically, the problem has to be solved that if a form is moved from one monitor to another with a different pixel density, the size of the form and also the size of all controls placed on it have to be adapted to the new resolution.
One starting point here is the fact that the font size of the form is adjusted according to the new Monitor DPI value. Since the font property is an ambient property, the font size changes propagate to all hosted controls so that they are also affected by the font size changes, and for which the display has already been adapted to the changed pixel density. The trigger for the font adjustment is the method WmDpiChangedBeforeParent in the control class.
Secondly, further DPI-dependent adjustments must be made, and the RescaleConstantsForDpi method, which can be overwritten, provides the second approach.
Challenges with ToolStrip based Controls
All classes inherited from ToolStripItem are not controls, but components, so they are not included in the normal method inheritance chain, or in other words: RescaleConstantsForDpi is not invoked for component based classes. ToolStripDropDown, which forms the container for an opened pulldown menu/pulldown ToolBoxButton, is only instantiated if needed and is not part of the normal control tree of a Forms/Control container. For this reason, this control does not implicitly gets its overridden RescaleConstantsForDPI called either.
Solution
In order to get the right point in time within the normally excluded control classes when the DPI change (the transition from one monitor to another) occurs, we enable ToolStrip itself in a way other controls/components can register at the ToolStrip to receive a kind of Rescale event. This is done with the new field variable rescaleConstsCallbackDelegate. Because ToolStrip itself is placed on a form or container, its RescaleConstantsForDPI is called accordingly, so it performs the Invoke based on this delegate, triggering all listening components. However, this event must be initially wired for all of the ToolStripItem-based components it is hosting, and this is done in ToolStripItem's SetOwner method.
This means that ToolStrip, all controls derived from ToolStrip and also all components derived from ToolStripItem are triggered when the DPI changes and the required size adjustments are due. A problem remains with the ToolStripDropDown containers, which host only ToolStripDropDownItem-based components, but which again are derived from ToolStripItem. The latter enables us to intervene in the rescaling event, determine if the affected ToolStripItem "has" a DropDown, and then make any necessary rescaling here (and in any subordinate) as well. This happens in the ProcessDropDownItems method of the ToolStripDropDownItem class, which in turn is called by ToolStrip_RescaleConstants (and which in turn is triggered by the new delegate in ToolStrip and the wiring of ToolStripItem).
Code changes in all remaining classes then serve as far as possible to make the actual size adjustments for the new DPI value.
@KlausLoeffelmann, when you get the opportunity, can you update this thread with your progress here?
PR is waiting in a private branch, and is ready to go, but needs testing beforehand. For this to make easier for the CTI Team, I would rather have #135 in first, which has the same state. We need to re-prioritize internal tasks to see, when the right time is to kick this off.
Where are we on this port?
Most helpful comment
PR is waiting in a private branch, and is ready to go, but needs testing beforehand. For this to make easier for the CTI Team, I would rather have #135 in first, which has the same state. We need to re-prioritize internal tasks to see, when the right time is to kick this off.