I too develop for industrial automation equipment, but in my case I target 1080p
displays but also want a laptop to be able to demo the software at a lower resolution (e.g. 1440x900
).
When run on my development machine with a screen resolution on 1920x1080 it looks a certain way but when I run it on my target machine 1024x768 it changes. Mostly everything grows.
What you will find is that for Microsoft Windows screen resolutions all default to 96dpi
no matter what size the monitor is. For example I have a 22" monitor running at 1080p
, I can also connect the same PC to my 55" TV at 1080p
also. Even though the 22" monitor (using a tape measure) would physically fit in a 2x2 grid in the same size as my 55" TV the output bitmap of 1920x1080
for the two displays is exactly the same if I took a screen grab.
So all monitors are 96dpi
which is now starting to cause problems when you have the Retina type displays of 1920x1080
on a 10" tablet, and more frequently the "Make text and other Items larger" settings are available in the display settings to tweak away from the 96dpi to cope with these situations. WPF applications will scale properly but Win32 apps look a bit fuzzy.
In your case you have 96dpi
for both 1024x768
and 1920x1080
meaning the vector graphics will translate to the same pixels size on both displays. The difference being that the maximum form size is only 1024x768
on one monitor and 1920x1080
on the other. If you divide the display using a Grid
then each column has less pixels on the smaller resolution display meaning any shapes (knowing that the shapes on both monitors will need the same width in pixels) makes a particular shape take more proportion of the column.
Three different Solutions
- On your
1920x1080
development PC then make your WPF application window a fixed size of 1024x768. Your deployed app will then look exactly like your development machine when it is Maximised
to full screen
<Window x:Class=" ...
xmlns=" ....
Width="1024" Height="768" >
....
</Window>
- Surround all controls with a
Viewbox
and set the Stretch
attribute to UniformToFill
. This will allow your controls to report their size (during the Measure
phase of the WPF layout process) and then scale uniformly the controls to fit in the available size.
<Window x:Class=" ...
....
<Viewbox Stretch="UniformToFill">
<Grid>
....
</Grid>
</Viewbox>
</Window>
- Only scale a proportion of the screen leaving some areas (e.g. text for buttons) rendering at
96dpi
. You could only enclose parts of the display again with a Viewbox
or alternatively use the underline function of the Viewbox
which is available to any control via the LayoutTransform
dependency property.
<Window x:Class=" ...
LayoutUpdated="Window_LayoutUpdated" >
....
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20*"/>
<ColumnDefinition Width="60*"/>
<ColumnDefinition Width="20*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20*"/>
<RowDefinition Height="60*"/>
<RowDefinition Height="20*"/>
</Grid.RowDefinitions>
<Canvas Grid.Row="1" Grid.Column="1">
<Canvas.LayoutTransform>
<ScaleTransform x:Name="PageScale" ScaleX="1" ScaleY="1"/>
</Canvas.LayoutTransform>
<Rectangle Width="700" Height="700" Fill="Red"/>
</Canvas>
</Grid>
</Window>
Then in the code-behind
private void Window_LayoutUpdated(object sender, EventArgs e)
{
if ((ActualWidth != 0) && (ActualWidth < 1900))
{
var scale = ActualWidth / 1920.0;
PageScale.ScaleX = scale;
PageScale.ScaleY = scale;
}
else
{
PageScale.ScaleX = 1.0;
PageScale.ScaleY = 1.0;
}
}
Here we have a callback when the windows size has changed (via the handler for LayoutUpdated
of the Window
class). We see if the width of the window is less than the design time window size and scale appropriately the Canvas
UI element only, leaving the other controls scaling to 100%. LayoutTransform
can be used on most WPF UI controls, not just Canvas
.
Solutions 2 & 3 provide the scaling but they do have some side-effects, namely if you have developed a diagram where a connector line is drawn between two shapes with a width of 1 pixel then this is renders correctly when the scaling is 1:1, but anti-aliasing causes the single pixel line to show solid 1 pixel in some cases but a dithered in others when the scaling is significantly different. There are advanced options that require lots of research (e.g. RenderOptions
) and an understanding of the rendering process to eliminate these effects.