Andrei Smolin

Add-in Express version 9 adds support for multiple monitors in Office

Add-in Express 9 supports a recent change in Office 2016: support of multiple monitors having different DPI settings.

This story started with Windows 10 Fall Creators Update that allowed using different DPIs on different monitors. As said on this page at support.office.com, starting from version 1803, Word, PowerPoint and Visio support multiple monitors having different DPI settings. According to that page, other Office applications will support multiple DPIs in Spring 2018. That’s all. No more official Office-related information is available.

How does the above affect Office extensions? Not affected are non-UI extensions such as Excel UDFs and RTD servers. Affected are COM add-ins that show 1) panes/forms and 2) icons in the Ribbon UI.

What happens if you don’t react to this change? The previous Add-in Express versions are DPI unaware and the panes/forms they create are bitmap-stretched by Windows. Such a stretch makes your form/pane/Ribbon icons blurry.

To avoid bitmap-stretching, you need to get notified about the DPI change and update the UI of your form/pane: resize the form/pane and controls on it, use a different font size, different images (optional) and the form icon.

To do this, you handle the ADXDPIChanged event available on Add-in Express task panes and forms starting from Add-in Express version 9.

This event is raised when the host application has reacted to a DPI change and now it’s your turn to update the UI of your pane. A trivial variant of updating your controls when reacting to a DPI change is the method shown below; you call this method from the ADXDPIChanged event as demonstrated in the code of the sample add-in:

C#

private void UpdateDPI(Control root, int dpi, float dpiFactor)
{
    foreach (Control cl in root.Controls)
    {
        cl.Bounds = Rectangle.Round(new RectangleF(cl.Left * dpiFactor, cl.Top * dpiFactor, cl.Width * dpiFactor, cl.Height * dpiFactor));
        if (cl.Controls.Count > 0)
            UpdateDPI(cl, dpi, dpiFactor);
        cl.Font = new Font(cl.Font.FontFamily, cl.Font.Size * dpiFactor, cl.Font.Unit);
    }
}

VB.NET

Private Sub UpdateDPI(ByVal root As Control, ByVal dpi As Integer, ByVal dpiFactor As Single)
    For Each cl As Control In root.Controls
        cl.Bounds = Rectangle.Round(New RectangleF(cl.Left * dpiFactor, cl.Top * dpiFactor, cl.Width * dpiFactor, cl.Height * dpiFactor))
        If cl.Controls.Count > 0 Then UpdateDPI(cl, dpi, dpiFactor)
        cl.Font = New Font(cl.Font.FontFamily, cl.Font.Size * dpiFactor, cl.Font.Unit)
    Next
End Sub

Your variant of this method will consider the actual controls used on your forms. Say, you may apply different images/controls or update your controls in a complex way to conform with the current DPI.

You may also want to get some controls hidden when you update them to minimize visual artefacts. You use the ADXBeforeDPIChanged event to hide these controls. In this case, in the code of the ADXDPIChanged event, you make the controls visible again after you update them.

Finally, about icons that you use on your Ribbon controls. If you use a multiple-image icon on your Ribbon control specifying the icon in the Glyph property of the control, Add-in Express 9 retrieves the best suited image when responding to a DPI change.

Below are two screenshots showing the same Add-in Express-based add-in in Excel 2016. Please notice the difference between the images: the controls are updated using the UpdateDPI() method above and the icon in the Ribbon is retrieved from the multi-image .ICO specified in the Glyph property of the Ribbon button. The information shown on the pane is updated using a separate method not shown above; see the sample project.

Find background information here:

Finally, you may want to disable all this DPI-related stuff (e.g. for testing) by switching to the compatibility mode. For this, go to File >Options > General, and select the Optimize for compatibility option. In some Office builds, you can also find it under Display Settings in the status bar. If this option is not available anywhere, it means you are already in the compatibility mode.

Good luck!

Add-in Express based add-in on 100% DPI

Add-in Express based add-in on 200% DPI

