Can't Interact with Elements at Right and Bottom Edges of DPI-Scaled WPF Window

182 Views Asked by At

I'm attempting to finalise high DPI support for an Office COM Add-In running .NET Framework 4.6.2. I'm following the principles outlined by Microsoft for handling DPI Scaling in Office. The only approach that can be used for Office add-ins is to set the DPI awareness per-thread, as the typical manifest and startup solutions aren't available.

The form I'm scaling is a Forms window hosting a WPF UserControl in an Element Host. This was proposed as a working solution and I've observed it being used successfully in demo code provided by Add-In Express.

As recommended by Microsoft, I'm opening my form after setting the thread DPI context to Per Monitor. Using this, the demo code provided above by Add-In Express is being used to apply a LayoutTransform to the UserControl, and scale all elements to the correct size.

private void ApplyScale(int dpi)
{
    var source = (HwndSource) PresentationSource.FromVisual(this);
    if (source is null) return;

    var wpfDpi = 96 * source.CompositionTarget.TransformToDevice.M11;
    var scaleFactor = dpi / wpfDpi;

    var scaleTransform = Math.Abs(scaleFactor - 1.0) < 0.001 ? null : new ScaleTransform(scaleFactor, scaleFactor);
    this.SetValue(LayoutTransformProperty, scaleTransform);
}

This is working as expected, and the window is successfully scaling on each monitor with differing DPI, increasing the size of both window and contents to the correct size.

The issue arises when I attempt to interact with the scaled content. On monitors above 100% scaling, a percentage of the right and bottom edges of the window cannot be interacted with correctly. I have Save and Cancel buttons at the bottom right corner of the form, and when mousing over them the hover event will flicker, but they cannot be clicked normally. This is also true of a ListView featured in the form.

I can confirm the thread is definitely set to Per Monitor awareness, and the visual scaling is working perfectly. No elements are overlapping, and the size of the area that can't be clicked increases with scale.

I'm two days into debugging this and have attempted everything I can think of. What could be causing this?

1

There are 1 best solutions below

0
On

TLDR: Construct a WPF UserControl before a Dispatcher is created for the UI thread.


We were experiencing the same issue when a WPF view was placed inside an ElementHost in an ADXOlForm, and the LayoutTransform was set in the event handler of the form's ADXDPIChanged event.

The solution proposed in the linked forum thread ("use OnSendMessage instead of Dispatcher") gave me the idea that the issue might have something to do with the Dispatcher being initialized in a "wrong" way. Since WPF uses Dispatcher internally, its presence should not be the problem itself.

If no reference to the UI thread's Dispatcher was stored, then the issue was not present (just like the post said), but it provides essential functionality, so like you mentioned, it would mean extensive rewrite in our case as well. But our project doesn't need the Dispatcher during startup, so I checked if we initialized it after a WPF view was displayed fixed the issue, and it did. Fortunately, it even works if the WPF control is not displayed.

Only a small modification in the startup event handler was required to fix the flickering:

private void ThisAddin_Startup(object sender, EventArgs e)
{
    // ... initialization ...

    var dummyWpfControl = new DummyWpfControl();

    this.Dispatcher = Dispatcher.CurrentDispatcher;
}

I'm not sure why this modification fixes the flickering. My guess is that the WPF control initializes the Dispatcher "correctly". Maybe it adds some tasks with the right priority, but I was not able to verify this theory.