Menu

#9 CResizer issue on HighDPI-aware dialog

v1.0 (example)
closed
David
None
5
2022-09-08
2021-03-04
No

Hi David,
I'm reporting a bug of CResizer when used on an HighDPI-Aware dialog.

I'm going to let my app be HighDPI-aware; I've changed its manifest and declared it PerMonitorV2-DPIAware. After that, Windows sends the WM_DPICHANGED message to my dialogs.

The bug happens when dragging dialogs, in multi-DPI configurations, between screens with different DPIs. If you don't have an HD screen + a 4K screen (like me), you can emulate the effect by setting a different scaling-percentage on the two screens, like 100% on screen 1 and 150% on screen 2.

When you move a dialog between screens, Windows sends the WM_DPICHANGED to the dialog and it automatically resizes it based on the destination screen DPI value. When I say "resizes" I mean a well done resize (each CWnd scaled and font sizes changed) and not the bad/blurred bitmap resize done on non-DPI-aware applications.

Now comes the issue: if the dialog has a CResizer attached, the CResizer will undo the effect leaving the dialog controls with a wrong layout.

I've made a sample (based on your DialogResizing sample) to show it, together with two screenshots to show you what happens when dragging the same dialog from screen1 (100%) to screen2 (150%) with and without CResizer.

I've tried to "pause" CResizer before WM_DPICHANGED message and "resume" it after WM_SIZE completes, but it didn't work; I've also tried to forcibly re-add child controls to CResizer after WM_DPICHANGED, but it failed too...

Hope I've been clear enough to ease your work...

3 Attachments

Discussion

  • Claudio Nicora

    Claudio Nicora - 2021-03-04

    Additional note: the bug happens only when moving an already created dialog from one screen to another (with different DPI/Zoom).
    If the dialog is created directly on screen2 (150%) then its layout is correct; it could be because CResizer is initialized after windows have already resized the dialog upon creation.

     

    Last edit: Claudio Nicora 2021-03-04
  • David

    David - 2021-03-08
     
  • David

    David - 2021-03-08

    Hi Claudio,

    Unfortunately this is a 'feature' of the way dialogs are rendered on screens with different DPI scaling settings on Windows 10. They are re-arranged in ways that are impractical to predict by something like CResizer.

    Fortunately there is an easy work around. We can handle the WM_DPICHANGED message and recreate the dialog. This is the approach the MovieShow sample uses to allow the application to be moved between screens with different DPI screen scaling.

    The code used to handle the WM_DPICHANGED message is shown below. The dialog in question is the view window of a docker, so its parent is the DockClient.

    LRESULT CMainFrame::OnDPIChanged()
    {
        // Dialogs handle DPI changes rather badly. The easiest
        // approach is to destroy and recreate the dialog.
        m_pDockDialog->GetView().Destroy();
        m_pDockDialog->GetView().Create(m_pDockDialog->GetDockClient());
    
        // Re-select the list view item.
        int item = m_viewList.GetNextItem(-1, LVNI_SELECTED);
        m_viewList.SetItemState(item, 0, 0x000F);
        m_viewList.SetItemState(item, LVIS_FOCUSED | LVIS_SELECTED, 0x000F);
    
        return 0;
    }
    

    Best regards,
    David

     
  • Claudio Nicora

    Claudio Nicora - 2021-03-08

    Yes. I've seen that example but I'm not able to adapt it to my example.

    In the example I've attached (based on your DialogResizing sample), the dialog is a modal CDialog based window, without a View.

    // Constructor.
    CDialogApp::CDialogApp() : m_myDialog(IDD_DIALOG1)
    {
    }
    
    // Called when the application starts.
    BOOL CDialogApp::InitInstance()
    {
        //Display the modal dialog.
        m_myDialog.DoModal();   // throws a CWinException on failure.
    
        return TRUE;
    }
    

    Destroying it will terminate the app...

    I suppose there should be a way to call ::DialogBox() again, like CDialog.DoModal() does, am I right?

     
  • David

    David - 2021-03-09

    Hi Claudio

    For the DialogResizing sample, the application ends when the dialog is destroyed because OnDestroy calls PostQuitMessage.

    // Called when the dialog window is destroyed.
    void CMyDialog::OnDestroy()
    {
        // End the application
        ::PostQuitMessage(0);
    }
    

    You might like to consider making your dialog the view window of a frame, much like the FormDocView sample. The menu, toolbar, and statusbar are all optional, so the appearance of your application need not change. The definition of the dialog in resource.rc could remain largely unchanged, but the style would need to be modified to make the dialog a child window without a caption.

    The use of a frame would probably make things easier. Destroying and recreating the dialog while moving it between screens might prove rather problematic unless it is contained within another window such as a frame. You could even choose to keep the statusbar and do away with drawing the gripper in the dialog.

    Hope that helps,
    David

     
  • David

    David - 2021-03-15
    • status: open --> closed
     
  • David

    David - 2021-04-29
    • status: closed --> open
     
  • David

    David - 2021-04-29

    Hi Claudio,

    Try the attached minifest file in place of your current one.

    It utilises gdiScaling in place of PerMonitorV2-DPIAware. That should produce a much better result.

    Best regards,
    David

     
  • Claudio Nicora

    Claudio Nicora - 2021-04-30

    Hi David, thanks for it.
    Will check it out, but I suppose it'll be supported starting from some Win10 version (1809...?).

    Anyway, meanwhile, I've subclassed CDialog to properly manage WM_DPICHANGED messages.
    Once the message is received, I disable CResizer, let Windows properly resize the dialog (it only changes locations and sizes), then I scale fonts and reactivate CResizer with the new positions.
    It works well and I'm satisfied ;)

    I've also switched my graphics (icons, bitmaps, ...) to vectorial versions (EMF and SVG), but that's another story.

    Once completed I could send you a cleaned up sample of my CDpiAwareDialog if you want to have a look.

    Thanks again

     

    Last edit: Claudio Nicora 2021-04-30
  • David

    David - 2021-04-30

    Hi Claudio,

    The minimum version of the operating system that supports the gdiScaling element is Windows 10 version 1703.

    I'd be happy to have a look at your CDpiAwareDialog when it is ready.

    Best regards,
    David

     
  • David

    David - 2022-09-08
    • status: open --> closed
     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.