15 Comments

  • Taylour says:

    Hi Andrei,

    Thanks for the article and new release. It should be noted that the per-monitor-DPI-aware version of Microsoft Excel is currently only available in the 32-bit insider track version of Office365. There is also a compatibility setting that can be toggled which reverts Excel back to system-DPI-aware mode.

    Here are a few notes and comments:

    1) You said that in the previous versions of AddInExpress were DPI-unaware. I believe that they were system-DPI-aware since DPI awareness is per-process and add-ins run within the parent Office process and so inherit it’s DPI awareness (and previous Office releases were system-dpi-aware).

    2) Have you found a way to reliably scale CheckBoxes and RadioButtons in the CTP? Scaling the font is easy enough but the checkbox/radio-button icons do not seem to scale.

    3) Have you found a way to reliably get scrollbars in a CTP to scale? It looks like EnableNonClientDpiScaling() should help with this, but I was unable to get it to work.

    4) If you want your addin to be compatible with the per-monitor DPI aware version of Excel, you are also responsible for scaling any forms, that the addin displays, when the DPI changes (by handling WM_DPICHANGED in WndProc). As mentioned previously, certain controls like checkboxes, radio buttons, menu bars and scrollbars may be difficult (or not currently possible) to correctly scale. There is, however, a way to show forms and system dialogs (ex. OpenFileDialog and SaveFileDialog) as system-DPI-aware (where bitmap scaling will occur on secondary monitors and the forms will at least appear at the correct size with correct layout) even if the parent Excel process is per-monitor-DPI-aware. For this, see SetThreadDpiAwarenessContext: https://msdn.microsoft.com/en-us/library/windows/desktop/mt748629(v=vs.85).aspx
    Since this is all relatively new, there aren’t many C# examples regarding the correct values to use for DPI_AWARENESS_CONTEXT. This was helpful getting the native methods set up correctly for a C# project: https://github.com/emoacht/WpfBuiltinDpiTest/blob/master/WpfApiTest/DpiHelper.cs
    Note that you should also add this to the DPI_AWARENESS_CONTEXT enum: DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = 18

    Note that you should call SetThreadDpiAwarenessContext with DPI_AWARENESS_CONTEXT_SYSTEM_AWARE before showing a form and then reset it back afterwards by calling SetThreadDpiAwarenessContext with its previous value(which should be DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2).

    Cheers,
    Taylour

  • Andrei Smolin (Add-in Express Team) says:

    Hello Taylour,

    We’ve tested all these and we didn’t find a way to influence the controls that the COMCTL32 library draws: check box, option button, scroll bars, tree view, etc.

    Also, we’ve found that Office applications work in DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, not DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2. This also influences the controls.

    This said, we hope that Office will provide some support for developers who run in DPI-related issues with Windows Forms, WPF, and COMCTL32 controls. As to now, the only way is to develop controls of your own. Some COMCTL32 controls support customizations and this may help you develop custom controls.

    The .NET Framework 4.7.2 started supporting multiple monitors in standalone applications only. Doing this in DLLs is still an open question. It would be helpful if a custom control has a way for developer to specify the current (required) DPI so that the control redraws itself with this DPI. It would be easy to use such controls. Our Ablebits add-ins use custom controls written in this way.

  • Nico says:

    Hello Andrei,

    This comment has nothing to to with the feature presented.
    I’ve been an Office addin developer for 8 years now, and I started from scratch, using raw MS object model. It’s tough as you can guess.
    But even if I’m not using Add-in Express, I have to tell you that your support forum helped me A LOT during those years.
    Last time was one hour ago, with this thread:https://www.add-in-express.com/forum/read.php?FID=5&TID=13528

    I really want to thank you very much. Giving freely these extremely useful pieces of information is absolutely valuable for developers like me.

    Thanks from Switzerland!

    Best regards,

    Nico

  • Andrei Smolin (Add-in Express Team) says:

    Thank you for the kind words, Nico!

  • Anonymous says:

    It’s a not a good idea to change controls manually, but as a Add-in ,that’s what we can do.

  • goldli says:

    according to the function:
    Private Sub UpdateDPI(ByVal root As Control, ByVal dpi As Integer, ByVal dpiFactor As Single)

    what’s the value of dpi and dpiFactor ?

    i have the same problem , and i want to know how to resolve it .

    thanks.

  • Andrei Smolin (Add-in Express Team) says:

    Hello Goldli,

    Those values are provided by the caller which is the Add-in Express code responsible for detecting and dealing with DPI changes.

  • Sam says:

    Hi Andre,

    Thank you for taking the time to write this informative post.

    Our company is also experiencing the issue of our Outlook Addin appearing out of the main application window.

    We upgraded to v9 as per your recommendations and attempted to catch the DPI change event (ADXDPIChanged) and act accordingly. However this event is never raised.

    So on the machine with dual monitors setup, we physically removed one monitor, retested and still had the same issue. So it might not directly be related to having multiple monitors.

    We have a large number of clients using this Addin and cannot ask all users to set their Outlook to compatibility mode. Do you have any other suggestions/recommendation on how we can resolve this?

    This is affecting our production environment as some clients have their outlooks updated via group policy. Any updates or anything else we can try?

  • Andrei Smolin (Add-in Express Team) says:

    Hello Sam,

    Our guys explained that the ADXDPIChanged event occurs in two scenarios: 1) when the Office window is created on a monitor the DPI of which is different from the DPI of the main monitor in the system, and 2) when you move the Office window to a monitor with a different DPI.

    Also, please pay attention to the sample project at https://www.add-in-express.com/creating-addins-blog/panes-problems-solved/; see section Available Downloads on that page.

    > So it might not directly be related to having multiple monitors.

    Sure. This relates to Windows/Office supporting multiple monitors.

  • Sam says:

    Hi Andrei,

    Thank you for the response. Our team has reviewed your provided solution and even implemented the provided code as is… without success.

    As mentioned earlier, the ‘ADXDPIChanged’ event that is mentioned and provided in the code is never fired on the machines that have this issue. So we don’t even get to a point where we can start comparing/modifying DPIs.

    While I do understand that “This relates to Windows/Office supporting multiple monitors.” the provided solution/fix does not work for us and it’s causing us some concern.

    Is there any other advice/suggestion/recommendation you can provide so we can continue using this plugin?

    Thnx again!

  • Andrei Smolin (Add-in Express Team) says:

    Hello Sam,

    I have consulted our developers and they explained that the ADXDPIChanged event occurs in two scenarios: 1) when the Office window is created on a monitor the DPI of which is different from the DPI of the main monitor in the system, and 2) when you move the Office window to a monitor with a different DPI.

    We’ve replaced the download link at https://www.add-in-express.com/creating-addins-blog/panes-problems-solved/ so that it provides a sample project; please check it and let me know whether it helps or not. Thank you in advance.

  • Taylour says:

    For more info on handling DPI scaling in Office apps, check out Microsoft’s article: https://docs.microsoft.com/en-us/office/client-developer/ddpi/handle-high-dpi-and-dpi-scaling-in-your-office-solution

  • Andrei Smolin (Add-in Express Team) says:

    Hello Taylour,

    Thank you very much!!!

  • Gert-Jan van der Kamp says:

    This works when the DPI is changed while Excel is running. I have an issue where my winform controls don’t load correctly.
    Wher can I get the DPI and DPI scaling factors to run this code after initialization?

  • Dmitry Kostochko (Add-in Express Team) says:

    Hello Gert-Jan,

    I think you can try to use the GetDpiForWindow() and other High DPI related Windows API functions in your System.Windows.Forms.Form based dialog:
    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforwindow
    https://docs.microsoft.com/en-us/windows/win32/api/_hidpi/

Post a comment

Have any questions? Ask us right now!