0

im developing a WPF application targeting .NET Framework 4.7.1. I disabled dpi scaling for the main window by applying a scale transform to the main window (see attached property code below).

This works well for the main window but it does not work for any other windows created by the application (like ContextMenus or other Popups). I tried all answers from this question to disable DPI awareness for the application:

  • Added the [assembly: System.Windows.Media.DisableDpiAwareness] attribute.
  • Created an app.manifest file and disabled dpi scaling in there.
  • Changed the compatibility settings of the executable to disable dpi scaling.

but none of them worked for me.

The only solution I can think of is to apply the trick with the LayoutTransfrom to the popup window as well. To do that, I tried the following:

I created an attached property that applies the layout transform to a given window:

public class DPIScalingBehavior
{
    private class Win32
    {
        [DllImport("gdi32.dll")]
        public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
        public enum DeviceCap
        {
            VERTRES = 10,
            LOGPIXELSX = 88,
            LOGPIXELSY = 90,
            DESKTOPVERTRES = 117,
        }
    }

    public static readonly DependencyProperty DisableDPIScalingProperty =
        DependencyProperty.RegisterAttached(
            "DisableDPIScaling",
            typeof(bool),
            typeof(Window),
            new PropertyMetadata(false, OnDisableDpiScalingChanged));

    public static void SetDisableDPIScaling(DependencyObject dpo, bool disableDPIScaling)
    {
        dpo.SetValue(DisableDPIScalingProperty, disableDPIScaling);
    }

    public static bool GetDisableDPIScaling(DependencyObject window)
    {
        return (bool)window.GetValue(DisableDPIScalingProperty);
    }

    private static void OnDisableDpiScalingChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs args)
    {
        var disableDPIScaling = (bool)args.NewValue;
        if (disableDPIScaling)
        {
            var window = dpo as Window;
            if (window != null)
            {
                var scaleFactor = GetDPIScaleFactor();

                var scaleTransform = new ScaleTransform(scaleFactor, scaleFactor);

                window.SetValue(Window.LayoutTransformProperty, scaleTransform);
            }
        }
    }

    private static double GetDPIScaleFactor()
    {
        // Monitor DPI
        var g = Graphics.FromHwnd(IntPtr.Zero);
        var desktop = g.GetHdc();
        // System Settings DPI
        var Xdpi = Win32.GetDeviceCaps(desktop, (int)Win32.DeviceCap.LOGPIXELSX);
        return 96d / Xdpi; // 1.25 = 125%
    }
}

Then i created an implicit style in App.xaml that sets this attached property.

<Style TargetType="Window">
   <Setter Property="b:DPIScalingBehavior.DisableDPIScaling" Value="True" />
</Style>

But the style does not get picked up by the popup windows.

Do you have an idea how to disable dpi scaling for a WPF ContextMenu?

Tobias Hoefer
  • 1,058
  • 12
  • 29
  • What do you expect `window.SetValue(Window.LayoutTransformProperty, scaleTransform)`? It won't change that window's size nor its content's size. – emoacht Dec 03 '22 at 13:36
  • Yes it does. It scales the window and all its content. It works perfectly fine when I set the property an the window. But setting it via an implicit style is not working for me. – Tobias Hoefer Dec 05 '22 at 09:01
  • Thanks for enlighting me. I have never succeeded to change window's size by ScaleTransform. Anyway you are talking about ContextMenu but not Window. It's my misunderstanding. – emoacht Dec 05 '22 at 09:41

1 Answers1

0

I could not make the implicit Window style work. My solution for now is to set the property on each ContextMenu explicitly.

To do that, i updated the attached property to be applicable to any FrameworkElement not just on Window:

public class DPIScalingBehavior
{
    private class Win32
    {
        [DllImport("gdi32.dll")]
        public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
        public enum DeviceCap
        {
            VERTRES = 10,
            LOGPIXELSX = 88,
            LOGPIXELSY = 90,
            DESKTOPVERTRES = 117,
        }
    }

    public static readonly DependencyProperty DisableDPIScalingProperty =
        DependencyProperty.RegisterAttached(
            "DisableDPIScaling",
            typeof(bool),
            typeof(FrameworkElement),
            new PropertyMetadata(false, OnDisableDpiScalingChanged));

    public static void SetDisableDPIScaling(DependencyObject dpo, bool disableDPIScaling)
    {
        dpo.SetValue(DisableDPIScalingProperty, disableDPIScaling);
    }

    public static bool GetDisableDPIScaling(DependencyObject dpo)
    {
        return (bool)dpo.GetValue(DisableDPIScalingProperty);
    }

    private static void OnDisableDpiScalingChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs args)
    {
        var disableDPIScaling = (bool)args.NewValue;
        if (disableDPIScaling)
        {
            var frameworkElement = dpo as FrameworkElement;
            if (frameworkElement != null)
            {
                var scaleFactor = GetDPIScaleFactor();

                var scaleTransform = new ScaleTransform(scaleFactor, scaleFactor);

                frameworkElement.SetValue(FrameworkElement.LayoutTransformProperty, scaleTransform);
            }
        }
    }

    private static double GetDPIScaleFactor()
    {
        // Monitor DPI
        var g = Graphics.FromHwnd(IntPtr.Zero);
        var desktop = g.GetHdc();
        // System Settings DPI
        var Xdpi = Win32.GetDeviceCaps(desktop, (int)Win32.DeviceCap.LOGPIXELSX);
        return 96d / Xdpi; // 1.25 = 125%
    }
}

Disable DPI Scaling for a ContextMenu:

<ContextMenu b:DPIScalingBehavior.DisableDPIScaling="True">
Tobias Hoefer
  • 1,058
  • 12
  • 29