Home / WPF / Create DPI-aware and resolution-aware WPF applications

Create DPI-aware and resolution-aware WPF applications

In this article i will analyze two techniques that can help you on writing a DPI-aware application and how to scale it properly.
There are two common scenarios which force you to make your application to scale to fit the screen:

  • Different resolution
  • Different DPI

In fact it’s very common now to find notebooks with 15” display with 1920×1080 resolution and DPI set to 125%, or with a resolution 1366×768, 1600×900, etc.
The biggest problem is that the application must look good in all the cases, and of course we can’t waste months to adapt the application for every screen.

DPI aware application

WPF DPI awareness
What happens to the screen area if I set 125%?
The DPI goes from 96 to 120, the screen resolution decrease of 25%, because the size of the pixel get increased, and all the controls and texts gets bigger, invalidating the layout.
Is this an expected behavior? I mean, the layout of the application can support this kind resolution decrease, or is it going to break the layout?
If the layout gets broken, you can just scale your application by returning to the native pixels with a simple class:

public class DpiDecorator : Decorator
{
    public DpiDecorator()
    {
        this.Loaded += (s, e) =>
        {
            Matrix m = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice;
            ScaleTransform dpiTransform = new ScaleTransform(1 / m.M11, 1 / m.M22);
            if (dpiTransform.CanFreeze)
                dpiTransform.Freeze();
            this.LayoutTransform = dpiTransform;
        };
    }
}

And to use it you can just wrap your window in the DpiDecorator:

<Window x:Class="DPIHelper.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DPIHelper"
        Title="MainWindow" Height="350" Width="525">
    <local:DpiDecorator>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>       
            <TextBlock Text="With DPI decorator" FontSize="20"/>
        </Grid>
    </local:DpiDecorator>
</Window>

This will make your application usable even with different DPI, without much effort.

Scaling for a different resolution

WPF scaling app resolution
Scaling for a different resolution presents some different scenarios than before, because the DPI are 96 and the controls and text will not scale.
There are some different techniques to get an application that adapts to the size, like:

  • setting the font size with settings,
  • avoid to use pixels, margins, paddings and use only relative positioning,
  • use grids, dockpanels and stackpanels, etc…

There is plenty of articles about those strategies, but what if the layout is static and we are making a large use of margins, paddings and pixel sized controls ?
In this case the approach is to optimize the application for a single resolution, then wrap everything in a viewbox and hardcode the resolution. With this method you meet both font scaling and maintenance of the layout, but only for a predetermined width/height ratio.

<Window x:Class="DPIHelper.AutoScaledWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="AutoScaledWindow" Width="816" Height="638" Background="red">
    <Viewbox>
        <Grid Width="800" Height="600" Background="White">
            <Button Width="100" Height="50" Content="Button" Margin="10,10,0,0" FontSize="20" HorizontalAlignment="Left" VerticalAlignment="Top"/>
            <Button Width="100" Height="50" Content="Button" Margin="0,0,10,10" FontSize="20" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
        </Grid>
    </Viewbox>
</Window>

As you can see by running the code, the application resize itself with a Uniform Stretch, so it keeps the 4:3 ratio (800 x 600) and it presents red bands if it gets resized on different ratio, like 16:9 or 16:10.

Sample code

You can download the sample application here: https://github.com/mesta1/DPIHelper

6 comments

  1. Pandurang Kumbhar

    It will not work properly you have to set following configuration in app.mainfeist file

    false

    • You should probably read the full article, then put a real world example, before commenting that it do not work and put a link to your blog that contains just a copy-paste of an msdn article.

  2. Worked fine for me, thanks

  3. I used this a bit differently. Some of our users needed to simply adjust the display size of our app on their screen. The app was targeted for a PC but was too small for some tablet users. So we set the ScaleTransform values (probably from a user setting ultimately). All windows in the app are WindowStyle=”None” BorderThickness=”0″ ResizeMode=”NoResize” with a visual tree:

    Window
    Border
    Grid

    We add the DpiDecorator around the grid to get everything inside the grid scaling properly and scale the Window with the same scale factor. Seems to work. We have not implanted this on all Windows yet but it looks promising.

    P.S. I’m a mild-mannered mechanical engineer just masquerading as a programmer 😉 . There may very well be a better way to accomplish this. If so, would appreciate the input.

  4. One more comment. In our app windows are different sizes. Larger windows could be too large due to scaling. We set a second setting to the max screen scale factor for the windows being loaded then used the min of what the user entered as a setting and the max in DPIHelper class. Not very elegant but works.

Leave a Reply to Jim Judge Cancel reply

Your email address will not be published.