Search code examples
visual-studio-2008compact-frameworkscreen-resolution

Why do all GUI elements suddenly have twice their height and width?


I developed a control that positions a child element based on its Height. Everything works fine when I test it with the emulator, but on the actual device I have to use (Height * 2) to position it correctly.

In addition to this, icons that are 16x16 look fine with the emulator, but are scaled up terribly on the actual device, because the PictureBox suddenly has twice its height and width.

This wired phenomena happens on all devices I tested the application with.

Is that somehow related to the mobile device's screen resolution? What is cause for this problem and how can I fix this?

EDIT: Here is the code that calculates the next location of the element. The commented out code works on the devices, but not the emulator.

    private Point GetNextUserControlLocation(Control control)
    {
        var numberOfControls = Controls.Count;
        if (numberOfControls < 1)
            return new Point(1, 1);

        var latest = Controls[numberOfControls - 1];
        var x = latest.Location.X;
        // var y = latest.Location.Y + control.Height * 2 + 1;
        var y = latest.Location.Y + control.Height + 1;

        return new Point(x, y);
    }

Solution

  • Sounds like the issue is that your device's resolution is different from the emulators, even if the screen size is the same. I know that some of the newer devices (from Motorola particularly) have 640x480 screen res rather than the standard 320x240.

    There are two things you should do to resolve it. For positioning that you do at the design level, simply set your Form's AutoScaleMode property to System.Windows.Forms.AutoScaleMode.Dpi. This is cause the form to use DPI as a baseline to correctly position everything laid out in the designer. If your device's screen res is 2x the emulators, you'll see that all heights and widths of child controls are doubled on the device.

    For manual positioning, you'll have to do a bit of math. The .NET CF uses 96 DPI as the baseline for measuring, so when the screen DPI = 96, then 1 unit = 1 pixel. You can correctly determine how much to scale the units by comparing your device's DPI by 96.

    To calculate your device's screen DPI, use the following P/Invokes:

        [DllImport("coredll.dll", EntryPoint = "GetDC", SetLastError = true)]
        public static extern IntPtr GetDC(IntPtr hWnd);
    
        [DllImport("coredll.dll", SetLastError = true)]
        public static extern int GetDeviceCaps(IntPtr hDC, int nIndex);
    
        [DllImport("coredll.dll", SetLastError = true)]
        public static extern void ReleaseDC(IntPtr hDC);
    
        private const int LOGPIXELSX = 88;
        private const int LOGPIXELSY = 90;
    

    Here's an excerpt of a helper function to get the scalar value in the X and Y directions:

                //get handle to desktop
                IntPtr hDC = GetDC(IntPtr.Zero);
    
                try
                {
                    if (hDC != IntPtr.Zero)
                    {
                        int logPixelX = GetDeviceCaps(hDC, LOGPIXELSX);
                        int logPixelY = GetDeviceCaps(hDC, LOGPIXELSY);
    
                        _scaleX = (float)logPixelX / 96.0f;
                        _scaleY = (float)logPixelY / 96.0f;
                    }
                }
                finally
                {
                    if (hDC != IntPtr.Zero)
                        ReleaseDC(hDC);
                }
    

    Wrap that into a class, add a ScaleX and ScaleY function, and you can use the values of scaleX and scaleY to adjust your positioning. Your positioning code can now look like:

        var y = latest.Location.Y + control.Height + UICoordinateScalar.ScaleY(1);
    

    Hope that helps!