I'm developing a WinForms application along with a large number of developers, who have different screen configurations, including different DPI settings. So our application can scale we've set all of the forms to auto-scale using AutoScaleMode = AutoScaleMode.Font
, and having the corresponding AutoScaleDimensions
set, depending on the setting the form was developed on.
With these configurations WinForms scales properly across different screens, the problem is that when a developer with a different screen configuration opens the form in designer mode, Visual Studio scales the controls by actually modifying the auto-generated code to contain the 'new' dimensions of the objects, and by also modifying the and AutoScaleDimensions
property to match the new monitor.
This behavior would be OK, if I hadn't several developers working on the same form. If this happens, and those developers have different screen configurations, a whole lot of conflicts will be generated when merging the changes using our VCS, not to say that I would have values stored for different screen resolutions, hence messing up UI.
To solve this I tried turning auto-scaling off by setting AutoScaleMode = AutoScaleMode.None
and implementing a custom designer for my controls. This designer only saved object's sizes in one resolution and then, by shadowing the Size
property and depending on the current DPI, returned a scaled value. I did this only to find out that VS designer generates the code out of what the custom designer says, and not the values actually stored in the object.
So, does anyone know how to go around this issue?
Well, seems like I found an answer myself.
The solution is to create a custom designer that tricks the designer into believing that it's receiving a scaled value when showing the form, but then when generating the code giving him the unscaled value, by the means of the ShadowedProperties
array of the custom designer, as explained in the code example below:
// This is the custom designer
public class ScalingDesigner : ControlDesigner
{
public ScalingDesigner(){};
// Say we want to correct the Size property
public Size Size
{
get
{
// When the serializer asks for the value, give him the shadowed one
return (Size)this.ShadowedProperties["Size"]
}
set
{
// When setting the value, assign the standard-DPI based value to the one the serializer would use
this.ShadowedProperties["Size"] = value;
// ... perform all the DPI scaling logic ...
// Then assign a scaled value to the displayed control
this.Control.Size = new Size(scaledWidth, scaledHeight)
}
}
// Associate the shadowed values
public override void PreFilterProperties(IDictionary properties)
{
base.PreFilterProperties(properties);
properties["Size"] =
TypeDescriptor.CreateProperty(
typeof(ScalingDesigner),
(PropertyDescriptor)properties["Size"],
new Attribute[0]);
}
}
// ... and on your control ...
[Designer(typeof(ScalingDesigner))]
public class MyControl : Control
{
// ... whatever logic you want to implement ...
}